fix popover issue
[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      * equals
1021      * @param {Array} o The array to compare to
1022      * @returns {Boolean} true if the same
1023      */
1024     equals : function(b)
1025     {
1026         // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1027         if (this === b) {
1028             return true;
1029          }
1030         if (b == null) {
1031             return false;
1032         }
1033         if (this.length !== b.length) {
1034             return false;
1035         }
1036       
1037         // sort?? a.sort().equals(b.sort());
1038       
1039         for (var i = 0; i < this.length; ++i) {
1040             if (this[i] !== b[i]) {
1041                 return false;
1042             }
1043         }
1044         return true;
1045     }
1046 });
1047
1048
1049  
1050 /*
1051  * Based on:
1052  * Ext JS Library 1.1.1
1053  * Copyright(c) 2006-2007, Ext JS, LLC.
1054  *
1055  * Originally Released Under LGPL - original licence link has changed is not relivant.
1056  *
1057  * Fork - LGPL
1058  * <script type="text/javascript">
1059  */
1060
1061 /**
1062  * @class Date
1063  *
1064  * The date parsing and format syntax is a subset of
1065  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1066  * supported will provide results equivalent to their PHP versions.
1067  *
1068  * Following is the list of all currently supported formats:
1069  *<pre>
1070 Sample date:
1071 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1072
1073 Format  Output      Description
1074 ------  ----------  --------------------------------------------------------------
1075   d      10         Day of the month, 2 digits with leading zeros
1076   D      Wed        A textual representation of a day, three letters
1077   j      10         Day of the month without leading zeros
1078   l      Wednesday  A full textual representation of the day of the week
1079   S      th         English ordinal day of month suffix, 2 chars (use with j)
1080   w      3          Numeric representation of the day of the week
1081   z      9          The julian date, or day of the year (0-365)
1082   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1083   F      January    A full textual representation of the month
1084   m      01         Numeric representation of a month, with leading zeros
1085   M      Jan        Month name abbreviation, three letters
1086   n      1          Numeric representation of a month, without leading zeros
1087   t      31         Number of days in the given month
1088   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1089   Y      2007       A full numeric representation of a year, 4 digits
1090   y      07         A two digit representation of a year
1091   a      pm         Lowercase Ante meridiem and Post meridiem
1092   A      PM         Uppercase Ante meridiem and Post meridiem
1093   g      3          12-hour format of an hour without leading zeros
1094   G      15         24-hour format of an hour without leading zeros
1095   h      03         12-hour format of an hour with leading zeros
1096   H      15         24-hour format of an hour with leading zeros
1097   i      05         Minutes with leading zeros
1098   s      01         Seconds, with leading zeros
1099   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1100   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1101   T      CST        Timezone setting of the machine running the code
1102   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1103 </pre>
1104  *
1105  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1106  * <pre><code>
1107 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1108 document.write(dt.format('Y-m-d'));                         //2007-01-10
1109 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1110 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
1111  </code></pre>
1112  *
1113  * Here are some standard date/time patterns that you might find helpful.  They
1114  * are not part of the source of Date.js, but to use them you can simply copy this
1115  * block of code into any script that is included after Date.js and they will also become
1116  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1117  * <pre><code>
1118 Date.patterns = {
1119     ISO8601Long:"Y-m-d H:i:s",
1120     ISO8601Short:"Y-m-d",
1121     ShortDate: "n/j/Y",
1122     LongDate: "l, F d, Y",
1123     FullDateTime: "l, F d, Y g:i:s A",
1124     MonthDay: "F d",
1125     ShortTime: "g:i A",
1126     LongTime: "g:i:s A",
1127     SortableDateTime: "Y-m-d\\TH:i:s",
1128     UniversalSortableDateTime: "Y-m-d H:i:sO",
1129     YearMonth: "F, Y"
1130 };
1131 </code></pre>
1132  *
1133  * Example usage:
1134  * <pre><code>
1135 var dt = new Date();
1136 document.write(dt.format(Date.patterns.ShortDate));
1137  </code></pre>
1138  */
1139
1140 /*
1141  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1142  * They generate precompiled functions from date formats instead of parsing and
1143  * processing the pattern every time you format a date.  These functions are available
1144  * on every Date object (any javascript function).
1145  *
1146  * The original article and download are here:
1147  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1148  *
1149  */
1150  
1151  
1152  // was in core
1153 /**
1154  Returns the number of milliseconds between this date and date
1155  @param {Date} date (optional) Defaults to now
1156  @return {Number} The diff in milliseconds
1157  @member Date getElapsed
1158  */
1159 Date.prototype.getElapsed = function(date) {
1160         return Math.abs((date || new Date()).getTime()-this.getTime());
1161 };
1162 // was in date file..
1163
1164
1165 // private
1166 Date.parseFunctions = {count:0};
1167 // private
1168 Date.parseRegexes = [];
1169 // private
1170 Date.formatFunctions = {count:0};
1171
1172 // private
1173 Date.prototype.dateFormat = function(format) {
1174     if (Date.formatFunctions[format] == null) {
1175         Date.createNewFormat(format);
1176     }
1177     var func = Date.formatFunctions[format];
1178     return this[func]();
1179 };
1180
1181
1182 /**
1183  * Formats a date given the supplied format string
1184  * @param {String} format The format string
1185  * @return {String} The formatted date
1186  * @method
1187  */
1188 Date.prototype.format = Date.prototype.dateFormat;
1189
1190 // private
1191 Date.createNewFormat = function(format) {
1192     var funcName = "format" + Date.formatFunctions.count++;
1193     Date.formatFunctions[format] = funcName;
1194     var code = "Date.prototype." + funcName + " = function(){return ";
1195     var special = false;
1196     var ch = '';
1197     for (var i = 0; i < format.length; ++i) {
1198         ch = format.charAt(i);
1199         if (!special && ch == "\\") {
1200             special = true;
1201         }
1202         else if (special) {
1203             special = false;
1204             code += "'" + String.escape(ch) + "' + ";
1205         }
1206         else {
1207             code += Date.getFormatCode(ch);
1208         }
1209     }
1210     /** eval:var:zzzzzzzzzzzzz */
1211     eval(code.substring(0, code.length - 3) + ";}");
1212 };
1213
1214 // private
1215 Date.getFormatCode = function(character) {
1216     switch (character) {
1217     case "d":
1218         return "String.leftPad(this.getDate(), 2, '0') + ";
1219     case "D":
1220         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1221     case "j":
1222         return "this.getDate() + ";
1223     case "l":
1224         return "Date.dayNames[this.getDay()] + ";
1225     case "S":
1226         return "this.getSuffix() + ";
1227     case "w":
1228         return "this.getDay() + ";
1229     case "z":
1230         return "this.getDayOfYear() + ";
1231     case "W":
1232         return "this.getWeekOfYear() + ";
1233     case "F":
1234         return "Date.monthNames[this.getMonth()] + ";
1235     case "m":
1236         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1237     case "M":
1238         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1239     case "n":
1240         return "(this.getMonth() + 1) + ";
1241     case "t":
1242         return "this.getDaysInMonth() + ";
1243     case "L":
1244         return "(this.isLeapYear() ? 1 : 0) + ";
1245     case "Y":
1246         return "this.getFullYear() + ";
1247     case "y":
1248         return "('' + this.getFullYear()).substring(2, 4) + ";
1249     case "a":
1250         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1251     case "A":
1252         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1253     case "g":
1254         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1255     case "G":
1256         return "this.getHours() + ";
1257     case "h":
1258         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1259     case "H":
1260         return "String.leftPad(this.getHours(), 2, '0') + ";
1261     case "i":
1262         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1263     case "s":
1264         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1265     case "O":
1266         return "this.getGMTOffset() + ";
1267     case "P":
1268         return "this.getGMTColonOffset() + ";
1269     case "T":
1270         return "this.getTimezone() + ";
1271     case "Z":
1272         return "(this.getTimezoneOffset() * -60) + ";
1273     default:
1274         return "'" + String.escape(character) + "' + ";
1275     }
1276 };
1277
1278 /**
1279  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1280  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1281  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1282  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1283  * string or the parse operation will fail.
1284  * Example Usage:
1285 <pre><code>
1286 //dt = Fri May 25 2007 (current date)
1287 var dt = new Date();
1288
1289 //dt = Thu May 25 2006 (today's month/day in 2006)
1290 dt = Date.parseDate("2006", "Y");
1291
1292 //dt = Sun Jan 15 2006 (all date parts specified)
1293 dt = Date.parseDate("2006-1-15", "Y-m-d");
1294
1295 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1296 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1297 </code></pre>
1298  * @param {String} input The unparsed date as a string
1299  * @param {String} format The format the date is in
1300  * @return {Date} The parsed date
1301  * @static
1302  */
1303 Date.parseDate = function(input, format) {
1304     if (Date.parseFunctions[format] == null) {
1305         Date.createParser(format);
1306     }
1307     var func = Date.parseFunctions[format];
1308     return Date[func](input);
1309 };
1310 /**
1311  * @private
1312  */
1313
1314 Date.createParser = function(format) {
1315     var funcName = "parse" + Date.parseFunctions.count++;
1316     var regexNum = Date.parseRegexes.length;
1317     var currentGroup = 1;
1318     Date.parseFunctions[format] = funcName;
1319
1320     var code = "Date." + funcName + " = function(input){\n"
1321         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1322         + "var d = new Date();\n"
1323         + "y = d.getFullYear();\n"
1324         + "m = d.getMonth();\n"
1325         + "d = d.getDate();\n"
1326         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1327         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1328         + "if (results && results.length > 0) {";
1329     var regex = "";
1330
1331     var special = false;
1332     var ch = '';
1333     for (var i = 0; i < format.length; ++i) {
1334         ch = format.charAt(i);
1335         if (!special && ch == "\\") {
1336             special = true;
1337         }
1338         else if (special) {
1339             special = false;
1340             regex += String.escape(ch);
1341         }
1342         else {
1343             var obj = Date.formatCodeToRegex(ch, currentGroup);
1344             currentGroup += obj.g;
1345             regex += obj.s;
1346             if (obj.g && obj.c) {
1347                 code += obj.c;
1348             }
1349         }
1350     }
1351
1352     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1353         + "{v = new Date(y, m, d, h, i, s);}\n"
1354         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1355         + "{v = new Date(y, m, d, h, i);}\n"
1356         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1357         + "{v = new Date(y, m, d, h);}\n"
1358         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1359         + "{v = new Date(y, m, d);}\n"
1360         + "else if (y >= 0 && m >= 0)\n"
1361         + "{v = new Date(y, m);}\n"
1362         + "else if (y >= 0)\n"
1363         + "{v = new Date(y);}\n"
1364         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1365         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1366         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1367         + ";}";
1368
1369     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1370     /** eval:var:zzzzzzzzzzzzz */
1371     eval(code);
1372 };
1373
1374 // private
1375 Date.formatCodeToRegex = function(character, currentGroup) {
1376     switch (character) {
1377     case "D":
1378         return {g:0,
1379         c:null,
1380         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1381     case "j":
1382         return {g:1,
1383             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1384             s:"(\\d{1,2})"}; // day of month without leading zeroes
1385     case "d":
1386         return {g:1,
1387             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1388             s:"(\\d{2})"}; // day of month with leading zeroes
1389     case "l":
1390         return {g:0,
1391             c:null,
1392             s:"(?:" + Date.dayNames.join("|") + ")"};
1393     case "S":
1394         return {g:0,
1395             c:null,
1396             s:"(?:st|nd|rd|th)"};
1397     case "w":
1398         return {g:0,
1399             c:null,
1400             s:"\\d"};
1401     case "z":
1402         return {g:0,
1403             c:null,
1404             s:"(?:\\d{1,3})"};
1405     case "W":
1406         return {g:0,
1407             c:null,
1408             s:"(?:\\d{2})"};
1409     case "F":
1410         return {g:1,
1411             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1412             s:"(" + Date.monthNames.join("|") + ")"};
1413     case "M":
1414         return {g:1,
1415             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1416             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1417     case "n":
1418         return {g:1,
1419             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1420             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1421     case "m":
1422         return {g:1,
1423             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1424             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1425     case "t":
1426         return {g:0,
1427             c:null,
1428             s:"\\d{1,2}"};
1429     case "L":
1430         return {g:0,
1431             c:null,
1432             s:"(?:1|0)"};
1433     case "Y":
1434         return {g:1,
1435             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1436             s:"(\\d{4})"};
1437     case "y":
1438         return {g:1,
1439             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1440                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1441             s:"(\\d{1,2})"};
1442     case "a":
1443         return {g:1,
1444             c:"if (results[" + currentGroup + "] == 'am') {\n"
1445                 + "if (h == 12) { h = 0; }\n"
1446                 + "} else { if (h < 12) { h += 12; }}",
1447             s:"(am|pm)"};
1448     case "A":
1449         return {g:1,
1450             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1451                 + "if (h == 12) { h = 0; }\n"
1452                 + "} else { if (h < 12) { h += 12; }}",
1453             s:"(AM|PM)"};
1454     case "g":
1455     case "G":
1456         return {g:1,
1457             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1458             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1459     case "h":
1460     case "H":
1461         return {g:1,
1462             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1463             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1464     case "i":
1465         return {g:1,
1466             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1467             s:"(\\d{2})"};
1468     case "s":
1469         return {g:1,
1470             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1471             s:"(\\d{2})"};
1472     case "O":
1473         return {g:1,
1474             c:[
1475                 "o = results[", currentGroup, "];\n",
1476                 "var sn = o.substring(0,1);\n", // get + / - sign
1477                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1478                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1479                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1480                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1481             ].join(""),
1482             s:"([+\-]\\d{2,4})"};
1483     
1484     
1485     case "P":
1486         return {g:1,
1487                 c:[
1488                    "o = results[", currentGroup, "];\n",
1489                    "var sn = o.substring(0,1);\n",
1490                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1491                    "var mn = o.substring(4,6) % 60;\n",
1492                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1493                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1494             ].join(""),
1495             s:"([+\-]\\d{4})"};
1496     case "T":
1497         return {g:0,
1498             c:null,
1499             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1500     case "Z":
1501         return {g:1,
1502             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1503                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1504             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1505     default:
1506         return {g:0,
1507             c:null,
1508             s:String.escape(character)};
1509     }
1510 };
1511
1512 /**
1513  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1514  * @return {String} The abbreviated timezone name (e.g. 'CST')
1515  */
1516 Date.prototype.getTimezone = function() {
1517     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1518 };
1519
1520 /**
1521  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1522  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1523  */
1524 Date.prototype.getGMTOffset = function() {
1525     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1526         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1527         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1528 };
1529
1530 /**
1531  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1532  * @return {String} 2-characters representing hours and 2-characters representing minutes
1533  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1534  */
1535 Date.prototype.getGMTColonOffset = function() {
1536         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1537                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1538                 + ":"
1539                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1540 }
1541
1542 /**
1543  * Get the numeric day number of the year, adjusted for leap year.
1544  * @return {Number} 0 through 364 (365 in leap years)
1545  */
1546 Date.prototype.getDayOfYear = function() {
1547     var num = 0;
1548     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1549     for (var i = 0; i < this.getMonth(); ++i) {
1550         num += Date.daysInMonth[i];
1551     }
1552     return num + this.getDate() - 1;
1553 };
1554
1555 /**
1556  * Get the string representation of the numeric week number of the year
1557  * (equivalent to the format specifier 'W').
1558  * @return {String} '00' through '52'
1559  */
1560 Date.prototype.getWeekOfYear = function() {
1561     // Skip to Thursday of this week
1562     var now = this.getDayOfYear() + (4 - this.getDay());
1563     // Find the first Thursday of the year
1564     var jan1 = new Date(this.getFullYear(), 0, 1);
1565     var then = (7 - jan1.getDay() + 4);
1566     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1567 };
1568
1569 /**
1570  * Whether or not the current date is in a leap year.
1571  * @return {Boolean} True if the current date is in a leap year, else false
1572  */
1573 Date.prototype.isLeapYear = function() {
1574     var year = this.getFullYear();
1575     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1576 };
1577
1578 /**
1579  * Get the first day of the current month, adjusted for leap year.  The returned value
1580  * is the numeric day index within the week (0-6) which can be used in conjunction with
1581  * the {@link #monthNames} array to retrieve the textual day name.
1582  * Example:
1583  *<pre><code>
1584 var dt = new Date('1/10/2007');
1585 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1586 </code></pre>
1587  * @return {Number} The day number (0-6)
1588  */
1589 Date.prototype.getFirstDayOfMonth = function() {
1590     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1591     return (day < 0) ? (day + 7) : day;
1592 };
1593
1594 /**
1595  * Get the last day of the current month, adjusted for leap year.  The returned value
1596  * is the numeric day index within the week (0-6) which can be used in conjunction with
1597  * the {@link #monthNames} array to retrieve the textual day name.
1598  * Example:
1599  *<pre><code>
1600 var dt = new Date('1/10/2007');
1601 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1602 </code></pre>
1603  * @return {Number} The day number (0-6)
1604  */
1605 Date.prototype.getLastDayOfMonth = function() {
1606     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1607     return (day < 0) ? (day + 7) : day;
1608 };
1609
1610
1611 /**
1612  * Get the first date of this date's month
1613  * @return {Date}
1614  */
1615 Date.prototype.getFirstDateOfMonth = function() {
1616     return new Date(this.getFullYear(), this.getMonth(), 1);
1617 };
1618
1619 /**
1620  * Get the last date of this date's month
1621  * @return {Date}
1622  */
1623 Date.prototype.getLastDateOfMonth = function() {
1624     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1625 };
1626 /**
1627  * Get the number of days in the current month, adjusted for leap year.
1628  * @return {Number} The number of days in the month
1629  */
1630 Date.prototype.getDaysInMonth = function() {
1631     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1632     return Date.daysInMonth[this.getMonth()];
1633 };
1634
1635 /**
1636  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1637  * @return {String} 'st, 'nd', 'rd' or 'th'
1638  */
1639 Date.prototype.getSuffix = function() {
1640     switch (this.getDate()) {
1641         case 1:
1642         case 21:
1643         case 31:
1644             return "st";
1645         case 2:
1646         case 22:
1647             return "nd";
1648         case 3:
1649         case 23:
1650             return "rd";
1651         default:
1652             return "th";
1653     }
1654 };
1655
1656 // private
1657 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1658
1659 /**
1660  * An array of textual month names.
1661  * Override these values for international dates, for example...
1662  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1663  * @type Array
1664  * @static
1665  */
1666 Date.monthNames =
1667    ["January",
1668     "February",
1669     "March",
1670     "April",
1671     "May",
1672     "June",
1673     "July",
1674     "August",
1675     "September",
1676     "October",
1677     "November",
1678     "December"];
1679
1680 /**
1681  * An array of textual day names.
1682  * Override these values for international dates, for example...
1683  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1684  * @type Array
1685  * @static
1686  */
1687 Date.dayNames =
1688    ["Sunday",
1689     "Monday",
1690     "Tuesday",
1691     "Wednesday",
1692     "Thursday",
1693     "Friday",
1694     "Saturday"];
1695
1696 // private
1697 Date.y2kYear = 50;
1698 // private
1699 Date.monthNumbers = {
1700     Jan:0,
1701     Feb:1,
1702     Mar:2,
1703     Apr:3,
1704     May:4,
1705     Jun:5,
1706     Jul:6,
1707     Aug:7,
1708     Sep:8,
1709     Oct:9,
1710     Nov:10,
1711     Dec:11};
1712
1713 /**
1714  * Creates and returns a new Date instance with the exact same date value as the called instance.
1715  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1716  * variable will also be changed.  When the intention is to create a new variable that will not
1717  * modify the original instance, you should create a clone.
1718  *
1719  * Example of correctly cloning a date:
1720  * <pre><code>
1721 //wrong way:
1722 var orig = new Date('10/1/2006');
1723 var copy = orig;
1724 copy.setDate(5);
1725 document.write(orig);  //returns 'Thu Oct 05 2006'!
1726
1727 //correct way:
1728 var orig = new Date('10/1/2006');
1729 var copy = orig.clone();
1730 copy.setDate(5);
1731 document.write(orig);  //returns 'Thu Oct 01 2006'
1732 </code></pre>
1733  * @return {Date} The new Date instance
1734  */
1735 Date.prototype.clone = function() {
1736         return new Date(this.getTime());
1737 };
1738
1739 /**
1740  * Clears any time information from this date
1741  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1742  @return {Date} this or the clone
1743  */
1744 Date.prototype.clearTime = function(clone){
1745     if(clone){
1746         return this.clone().clearTime();
1747     }
1748     this.setHours(0);
1749     this.setMinutes(0);
1750     this.setSeconds(0);
1751     this.setMilliseconds(0);
1752     return this;
1753 };
1754
1755 // private
1756 // safari setMonth is broken -- check that this is only donw once...
1757 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1758     Date.brokenSetMonth = Date.prototype.setMonth;
1759         Date.prototype.setMonth = function(num){
1760                 if(num <= -1){
1761                         var n = Math.ceil(-num);
1762                         var back_year = Math.ceil(n/12);
1763                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1764                         this.setFullYear(this.getFullYear() - back_year);
1765                         return Date.brokenSetMonth.call(this, month);
1766                 } else {
1767                         return Date.brokenSetMonth.apply(this, arguments);
1768                 }
1769         };
1770 }
1771
1772 /** Date interval constant 
1773 * @static 
1774 * @type String */
1775 Date.MILLI = "ms";
1776 /** Date interval constant 
1777 * @static 
1778 * @type String */
1779 Date.SECOND = "s";
1780 /** Date interval constant 
1781 * @static 
1782 * @type String */
1783 Date.MINUTE = "mi";
1784 /** Date interval constant 
1785 * @static 
1786 * @type String */
1787 Date.HOUR = "h";
1788 /** Date interval constant 
1789 * @static 
1790 * @type String */
1791 Date.DAY = "d";
1792 /** Date interval constant 
1793 * @static 
1794 * @type String */
1795 Date.MONTH = "mo";
1796 /** Date interval constant 
1797 * @static 
1798 * @type String */
1799 Date.YEAR = "y";
1800
1801 /**
1802  * Provides a convenient method of performing basic date arithmetic.  This method
1803  * does not modify the Date instance being called - it creates and returns
1804  * a new Date instance containing the resulting date value.
1805  *
1806  * Examples:
1807  * <pre><code>
1808 //Basic usage:
1809 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1810 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1811
1812 //Negative values will subtract correctly:
1813 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1814 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1815
1816 //You can even chain several calls together in one line!
1817 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1818 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1819  </code></pre>
1820  *
1821  * @param {String} interval   A valid date interval enum value
1822  * @param {Number} value      The amount to add to the current date
1823  * @return {Date} The new Date instance
1824  */
1825 Date.prototype.add = function(interval, value){
1826   var d = this.clone();
1827   if (!interval || value === 0) { return d; }
1828   switch(interval.toLowerCase()){
1829     case Date.MILLI:
1830       d.setMilliseconds(this.getMilliseconds() + value);
1831       break;
1832     case Date.SECOND:
1833       d.setSeconds(this.getSeconds() + value);
1834       break;
1835     case Date.MINUTE:
1836       d.setMinutes(this.getMinutes() + value);
1837       break;
1838     case Date.HOUR:
1839       d.setHours(this.getHours() + value);
1840       break;
1841     case Date.DAY:
1842       d.setDate(this.getDate() + value);
1843       break;
1844     case Date.MONTH:
1845       var day = this.getDate();
1846       if(day > 28){
1847           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1848       }
1849       d.setDate(day);
1850       d.setMonth(this.getMonth() + value);
1851       break;
1852     case Date.YEAR:
1853       d.setFullYear(this.getFullYear() + value);
1854       break;
1855   }
1856   return d;
1857 };
1858 /*
1859  * Based on:
1860  * Ext JS Library 1.1.1
1861  * Copyright(c) 2006-2007, Ext JS, LLC.
1862  *
1863  * Originally Released Under LGPL - original licence link has changed is not relivant.
1864  *
1865  * Fork - LGPL
1866  * <script type="text/javascript">
1867  */
1868
1869 /**
1870  * @class Roo.lib.Dom
1871  * @static
1872  * 
1873  * Dom utils (from YIU afaik)
1874  * 
1875  **/
1876 Roo.lib.Dom = {
1877     /**
1878      * Get the view width
1879      * @param {Boolean} full True will get the full document, otherwise it's the view width
1880      * @return {Number} The width
1881      */
1882      
1883     getViewWidth : function(full) {
1884         return full ? this.getDocumentWidth() : this.getViewportWidth();
1885     },
1886     /**
1887      * Get the view height
1888      * @param {Boolean} full True will get the full document, otherwise it's the view height
1889      * @return {Number} The height
1890      */
1891     getViewHeight : function(full) {
1892         return full ? this.getDocumentHeight() : this.getViewportHeight();
1893     },
1894
1895     getDocumentHeight: function() {
1896         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1897         return Math.max(scrollHeight, this.getViewportHeight());
1898     },
1899
1900     getDocumentWidth: function() {
1901         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1902         return Math.max(scrollWidth, this.getViewportWidth());
1903     },
1904
1905     getViewportHeight: function() {
1906         var height = self.innerHeight;
1907         var mode = document.compatMode;
1908
1909         if ((mode || Roo.isIE) && !Roo.isOpera) {
1910             height = (mode == "CSS1Compat") ?
1911                      document.documentElement.clientHeight :
1912                      document.body.clientHeight;
1913         }
1914
1915         return height;
1916     },
1917
1918     getViewportWidth: function() {
1919         var width = self.innerWidth;
1920         var mode = document.compatMode;
1921
1922         if (mode || Roo.isIE) {
1923             width = (mode == "CSS1Compat") ?
1924                     document.documentElement.clientWidth :
1925                     document.body.clientWidth;
1926         }
1927         return width;
1928     },
1929
1930     isAncestor : function(p, c) {
1931         p = Roo.getDom(p);
1932         c = Roo.getDom(c);
1933         if (!p || !c) {
1934             return false;
1935         }
1936
1937         if (p.contains && !Roo.isSafari) {
1938             return p.contains(c);
1939         } else if (p.compareDocumentPosition) {
1940             return !!(p.compareDocumentPosition(c) & 16);
1941         } else {
1942             var parent = c.parentNode;
1943             while (parent) {
1944                 if (parent == p) {
1945                     return true;
1946                 }
1947                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1948                     return false;
1949                 }
1950                 parent = parent.parentNode;
1951             }
1952             return false;
1953         }
1954     },
1955
1956     getRegion : function(el) {
1957         return Roo.lib.Region.getRegion(el);
1958     },
1959
1960     getY : function(el) {
1961         return this.getXY(el)[1];
1962     },
1963
1964     getX : function(el) {
1965         return this.getXY(el)[0];
1966     },
1967
1968     getXY : function(el) {
1969         var p, pe, b, scroll, bd = document.body;
1970         el = Roo.getDom(el);
1971         var fly = Roo.lib.AnimBase.fly;
1972         if (el.getBoundingClientRect) {
1973             b = el.getBoundingClientRect();
1974             scroll = fly(document).getScroll();
1975             return [b.left + scroll.left, b.top + scroll.top];
1976         }
1977         var x = 0, y = 0;
1978
1979         p = el;
1980
1981         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1982
1983         while (p) {
1984
1985             x += p.offsetLeft;
1986             y += p.offsetTop;
1987
1988             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1989                 hasAbsolute = true;
1990             }
1991
1992             if (Roo.isGecko) {
1993                 pe = fly(p);
1994
1995                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1996                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1997
1998
1999                 x += bl;
2000                 y += bt;
2001
2002
2003                 if (p != el && pe.getStyle('overflow') != 'visible') {
2004                     x += bl;
2005                     y += bt;
2006                 }
2007             }
2008             p = p.offsetParent;
2009         }
2010
2011         if (Roo.isSafari && hasAbsolute) {
2012             x -= bd.offsetLeft;
2013             y -= bd.offsetTop;
2014         }
2015
2016         if (Roo.isGecko && !hasAbsolute) {
2017             var dbd = fly(bd);
2018             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2019             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2020         }
2021
2022         p = el.parentNode;
2023         while (p && p != bd) {
2024             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2025                 x -= p.scrollLeft;
2026                 y -= p.scrollTop;
2027             }
2028             p = p.parentNode;
2029         }
2030         return [x, y];
2031     },
2032  
2033   
2034
2035
2036     setXY : function(el, xy) {
2037         el = Roo.fly(el, '_setXY');
2038         el.position();
2039         var pts = el.translatePoints(xy);
2040         if (xy[0] !== false) {
2041             el.dom.style.left = pts.left + "px";
2042         }
2043         if (xy[1] !== false) {
2044             el.dom.style.top = pts.top + "px";
2045         }
2046     },
2047
2048     setX : function(el, x) {
2049         this.setXY(el, [x, false]);
2050     },
2051
2052     setY : function(el, y) {
2053         this.setXY(el, [false, y]);
2054     }
2055 };
2056 /*
2057  * Portions of this file are based on pieces of Yahoo User Interface Library
2058  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2059  * YUI licensed under the BSD License:
2060  * http://developer.yahoo.net/yui/license.txt
2061  * <script type="text/javascript">
2062  *
2063  */
2064
2065 Roo.lib.Event = function() {
2066     var loadComplete = false;
2067     var listeners = [];
2068     var unloadListeners = [];
2069     var retryCount = 0;
2070     var onAvailStack = [];
2071     var counter = 0;
2072     var lastError = null;
2073
2074     return {
2075         POLL_RETRYS: 200,
2076         POLL_INTERVAL: 20,
2077         EL: 0,
2078         TYPE: 1,
2079         FN: 2,
2080         WFN: 3,
2081         OBJ: 3,
2082         ADJ_SCOPE: 4,
2083         _interval: null,
2084
2085         startInterval: function() {
2086             if (!this._interval) {
2087                 var self = this;
2088                 var callback = function() {
2089                     self._tryPreloadAttach();
2090                 };
2091                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2092
2093             }
2094         },
2095
2096         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2097             onAvailStack.push({ id:         p_id,
2098                 fn:         p_fn,
2099                 obj:        p_obj,
2100                 override:   p_override,
2101                 checkReady: false    });
2102
2103             retryCount = this.POLL_RETRYS;
2104             this.startInterval();
2105         },
2106
2107
2108         addListener: function(el, eventName, fn) {
2109             el = Roo.getDom(el);
2110             if (!el || !fn) {
2111                 return false;
2112             }
2113
2114             if ("unload" == eventName) {
2115                 unloadListeners[unloadListeners.length] =
2116                 [el, eventName, fn];
2117                 return true;
2118             }
2119
2120             var wrappedFn = function(e) {
2121                 return fn(Roo.lib.Event.getEvent(e));
2122             };
2123
2124             var li = [el, eventName, fn, wrappedFn];
2125
2126             var index = listeners.length;
2127             listeners[index] = li;
2128
2129             this.doAdd(el, eventName, wrappedFn, false);
2130             return true;
2131
2132         },
2133
2134
2135         removeListener: function(el, eventName, fn) {
2136             var i, len;
2137
2138             el = Roo.getDom(el);
2139
2140             if(!fn) {
2141                 return this.purgeElement(el, false, eventName);
2142             }
2143
2144
2145             if ("unload" == eventName) {
2146
2147                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2148                     var li = unloadListeners[i];
2149                     if (li &&
2150                         li[0] == el &&
2151                         li[1] == eventName &&
2152                         li[2] == fn) {
2153                         unloadListeners.splice(i, 1);
2154                         return true;
2155                     }
2156                 }
2157
2158                 return false;
2159             }
2160
2161             var cacheItem = null;
2162
2163
2164             var index = arguments[3];
2165
2166             if ("undefined" == typeof index) {
2167                 index = this._getCacheIndex(el, eventName, fn);
2168             }
2169
2170             if (index >= 0) {
2171                 cacheItem = listeners[index];
2172             }
2173
2174             if (!el || !cacheItem) {
2175                 return false;
2176             }
2177
2178             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2179
2180             delete listeners[index][this.WFN];
2181             delete listeners[index][this.FN];
2182             listeners.splice(index, 1);
2183
2184             return true;
2185
2186         },
2187
2188
2189         getTarget: function(ev, resolveTextNode) {
2190             ev = ev.browserEvent || ev;
2191             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2192             var t = ev.target || ev.srcElement;
2193             return this.resolveTextNode(t);
2194         },
2195
2196
2197         resolveTextNode: function(node) {
2198             if (Roo.isSafari && node && 3 == node.nodeType) {
2199                 return node.parentNode;
2200             } else {
2201                 return node;
2202             }
2203         },
2204
2205
2206         getPageX: function(ev) {
2207             ev = ev.browserEvent || ev;
2208             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2209             var x = ev.pageX;
2210             if (!x && 0 !== x) {
2211                 x = ev.clientX || 0;
2212
2213                 if (Roo.isIE) {
2214                     x += this.getScroll()[1];
2215                 }
2216             }
2217
2218             return x;
2219         },
2220
2221
2222         getPageY: function(ev) {
2223             ev = ev.browserEvent || ev;
2224             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2225             var y = ev.pageY;
2226             if (!y && 0 !== y) {
2227                 y = ev.clientY || 0;
2228
2229                 if (Roo.isIE) {
2230                     y += this.getScroll()[0];
2231                 }
2232             }
2233
2234
2235             return y;
2236         },
2237
2238
2239         getXY: function(ev) {
2240             ev = ev.browserEvent || ev;
2241             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2242             return [this.getPageX(ev), this.getPageY(ev)];
2243         },
2244
2245
2246         getRelatedTarget: function(ev) {
2247             ev = ev.browserEvent || ev;
2248             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2249             var t = ev.relatedTarget;
2250             if (!t) {
2251                 if (ev.type == "mouseout") {
2252                     t = ev.toElement;
2253                 } else if (ev.type == "mouseover") {
2254                     t = ev.fromElement;
2255                 }
2256             }
2257
2258             return this.resolveTextNode(t);
2259         },
2260
2261
2262         getTime: function(ev) {
2263             ev = ev.browserEvent || ev;
2264             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2265             if (!ev.time) {
2266                 var t = new Date().getTime();
2267                 try {
2268                     ev.time = t;
2269                 } catch(ex) {
2270                     this.lastError = ex;
2271                     return t;
2272                 }
2273             }
2274
2275             return ev.time;
2276         },
2277
2278
2279         stopEvent: function(ev) {
2280             this.stopPropagation(ev);
2281             this.preventDefault(ev);
2282         },
2283
2284
2285         stopPropagation: function(ev) {
2286             ev = ev.browserEvent || ev;
2287             if (ev.stopPropagation) {
2288                 ev.stopPropagation();
2289             } else {
2290                 ev.cancelBubble = true;
2291             }
2292         },
2293
2294
2295         preventDefault: function(ev) {
2296             ev = ev.browserEvent || ev;
2297             if(ev.preventDefault) {
2298                 ev.preventDefault();
2299             } else {
2300                 ev.returnValue = false;
2301             }
2302         },
2303
2304
2305         getEvent: function(e) {
2306             var ev = e || window.event;
2307             if (!ev) {
2308                 var c = this.getEvent.caller;
2309                 while (c) {
2310                     ev = c.arguments[0];
2311                     if (ev && Event == ev.constructor) {
2312                         break;
2313                     }
2314                     c = c.caller;
2315                 }
2316             }
2317             return ev;
2318         },
2319
2320
2321         getCharCode: function(ev) {
2322             ev = ev.browserEvent || ev;
2323             return ev.charCode || ev.keyCode || 0;
2324         },
2325
2326
2327         _getCacheIndex: function(el, eventName, fn) {
2328             for (var i = 0,len = listeners.length; i < len; ++i) {
2329                 var li = listeners[i];
2330                 if (li &&
2331                     li[this.FN] == fn &&
2332                     li[this.EL] == el &&
2333                     li[this.TYPE] == eventName) {
2334                     return i;
2335                 }
2336             }
2337
2338             return -1;
2339         },
2340
2341
2342         elCache: {},
2343
2344
2345         getEl: function(id) {
2346             return document.getElementById(id);
2347         },
2348
2349
2350         clearCache: function() {
2351         },
2352
2353
2354         _load: function(e) {
2355             loadComplete = true;
2356             var EU = Roo.lib.Event;
2357
2358
2359             if (Roo.isIE) {
2360                 EU.doRemove(window, "load", EU._load);
2361             }
2362         },
2363
2364
2365         _tryPreloadAttach: function() {
2366
2367             if (this.locked) {
2368                 return false;
2369             }
2370
2371             this.locked = true;
2372
2373
2374             var tryAgain = !loadComplete;
2375             if (!tryAgain) {
2376                 tryAgain = (retryCount > 0);
2377             }
2378
2379
2380             var notAvail = [];
2381             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2382                 var item = onAvailStack[i];
2383                 if (item) {
2384                     var el = this.getEl(item.id);
2385
2386                     if (el) {
2387                         if (!item.checkReady ||
2388                             loadComplete ||
2389                             el.nextSibling ||
2390                             (document && document.body)) {
2391
2392                             var scope = el;
2393                             if (item.override) {
2394                                 if (item.override === true) {
2395                                     scope = item.obj;
2396                                 } else {
2397                                     scope = item.override;
2398                                 }
2399                             }
2400                             item.fn.call(scope, item.obj);
2401                             onAvailStack[i] = null;
2402                         }
2403                     } else {
2404                         notAvail.push(item);
2405                     }
2406                 }
2407             }
2408
2409             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2410
2411             if (tryAgain) {
2412
2413                 this.startInterval();
2414             } else {
2415                 clearInterval(this._interval);
2416                 this._interval = null;
2417             }
2418
2419             this.locked = false;
2420
2421             return true;
2422
2423         },
2424
2425
2426         purgeElement: function(el, recurse, eventName) {
2427             var elListeners = this.getListeners(el, eventName);
2428             if (elListeners) {
2429                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2430                     var l = elListeners[i];
2431                     this.removeListener(el, l.type, l.fn);
2432                 }
2433             }
2434
2435             if (recurse && el && el.childNodes) {
2436                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2437                     this.purgeElement(el.childNodes[i], recurse, eventName);
2438                 }
2439             }
2440         },
2441
2442
2443         getListeners: function(el, eventName) {
2444             var results = [], searchLists;
2445             if (!eventName) {
2446                 searchLists = [listeners, unloadListeners];
2447             } else if (eventName == "unload") {
2448                 searchLists = [unloadListeners];
2449             } else {
2450                 searchLists = [listeners];
2451             }
2452
2453             for (var j = 0; j < searchLists.length; ++j) {
2454                 var searchList = searchLists[j];
2455                 if (searchList && searchList.length > 0) {
2456                     for (var i = 0,len = searchList.length; i < len; ++i) {
2457                         var l = searchList[i];
2458                         if (l && l[this.EL] === el &&
2459                             (!eventName || eventName === l[this.TYPE])) {
2460                             results.push({
2461                                 type:   l[this.TYPE],
2462                                 fn:     l[this.FN],
2463                                 obj:    l[this.OBJ],
2464                                 adjust: l[this.ADJ_SCOPE],
2465                                 index:  i
2466                             });
2467                         }
2468                     }
2469                 }
2470             }
2471
2472             return (results.length) ? results : null;
2473         },
2474
2475
2476         _unload: function(e) {
2477
2478             var EU = Roo.lib.Event, i, j, l, len, index;
2479
2480             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2481                 l = unloadListeners[i];
2482                 if (l) {
2483                     var scope = window;
2484                     if (l[EU.ADJ_SCOPE]) {
2485                         if (l[EU.ADJ_SCOPE] === true) {
2486                             scope = l[EU.OBJ];
2487                         } else {
2488                             scope = l[EU.ADJ_SCOPE];
2489                         }
2490                     }
2491                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2492                     unloadListeners[i] = null;
2493                     l = null;
2494                     scope = null;
2495                 }
2496             }
2497
2498             unloadListeners = null;
2499
2500             if (listeners && listeners.length > 0) {
2501                 j = listeners.length;
2502                 while (j) {
2503                     index = j - 1;
2504                     l = listeners[index];
2505                     if (l) {
2506                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2507                                 l[EU.FN], index);
2508                     }
2509                     j = j - 1;
2510                 }
2511                 l = null;
2512
2513                 EU.clearCache();
2514             }
2515
2516             EU.doRemove(window, "unload", EU._unload);
2517
2518         },
2519
2520
2521         getScroll: function() {
2522             var dd = document.documentElement, db = document.body;
2523             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2524                 return [dd.scrollTop, dd.scrollLeft];
2525             } else if (db) {
2526                 return [db.scrollTop, db.scrollLeft];
2527             } else {
2528                 return [0, 0];
2529             }
2530         },
2531
2532
2533         doAdd: function () {
2534             if (window.addEventListener) {
2535                 return function(el, eventName, fn, capture) {
2536                     el.addEventListener(eventName, fn, (capture));
2537                 };
2538             } else if (window.attachEvent) {
2539                 return function(el, eventName, fn, capture) {
2540                     el.attachEvent("on" + eventName, fn);
2541                 };
2542             } else {
2543                 return function() {
2544                 };
2545             }
2546         }(),
2547
2548
2549         doRemove: function() {
2550             if (window.removeEventListener) {
2551                 return function (el, eventName, fn, capture) {
2552                     el.removeEventListener(eventName, fn, (capture));
2553                 };
2554             } else if (window.detachEvent) {
2555                 return function (el, eventName, fn) {
2556                     el.detachEvent("on" + eventName, fn);
2557                 };
2558             } else {
2559                 return function() {
2560                 };
2561             }
2562         }()
2563     };
2564     
2565 }();
2566 (function() {     
2567    
2568     var E = Roo.lib.Event;
2569     E.on = E.addListener;
2570     E.un = E.removeListener;
2571
2572     if (document && document.body) {
2573         E._load();
2574     } else {
2575         E.doAdd(window, "load", E._load);
2576     }
2577     E.doAdd(window, "unload", E._unload);
2578     E._tryPreloadAttach();
2579 })();
2580
2581 /*
2582  * Portions of this file are based on pieces of Yahoo User Interface Library
2583  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2584  * YUI licensed under the BSD License:
2585  * http://developer.yahoo.net/yui/license.txt
2586  * <script type="text/javascript">
2587  *
2588  */
2589
2590 (function() {
2591     /**
2592      * @class Roo.lib.Ajax
2593      *
2594      */
2595     Roo.lib.Ajax = {
2596         /**
2597          * @static 
2598          */
2599         request : function(method, uri, cb, data, options) {
2600             if(options){
2601                 var hs = options.headers;
2602                 if(hs){
2603                     for(var h in hs){
2604                         if(hs.hasOwnProperty(h)){
2605                             this.initHeader(h, hs[h], false);
2606                         }
2607                     }
2608                 }
2609                 if(options.xmlData){
2610                     this.initHeader('Content-Type', 'text/xml', false);
2611                     method = 'POST';
2612                     data = options.xmlData;
2613                 }
2614             }
2615
2616             return this.asyncRequest(method, uri, cb, data);
2617         },
2618
2619         serializeForm : function(form) {
2620             if(typeof form == 'string') {
2621                 form = (document.getElementById(form) || document.forms[form]);
2622             }
2623
2624             var el, name, val, disabled, data = '', hasSubmit = false;
2625             for (var i = 0; i < form.elements.length; i++) {
2626                 el = form.elements[i];
2627                 disabled = form.elements[i].disabled;
2628                 name = form.elements[i].name;
2629                 val = form.elements[i].value;
2630
2631                 if (!disabled && name){
2632                     switch (el.type)
2633                             {
2634                         case 'select-one':
2635                         case 'select-multiple':
2636                             for (var j = 0; j < el.options.length; j++) {
2637                                 if (el.options[j].selected) {
2638                                     if (Roo.isIE) {
2639                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2640                                     }
2641                                     else {
2642                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2643                                     }
2644                                 }
2645                             }
2646                             break;
2647                         case 'radio':
2648                         case 'checkbox':
2649                             if (el.checked) {
2650                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2651                             }
2652                             break;
2653                         case 'file':
2654
2655                         case undefined:
2656
2657                         case 'reset':
2658
2659                         case 'button':
2660
2661                             break;
2662                         case 'submit':
2663                             if(hasSubmit == false) {
2664                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2665                                 hasSubmit = true;
2666                             }
2667                             break;
2668                         default:
2669                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2670                             break;
2671                     }
2672                 }
2673             }
2674             data = data.substr(0, data.length - 1);
2675             return data;
2676         },
2677
2678         headers:{},
2679
2680         hasHeaders:false,
2681
2682         useDefaultHeader:true,
2683
2684         defaultPostHeader:'application/x-www-form-urlencoded',
2685
2686         useDefaultXhrHeader:true,
2687
2688         defaultXhrHeader:'XMLHttpRequest',
2689
2690         hasDefaultHeaders:true,
2691
2692         defaultHeaders:{},
2693
2694         poll:{},
2695
2696         timeout:{},
2697
2698         pollInterval:50,
2699
2700         transactionId:0,
2701
2702         setProgId:function(id)
2703         {
2704             this.activeX.unshift(id);
2705         },
2706
2707         setDefaultPostHeader:function(b)
2708         {
2709             this.useDefaultHeader = b;
2710         },
2711
2712         setDefaultXhrHeader:function(b)
2713         {
2714             this.useDefaultXhrHeader = b;
2715         },
2716
2717         setPollingInterval:function(i)
2718         {
2719             if (typeof i == 'number' && isFinite(i)) {
2720                 this.pollInterval = i;
2721             }
2722         },
2723
2724         createXhrObject:function(transactionId)
2725         {
2726             var obj,http;
2727             try
2728             {
2729
2730                 http = new XMLHttpRequest();
2731
2732                 obj = { conn:http, tId:transactionId };
2733             }
2734             catch(e)
2735             {
2736                 for (var i = 0; i < this.activeX.length; ++i) {
2737                     try
2738                     {
2739
2740                         http = new ActiveXObject(this.activeX[i]);
2741
2742                         obj = { conn:http, tId:transactionId };
2743                         break;
2744                     }
2745                     catch(e) {
2746                     }
2747                 }
2748             }
2749             finally
2750             {
2751                 return obj;
2752             }
2753         },
2754
2755         getConnectionObject:function()
2756         {
2757             var o;
2758             var tId = this.transactionId;
2759
2760             try
2761             {
2762                 o = this.createXhrObject(tId);
2763                 if (o) {
2764                     this.transactionId++;
2765                 }
2766             }
2767             catch(e) {
2768             }
2769             finally
2770             {
2771                 return o;
2772             }
2773         },
2774
2775         asyncRequest:function(method, uri, callback, postData)
2776         {
2777             var o = this.getConnectionObject();
2778
2779             if (!o) {
2780                 return null;
2781             }
2782             else {
2783                 o.conn.open(method, uri, true);
2784
2785                 if (this.useDefaultXhrHeader) {
2786                     if (!this.defaultHeaders['X-Requested-With']) {
2787                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2788                     }
2789                 }
2790
2791                 if(postData && this.useDefaultHeader){
2792                     this.initHeader('Content-Type', this.defaultPostHeader);
2793                 }
2794
2795                  if (this.hasDefaultHeaders || this.hasHeaders) {
2796                     this.setHeader(o);
2797                 }
2798
2799                 this.handleReadyState(o, callback);
2800                 o.conn.send(postData || null);
2801
2802                 return o;
2803             }
2804         },
2805
2806         handleReadyState:function(o, callback)
2807         {
2808             var oConn = this;
2809
2810             if (callback && callback.timeout) {
2811                 
2812                 this.timeout[o.tId] = window.setTimeout(function() {
2813                     oConn.abort(o, callback, true);
2814                 }, callback.timeout);
2815             }
2816
2817             this.poll[o.tId] = window.setInterval(
2818                     function() {
2819                         if (o.conn && o.conn.readyState == 4) {
2820                             window.clearInterval(oConn.poll[o.tId]);
2821                             delete oConn.poll[o.tId];
2822
2823                             if(callback && callback.timeout) {
2824                                 window.clearTimeout(oConn.timeout[o.tId]);
2825                                 delete oConn.timeout[o.tId];
2826                             }
2827
2828                             oConn.handleTransactionResponse(o, callback);
2829                         }
2830                     }
2831                     , this.pollInterval);
2832         },
2833
2834         handleTransactionResponse:function(o, callback, isAbort)
2835         {
2836
2837             if (!callback) {
2838                 this.releaseObject(o);
2839                 return;
2840             }
2841
2842             var httpStatus, responseObject;
2843
2844             try
2845             {
2846                 if (o.conn.status !== undefined && o.conn.status != 0) {
2847                     httpStatus = o.conn.status;
2848                 }
2849                 else {
2850                     httpStatus = 13030;
2851                 }
2852             }
2853             catch(e) {
2854
2855
2856                 httpStatus = 13030;
2857             }
2858
2859             if (httpStatus >= 200 && httpStatus < 300) {
2860                 responseObject = this.createResponseObject(o, callback.argument);
2861                 if (callback.success) {
2862                     if (!callback.scope) {
2863                         callback.success(responseObject);
2864                     }
2865                     else {
2866
2867
2868                         callback.success.apply(callback.scope, [responseObject]);
2869                     }
2870                 }
2871             }
2872             else {
2873                 switch (httpStatus) {
2874
2875                     case 12002:
2876                     case 12029:
2877                     case 12030:
2878                     case 12031:
2879                     case 12152:
2880                     case 13030:
2881                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2882                         if (callback.failure) {
2883                             if (!callback.scope) {
2884                                 callback.failure(responseObject);
2885                             }
2886                             else {
2887                                 callback.failure.apply(callback.scope, [responseObject]);
2888                             }
2889                         }
2890                         break;
2891                     default:
2892                         responseObject = this.createResponseObject(o, callback.argument);
2893                         if (callback.failure) {
2894                             if (!callback.scope) {
2895                                 callback.failure(responseObject);
2896                             }
2897                             else {
2898                                 callback.failure.apply(callback.scope, [responseObject]);
2899                             }
2900                         }
2901                 }
2902             }
2903
2904             this.releaseObject(o);
2905             responseObject = null;
2906         },
2907
2908         createResponseObject:function(o, callbackArg)
2909         {
2910             var obj = {};
2911             var headerObj = {};
2912
2913             try
2914             {
2915                 var headerStr = o.conn.getAllResponseHeaders();
2916                 var header = headerStr.split('\n');
2917                 for (var i = 0; i < header.length; i++) {
2918                     var delimitPos = header[i].indexOf(':');
2919                     if (delimitPos != -1) {
2920                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2921                     }
2922                 }
2923             }
2924             catch(e) {
2925             }
2926
2927             obj.tId = o.tId;
2928             obj.status = o.conn.status;
2929             obj.statusText = o.conn.statusText;
2930             obj.getResponseHeader = headerObj;
2931             obj.getAllResponseHeaders = headerStr;
2932             obj.responseText = o.conn.responseText;
2933             obj.responseXML = o.conn.responseXML;
2934
2935             if (typeof callbackArg !== undefined) {
2936                 obj.argument = callbackArg;
2937             }
2938
2939             return obj;
2940         },
2941
2942         createExceptionObject:function(tId, callbackArg, isAbort)
2943         {
2944             var COMM_CODE = 0;
2945             var COMM_ERROR = 'communication failure';
2946             var ABORT_CODE = -1;
2947             var ABORT_ERROR = 'transaction aborted';
2948
2949             var obj = {};
2950
2951             obj.tId = tId;
2952             if (isAbort) {
2953                 obj.status = ABORT_CODE;
2954                 obj.statusText = ABORT_ERROR;
2955             }
2956             else {
2957                 obj.status = COMM_CODE;
2958                 obj.statusText = COMM_ERROR;
2959             }
2960
2961             if (callbackArg) {
2962                 obj.argument = callbackArg;
2963             }
2964
2965             return obj;
2966         },
2967
2968         initHeader:function(label, value, isDefault)
2969         {
2970             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2971
2972             if (headerObj[label] === undefined) {
2973                 headerObj[label] = value;
2974             }
2975             else {
2976
2977
2978                 headerObj[label] = value + "," + headerObj[label];
2979             }
2980
2981             if (isDefault) {
2982                 this.hasDefaultHeaders = true;
2983             }
2984             else {
2985                 this.hasHeaders = true;
2986             }
2987         },
2988
2989
2990         setHeader:function(o)
2991         {
2992             if (this.hasDefaultHeaders) {
2993                 for (var prop in this.defaultHeaders) {
2994                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2995                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2996                     }
2997                 }
2998             }
2999
3000             if (this.hasHeaders) {
3001                 for (var prop in this.headers) {
3002                     if (this.headers.hasOwnProperty(prop)) {
3003                         o.conn.setRequestHeader(prop, this.headers[prop]);
3004                     }
3005                 }
3006                 this.headers = {};
3007                 this.hasHeaders = false;
3008             }
3009         },
3010
3011         resetDefaultHeaders:function() {
3012             delete this.defaultHeaders;
3013             this.defaultHeaders = {};
3014             this.hasDefaultHeaders = false;
3015         },
3016
3017         abort:function(o, callback, isTimeout)
3018         {
3019             if(this.isCallInProgress(o)) {
3020                 o.conn.abort();
3021                 window.clearInterval(this.poll[o.tId]);
3022                 delete this.poll[o.tId];
3023                 if (isTimeout) {
3024                     delete this.timeout[o.tId];
3025                 }
3026
3027                 this.handleTransactionResponse(o, callback, true);
3028
3029                 return true;
3030             }
3031             else {
3032                 return false;
3033             }
3034         },
3035
3036
3037         isCallInProgress:function(o)
3038         {
3039             if (o && o.conn) {
3040                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3041             }
3042             else {
3043
3044                 return false;
3045             }
3046         },
3047
3048
3049         releaseObject:function(o)
3050         {
3051
3052             o.conn = null;
3053
3054             o = null;
3055         },
3056
3057         activeX:[
3058         'MSXML2.XMLHTTP.3.0',
3059         'MSXML2.XMLHTTP',
3060         'Microsoft.XMLHTTP'
3061         ]
3062
3063
3064     };
3065 })();/*
3066  * Portions of this file are based on pieces of Yahoo User Interface Library
3067  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3068  * YUI licensed under the BSD License:
3069  * http://developer.yahoo.net/yui/license.txt
3070  * <script type="text/javascript">
3071  *
3072  */
3073
3074 Roo.lib.Region = function(t, r, b, l) {
3075     this.top = t;
3076     this[1] = t;
3077     this.right = r;
3078     this.bottom = b;
3079     this.left = l;
3080     this[0] = l;
3081 };
3082
3083
3084 Roo.lib.Region.prototype = {
3085     contains : function(region) {
3086         return ( region.left >= this.left &&
3087                  region.right <= this.right &&
3088                  region.top >= this.top &&
3089                  region.bottom <= this.bottom    );
3090
3091     },
3092
3093     getArea : function() {
3094         return ( (this.bottom - this.top) * (this.right - this.left) );
3095     },
3096
3097     intersect : function(region) {
3098         var t = Math.max(this.top, region.top);
3099         var r = Math.min(this.right, region.right);
3100         var b = Math.min(this.bottom, region.bottom);
3101         var l = Math.max(this.left, region.left);
3102
3103         if (b >= t && r >= l) {
3104             return new Roo.lib.Region(t, r, b, l);
3105         } else {
3106             return null;
3107         }
3108     },
3109     union : function(region) {
3110         var t = Math.min(this.top, region.top);
3111         var r = Math.max(this.right, region.right);
3112         var b = Math.max(this.bottom, region.bottom);
3113         var l = Math.min(this.left, region.left);
3114
3115         return new Roo.lib.Region(t, r, b, l);
3116     },
3117
3118     adjust : function(t, l, b, r) {
3119         this.top += t;
3120         this.left += l;
3121         this.right += r;
3122         this.bottom += b;
3123         return this;
3124     }
3125 };
3126
3127 Roo.lib.Region.getRegion = function(el) {
3128     var p = Roo.lib.Dom.getXY(el);
3129
3130     var t = p[1];
3131     var r = p[0] + el.offsetWidth;
3132     var b = p[1] + el.offsetHeight;
3133     var l = p[0];
3134
3135     return new Roo.lib.Region(t, r, b, l);
3136 };
3137 /*
3138  * Portions of this file are based on pieces of Yahoo User Interface Library
3139  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3140  * YUI licensed under the BSD License:
3141  * http://developer.yahoo.net/yui/license.txt
3142  * <script type="text/javascript">
3143  *
3144  */
3145 //@@dep Roo.lib.Region
3146
3147
3148 Roo.lib.Point = function(x, y) {
3149     if (x instanceof Array) {
3150         y = x[1];
3151         x = x[0];
3152     }
3153     this.x = this.right = this.left = this[0] = x;
3154     this.y = this.top = this.bottom = this[1] = y;
3155 };
3156
3157 Roo.lib.Point.prototype = new Roo.lib.Region();
3158 /*
3159  * Portions of this file are based on pieces of Yahoo User Interface Library
3160  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3161  * YUI licensed under the BSD License:
3162  * http://developer.yahoo.net/yui/license.txt
3163  * <script type="text/javascript">
3164  *
3165  */
3166  
3167 (function() {   
3168
3169     Roo.lib.Anim = {
3170         scroll : function(el, args, duration, easing, cb, scope) {
3171             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3172         },
3173
3174         motion : function(el, args, duration, easing, cb, scope) {
3175             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3176         },
3177
3178         color : function(el, args, duration, easing, cb, scope) {
3179             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3180         },
3181
3182         run : function(el, args, duration, easing, cb, scope, type) {
3183             type = type || Roo.lib.AnimBase;
3184             if (typeof easing == "string") {
3185                 easing = Roo.lib.Easing[easing];
3186             }
3187             var anim = new type(el, args, duration, easing);
3188             anim.animateX(function() {
3189                 Roo.callback(cb, scope);
3190             });
3191             return anim;
3192         }
3193     };
3194 })();/*
3195  * Portions of this file are based on pieces of Yahoo User Interface Library
3196  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3197  * YUI licensed under the BSD License:
3198  * http://developer.yahoo.net/yui/license.txt
3199  * <script type="text/javascript">
3200  *
3201  */
3202
3203 (function() {    
3204     var libFlyweight;
3205     
3206     function fly(el) {
3207         if (!libFlyweight) {
3208             libFlyweight = new Roo.Element.Flyweight();
3209         }
3210         libFlyweight.dom = el;
3211         return libFlyweight;
3212     }
3213
3214     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3215     
3216    
3217     
3218     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3219         if (el) {
3220             this.init(el, attributes, duration, method);
3221         }
3222     };
3223
3224     Roo.lib.AnimBase.fly = fly;
3225     
3226     
3227     
3228     Roo.lib.AnimBase.prototype = {
3229
3230         toString: function() {
3231             var el = this.getEl();
3232             var id = el.id || el.tagName;
3233             return ("Anim " + id);
3234         },
3235
3236         patterns: {
3237             noNegatives:        /width|height|opacity|padding/i,
3238             offsetAttribute:  /^((width|height)|(top|left))$/,
3239             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3240             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3241         },
3242
3243
3244         doMethod: function(attr, start, end) {
3245             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3246         },
3247
3248
3249         setAttribute: function(attr, val, unit) {
3250             if (this.patterns.noNegatives.test(attr)) {
3251                 val = (val > 0) ? val : 0;
3252             }
3253
3254             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3255         },
3256
3257
3258         getAttribute: function(attr) {
3259             var el = this.getEl();
3260             var val = fly(el).getStyle(attr);
3261
3262             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3263                 return parseFloat(val);
3264             }
3265
3266             var a = this.patterns.offsetAttribute.exec(attr) || [];
3267             var pos = !!( a[3] );
3268             var box = !!( a[2] );
3269
3270
3271             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3272                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3273             } else {
3274                 val = 0;
3275             }
3276
3277             return val;
3278         },
3279
3280
3281         getDefaultUnit: function(attr) {
3282             if (this.patterns.defaultUnit.test(attr)) {
3283                 return 'px';
3284             }
3285
3286             return '';
3287         },
3288
3289         animateX : function(callback, scope) {
3290             var f = function() {
3291                 this.onComplete.removeListener(f);
3292                 if (typeof callback == "function") {
3293                     callback.call(scope || this, this);
3294                 }
3295             };
3296             this.onComplete.addListener(f, this);
3297             this.animate();
3298         },
3299
3300
3301         setRuntimeAttribute: function(attr) {
3302             var start;
3303             var end;
3304             var attributes = this.attributes;
3305
3306             this.runtimeAttributes[attr] = {};
3307
3308             var isset = function(prop) {
3309                 return (typeof prop !== 'undefined');
3310             };
3311
3312             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3313                 return false;
3314             }
3315
3316             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3317
3318
3319             if (isset(attributes[attr]['to'])) {
3320                 end = attributes[attr]['to'];
3321             } else if (isset(attributes[attr]['by'])) {
3322                 if (start.constructor == Array) {
3323                     end = [];
3324                     for (var i = 0, len = start.length; i < len; ++i) {
3325                         end[i] = start[i] + attributes[attr]['by'][i];
3326                     }
3327                 } else {
3328                     end = start + attributes[attr]['by'];
3329                 }
3330             }
3331
3332             this.runtimeAttributes[attr].start = start;
3333             this.runtimeAttributes[attr].end = end;
3334
3335
3336             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3337         },
3338
3339
3340         init: function(el, attributes, duration, method) {
3341
3342             var isAnimated = false;
3343
3344
3345             var startTime = null;
3346
3347
3348             var actualFrames = 0;
3349
3350
3351             el = Roo.getDom(el);
3352
3353
3354             this.attributes = attributes || {};
3355
3356
3357             this.duration = duration || 1;
3358
3359
3360             this.method = method || Roo.lib.Easing.easeNone;
3361
3362
3363             this.useSeconds = true;
3364
3365
3366             this.currentFrame = 0;
3367
3368
3369             this.totalFrames = Roo.lib.AnimMgr.fps;
3370
3371
3372             this.getEl = function() {
3373                 return el;
3374             };
3375
3376
3377             this.isAnimated = function() {
3378                 return isAnimated;
3379             };
3380
3381
3382             this.getStartTime = function() {
3383                 return startTime;
3384             };
3385
3386             this.runtimeAttributes = {};
3387
3388
3389             this.animate = function() {
3390                 if (this.isAnimated()) {
3391                     return false;
3392                 }
3393
3394                 this.currentFrame = 0;
3395
3396                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3397
3398                 Roo.lib.AnimMgr.registerElement(this);
3399             };
3400
3401
3402             this.stop = function(finish) {
3403                 if (finish) {
3404                     this.currentFrame = this.totalFrames;
3405                     this._onTween.fire();
3406                 }
3407                 Roo.lib.AnimMgr.stop(this);
3408             };
3409
3410             var onStart = function() {
3411                 this.onStart.fire();
3412
3413                 this.runtimeAttributes = {};
3414                 for (var attr in this.attributes) {
3415                     this.setRuntimeAttribute(attr);
3416                 }
3417
3418                 isAnimated = true;
3419                 actualFrames = 0;
3420                 startTime = new Date();
3421             };
3422
3423
3424             var onTween = function() {
3425                 var data = {
3426                     duration: new Date() - this.getStartTime(),
3427                     currentFrame: this.currentFrame
3428                 };
3429
3430                 data.toString = function() {
3431                     return (
3432                             'duration: ' + data.duration +
3433                             ', currentFrame: ' + data.currentFrame
3434                             );
3435                 };
3436
3437                 this.onTween.fire(data);
3438
3439                 var runtimeAttributes = this.runtimeAttributes;
3440
3441                 for (var attr in runtimeAttributes) {
3442                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3443                 }
3444
3445                 actualFrames += 1;
3446             };
3447
3448             var onComplete = function() {
3449                 var actual_duration = (new Date() - startTime) / 1000 ;
3450
3451                 var data = {
3452                     duration: actual_duration,
3453                     frames: actualFrames,
3454                     fps: actualFrames / actual_duration
3455                 };
3456
3457                 data.toString = function() {
3458                     return (
3459                             'duration: ' + data.duration +
3460                             ', frames: ' + data.frames +
3461                             ', fps: ' + data.fps
3462                             );
3463                 };
3464
3465                 isAnimated = false;
3466                 actualFrames = 0;
3467                 this.onComplete.fire(data);
3468             };
3469
3470
3471             this._onStart = new Roo.util.Event(this);
3472             this.onStart = new Roo.util.Event(this);
3473             this.onTween = new Roo.util.Event(this);
3474             this._onTween = new Roo.util.Event(this);
3475             this.onComplete = new Roo.util.Event(this);
3476             this._onComplete = new Roo.util.Event(this);
3477             this._onStart.addListener(onStart);
3478             this._onTween.addListener(onTween);
3479             this._onComplete.addListener(onComplete);
3480         }
3481     };
3482 })();
3483 /*
3484  * Portions of this file are based on pieces of Yahoo User Interface Library
3485  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3486  * YUI licensed under the BSD License:
3487  * http://developer.yahoo.net/yui/license.txt
3488  * <script type="text/javascript">
3489  *
3490  */
3491
3492 Roo.lib.AnimMgr = new function() {
3493
3494     var thread = null;
3495
3496
3497     var queue = [];
3498
3499
3500     var tweenCount = 0;
3501
3502
3503     this.fps = 1000;
3504
3505
3506     this.delay = 1;
3507
3508
3509     this.registerElement = function(tween) {
3510         queue[queue.length] = tween;
3511         tweenCount += 1;
3512         tween._onStart.fire();
3513         this.start();
3514     };
3515
3516
3517     this.unRegister = function(tween, index) {
3518         tween._onComplete.fire();
3519         index = index || getIndex(tween);
3520         if (index != -1) {
3521             queue.splice(index, 1);
3522         }
3523
3524         tweenCount -= 1;
3525         if (tweenCount <= 0) {
3526             this.stop();
3527         }
3528     };
3529
3530
3531     this.start = function() {
3532         if (thread === null) {
3533             thread = setInterval(this.run, this.delay);
3534         }
3535     };
3536
3537
3538     this.stop = function(tween) {
3539         if (!tween) {
3540             clearInterval(thread);
3541
3542             for (var i = 0, len = queue.length; i < len; ++i) {
3543                 if (queue[0].isAnimated()) {
3544                     this.unRegister(queue[0], 0);
3545                 }
3546             }
3547
3548             queue = [];
3549             thread = null;
3550             tweenCount = 0;
3551         }
3552         else {
3553             this.unRegister(tween);
3554         }
3555     };
3556
3557
3558     this.run = function() {
3559         for (var i = 0, len = queue.length; i < len; ++i) {
3560             var tween = queue[i];
3561             if (!tween || !tween.isAnimated()) {
3562                 continue;
3563             }
3564
3565             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3566             {
3567                 tween.currentFrame += 1;
3568
3569                 if (tween.useSeconds) {
3570                     correctFrame(tween);
3571                 }
3572                 tween._onTween.fire();
3573             }
3574             else {
3575                 Roo.lib.AnimMgr.stop(tween, i);
3576             }
3577         }
3578     };
3579
3580     var getIndex = function(anim) {
3581         for (var i = 0, len = queue.length; i < len; ++i) {
3582             if (queue[i] == anim) {
3583                 return i;
3584             }
3585         }
3586         return -1;
3587     };
3588
3589
3590     var correctFrame = function(tween) {
3591         var frames = tween.totalFrames;
3592         var frame = tween.currentFrame;
3593         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3594         var elapsed = (new Date() - tween.getStartTime());
3595         var tweak = 0;
3596
3597         if (elapsed < tween.duration * 1000) {
3598             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3599         } else {
3600             tweak = frames - (frame + 1);
3601         }
3602         if (tweak > 0 && isFinite(tweak)) {
3603             if (tween.currentFrame + tweak >= frames) {
3604                 tweak = frames - (frame + 1);
3605             }
3606
3607             tween.currentFrame += tweak;
3608         }
3609     };
3610 };
3611
3612     /*
3613  * Portions of this file are based on pieces of Yahoo User Interface Library
3614  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3615  * YUI licensed under the BSD License:
3616  * http://developer.yahoo.net/yui/license.txt
3617  * <script type="text/javascript">
3618  *
3619  */
3620 Roo.lib.Bezier = new function() {
3621
3622         this.getPosition = function(points, t) {
3623             var n = points.length;
3624             var tmp = [];
3625
3626             for (var i = 0; i < n; ++i) {
3627                 tmp[i] = [points[i][0], points[i][1]];
3628             }
3629
3630             for (var j = 1; j < n; ++j) {
3631                 for (i = 0; i < n - j; ++i) {
3632                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3633                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3634                 }
3635             }
3636
3637             return [ tmp[0][0], tmp[0][1] ];
3638
3639         };
3640     };/*
3641  * Portions of this file are based on pieces of Yahoo User Interface Library
3642  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3643  * YUI licensed under the BSD License:
3644  * http://developer.yahoo.net/yui/license.txt
3645  * <script type="text/javascript">
3646  *
3647  */
3648 (function() {
3649
3650     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3651         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3652     };
3653
3654     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3655
3656     var fly = Roo.lib.AnimBase.fly;
3657     var Y = Roo.lib;
3658     var superclass = Y.ColorAnim.superclass;
3659     var proto = Y.ColorAnim.prototype;
3660
3661     proto.toString = function() {
3662         var el = this.getEl();
3663         var id = el.id || el.tagName;
3664         return ("ColorAnim " + id);
3665     };
3666
3667     proto.patterns.color = /color$/i;
3668     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3669     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3670     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3671     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3672
3673
3674     proto.parseColor = function(s) {
3675         if (s.length == 3) {
3676             return s;
3677         }
3678
3679         var c = this.patterns.hex.exec(s);
3680         if (c && c.length == 4) {
3681             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3682         }
3683
3684         c = this.patterns.rgb.exec(s);
3685         if (c && c.length == 4) {
3686             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3687         }
3688
3689         c = this.patterns.hex3.exec(s);
3690         if (c && c.length == 4) {
3691             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3692         }
3693
3694         return null;
3695     };
3696     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3697     proto.getAttribute = function(attr) {
3698         var el = this.getEl();
3699         if (this.patterns.color.test(attr)) {
3700             var val = fly(el).getStyle(attr);
3701
3702             if (this.patterns.transparent.test(val)) {
3703                 var parent = el.parentNode;
3704                 val = fly(parent).getStyle(attr);
3705
3706                 while (parent && this.patterns.transparent.test(val)) {
3707                     parent = parent.parentNode;
3708                     val = fly(parent).getStyle(attr);
3709                     if (parent.tagName.toUpperCase() == 'HTML') {
3710                         val = '#fff';
3711                     }
3712                 }
3713             }
3714         } else {
3715             val = superclass.getAttribute.call(this, attr);
3716         }
3717
3718         return val;
3719     };
3720     proto.getAttribute = function(attr) {
3721         var el = this.getEl();
3722         if (this.patterns.color.test(attr)) {
3723             var val = fly(el).getStyle(attr);
3724
3725             if (this.patterns.transparent.test(val)) {
3726                 var parent = el.parentNode;
3727                 val = fly(parent).getStyle(attr);
3728
3729                 while (parent && this.patterns.transparent.test(val)) {
3730                     parent = parent.parentNode;
3731                     val = fly(parent).getStyle(attr);
3732                     if (parent.tagName.toUpperCase() == 'HTML') {
3733                         val = '#fff';
3734                     }
3735                 }
3736             }
3737         } else {
3738             val = superclass.getAttribute.call(this, attr);
3739         }
3740
3741         return val;
3742     };
3743
3744     proto.doMethod = function(attr, start, end) {
3745         var val;
3746
3747         if (this.patterns.color.test(attr)) {
3748             val = [];
3749             for (var i = 0, len = start.length; i < len; ++i) {
3750                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3751             }
3752
3753             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3754         }
3755         else {
3756             val = superclass.doMethod.call(this, attr, start, end);
3757         }
3758
3759         return val;
3760     };
3761
3762     proto.setRuntimeAttribute = function(attr) {
3763         superclass.setRuntimeAttribute.call(this, attr);
3764
3765         if (this.patterns.color.test(attr)) {
3766             var attributes = this.attributes;
3767             var start = this.parseColor(this.runtimeAttributes[attr].start);
3768             var end = this.parseColor(this.runtimeAttributes[attr].end);
3769
3770             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3771                 end = this.parseColor(attributes[attr].by);
3772
3773                 for (var i = 0, len = start.length; i < len; ++i) {
3774                     end[i] = start[i] + end[i];
3775                 }
3776             }
3777
3778             this.runtimeAttributes[attr].start = start;
3779             this.runtimeAttributes[attr].end = end;
3780         }
3781     };
3782 })();
3783
3784 /*
3785  * Portions of this file are based on pieces of Yahoo User Interface Library
3786  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3787  * YUI licensed under the BSD License:
3788  * http://developer.yahoo.net/yui/license.txt
3789  * <script type="text/javascript">
3790  *
3791  */
3792 Roo.lib.Easing = {
3793
3794
3795     easeNone: function (t, b, c, d) {
3796         return c * t / d + b;
3797     },
3798
3799
3800     easeIn: function (t, b, c, d) {
3801         return c * (t /= d) * t + b;
3802     },
3803
3804
3805     easeOut: function (t, b, c, d) {
3806         return -c * (t /= d) * (t - 2) + b;
3807     },
3808
3809
3810     easeBoth: function (t, b, c, d) {
3811         if ((t /= d / 2) < 1) {
3812             return c / 2 * t * t + b;
3813         }
3814
3815         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3816     },
3817
3818
3819     easeInStrong: function (t, b, c, d) {
3820         return c * (t /= d) * t * t * t + b;
3821     },
3822
3823
3824     easeOutStrong: function (t, b, c, d) {
3825         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3826     },
3827
3828
3829     easeBothStrong: function (t, b, c, d) {
3830         if ((t /= d / 2) < 1) {
3831             return c / 2 * t * t * t * t + b;
3832         }
3833
3834         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3835     },
3836
3837
3838
3839     elasticIn: function (t, b, c, d, a, p) {
3840         if (t == 0) {
3841             return b;
3842         }
3843         if ((t /= d) == 1) {
3844             return b + c;
3845         }
3846         if (!p) {
3847             p = d * .3;
3848         }
3849
3850         if (!a || a < Math.abs(c)) {
3851             a = c;
3852             var s = p / 4;
3853         }
3854         else {
3855             var s = p / (2 * Math.PI) * Math.asin(c / a);
3856         }
3857
3858         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3859     },
3860
3861
3862     elasticOut: function (t, b, c, d, a, p) {
3863         if (t == 0) {
3864             return b;
3865         }
3866         if ((t /= d) == 1) {
3867             return b + c;
3868         }
3869         if (!p) {
3870             p = d * .3;
3871         }
3872
3873         if (!a || a < Math.abs(c)) {
3874             a = c;
3875             var s = p / 4;
3876         }
3877         else {
3878             var s = p / (2 * Math.PI) * Math.asin(c / a);
3879         }
3880
3881         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3882     },
3883
3884
3885     elasticBoth: function (t, b, c, d, a, p) {
3886         if (t == 0) {
3887             return b;
3888         }
3889
3890         if ((t /= d / 2) == 2) {
3891             return b + c;
3892         }
3893
3894         if (!p) {
3895             p = d * (.3 * 1.5);
3896         }
3897
3898         if (!a || a < Math.abs(c)) {
3899             a = c;
3900             var s = p / 4;
3901         }
3902         else {
3903             var s = p / (2 * Math.PI) * Math.asin(c / a);
3904         }
3905
3906         if (t < 1) {
3907             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3908                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3909         }
3910         return a * Math.pow(2, -10 * (t -= 1)) *
3911                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3912     },
3913
3914
3915
3916     backIn: function (t, b, c, d, s) {
3917         if (typeof s == 'undefined') {
3918             s = 1.70158;
3919         }
3920         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3921     },
3922
3923
3924     backOut: function (t, b, c, d, s) {
3925         if (typeof s == 'undefined') {
3926             s = 1.70158;
3927         }
3928         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3929     },
3930
3931
3932     backBoth: function (t, b, c, d, s) {
3933         if (typeof s == 'undefined') {
3934             s = 1.70158;
3935         }
3936
3937         if ((t /= d / 2 ) < 1) {
3938             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3939         }
3940         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3941     },
3942
3943
3944     bounceIn: function (t, b, c, d) {
3945         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3946     },
3947
3948
3949     bounceOut: function (t, b, c, d) {
3950         if ((t /= d) < (1 / 2.75)) {
3951             return c * (7.5625 * t * t) + b;
3952         } else if (t < (2 / 2.75)) {
3953             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3954         } else if (t < (2.5 / 2.75)) {
3955             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3956         }
3957         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3958     },
3959
3960
3961     bounceBoth: function (t, b, c, d) {
3962         if (t < d / 2) {
3963             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3964         }
3965         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3966     }
3967 };/*
3968  * Portions of this file are based on pieces of Yahoo User Interface Library
3969  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3970  * YUI licensed under the BSD License:
3971  * http://developer.yahoo.net/yui/license.txt
3972  * <script type="text/javascript">
3973  *
3974  */
3975     (function() {
3976         Roo.lib.Motion = function(el, attributes, duration, method) {
3977             if (el) {
3978                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3979             }
3980         };
3981
3982         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3983
3984
3985         var Y = Roo.lib;
3986         var superclass = Y.Motion.superclass;
3987         var proto = Y.Motion.prototype;
3988
3989         proto.toString = function() {
3990             var el = this.getEl();
3991             var id = el.id || el.tagName;
3992             return ("Motion " + id);
3993         };
3994
3995         proto.patterns.points = /^points$/i;
3996
3997         proto.setAttribute = function(attr, val, unit) {
3998             if (this.patterns.points.test(attr)) {
3999                 unit = unit || 'px';
4000                 superclass.setAttribute.call(this, 'left', val[0], unit);
4001                 superclass.setAttribute.call(this, 'top', val[1], unit);
4002             } else {
4003                 superclass.setAttribute.call(this, attr, val, unit);
4004             }
4005         };
4006
4007         proto.getAttribute = function(attr) {
4008             if (this.patterns.points.test(attr)) {
4009                 var val = [
4010                         superclass.getAttribute.call(this, 'left'),
4011                         superclass.getAttribute.call(this, 'top')
4012                         ];
4013             } else {
4014                 val = superclass.getAttribute.call(this, attr);
4015             }
4016
4017             return val;
4018         };
4019
4020         proto.doMethod = function(attr, start, end) {
4021             var val = null;
4022
4023             if (this.patterns.points.test(attr)) {
4024                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4025                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4026             } else {
4027                 val = superclass.doMethod.call(this, attr, start, end);
4028             }
4029             return val;
4030         };
4031
4032         proto.setRuntimeAttribute = function(attr) {
4033             if (this.patterns.points.test(attr)) {
4034                 var el = this.getEl();
4035                 var attributes = this.attributes;
4036                 var start;
4037                 var control = attributes['points']['control'] || [];
4038                 var end;
4039                 var i, len;
4040
4041                 if (control.length > 0 && !(control[0] instanceof Array)) {
4042                     control = [control];
4043                 } else {
4044                     var tmp = [];
4045                     for (i = 0,len = control.length; i < len; ++i) {
4046                         tmp[i] = control[i];
4047                     }
4048                     control = tmp;
4049                 }
4050
4051                 Roo.fly(el).position();
4052
4053                 if (isset(attributes['points']['from'])) {
4054                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4055                 }
4056                 else {
4057                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4058                 }
4059
4060                 start = this.getAttribute('points');
4061
4062
4063                 if (isset(attributes['points']['to'])) {
4064                     end = translateValues.call(this, attributes['points']['to'], start);
4065
4066                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4067                     for (i = 0,len = control.length; i < len; ++i) {
4068                         control[i] = translateValues.call(this, control[i], start);
4069                     }
4070
4071
4072                 } else if (isset(attributes['points']['by'])) {
4073                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4074
4075                     for (i = 0,len = control.length; i < len; ++i) {
4076                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4077                     }
4078                 }
4079
4080                 this.runtimeAttributes[attr] = [start];
4081
4082                 if (control.length > 0) {
4083                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4084                 }
4085
4086                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4087             }
4088             else {
4089                 superclass.setRuntimeAttribute.call(this, attr);
4090             }
4091         };
4092
4093         var translateValues = function(val, start) {
4094             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4095             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4096
4097             return val;
4098         };
4099
4100         var isset = function(prop) {
4101             return (typeof prop !== 'undefined');
4102         };
4103     })();
4104 /*
4105  * Portions of this file are based on pieces of Yahoo User Interface Library
4106  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4107  * YUI licensed under the BSD License:
4108  * http://developer.yahoo.net/yui/license.txt
4109  * <script type="text/javascript">
4110  *
4111  */
4112     (function() {
4113         Roo.lib.Scroll = function(el, attributes, duration, method) {
4114             if (el) {
4115                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4116             }
4117         };
4118
4119         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4120
4121
4122         var Y = Roo.lib;
4123         var superclass = Y.Scroll.superclass;
4124         var proto = Y.Scroll.prototype;
4125
4126         proto.toString = function() {
4127             var el = this.getEl();
4128             var id = el.id || el.tagName;
4129             return ("Scroll " + id);
4130         };
4131
4132         proto.doMethod = function(attr, start, end) {
4133             var val = null;
4134
4135             if (attr == 'scroll') {
4136                 val = [
4137                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4138                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4139                         ];
4140
4141             } else {
4142                 val = superclass.doMethod.call(this, attr, start, end);
4143             }
4144             return val;
4145         };
4146
4147         proto.getAttribute = function(attr) {
4148             var val = null;
4149             var el = this.getEl();
4150
4151             if (attr == 'scroll') {
4152                 val = [ el.scrollLeft, el.scrollTop ];
4153             } else {
4154                 val = superclass.getAttribute.call(this, attr);
4155             }
4156
4157             return val;
4158         };
4159
4160         proto.setAttribute = function(attr, val, unit) {
4161             var el = this.getEl();
4162
4163             if (attr == 'scroll') {
4164                 el.scrollLeft = val[0];
4165                 el.scrollTop = val[1];
4166             } else {
4167                 superclass.setAttribute.call(this, attr, val, unit);
4168             }
4169         };
4170     })();
4171 /*
4172  * Based on:
4173  * Ext JS Library 1.1.1
4174  * Copyright(c) 2006-2007, Ext JS, LLC.
4175  *
4176  * Originally Released Under LGPL - original licence link has changed is not relivant.
4177  *
4178  * Fork - LGPL
4179  * <script type="text/javascript">
4180  */
4181
4182
4183 // nasty IE9 hack - what a pile of crap that is..
4184
4185  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4186     Range.prototype.createContextualFragment = function (html) {
4187         var doc = window.document;
4188         var container = doc.createElement("div");
4189         container.innerHTML = html;
4190         var frag = doc.createDocumentFragment(), n;
4191         while ((n = container.firstChild)) {
4192             frag.appendChild(n);
4193         }
4194         return frag;
4195     };
4196 }
4197
4198 /**
4199  * @class Roo.DomHelper
4200  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4201  * 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>.
4202  * @singleton
4203  */
4204 Roo.DomHelper = function(){
4205     var tempTableEl = null;
4206     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4207     var tableRe = /^table|tbody|tr|td$/i;
4208     var xmlns = {};
4209     // build as innerHTML where available
4210     /** @ignore */
4211     var createHtml = function(o){
4212         if(typeof o == 'string'){
4213             return o;
4214         }
4215         var b = "";
4216         if(!o.tag){
4217             o.tag = "div";
4218         }
4219         b += "<" + o.tag;
4220         for(var attr in o){
4221             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4222             if(attr == "style"){
4223                 var s = o["style"];
4224                 if(typeof s == "function"){
4225                     s = s.call();
4226                 }
4227                 if(typeof s == "string"){
4228                     b += ' style="' + s + '"';
4229                 }else if(typeof s == "object"){
4230                     b += ' style="';
4231                     for(var key in s){
4232                         if(typeof s[key] != "function"){
4233                             b += key + ":" + s[key] + ";";
4234                         }
4235                     }
4236                     b += '"';
4237                 }
4238             }else{
4239                 if(attr == "cls"){
4240                     b += ' class="' + o["cls"] + '"';
4241                 }else if(attr == "htmlFor"){
4242                     b += ' for="' + o["htmlFor"] + '"';
4243                 }else{
4244                     b += " " + attr + '="' + o[attr] + '"';
4245                 }
4246             }
4247         }
4248         if(emptyTags.test(o.tag)){
4249             b += "/>";
4250         }else{
4251             b += ">";
4252             var cn = o.children || o.cn;
4253             if(cn){
4254                 //http://bugs.kde.org/show_bug.cgi?id=71506
4255                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4256                     for(var i = 0, len = cn.length; i < len; i++) {
4257                         b += createHtml(cn[i], b);
4258                     }
4259                 }else{
4260                     b += createHtml(cn, b);
4261                 }
4262             }
4263             if(o.html){
4264                 b += o.html;
4265             }
4266             b += "</" + o.tag + ">";
4267         }
4268         return b;
4269     };
4270
4271     // build as dom
4272     /** @ignore */
4273     var createDom = function(o, parentNode){
4274          
4275         // defininition craeted..
4276         var ns = false;
4277         if (o.ns && o.ns != 'html') {
4278                
4279             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4280                 xmlns[o.ns] = o.xmlns;
4281                 ns = o.xmlns;
4282             }
4283             if (typeof(xmlns[o.ns]) == 'undefined') {
4284                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4285             }
4286             ns = xmlns[o.ns];
4287         }
4288         
4289         
4290         if (typeof(o) == 'string') {
4291             return parentNode.appendChild(document.createTextNode(o));
4292         }
4293         o.tag = o.tag || div;
4294         if (o.ns && Roo.isIE) {
4295             ns = false;
4296             o.tag = o.ns + ':' + o.tag;
4297             
4298         }
4299         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4300         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4301         for(var attr in o){
4302             
4303             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4304                     attr == "style" || typeof o[attr] == "function") { continue; }
4305                     
4306             if(attr=="cls" && Roo.isIE){
4307                 el.className = o["cls"];
4308             }else{
4309                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4310                 else { 
4311                     el[attr] = o[attr];
4312                 }
4313             }
4314         }
4315         Roo.DomHelper.applyStyles(el, o.style);
4316         var cn = o.children || o.cn;
4317         if(cn){
4318             //http://bugs.kde.org/show_bug.cgi?id=71506
4319              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4320                 for(var i = 0, len = cn.length; i < len; i++) {
4321                     createDom(cn[i], el);
4322                 }
4323             }else{
4324                 createDom(cn, el);
4325             }
4326         }
4327         if(o.html){
4328             el.innerHTML = o.html;
4329         }
4330         if(parentNode){
4331            parentNode.appendChild(el);
4332         }
4333         return el;
4334     };
4335
4336     var ieTable = function(depth, s, h, e){
4337         tempTableEl.innerHTML = [s, h, e].join('');
4338         var i = -1, el = tempTableEl;
4339         while(++i < depth){
4340             el = el.firstChild;
4341         }
4342         return el;
4343     };
4344
4345     // kill repeat to save bytes
4346     var ts = '<table>',
4347         te = '</table>',
4348         tbs = ts+'<tbody>',
4349         tbe = '</tbody>'+te,
4350         trs = tbs + '<tr>',
4351         tre = '</tr>'+tbe;
4352
4353     /**
4354      * @ignore
4355      * Nasty code for IE's broken table implementation
4356      */
4357     var insertIntoTable = function(tag, where, el, html){
4358         if(!tempTableEl){
4359             tempTableEl = document.createElement('div');
4360         }
4361         var node;
4362         var before = null;
4363         if(tag == 'td'){
4364             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4365                 return;
4366             }
4367             if(where == 'beforebegin'){
4368                 before = el;
4369                 el = el.parentNode;
4370             } else{
4371                 before = el.nextSibling;
4372                 el = el.parentNode;
4373             }
4374             node = ieTable(4, trs, html, tre);
4375         }
4376         else if(tag == 'tr'){
4377             if(where == 'beforebegin'){
4378                 before = el;
4379                 el = el.parentNode;
4380                 node = ieTable(3, tbs, html, tbe);
4381             } else if(where == 'afterend'){
4382                 before = el.nextSibling;
4383                 el = el.parentNode;
4384                 node = ieTable(3, tbs, html, tbe);
4385             } else{ // INTO a TR
4386                 if(where == 'afterbegin'){
4387                     before = el.firstChild;
4388                 }
4389                 node = ieTable(4, trs, html, tre);
4390             }
4391         } else if(tag == 'tbody'){
4392             if(where == 'beforebegin'){
4393                 before = el;
4394                 el = el.parentNode;
4395                 node = ieTable(2, ts, html, te);
4396             } else if(where == 'afterend'){
4397                 before = el.nextSibling;
4398                 el = el.parentNode;
4399                 node = ieTable(2, ts, html, te);
4400             } else{
4401                 if(where == 'afterbegin'){
4402                     before = el.firstChild;
4403                 }
4404                 node = ieTable(3, tbs, html, tbe);
4405             }
4406         } else{ // TABLE
4407             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4408                 return;
4409             }
4410             if(where == 'afterbegin'){
4411                 before = el.firstChild;
4412             }
4413             node = ieTable(2, ts, html, te);
4414         }
4415         el.insertBefore(node, before);
4416         return node;
4417     };
4418
4419     return {
4420     /** True to force the use of DOM instead of html fragments @type Boolean */
4421     useDom : false,
4422
4423     /**
4424      * Returns the markup for the passed Element(s) config
4425      * @param {Object} o The Dom object spec (and children)
4426      * @return {String}
4427      */
4428     markup : function(o){
4429         return createHtml(o);
4430     },
4431
4432     /**
4433      * Applies a style specification to an element
4434      * @param {String/HTMLElement} el The element to apply styles to
4435      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4436      * a function which returns such a specification.
4437      */
4438     applyStyles : function(el, styles){
4439         if(styles){
4440            el = Roo.fly(el);
4441            if(typeof styles == "string"){
4442                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4443                var matches;
4444                while ((matches = re.exec(styles)) != null){
4445                    el.setStyle(matches[1], matches[2]);
4446                }
4447            }else if (typeof styles == "object"){
4448                for (var style in styles){
4449                   el.setStyle(style, styles[style]);
4450                }
4451            }else if (typeof styles == "function"){
4452                 Roo.DomHelper.applyStyles(el, styles.call());
4453            }
4454         }
4455     },
4456
4457     /**
4458      * Inserts an HTML fragment into the Dom
4459      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4460      * @param {HTMLElement} el The context element
4461      * @param {String} html The HTML fragmenet
4462      * @return {HTMLElement} The new node
4463      */
4464     insertHtml : function(where, el, html){
4465         where = where.toLowerCase();
4466         if(el.insertAdjacentHTML){
4467             if(tableRe.test(el.tagName)){
4468                 var rs;
4469                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4470                     return rs;
4471                 }
4472             }
4473             switch(where){
4474                 case "beforebegin":
4475                     el.insertAdjacentHTML('BeforeBegin', html);
4476                     return el.previousSibling;
4477                 case "afterbegin":
4478                     el.insertAdjacentHTML('AfterBegin', html);
4479                     return el.firstChild;
4480                 case "beforeend":
4481                     el.insertAdjacentHTML('BeforeEnd', html);
4482                     return el.lastChild;
4483                 case "afterend":
4484                     el.insertAdjacentHTML('AfterEnd', html);
4485                     return el.nextSibling;
4486             }
4487             throw 'Illegal insertion point -> "' + where + '"';
4488         }
4489         var range = el.ownerDocument.createRange();
4490         var frag;
4491         switch(where){
4492              case "beforebegin":
4493                 range.setStartBefore(el);
4494                 frag = range.createContextualFragment(html);
4495                 el.parentNode.insertBefore(frag, el);
4496                 return el.previousSibling;
4497              case "afterbegin":
4498                 if(el.firstChild){
4499                     range.setStartBefore(el.firstChild);
4500                     frag = range.createContextualFragment(html);
4501                     el.insertBefore(frag, el.firstChild);
4502                     return el.firstChild;
4503                 }else{
4504                     el.innerHTML = html;
4505                     return el.firstChild;
4506                 }
4507             case "beforeend":
4508                 if(el.lastChild){
4509                     range.setStartAfter(el.lastChild);
4510                     frag = range.createContextualFragment(html);
4511                     el.appendChild(frag);
4512                     return el.lastChild;
4513                 }else{
4514                     el.innerHTML = html;
4515                     return el.lastChild;
4516                 }
4517             case "afterend":
4518                 range.setStartAfter(el);
4519                 frag = range.createContextualFragment(html);
4520                 el.parentNode.insertBefore(frag, el.nextSibling);
4521                 return el.nextSibling;
4522             }
4523             throw 'Illegal insertion point -> "' + where + '"';
4524     },
4525
4526     /**
4527      * Creates new Dom element(s) and inserts them before el
4528      * @param {String/HTMLElement/Element} el The context element
4529      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4530      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4531      * @return {HTMLElement/Roo.Element} The new node
4532      */
4533     insertBefore : function(el, o, returnElement){
4534         return this.doInsert(el, o, returnElement, "beforeBegin");
4535     },
4536
4537     /**
4538      * Creates new Dom element(s) and inserts them after el
4539      * @param {String/HTMLElement/Element} el The context element
4540      * @param {Object} o The Dom object spec (and children)
4541      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4542      * @return {HTMLElement/Roo.Element} The new node
4543      */
4544     insertAfter : function(el, o, returnElement){
4545         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4546     },
4547
4548     /**
4549      * Creates new Dom element(s) and inserts them as the first child of el
4550      * @param {String/HTMLElement/Element} el The context element
4551      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4552      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4553      * @return {HTMLElement/Roo.Element} The new node
4554      */
4555     insertFirst : function(el, o, returnElement){
4556         return this.doInsert(el, o, returnElement, "afterBegin");
4557     },
4558
4559     // private
4560     doInsert : function(el, o, returnElement, pos, sibling){
4561         el = Roo.getDom(el);
4562         var newNode;
4563         if(this.useDom || o.ns){
4564             newNode = createDom(o, null);
4565             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4566         }else{
4567             var html = createHtml(o);
4568             newNode = this.insertHtml(pos, el, html);
4569         }
4570         return returnElement ? Roo.get(newNode, true) : newNode;
4571     },
4572
4573     /**
4574      * Creates new Dom element(s) and appends them to el
4575      * @param {String/HTMLElement/Element} el The context element
4576      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4577      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4578      * @return {HTMLElement/Roo.Element} The new node
4579      */
4580     append : function(el, o, returnElement){
4581         el = Roo.getDom(el);
4582         var newNode;
4583         if(this.useDom || o.ns){
4584             newNode = createDom(o, null);
4585             el.appendChild(newNode);
4586         }else{
4587             var html = createHtml(o);
4588             newNode = this.insertHtml("beforeEnd", el, html);
4589         }
4590         return returnElement ? Roo.get(newNode, true) : newNode;
4591     },
4592
4593     /**
4594      * Creates new Dom element(s) and overwrites the contents of el with them
4595      * @param {String/HTMLElement/Element} el The context element
4596      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4597      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4598      * @return {HTMLElement/Roo.Element} The new node
4599      */
4600     overwrite : function(el, o, returnElement){
4601         el = Roo.getDom(el);
4602         if (o.ns) {
4603           
4604             while (el.childNodes.length) {
4605                 el.removeChild(el.firstChild);
4606             }
4607             createDom(o, el);
4608         } else {
4609             el.innerHTML = createHtml(o);   
4610         }
4611         
4612         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4613     },
4614
4615     /**
4616      * Creates a new Roo.DomHelper.Template from the Dom object spec
4617      * @param {Object} o The Dom object spec (and children)
4618      * @return {Roo.DomHelper.Template} The new template
4619      */
4620     createTemplate : function(o){
4621         var html = createHtml(o);
4622         return new Roo.Template(html);
4623     }
4624     };
4625 }();
4626 /*
4627  * Based on:
4628  * Ext JS Library 1.1.1
4629  * Copyright(c) 2006-2007, Ext JS, LLC.
4630  *
4631  * Originally Released Under LGPL - original licence link has changed is not relivant.
4632  *
4633  * Fork - LGPL
4634  * <script type="text/javascript">
4635  */
4636  
4637 /**
4638 * @class Roo.Template
4639 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4640 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4641 * Usage:
4642 <pre><code>
4643 var t = new Roo.Template({
4644     html :  '&lt;div name="{id}"&gt;' + 
4645         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4646         '&lt;/div&gt;',
4647     myformat: function (value, allValues) {
4648         return 'XX' + value;
4649     }
4650 });
4651 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4652 </code></pre>
4653 * For more information see this blog post with examples:
4654 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4655      - Create Elements using DOM, HTML fragments and Templates</a>. 
4656 * @constructor
4657 * @param {Object} cfg - Configuration object.
4658 */
4659 Roo.Template = function(cfg){
4660     // BC!
4661     if(cfg instanceof Array){
4662         cfg = cfg.join("");
4663     }else if(arguments.length > 1){
4664         cfg = Array.prototype.join.call(arguments, "");
4665     }
4666     
4667     
4668     if (typeof(cfg) == 'object') {
4669         Roo.apply(this,cfg)
4670     } else {
4671         // bc
4672         this.html = cfg;
4673     }
4674     if (this.url) {
4675         this.load();
4676     }
4677     
4678 };
4679 Roo.Template.prototype = {
4680     
4681     /**
4682      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
4683      */
4684     onLoad : false,
4685     
4686     
4687     /**
4688      * @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..
4689      *                    it should be fixed so that template is observable...
4690      */
4691     url : false,
4692     /**
4693      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4694      */
4695     html : '',
4696     
4697     
4698     compiled : false,
4699     loaded : false,
4700     /**
4701      * Returns an HTML fragment of this template with the specified values applied.
4702      * @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'})
4703      * @return {String} The HTML fragment
4704      */
4705     
4706    
4707     
4708     applyTemplate : function(values){
4709         //Roo.log(["applyTemplate", values]);
4710         try {
4711            
4712             if(this.compiled){
4713                 return this.compiled(values);
4714             }
4715             var useF = this.disableFormats !== true;
4716             var fm = Roo.util.Format, tpl = this;
4717             var fn = function(m, name, format, args){
4718                 if(format && useF){
4719                     if(format.substr(0, 5) == "this."){
4720                         return tpl.call(format.substr(5), values[name], values);
4721                     }else{
4722                         if(args){
4723                             // quoted values are required for strings in compiled templates, 
4724                             // but for non compiled we need to strip them
4725                             // quoted reversed for jsmin
4726                             var re = /^\s*['"](.*)["']\s*$/;
4727                             args = args.split(',');
4728                             for(var i = 0, len = args.length; i < len; i++){
4729                                 args[i] = args[i].replace(re, "$1");
4730                             }
4731                             args = [values[name]].concat(args);
4732                         }else{
4733                             args = [values[name]];
4734                         }
4735                         return fm[format].apply(fm, args);
4736                     }
4737                 }else{
4738                     return values[name] !== undefined ? values[name] : "";
4739                 }
4740             };
4741             return this.html.replace(this.re, fn);
4742         } catch (e) {
4743             Roo.log(e);
4744             throw e;
4745         }
4746          
4747     },
4748     
4749     loading : false,
4750       
4751     load : function ()
4752     {
4753          
4754         if (this.loading) {
4755             return;
4756         }
4757         var _t = this;
4758         
4759         this.loading = true;
4760         this.compiled = false;
4761         
4762         var cx = new Roo.data.Connection();
4763         cx.request({
4764             url : this.url,
4765             method : 'GET',
4766             success : function (response) {
4767                 _t.loading = false;
4768                 _t.url = false;
4769                 
4770                 _t.set(response.responseText,true);
4771                 _t.loaded = true;
4772                 if (_t.onLoad) {
4773                     _t.onLoad();
4774                 }
4775              },
4776             failure : function(response) {
4777                 Roo.log("Template failed to load from " + _t.url);
4778                 _t.loading = false;
4779             }
4780         });
4781     },
4782
4783     /**
4784      * Sets the HTML used as the template and optionally compiles it.
4785      * @param {String} html
4786      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4787      * @return {Roo.Template} this
4788      */
4789     set : function(html, compile){
4790         this.html = html;
4791         this.compiled = false;
4792         if(compile){
4793             this.compile();
4794         }
4795         return this;
4796     },
4797     
4798     /**
4799      * True to disable format functions (defaults to false)
4800      * @type Boolean
4801      */
4802     disableFormats : false,
4803     
4804     /**
4805     * The regular expression used to match template variables 
4806     * @type RegExp
4807     * @property 
4808     */
4809     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4810     
4811     /**
4812      * Compiles the template into an internal function, eliminating the RegEx overhead.
4813      * @return {Roo.Template} this
4814      */
4815     compile : function(){
4816         var fm = Roo.util.Format;
4817         var useF = this.disableFormats !== true;
4818         var sep = Roo.isGecko ? "+" : ",";
4819         var fn = function(m, name, format, args){
4820             if(format && useF){
4821                 args = args ? ',' + args : "";
4822                 if(format.substr(0, 5) != "this."){
4823                     format = "fm." + format + '(';
4824                 }else{
4825                     format = 'this.call("'+ format.substr(5) + '", ';
4826                     args = ", values";
4827                 }
4828             }else{
4829                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4830             }
4831             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4832         };
4833         var body;
4834         // branched to use + in gecko and [].join() in others
4835         if(Roo.isGecko){
4836             body = "this.compiled = function(values){ return '" +
4837                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4838                     "';};";
4839         }else{
4840             body = ["this.compiled = function(values){ return ['"];
4841             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4842             body.push("'].join('');};");
4843             body = body.join('');
4844         }
4845         /**
4846          * eval:var:values
4847          * eval:var:fm
4848          */
4849         eval(body);
4850         return this;
4851     },
4852     
4853     // private function used to call members
4854     call : function(fnName, value, allValues){
4855         return this[fnName](value, allValues);
4856     },
4857     
4858     /**
4859      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4860      * @param {String/HTMLElement/Roo.Element} el The context element
4861      * @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'})
4862      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4863      * @return {HTMLElement/Roo.Element} The new node or Element
4864      */
4865     insertFirst: function(el, values, returnElement){
4866         return this.doInsert('afterBegin', el, values, returnElement);
4867     },
4868
4869     /**
4870      * Applies the supplied values to the template and inserts the new node(s) before el.
4871      * @param {String/HTMLElement/Roo.Element} el The context element
4872      * @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'})
4873      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4874      * @return {HTMLElement/Roo.Element} The new node or Element
4875      */
4876     insertBefore: function(el, values, returnElement){
4877         return this.doInsert('beforeBegin', el, values, returnElement);
4878     },
4879
4880     /**
4881      * Applies the supplied values to the template and inserts the new node(s) after el.
4882      * @param {String/HTMLElement/Roo.Element} el The context element
4883      * @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'})
4884      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4885      * @return {HTMLElement/Roo.Element} The new node or Element
4886      */
4887     insertAfter : function(el, values, returnElement){
4888         return this.doInsert('afterEnd', el, values, returnElement);
4889     },
4890     
4891     /**
4892      * Applies the supplied values to the template and appends the new node(s) to el.
4893      * @param {String/HTMLElement/Roo.Element} el The context element
4894      * @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'})
4895      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4896      * @return {HTMLElement/Roo.Element} The new node or Element
4897      */
4898     append : function(el, values, returnElement){
4899         return this.doInsert('beforeEnd', el, values, returnElement);
4900     },
4901
4902     doInsert : function(where, el, values, returnEl){
4903         el = Roo.getDom(el);
4904         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4905         return returnEl ? Roo.get(newNode, true) : newNode;
4906     },
4907
4908     /**
4909      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4910      * @param {String/HTMLElement/Roo.Element} el The context element
4911      * @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'})
4912      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4913      * @return {HTMLElement/Roo.Element} The new node or Element
4914      */
4915     overwrite : function(el, values, returnElement){
4916         el = Roo.getDom(el);
4917         el.innerHTML = this.applyTemplate(values);
4918         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4919     }
4920 };
4921 /**
4922  * Alias for {@link #applyTemplate}
4923  * @method
4924  */
4925 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4926
4927 // backwards compat
4928 Roo.DomHelper.Template = Roo.Template;
4929
4930 /**
4931  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4932  * @param {String/HTMLElement} el A DOM element or its id
4933  * @returns {Roo.Template} The created template
4934  * @static
4935  */
4936 Roo.Template.from = function(el){
4937     el = Roo.getDom(el);
4938     return new Roo.Template(el.value || el.innerHTML);
4939 };/*
4940  * Based on:
4941  * Ext JS Library 1.1.1
4942  * Copyright(c) 2006-2007, Ext JS, LLC.
4943  *
4944  * Originally Released Under LGPL - original licence link has changed is not relivant.
4945  *
4946  * Fork - LGPL
4947  * <script type="text/javascript">
4948  */
4949  
4950
4951 /*
4952  * This is code is also distributed under MIT license for use
4953  * with jQuery and prototype JavaScript libraries.
4954  */
4955 /**
4956  * @class Roo.DomQuery
4957 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).
4958 <p>
4959 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>
4960
4961 <p>
4962 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.
4963 </p>
4964 <h4>Element Selectors:</h4>
4965 <ul class="list">
4966     <li> <b>*</b> any element</li>
4967     <li> <b>E</b> an element with the tag E</li>
4968     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4969     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4970     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4971     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4972 </ul>
4973 <h4>Attribute Selectors:</h4>
4974 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4975 <ul class="list">
4976     <li> <b>E[foo]</b> has an attribute "foo"</li>
4977     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4978     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4979     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4980     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4981     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4982     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4983 </ul>
4984 <h4>Pseudo Classes:</h4>
4985 <ul class="list">
4986     <li> <b>E:first-child</b> E is the first child of its parent</li>
4987     <li> <b>E:last-child</b> E is the last child of its parent</li>
4988     <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>
4989     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4990     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4991     <li> <b>E:only-child</b> E is the only child of its parent</li>
4992     <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>
4993     <li> <b>E:first</b> the first E in the resultset</li>
4994     <li> <b>E:last</b> the last E in the resultset</li>
4995     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4996     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4997     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4998     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4999     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
5000     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
5001     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
5002     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
5003     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
5004 </ul>
5005 <h4>CSS Value Selectors:</h4>
5006 <ul class="list">
5007     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
5008     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
5009     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
5010     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
5011     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
5012     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
5013 </ul>
5014  * @singleton
5015  */
5016 Roo.DomQuery = function(){
5017     var cache = {}, simpleCache = {}, valueCache = {};
5018     var nonSpace = /\S/;
5019     var trimRe = /^\s+|\s+$/g;
5020     var tplRe = /\{(\d+)\}/g;
5021     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5022     var tagTokenRe = /^(#)?([\w-\*]+)/;
5023     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5024
5025     function child(p, index){
5026         var i = 0;
5027         var n = p.firstChild;
5028         while(n){
5029             if(n.nodeType == 1){
5030                if(++i == index){
5031                    return n;
5032                }
5033             }
5034             n = n.nextSibling;
5035         }
5036         return null;
5037     };
5038
5039     function next(n){
5040         while((n = n.nextSibling) && n.nodeType != 1);
5041         return n;
5042     };
5043
5044     function prev(n){
5045         while((n = n.previousSibling) && n.nodeType != 1);
5046         return n;
5047     };
5048
5049     function children(d){
5050         var n = d.firstChild, ni = -1;
5051             while(n){
5052                 var nx = n.nextSibling;
5053                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5054                     d.removeChild(n);
5055                 }else{
5056                     n.nodeIndex = ++ni;
5057                 }
5058                 n = nx;
5059             }
5060             return this;
5061         };
5062
5063     function byClassName(c, a, v){
5064         if(!v){
5065             return c;
5066         }
5067         var r = [], ri = -1, cn;
5068         for(var i = 0, ci; ci = c[i]; i++){
5069             if((' '+ci.className+' ').indexOf(v) != -1){
5070                 r[++ri] = ci;
5071             }
5072         }
5073         return r;
5074     };
5075
5076     function attrValue(n, attr){
5077         if(!n.tagName && typeof n.length != "undefined"){
5078             n = n[0];
5079         }
5080         if(!n){
5081             return null;
5082         }
5083         if(attr == "for"){
5084             return n.htmlFor;
5085         }
5086         if(attr == "class" || attr == "className"){
5087             return n.className;
5088         }
5089         return n.getAttribute(attr) || n[attr];
5090
5091     };
5092
5093     function getNodes(ns, mode, tagName){
5094         var result = [], ri = -1, cs;
5095         if(!ns){
5096             return result;
5097         }
5098         tagName = tagName || "*";
5099         if(typeof ns.getElementsByTagName != "undefined"){
5100             ns = [ns];
5101         }
5102         if(!mode){
5103             for(var i = 0, ni; ni = ns[i]; i++){
5104                 cs = ni.getElementsByTagName(tagName);
5105                 for(var j = 0, ci; ci = cs[j]; j++){
5106                     result[++ri] = ci;
5107                 }
5108             }
5109         }else if(mode == "/" || mode == ">"){
5110             var utag = tagName.toUpperCase();
5111             for(var i = 0, ni, cn; ni = ns[i]; i++){
5112                 cn = ni.children || ni.childNodes;
5113                 for(var j = 0, cj; cj = cn[j]; j++){
5114                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5115                         result[++ri] = cj;
5116                     }
5117                 }
5118             }
5119         }else if(mode == "+"){
5120             var utag = tagName.toUpperCase();
5121             for(var i = 0, n; n = ns[i]; i++){
5122                 while((n = n.nextSibling) && n.nodeType != 1);
5123                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5124                     result[++ri] = n;
5125                 }
5126             }
5127         }else if(mode == "~"){
5128             for(var i = 0, n; n = ns[i]; i++){
5129                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5130                 if(n){
5131                     result[++ri] = n;
5132                 }
5133             }
5134         }
5135         return result;
5136     };
5137
5138     function concat(a, b){
5139         if(b.slice){
5140             return a.concat(b);
5141         }
5142         for(var i = 0, l = b.length; i < l; i++){
5143             a[a.length] = b[i];
5144         }
5145         return a;
5146     }
5147
5148     function byTag(cs, tagName){
5149         if(cs.tagName || cs == document){
5150             cs = [cs];
5151         }
5152         if(!tagName){
5153             return cs;
5154         }
5155         var r = [], ri = -1;
5156         tagName = tagName.toLowerCase();
5157         for(var i = 0, ci; ci = cs[i]; i++){
5158             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5159                 r[++ri] = ci;
5160             }
5161         }
5162         return r;
5163     };
5164
5165     function byId(cs, attr, id){
5166         if(cs.tagName || cs == document){
5167             cs = [cs];
5168         }
5169         if(!id){
5170             return cs;
5171         }
5172         var r = [], ri = -1;
5173         for(var i = 0,ci; ci = cs[i]; i++){
5174             if(ci && ci.id == id){
5175                 r[++ri] = ci;
5176                 return r;
5177             }
5178         }
5179         return r;
5180     };
5181
5182     function byAttribute(cs, attr, value, op, custom){
5183         var r = [], ri = -1, st = custom=="{";
5184         var f = Roo.DomQuery.operators[op];
5185         for(var i = 0, ci; ci = cs[i]; i++){
5186             var a;
5187             if(st){
5188                 a = Roo.DomQuery.getStyle(ci, attr);
5189             }
5190             else if(attr == "class" || attr == "className"){
5191                 a = ci.className;
5192             }else if(attr == "for"){
5193                 a = ci.htmlFor;
5194             }else if(attr == "href"){
5195                 a = ci.getAttribute("href", 2);
5196             }else{
5197                 a = ci.getAttribute(attr);
5198             }
5199             if((f && f(a, value)) || (!f && a)){
5200                 r[++ri] = ci;
5201             }
5202         }
5203         return r;
5204     };
5205
5206     function byPseudo(cs, name, value){
5207         return Roo.DomQuery.pseudos[name](cs, value);
5208     };
5209
5210     // This is for IE MSXML which does not support expandos.
5211     // IE runs the same speed using setAttribute, however FF slows way down
5212     // and Safari completely fails so they need to continue to use expandos.
5213     var isIE = window.ActiveXObject ? true : false;
5214
5215     // this eval is stop the compressor from
5216     // renaming the variable to something shorter
5217     
5218     /** eval:var:batch */
5219     var batch = 30803; 
5220
5221     var key = 30803;
5222
5223     function nodupIEXml(cs){
5224         var d = ++key;
5225         cs[0].setAttribute("_nodup", d);
5226         var r = [cs[0]];
5227         for(var i = 1, len = cs.length; i < len; i++){
5228             var c = cs[i];
5229             if(!c.getAttribute("_nodup") != d){
5230                 c.setAttribute("_nodup", d);
5231                 r[r.length] = c;
5232             }
5233         }
5234         for(var i = 0, len = cs.length; i < len; i++){
5235             cs[i].removeAttribute("_nodup");
5236         }
5237         return r;
5238     }
5239
5240     function nodup(cs){
5241         if(!cs){
5242             return [];
5243         }
5244         var len = cs.length, c, i, r = cs, cj, ri = -1;
5245         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5246             return cs;
5247         }
5248         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5249             return nodupIEXml(cs);
5250         }
5251         var d = ++key;
5252         cs[0]._nodup = d;
5253         for(i = 1; c = cs[i]; i++){
5254             if(c._nodup != d){
5255                 c._nodup = d;
5256             }else{
5257                 r = [];
5258                 for(var j = 0; j < i; j++){
5259                     r[++ri] = cs[j];
5260                 }
5261                 for(j = i+1; cj = cs[j]; j++){
5262                     if(cj._nodup != d){
5263                         cj._nodup = d;
5264                         r[++ri] = cj;
5265                     }
5266                 }
5267                 return r;
5268             }
5269         }
5270         return r;
5271     }
5272
5273     function quickDiffIEXml(c1, c2){
5274         var d = ++key;
5275         for(var i = 0, len = c1.length; i < len; i++){
5276             c1[i].setAttribute("_qdiff", d);
5277         }
5278         var r = [];
5279         for(var i = 0, len = c2.length; i < len; i++){
5280             if(c2[i].getAttribute("_qdiff") != d){
5281                 r[r.length] = c2[i];
5282             }
5283         }
5284         for(var i = 0, len = c1.length; i < len; i++){
5285            c1[i].removeAttribute("_qdiff");
5286         }
5287         return r;
5288     }
5289
5290     function quickDiff(c1, c2){
5291         var len1 = c1.length;
5292         if(!len1){
5293             return c2;
5294         }
5295         if(isIE && c1[0].selectSingleNode){
5296             return quickDiffIEXml(c1, c2);
5297         }
5298         var d = ++key;
5299         for(var i = 0; i < len1; i++){
5300             c1[i]._qdiff = d;
5301         }
5302         var r = [];
5303         for(var i = 0, len = c2.length; i < len; i++){
5304             if(c2[i]._qdiff != d){
5305                 r[r.length] = c2[i];
5306             }
5307         }
5308         return r;
5309     }
5310
5311     function quickId(ns, mode, root, id){
5312         if(ns == root){
5313            var d = root.ownerDocument || root;
5314            return d.getElementById(id);
5315         }
5316         ns = getNodes(ns, mode, "*");
5317         return byId(ns, null, id);
5318     }
5319
5320     return {
5321         getStyle : function(el, name){
5322             return Roo.fly(el).getStyle(name);
5323         },
5324         /**
5325          * Compiles a selector/xpath query into a reusable function. The returned function
5326          * takes one parameter "root" (optional), which is the context node from where the query should start.
5327          * @param {String} selector The selector/xpath query
5328          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5329          * @return {Function}
5330          */
5331         compile : function(path, type){
5332             type = type || "select";
5333             
5334             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5335             var q = path, mode, lq;
5336             var tk = Roo.DomQuery.matchers;
5337             var tklen = tk.length;
5338             var mm;
5339
5340             // accept leading mode switch
5341             var lmode = q.match(modeRe);
5342             if(lmode && lmode[1]){
5343                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5344                 q = q.replace(lmode[1], "");
5345             }
5346             // strip leading slashes
5347             while(path.substr(0, 1)=="/"){
5348                 path = path.substr(1);
5349             }
5350
5351             while(q && lq != q){
5352                 lq = q;
5353                 var tm = q.match(tagTokenRe);
5354                 if(type == "select"){
5355                     if(tm){
5356                         if(tm[1] == "#"){
5357                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5358                         }else{
5359                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5360                         }
5361                         q = q.replace(tm[0], "");
5362                     }else if(q.substr(0, 1) != '@'){
5363                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5364                     }
5365                 }else{
5366                     if(tm){
5367                         if(tm[1] == "#"){
5368                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5369                         }else{
5370                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5371                         }
5372                         q = q.replace(tm[0], "");
5373                     }
5374                 }
5375                 while(!(mm = q.match(modeRe))){
5376                     var matched = false;
5377                     for(var j = 0; j < tklen; j++){
5378                         var t = tk[j];
5379                         var m = q.match(t.re);
5380                         if(m){
5381                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5382                                                     return m[i];
5383                                                 });
5384                             q = q.replace(m[0], "");
5385                             matched = true;
5386                             break;
5387                         }
5388                     }
5389                     // prevent infinite loop on bad selector
5390                     if(!matched){
5391                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5392                     }
5393                 }
5394                 if(mm[1]){
5395                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5396                     q = q.replace(mm[1], "");
5397                 }
5398             }
5399             fn[fn.length] = "return nodup(n);\n}";
5400             
5401              /** 
5402               * list of variables that need from compression as they are used by eval.
5403              *  eval:var:batch 
5404              *  eval:var:nodup
5405              *  eval:var:byTag
5406              *  eval:var:ById
5407              *  eval:var:getNodes
5408              *  eval:var:quickId
5409              *  eval:var:mode
5410              *  eval:var:root
5411              *  eval:var:n
5412              *  eval:var:byClassName
5413              *  eval:var:byPseudo
5414              *  eval:var:byAttribute
5415              *  eval:var:attrValue
5416              * 
5417              **/ 
5418             eval(fn.join(""));
5419             return f;
5420         },
5421
5422         /**
5423          * Selects a group of elements.
5424          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5425          * @param {Node} root (optional) The start of the query (defaults to document).
5426          * @return {Array}
5427          */
5428         select : function(path, root, type){
5429             if(!root || root == document){
5430                 root = document;
5431             }
5432             if(typeof root == "string"){
5433                 root = document.getElementById(root);
5434             }
5435             var paths = path.split(",");
5436             var results = [];
5437             for(var i = 0, len = paths.length; i < len; i++){
5438                 var p = paths[i].replace(trimRe, "");
5439                 if(!cache[p]){
5440                     cache[p] = Roo.DomQuery.compile(p);
5441                     if(!cache[p]){
5442                         throw p + " is not a valid selector";
5443                     }
5444                 }
5445                 var result = cache[p](root);
5446                 if(result && result != document){
5447                     results = results.concat(result);
5448                 }
5449             }
5450             if(paths.length > 1){
5451                 return nodup(results);
5452             }
5453             return results;
5454         },
5455
5456         /**
5457          * Selects a single element.
5458          * @param {String} selector The selector/xpath query
5459          * @param {Node} root (optional) The start of the query (defaults to document).
5460          * @return {Element}
5461          */
5462         selectNode : function(path, root){
5463             return Roo.DomQuery.select(path, root)[0];
5464         },
5465
5466         /**
5467          * Selects the value of a node, optionally replacing null with the defaultValue.
5468          * @param {String} selector The selector/xpath query
5469          * @param {Node} root (optional) The start of the query (defaults to document).
5470          * @param {String} defaultValue
5471          */
5472         selectValue : function(path, root, defaultValue){
5473             path = path.replace(trimRe, "");
5474             if(!valueCache[path]){
5475                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5476             }
5477             var n = valueCache[path](root);
5478             n = n[0] ? n[0] : n;
5479             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5480             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5481         },
5482
5483         /**
5484          * Selects the value of a node, parsing integers and floats.
5485          * @param {String} selector The selector/xpath query
5486          * @param {Node} root (optional) The start of the query (defaults to document).
5487          * @param {Number} defaultValue
5488          * @return {Number}
5489          */
5490         selectNumber : function(path, root, defaultValue){
5491             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5492             return parseFloat(v);
5493         },
5494
5495         /**
5496          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5497          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5498          * @param {String} selector The simple selector to test
5499          * @return {Boolean}
5500          */
5501         is : function(el, ss){
5502             if(typeof el == "string"){
5503                 el = document.getElementById(el);
5504             }
5505             var isArray = (el instanceof Array);
5506             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5507             return isArray ? (result.length == el.length) : (result.length > 0);
5508         },
5509
5510         /**
5511          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5512          * @param {Array} el An array of elements to filter
5513          * @param {String} selector The simple selector to test
5514          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5515          * the selector instead of the ones that match
5516          * @return {Array}
5517          */
5518         filter : function(els, ss, nonMatches){
5519             ss = ss.replace(trimRe, "");
5520             if(!simpleCache[ss]){
5521                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5522             }
5523             var result = simpleCache[ss](els);
5524             return nonMatches ? quickDiff(result, els) : result;
5525         },
5526
5527         /**
5528          * Collection of matching regular expressions and code snippets.
5529          */
5530         matchers : [{
5531                 re: /^\.([\w-]+)/,
5532                 select: 'n = byClassName(n, null, " {1} ");'
5533             }, {
5534                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5535                 select: 'n = byPseudo(n, "{1}", "{2}");'
5536             },{
5537                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5538                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5539             }, {
5540                 re: /^#([\w-]+)/,
5541                 select: 'n = byId(n, null, "{1}");'
5542             },{
5543                 re: /^@([\w-]+)/,
5544                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5545             }
5546         ],
5547
5548         /**
5549          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5550          * 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;.
5551          */
5552         operators : {
5553             "=" : function(a, v){
5554                 return a == v;
5555             },
5556             "!=" : function(a, v){
5557                 return a != v;
5558             },
5559             "^=" : function(a, v){
5560                 return a && a.substr(0, v.length) == v;
5561             },
5562             "$=" : function(a, v){
5563                 return a && a.substr(a.length-v.length) == v;
5564             },
5565             "*=" : function(a, v){
5566                 return a && a.indexOf(v) !== -1;
5567             },
5568             "%=" : function(a, v){
5569                 return (a % v) == 0;
5570             },
5571             "|=" : function(a, v){
5572                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5573             },
5574             "~=" : function(a, v){
5575                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5576             }
5577         },
5578
5579         /**
5580          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5581          * and the argument (if any) supplied in the selector.
5582          */
5583         pseudos : {
5584             "first-child" : function(c){
5585                 var r = [], ri = -1, n;
5586                 for(var i = 0, ci; ci = n = c[i]; i++){
5587                     while((n = n.previousSibling) && n.nodeType != 1);
5588                     if(!n){
5589                         r[++ri] = ci;
5590                     }
5591                 }
5592                 return r;
5593             },
5594
5595             "last-child" : function(c){
5596                 var r = [], ri = -1, n;
5597                 for(var i = 0, ci; ci = n = c[i]; i++){
5598                     while((n = n.nextSibling) && n.nodeType != 1);
5599                     if(!n){
5600                         r[++ri] = ci;
5601                     }
5602                 }
5603                 return r;
5604             },
5605
5606             "nth-child" : function(c, a) {
5607                 var r = [], ri = -1;
5608                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5609                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5610                 for(var i = 0, n; n = c[i]; i++){
5611                     var pn = n.parentNode;
5612                     if (batch != pn._batch) {
5613                         var j = 0;
5614                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5615                             if(cn.nodeType == 1){
5616                                cn.nodeIndex = ++j;
5617                             }
5618                         }
5619                         pn._batch = batch;
5620                     }
5621                     if (f == 1) {
5622                         if (l == 0 || n.nodeIndex == l){
5623                             r[++ri] = n;
5624                         }
5625                     } else if ((n.nodeIndex + l) % f == 0){
5626                         r[++ri] = n;
5627                     }
5628                 }
5629
5630                 return r;
5631             },
5632
5633             "only-child" : function(c){
5634                 var r = [], ri = -1;;
5635                 for(var i = 0, ci; ci = c[i]; i++){
5636                     if(!prev(ci) && !next(ci)){
5637                         r[++ri] = ci;
5638                     }
5639                 }
5640                 return r;
5641             },
5642
5643             "empty" : function(c){
5644                 var r = [], ri = -1;
5645                 for(var i = 0, ci; ci = c[i]; i++){
5646                     var cns = ci.childNodes, j = 0, cn, empty = true;
5647                     while(cn = cns[j]){
5648                         ++j;
5649                         if(cn.nodeType == 1 || cn.nodeType == 3){
5650                             empty = false;
5651                             break;
5652                         }
5653                     }
5654                     if(empty){
5655                         r[++ri] = ci;
5656                     }
5657                 }
5658                 return r;
5659             },
5660
5661             "contains" : function(c, v){
5662                 var r = [], ri = -1;
5663                 for(var i = 0, ci; ci = c[i]; i++){
5664                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5665                         r[++ri] = ci;
5666                     }
5667                 }
5668                 return r;
5669             },
5670
5671             "nodeValue" : function(c, v){
5672                 var r = [], ri = -1;
5673                 for(var i = 0, ci; ci = c[i]; i++){
5674                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5675                         r[++ri] = ci;
5676                     }
5677                 }
5678                 return r;
5679             },
5680
5681             "checked" : function(c){
5682                 var r = [], ri = -1;
5683                 for(var i = 0, ci; ci = c[i]; i++){
5684                     if(ci.checked == true){
5685                         r[++ri] = ci;
5686                     }
5687                 }
5688                 return r;
5689             },
5690
5691             "not" : function(c, ss){
5692                 return Roo.DomQuery.filter(c, ss, true);
5693             },
5694
5695             "odd" : function(c){
5696                 return this["nth-child"](c, "odd");
5697             },
5698
5699             "even" : function(c){
5700                 return this["nth-child"](c, "even");
5701             },
5702
5703             "nth" : function(c, a){
5704                 return c[a-1] || [];
5705             },
5706
5707             "first" : function(c){
5708                 return c[0] || [];
5709             },
5710
5711             "last" : function(c){
5712                 return c[c.length-1] || [];
5713             },
5714
5715             "has" : function(c, ss){
5716                 var s = Roo.DomQuery.select;
5717                 var r = [], ri = -1;
5718                 for(var i = 0, ci; ci = c[i]; i++){
5719                     if(s(ss, ci).length > 0){
5720                         r[++ri] = ci;
5721                     }
5722                 }
5723                 return r;
5724             },
5725
5726             "next" : function(c, ss){
5727                 var is = Roo.DomQuery.is;
5728                 var r = [], ri = -1;
5729                 for(var i = 0, ci; ci = c[i]; i++){
5730                     var n = next(ci);
5731                     if(n && is(n, ss)){
5732                         r[++ri] = ci;
5733                     }
5734                 }
5735                 return r;
5736             },
5737
5738             "prev" : function(c, ss){
5739                 var is = Roo.DomQuery.is;
5740                 var r = [], ri = -1;
5741                 for(var i = 0, ci; ci = c[i]; i++){
5742                     var n = prev(ci);
5743                     if(n && is(n, ss)){
5744                         r[++ri] = ci;
5745                     }
5746                 }
5747                 return r;
5748             }
5749         }
5750     };
5751 }();
5752
5753 /**
5754  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5755  * @param {String} path The selector/xpath query
5756  * @param {Node} root (optional) The start of the query (defaults to document).
5757  * @return {Array}
5758  * @member Roo
5759  * @method query
5760  */
5761 Roo.query = Roo.DomQuery.select;
5762 /*
5763  * Based on:
5764  * Ext JS Library 1.1.1
5765  * Copyright(c) 2006-2007, Ext JS, LLC.
5766  *
5767  * Originally Released Under LGPL - original licence link has changed is not relivant.
5768  *
5769  * Fork - LGPL
5770  * <script type="text/javascript">
5771  */
5772
5773 /**
5774  * @class Roo.util.Observable
5775  * Base class that provides a common interface for publishing events. Subclasses are expected to
5776  * to have a property "events" with all the events defined.<br>
5777  * For example:
5778  * <pre><code>
5779  Employee = function(name){
5780     this.name = name;
5781     this.addEvents({
5782         "fired" : true,
5783         "quit" : true
5784     });
5785  }
5786  Roo.extend(Employee, Roo.util.Observable);
5787 </code></pre>
5788  * @param {Object} config properties to use (incuding events / listeners)
5789  */
5790
5791 Roo.util.Observable = function(cfg){
5792     
5793     cfg = cfg|| {};
5794     this.addEvents(cfg.events || {});
5795     if (cfg.events) {
5796         delete cfg.events; // make sure
5797     }
5798      
5799     Roo.apply(this, cfg);
5800     
5801     if(this.listeners){
5802         this.on(this.listeners);
5803         delete this.listeners;
5804     }
5805 };
5806 Roo.util.Observable.prototype = {
5807     /** 
5808  * @cfg {Object} listeners  list of events and functions to call for this object, 
5809  * For example :
5810  * <pre><code>
5811     listeners :  { 
5812        'click' : function(e) {
5813            ..... 
5814         } ,
5815         .... 
5816     } 
5817   </code></pre>
5818  */
5819     
5820     
5821     /**
5822      * Fires the specified event with the passed parameters (minus the event name).
5823      * @param {String} eventName
5824      * @param {Object...} args Variable number of parameters are passed to handlers
5825      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5826      */
5827     fireEvent : function(){
5828         var ce = this.events[arguments[0].toLowerCase()];
5829         if(typeof ce == "object"){
5830             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5831         }else{
5832             return true;
5833         }
5834     },
5835
5836     // private
5837     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5838
5839     /**
5840      * Appends an event handler to this component
5841      * @param {String}   eventName The type of event to listen for
5842      * @param {Function} handler The method the event invokes
5843      * @param {Object}   scope (optional) The scope in which to execute the handler
5844      * function. The handler function's "this" context.
5845      * @param {Object}   options (optional) An object containing handler configuration
5846      * properties. This may contain any of the following properties:<ul>
5847      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5848      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5849      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5850      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5851      * by the specified number of milliseconds. If the event fires again within that time, the original
5852      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5853      * </ul><br>
5854      * <p>
5855      * <b>Combining Options</b><br>
5856      * Using the options argument, it is possible to combine different types of listeners:<br>
5857      * <br>
5858      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5859                 <pre><code>
5860                 el.on('click', this.onClick, this, {
5861                         single: true,
5862                 delay: 100,
5863                 forumId: 4
5864                 });
5865                 </code></pre>
5866      * <p>
5867      * <b>Attaching multiple handlers in 1 call</b><br>
5868      * The method also allows for a single argument to be passed which is a config object containing properties
5869      * which specify multiple handlers.
5870      * <pre><code>
5871                 el.on({
5872                         'click': {
5873                         fn: this.onClick,
5874                         scope: this,
5875                         delay: 100
5876                 }, 
5877                 'mouseover': {
5878                         fn: this.onMouseOver,
5879                         scope: this
5880                 },
5881                 'mouseout': {
5882                         fn: this.onMouseOut,
5883                         scope: this
5884                 }
5885                 });
5886                 </code></pre>
5887      * <p>
5888      * Or a shorthand syntax which passes the same scope object to all handlers:
5889         <pre><code>
5890                 el.on({
5891                         'click': this.onClick,
5892                 'mouseover': this.onMouseOver,
5893                 'mouseout': this.onMouseOut,
5894                 scope: this
5895                 });
5896                 </code></pre>
5897      */
5898     addListener : function(eventName, fn, scope, o){
5899         if(typeof eventName == "object"){
5900             o = eventName;
5901             for(var e in o){
5902                 if(this.filterOptRe.test(e)){
5903                     continue;
5904                 }
5905                 if(typeof o[e] == "function"){
5906                     // shared options
5907                     this.addListener(e, o[e], o.scope,  o);
5908                 }else{
5909                     // individual options
5910                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5911                 }
5912             }
5913             return;
5914         }
5915         o = (!o || typeof o == "boolean") ? {} : o;
5916         eventName = eventName.toLowerCase();
5917         var ce = this.events[eventName] || true;
5918         if(typeof ce == "boolean"){
5919             ce = new Roo.util.Event(this, eventName);
5920             this.events[eventName] = ce;
5921         }
5922         ce.addListener(fn, scope, o);
5923     },
5924
5925     /**
5926      * Removes a listener
5927      * @param {String}   eventName     The type of event to listen for
5928      * @param {Function} handler        The handler to remove
5929      * @param {Object}   scope  (optional) The scope (this object) for the handler
5930      */
5931     removeListener : function(eventName, fn, scope){
5932         var ce = this.events[eventName.toLowerCase()];
5933         if(typeof ce == "object"){
5934             ce.removeListener(fn, scope);
5935         }
5936     },
5937
5938     /**
5939      * Removes all listeners for this object
5940      */
5941     purgeListeners : function(){
5942         for(var evt in this.events){
5943             if(typeof this.events[evt] == "object"){
5944                  this.events[evt].clearListeners();
5945             }
5946         }
5947     },
5948
5949     relayEvents : function(o, events){
5950         var createHandler = function(ename){
5951             return function(){
5952                  
5953                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5954             };
5955         };
5956         for(var i = 0, len = events.length; i < len; i++){
5957             var ename = events[i];
5958             if(!this.events[ename]){
5959                 this.events[ename] = true;
5960             };
5961             o.on(ename, createHandler(ename), this);
5962         }
5963     },
5964
5965     /**
5966      * Used to define events on this Observable
5967      * @param {Object} object The object with the events defined
5968      */
5969     addEvents : function(o){
5970         if(!this.events){
5971             this.events = {};
5972         }
5973         Roo.applyIf(this.events, o);
5974     },
5975
5976     /**
5977      * Checks to see if this object has any listeners for a specified event
5978      * @param {String} eventName The name of the event to check for
5979      * @return {Boolean} True if the event is being listened for, else false
5980      */
5981     hasListener : function(eventName){
5982         var e = this.events[eventName];
5983         return typeof e == "object" && e.listeners.length > 0;
5984     }
5985 };
5986 /**
5987  * Appends an event handler to this element (shorthand for addListener)
5988  * @param {String}   eventName     The type of event to listen for
5989  * @param {Function} handler        The method the event invokes
5990  * @param {Object}   scope (optional) The scope in which to execute the handler
5991  * function. The handler function's "this" context.
5992  * @param {Object}   options  (optional)
5993  * @method
5994  */
5995 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5996 /**
5997  * Removes a listener (shorthand for removeListener)
5998  * @param {String}   eventName     The type of event to listen for
5999  * @param {Function} handler        The handler to remove
6000  * @param {Object}   scope  (optional) The scope (this object) for the handler
6001  * @method
6002  */
6003 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
6004
6005 /**
6006  * Starts capture on the specified Observable. All events will be passed
6007  * to the supplied function with the event name + standard signature of the event
6008  * <b>before</b> the event is fired. If the supplied function returns false,
6009  * the event will not fire.
6010  * @param {Observable} o The Observable to capture
6011  * @param {Function} fn The function to call
6012  * @param {Object} scope (optional) The scope (this object) for the fn
6013  * @static
6014  */
6015 Roo.util.Observable.capture = function(o, fn, scope){
6016     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
6017 };
6018
6019 /**
6020  * Removes <b>all</b> added captures from the Observable.
6021  * @param {Observable} o The Observable to release
6022  * @static
6023  */
6024 Roo.util.Observable.releaseCapture = function(o){
6025     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6026 };
6027
6028 (function(){
6029
6030     var createBuffered = function(h, o, scope){
6031         var task = new Roo.util.DelayedTask();
6032         return function(){
6033             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6034         };
6035     };
6036
6037     var createSingle = function(h, e, fn, scope){
6038         return function(){
6039             e.removeListener(fn, scope);
6040             return h.apply(scope, arguments);
6041         };
6042     };
6043
6044     var createDelayed = function(h, o, scope){
6045         return function(){
6046             var args = Array.prototype.slice.call(arguments, 0);
6047             setTimeout(function(){
6048                 h.apply(scope, args);
6049             }, o.delay || 10);
6050         };
6051     };
6052
6053     Roo.util.Event = function(obj, name){
6054         this.name = name;
6055         this.obj = obj;
6056         this.listeners = [];
6057     };
6058
6059     Roo.util.Event.prototype = {
6060         addListener : function(fn, scope, options){
6061             var o = options || {};
6062             scope = scope || this.obj;
6063             if(!this.isListening(fn, scope)){
6064                 var l = {fn: fn, scope: scope, options: o};
6065                 var h = fn;
6066                 if(o.delay){
6067                     h = createDelayed(h, o, scope);
6068                 }
6069                 if(o.single){
6070                     h = createSingle(h, this, fn, scope);
6071                 }
6072                 if(o.buffer){
6073                     h = createBuffered(h, o, scope);
6074                 }
6075                 l.fireFn = h;
6076                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6077                     this.listeners.push(l);
6078                 }else{
6079                     this.listeners = this.listeners.slice(0);
6080                     this.listeners.push(l);
6081                 }
6082             }
6083         },
6084
6085         findListener : function(fn, scope){
6086             scope = scope || this.obj;
6087             var ls = this.listeners;
6088             for(var i = 0, len = ls.length; i < len; i++){
6089                 var l = ls[i];
6090                 if(l.fn == fn && l.scope == scope){
6091                     return i;
6092                 }
6093             }
6094             return -1;
6095         },
6096
6097         isListening : function(fn, scope){
6098             return this.findListener(fn, scope) != -1;
6099         },
6100
6101         removeListener : function(fn, scope){
6102             var index;
6103             if((index = this.findListener(fn, scope)) != -1){
6104                 if(!this.firing){
6105                     this.listeners.splice(index, 1);
6106                 }else{
6107                     this.listeners = this.listeners.slice(0);
6108                     this.listeners.splice(index, 1);
6109                 }
6110                 return true;
6111             }
6112             return false;
6113         },
6114
6115         clearListeners : function(){
6116             this.listeners = [];
6117         },
6118
6119         fire : function(){
6120             var ls = this.listeners, scope, len = ls.length;
6121             if(len > 0){
6122                 this.firing = true;
6123                 var args = Array.prototype.slice.call(arguments, 0);                
6124                 for(var i = 0; i < len; i++){
6125                     var l = ls[i];
6126                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6127                         this.firing = false;
6128                         return false;
6129                     }
6130                 }
6131                 this.firing = false;
6132             }
6133             return true;
6134         }
6135     };
6136 })();/*
6137  * RooJS Library 
6138  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6139  *
6140  * Licence LGPL 
6141  *
6142  */
6143  
6144 /**
6145  * @class Roo.Document
6146  * @extends Roo.util.Observable
6147  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6148  * 
6149  * @param {Object} config the methods and properties of the 'base' class for the application.
6150  * 
6151  *  Generic Page handler - implement this to start your app..
6152  * 
6153  * eg.
6154  *  MyProject = new Roo.Document({
6155         events : {
6156             'load' : true // your events..
6157         },
6158         listeners : {
6159             'ready' : function() {
6160                 // fired on Roo.onReady()
6161             }
6162         }
6163  * 
6164  */
6165 Roo.Document = function(cfg) {
6166      
6167     this.addEvents({ 
6168         'ready' : true
6169     });
6170     Roo.util.Observable.call(this,cfg);
6171     
6172     var _this = this;
6173     
6174     Roo.onReady(function() {
6175         _this.fireEvent('ready');
6176     },null,false);
6177     
6178     
6179 }
6180
6181 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6182  * Based on:
6183  * Ext JS Library 1.1.1
6184  * Copyright(c) 2006-2007, Ext JS, LLC.
6185  *
6186  * Originally Released Under LGPL - original licence link has changed is not relivant.
6187  *
6188  * Fork - LGPL
6189  * <script type="text/javascript">
6190  */
6191
6192 /**
6193  * @class Roo.EventManager
6194  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6195  * several useful events directly.
6196  * See {@link Roo.EventObject} for more details on normalized event objects.
6197  * @singleton
6198  */
6199 Roo.EventManager = function(){
6200     var docReadyEvent, docReadyProcId, docReadyState = false;
6201     var resizeEvent, resizeTask, textEvent, textSize;
6202     var E = Roo.lib.Event;
6203     var D = Roo.lib.Dom;
6204
6205     
6206     
6207
6208     var fireDocReady = function(){
6209         if(!docReadyState){
6210             docReadyState = true;
6211             Roo.isReady = true;
6212             if(docReadyProcId){
6213                 clearInterval(docReadyProcId);
6214             }
6215             if(Roo.isGecko || Roo.isOpera) {
6216                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6217             }
6218             if(Roo.isIE){
6219                 var defer = document.getElementById("ie-deferred-loader");
6220                 if(defer){
6221                     defer.onreadystatechange = null;
6222                     defer.parentNode.removeChild(defer);
6223                 }
6224             }
6225             if(docReadyEvent){
6226                 docReadyEvent.fire();
6227                 docReadyEvent.clearListeners();
6228             }
6229         }
6230     };
6231     
6232     var initDocReady = function(){
6233         docReadyEvent = new Roo.util.Event();
6234         if(Roo.isGecko || Roo.isOpera) {
6235             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6236         }else if(Roo.isIE){
6237             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6238             var defer = document.getElementById("ie-deferred-loader");
6239             defer.onreadystatechange = function(){
6240                 if(this.readyState == "complete"){
6241                     fireDocReady();
6242                 }
6243             };
6244         }else if(Roo.isSafari){ 
6245             docReadyProcId = setInterval(function(){
6246                 var rs = document.readyState;
6247                 if(rs == "complete") {
6248                     fireDocReady();     
6249                  }
6250             }, 10);
6251         }
6252         // no matter what, make sure it fires on load
6253         E.on(window, "load", fireDocReady);
6254     };
6255
6256     var createBuffered = function(h, o){
6257         var task = new Roo.util.DelayedTask(h);
6258         return function(e){
6259             // create new event object impl so new events don't wipe out properties
6260             e = new Roo.EventObjectImpl(e);
6261             task.delay(o.buffer, h, null, [e]);
6262         };
6263     };
6264
6265     var createSingle = function(h, el, ename, fn){
6266         return function(e){
6267             Roo.EventManager.removeListener(el, ename, fn);
6268             h(e);
6269         };
6270     };
6271
6272     var createDelayed = function(h, o){
6273         return function(e){
6274             // create new event object impl so new events don't wipe out properties
6275             e = new Roo.EventObjectImpl(e);
6276             setTimeout(function(){
6277                 h(e);
6278             }, o.delay || 10);
6279         };
6280     };
6281     var transitionEndVal = false;
6282     
6283     var transitionEnd = function()
6284     {
6285         if (transitionEndVal) {
6286             return transitionEndVal;
6287         }
6288         var el = document.createElement('div');
6289
6290         var transEndEventNames = {
6291             WebkitTransition : 'webkitTransitionEnd',
6292             MozTransition    : 'transitionend',
6293             OTransition      : 'oTransitionEnd otransitionend',
6294             transition       : 'transitionend'
6295         };
6296     
6297         for (var name in transEndEventNames) {
6298             if (el.style[name] !== undefined) {
6299                 transitionEndVal = transEndEventNames[name];
6300                 return  transitionEndVal ;
6301             }
6302         }
6303     }
6304     
6305   
6306
6307     var listen = function(element, ename, opt, fn, scope)
6308     {
6309         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6310         fn = fn || o.fn; scope = scope || o.scope;
6311         var el = Roo.getDom(element);
6312         
6313         
6314         if(!el){
6315             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6316         }
6317         
6318         if (ename == 'transitionend') {
6319             ename = transitionEnd();
6320         }
6321         var h = function(e){
6322             e = Roo.EventObject.setEvent(e);
6323             var t;
6324             if(o.delegate){
6325                 t = e.getTarget(o.delegate, el);
6326                 if(!t){
6327                     return;
6328                 }
6329             }else{
6330                 t = e.target;
6331             }
6332             if(o.stopEvent === true){
6333                 e.stopEvent();
6334             }
6335             if(o.preventDefault === true){
6336                e.preventDefault();
6337             }
6338             if(o.stopPropagation === true){
6339                 e.stopPropagation();
6340             }
6341
6342             if(o.normalized === false){
6343                 e = e.browserEvent;
6344             }
6345
6346             fn.call(scope || el, e, t, o);
6347         };
6348         if(o.delay){
6349             h = createDelayed(h, o);
6350         }
6351         if(o.single){
6352             h = createSingle(h, el, ename, fn);
6353         }
6354         if(o.buffer){
6355             h = createBuffered(h, o);
6356         }
6357         
6358         fn._handlers = fn._handlers || [];
6359         
6360         
6361         fn._handlers.push([Roo.id(el), ename, h]);
6362         
6363         
6364          
6365         E.on(el, ename, h); // this adds the actuall listener to the object..
6366         
6367         
6368         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6369             el.addEventListener("DOMMouseScroll", h, false);
6370             E.on(window, 'unload', function(){
6371                 el.removeEventListener("DOMMouseScroll", h, false);
6372             });
6373         }
6374         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6375             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6376         }
6377         return h;
6378     };
6379
6380     var stopListening = function(el, ename, fn){
6381         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6382         if(hds){
6383             for(var i = 0, len = hds.length; i < len; i++){
6384                 var h = hds[i];
6385                 if(h[0] == id && h[1] == ename){
6386                     hd = h[2];
6387                     hds.splice(i, 1);
6388                     break;
6389                 }
6390             }
6391         }
6392         E.un(el, ename, hd);
6393         el = Roo.getDom(el);
6394         if(ename == "mousewheel" && el.addEventListener){
6395             el.removeEventListener("DOMMouseScroll", hd, false);
6396         }
6397         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6398             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6399         }
6400     };
6401
6402     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6403     
6404     var pub = {
6405         
6406         
6407         /** 
6408          * Fix for doc tools
6409          * @scope Roo.EventManager
6410          */
6411         
6412         
6413         /** 
6414          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6415          * object with a Roo.EventObject
6416          * @param {Function} fn        The method the event invokes
6417          * @param {Object}   scope    An object that becomes the scope of the handler
6418          * @param {boolean}  override If true, the obj passed in becomes
6419          *                             the execution scope of the listener
6420          * @return {Function} The wrapped function
6421          * @deprecated
6422          */
6423         wrap : function(fn, scope, override){
6424             return function(e){
6425                 Roo.EventObject.setEvent(e);
6426                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6427             };
6428         },
6429         
6430         /**
6431      * Appends an event handler to an element (shorthand for addListener)
6432      * @param {String/HTMLElement}   element        The html element or id to assign the
6433      * @param {String}   eventName The type of event to listen for
6434      * @param {Function} handler The method the event invokes
6435      * @param {Object}   scope (optional) The scope in which to execute the handler
6436      * function. The handler function's "this" context.
6437      * @param {Object}   options (optional) An object containing handler configuration
6438      * properties. This may contain any of the following properties:<ul>
6439      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6440      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6441      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6442      * <li>preventDefault {Boolean} True to prevent the default action</li>
6443      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6444      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6445      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6446      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6447      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6448      * by the specified number of milliseconds. If the event fires again within that time, the original
6449      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6450      * </ul><br>
6451      * <p>
6452      * <b>Combining Options</b><br>
6453      * Using the options argument, it is possible to combine different types of listeners:<br>
6454      * <br>
6455      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6456      * Code:<pre><code>
6457 el.on('click', this.onClick, this, {
6458     single: true,
6459     delay: 100,
6460     stopEvent : true,
6461     forumId: 4
6462 });</code></pre>
6463      * <p>
6464      * <b>Attaching multiple handlers in 1 call</b><br>
6465       * The method also allows for a single argument to be passed which is a config object containing properties
6466      * which specify multiple handlers.
6467      * <p>
6468      * Code:<pre><code>
6469 el.on({
6470     'click' : {
6471         fn: this.onClick
6472         scope: this,
6473         delay: 100
6474     },
6475     'mouseover' : {
6476         fn: this.onMouseOver
6477         scope: this
6478     },
6479     'mouseout' : {
6480         fn: this.onMouseOut
6481         scope: this
6482     }
6483 });</code></pre>
6484      * <p>
6485      * Or a shorthand syntax:<br>
6486      * Code:<pre><code>
6487 el.on({
6488     'click' : this.onClick,
6489     'mouseover' : this.onMouseOver,
6490     'mouseout' : this.onMouseOut
6491     scope: this
6492 });</code></pre>
6493      */
6494         addListener : function(element, eventName, fn, scope, options){
6495             if(typeof eventName == "object"){
6496                 var o = eventName;
6497                 for(var e in o){
6498                     if(propRe.test(e)){
6499                         continue;
6500                     }
6501                     if(typeof o[e] == "function"){
6502                         // shared options
6503                         listen(element, e, o, o[e], o.scope);
6504                     }else{
6505                         // individual options
6506                         listen(element, e, o[e]);
6507                     }
6508                 }
6509                 return;
6510             }
6511             return listen(element, eventName, options, fn, scope);
6512         },
6513         
6514         /**
6515          * Removes an event handler
6516          *
6517          * @param {String/HTMLElement}   element        The id or html element to remove the 
6518          *                             event from
6519          * @param {String}   eventName     The type of event
6520          * @param {Function} fn
6521          * @return {Boolean} True if a listener was actually removed
6522          */
6523         removeListener : function(element, eventName, fn){
6524             return stopListening(element, eventName, fn);
6525         },
6526         
6527         /**
6528          * Fires when the document is ready (before onload and before images are loaded). Can be 
6529          * accessed shorthanded Roo.onReady().
6530          * @param {Function} fn        The method the event invokes
6531          * @param {Object}   scope    An  object that becomes the scope of the handler
6532          * @param {boolean}  options
6533          */
6534         onDocumentReady : function(fn, scope, options){
6535             if(docReadyState){ // if it already fired
6536                 docReadyEvent.addListener(fn, scope, options);
6537                 docReadyEvent.fire();
6538                 docReadyEvent.clearListeners();
6539                 return;
6540             }
6541             if(!docReadyEvent){
6542                 initDocReady();
6543             }
6544             docReadyEvent.addListener(fn, scope, options);
6545         },
6546         
6547         /**
6548          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6549          * @param {Function} fn        The method the event invokes
6550          * @param {Object}   scope    An object that becomes the scope of the handler
6551          * @param {boolean}  options
6552          */
6553         onWindowResize : function(fn, scope, options){
6554             if(!resizeEvent){
6555                 resizeEvent = new Roo.util.Event();
6556                 resizeTask = new Roo.util.DelayedTask(function(){
6557                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6558                 });
6559                 E.on(window, "resize", function(){
6560                     if(Roo.isIE){
6561                         resizeTask.delay(50);
6562                     }else{
6563                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6564                     }
6565                 });
6566             }
6567             resizeEvent.addListener(fn, scope, options);
6568         },
6569
6570         /**
6571          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6572          * @param {Function} fn        The method the event invokes
6573          * @param {Object}   scope    An object that becomes the scope of the handler
6574          * @param {boolean}  options
6575          */
6576         onTextResize : function(fn, scope, options){
6577             if(!textEvent){
6578                 textEvent = new Roo.util.Event();
6579                 var textEl = new Roo.Element(document.createElement('div'));
6580                 textEl.dom.className = 'x-text-resize';
6581                 textEl.dom.innerHTML = 'X';
6582                 textEl.appendTo(document.body);
6583                 textSize = textEl.dom.offsetHeight;
6584                 setInterval(function(){
6585                     if(textEl.dom.offsetHeight != textSize){
6586                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6587                     }
6588                 }, this.textResizeInterval);
6589             }
6590             textEvent.addListener(fn, scope, options);
6591         },
6592
6593         /**
6594          * Removes the passed window resize listener.
6595          * @param {Function} fn        The method the event invokes
6596          * @param {Object}   scope    The scope of handler
6597          */
6598         removeResizeListener : function(fn, scope){
6599             if(resizeEvent){
6600                 resizeEvent.removeListener(fn, scope);
6601             }
6602         },
6603
6604         // private
6605         fireResize : function(){
6606             if(resizeEvent){
6607                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6608             }   
6609         },
6610         /**
6611          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6612          */
6613         ieDeferSrc : false,
6614         /**
6615          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6616          */
6617         textResizeInterval : 50
6618     };
6619     
6620     /**
6621      * Fix for doc tools
6622      * @scopeAlias pub=Roo.EventManager
6623      */
6624     
6625      /**
6626      * Appends an event handler to an element (shorthand for addListener)
6627      * @param {String/HTMLElement}   element        The html element or id to assign the
6628      * @param {String}   eventName The type of event to listen for
6629      * @param {Function} handler The method the event invokes
6630      * @param {Object}   scope (optional) The scope in which to execute the handler
6631      * function. The handler function's "this" context.
6632      * @param {Object}   options (optional) An object containing handler configuration
6633      * properties. This may contain any of the following properties:<ul>
6634      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6635      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6636      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6637      * <li>preventDefault {Boolean} True to prevent the default action</li>
6638      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6639      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6640      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6641      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6642      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6643      * by the specified number of milliseconds. If the event fires again within that time, the original
6644      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6645      * </ul><br>
6646      * <p>
6647      * <b>Combining Options</b><br>
6648      * Using the options argument, it is possible to combine different types of listeners:<br>
6649      * <br>
6650      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6651      * Code:<pre><code>
6652 el.on('click', this.onClick, this, {
6653     single: true,
6654     delay: 100,
6655     stopEvent : true,
6656     forumId: 4
6657 });</code></pre>
6658      * <p>
6659      * <b>Attaching multiple handlers in 1 call</b><br>
6660       * The method also allows for a single argument to be passed which is a config object containing properties
6661      * which specify multiple handlers.
6662      * <p>
6663      * Code:<pre><code>
6664 el.on({
6665     'click' : {
6666         fn: this.onClick
6667         scope: this,
6668         delay: 100
6669     },
6670     'mouseover' : {
6671         fn: this.onMouseOver
6672         scope: this
6673     },
6674     'mouseout' : {
6675         fn: this.onMouseOut
6676         scope: this
6677     }
6678 });</code></pre>
6679      * <p>
6680      * Or a shorthand syntax:<br>
6681      * Code:<pre><code>
6682 el.on({
6683     'click' : this.onClick,
6684     'mouseover' : this.onMouseOver,
6685     'mouseout' : this.onMouseOut
6686     scope: this
6687 });</code></pre>
6688      */
6689     pub.on = pub.addListener;
6690     pub.un = pub.removeListener;
6691
6692     pub.stoppedMouseDownEvent = new Roo.util.Event();
6693     return pub;
6694 }();
6695 /**
6696   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6697   * @param {Function} fn        The method the event invokes
6698   * @param {Object}   scope    An  object that becomes the scope of the handler
6699   * @param {boolean}  override If true, the obj passed in becomes
6700   *                             the execution scope of the listener
6701   * @member Roo
6702   * @method onReady
6703  */
6704 Roo.onReady = Roo.EventManager.onDocumentReady;
6705
6706 Roo.onReady(function(){
6707     var bd = Roo.get(document.body);
6708     if(!bd){ return; }
6709
6710     var cls = [
6711             Roo.isIE ? "roo-ie"
6712             : Roo.isIE11 ? "roo-ie11"
6713             : Roo.isEdge ? "roo-edge"
6714             : Roo.isGecko ? "roo-gecko"
6715             : Roo.isOpera ? "roo-opera"
6716             : Roo.isSafari ? "roo-safari" : ""];
6717
6718     if(Roo.isMac){
6719         cls.push("roo-mac");
6720     }
6721     if(Roo.isLinux){
6722         cls.push("roo-linux");
6723     }
6724     if(Roo.isIOS){
6725         cls.push("roo-ios");
6726     }
6727     if(Roo.isTouch){
6728         cls.push("roo-touch");
6729     }
6730     if(Roo.isBorderBox){
6731         cls.push('roo-border-box');
6732     }
6733     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6734         var p = bd.dom.parentNode;
6735         if(p){
6736             p.className += ' roo-strict';
6737         }
6738     }
6739     bd.addClass(cls.join(' '));
6740 });
6741
6742 /**
6743  * @class Roo.EventObject
6744  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6745  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6746  * Example:
6747  * <pre><code>
6748  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6749     e.preventDefault();
6750     var target = e.getTarget();
6751     ...
6752  }
6753  var myDiv = Roo.get("myDiv");
6754  myDiv.on("click", handleClick);
6755  //or
6756  Roo.EventManager.on("myDiv", 'click', handleClick);
6757  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6758  </code></pre>
6759  * @singleton
6760  */
6761 Roo.EventObject = function(){
6762     
6763     var E = Roo.lib.Event;
6764     
6765     // safari keypress events for special keys return bad keycodes
6766     var safariKeys = {
6767         63234 : 37, // left
6768         63235 : 39, // right
6769         63232 : 38, // up
6770         63233 : 40, // down
6771         63276 : 33, // page up
6772         63277 : 34, // page down
6773         63272 : 46, // delete
6774         63273 : 36, // home
6775         63275 : 35  // end
6776     };
6777
6778     // normalize button clicks
6779     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6780                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6781
6782     Roo.EventObjectImpl = function(e){
6783         if(e){
6784             this.setEvent(e.browserEvent || e);
6785         }
6786     };
6787     Roo.EventObjectImpl.prototype = {
6788         /**
6789          * Used to fix doc tools.
6790          * @scope Roo.EventObject.prototype
6791          */
6792             
6793
6794         
6795         
6796         /** The normal browser event */
6797         browserEvent : null,
6798         /** The button pressed in a mouse event */
6799         button : -1,
6800         /** True if the shift key was down during the event */
6801         shiftKey : false,
6802         /** True if the control key was down during the event */
6803         ctrlKey : false,
6804         /** True if the alt key was down during the event */
6805         altKey : false,
6806
6807         /** Key constant 
6808         * @type Number */
6809         BACKSPACE : 8,
6810         /** Key constant 
6811         * @type Number */
6812         TAB : 9,
6813         /** Key constant 
6814         * @type Number */
6815         RETURN : 13,
6816         /** Key constant 
6817         * @type Number */
6818         ENTER : 13,
6819         /** Key constant 
6820         * @type Number */
6821         SHIFT : 16,
6822         /** Key constant 
6823         * @type Number */
6824         CONTROL : 17,
6825         /** Key constant 
6826         * @type Number */
6827         ESC : 27,
6828         /** Key constant 
6829         * @type Number */
6830         SPACE : 32,
6831         /** Key constant 
6832         * @type Number */
6833         PAGEUP : 33,
6834         /** Key constant 
6835         * @type Number */
6836         PAGEDOWN : 34,
6837         /** Key constant 
6838         * @type Number */
6839         END : 35,
6840         /** Key constant 
6841         * @type Number */
6842         HOME : 36,
6843         /** Key constant 
6844         * @type Number */
6845         LEFT : 37,
6846         /** Key constant 
6847         * @type Number */
6848         UP : 38,
6849         /** Key constant 
6850         * @type Number */
6851         RIGHT : 39,
6852         /** Key constant 
6853         * @type Number */
6854         DOWN : 40,
6855         /** Key constant 
6856         * @type Number */
6857         DELETE : 46,
6858         /** Key constant 
6859         * @type Number */
6860         F5 : 116,
6861
6862            /** @private */
6863         setEvent : function(e){
6864             if(e == this || (e && e.browserEvent)){ // already wrapped
6865                 return e;
6866             }
6867             this.browserEvent = e;
6868             if(e){
6869                 // normalize buttons
6870                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6871                 if(e.type == 'click' && this.button == -1){
6872                     this.button = 0;
6873                 }
6874                 this.type = e.type;
6875                 this.shiftKey = e.shiftKey;
6876                 // mac metaKey behaves like ctrlKey
6877                 this.ctrlKey = e.ctrlKey || e.metaKey;
6878                 this.altKey = e.altKey;
6879                 // in getKey these will be normalized for the mac
6880                 this.keyCode = e.keyCode;
6881                 // keyup warnings on firefox.
6882                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6883                 // cache the target for the delayed and or buffered events
6884                 this.target = E.getTarget(e);
6885                 // same for XY
6886                 this.xy = E.getXY(e);
6887             }else{
6888                 this.button = -1;
6889                 this.shiftKey = false;
6890                 this.ctrlKey = false;
6891                 this.altKey = false;
6892                 this.keyCode = 0;
6893                 this.charCode =0;
6894                 this.target = null;
6895                 this.xy = [0, 0];
6896             }
6897             return this;
6898         },
6899
6900         /**
6901          * Stop the event (preventDefault and stopPropagation)
6902          */
6903         stopEvent : function(){
6904             if(this.browserEvent){
6905                 if(this.browserEvent.type == 'mousedown'){
6906                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6907                 }
6908                 E.stopEvent(this.browserEvent);
6909             }
6910         },
6911
6912         /**
6913          * Prevents the browsers default handling of the event.
6914          */
6915         preventDefault : function(){
6916             if(this.browserEvent){
6917                 E.preventDefault(this.browserEvent);
6918             }
6919         },
6920
6921         /** @private */
6922         isNavKeyPress : function(){
6923             var k = this.keyCode;
6924             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6925             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6926         },
6927
6928         isSpecialKey : function(){
6929             var k = this.keyCode;
6930             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6931             (k == 16) || (k == 17) ||
6932             (k >= 18 && k <= 20) ||
6933             (k >= 33 && k <= 35) ||
6934             (k >= 36 && k <= 39) ||
6935             (k >= 44 && k <= 45);
6936         },
6937         /**
6938          * Cancels bubbling of the event.
6939          */
6940         stopPropagation : function(){
6941             if(this.browserEvent){
6942                 if(this.type == 'mousedown'){
6943                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6944                 }
6945                 E.stopPropagation(this.browserEvent);
6946             }
6947         },
6948
6949         /**
6950          * Gets the key code for the event.
6951          * @return {Number}
6952          */
6953         getCharCode : function(){
6954             return this.charCode || this.keyCode;
6955         },
6956
6957         /**
6958          * Returns a normalized keyCode for the event.
6959          * @return {Number} The key code
6960          */
6961         getKey : function(){
6962             var k = this.keyCode || this.charCode;
6963             return Roo.isSafari ? (safariKeys[k] || k) : k;
6964         },
6965
6966         /**
6967          * Gets the x coordinate of the event.
6968          * @return {Number}
6969          */
6970         getPageX : function(){
6971             return this.xy[0];
6972         },
6973
6974         /**
6975          * Gets the y coordinate of the event.
6976          * @return {Number}
6977          */
6978         getPageY : function(){
6979             return this.xy[1];
6980         },
6981
6982         /**
6983          * Gets the time of the event.
6984          * @return {Number}
6985          */
6986         getTime : function(){
6987             if(this.browserEvent){
6988                 return E.getTime(this.browserEvent);
6989             }
6990             return null;
6991         },
6992
6993         /**
6994          * Gets the page coordinates of the event.
6995          * @return {Array} The xy values like [x, y]
6996          */
6997         getXY : function(){
6998             return this.xy;
6999         },
7000
7001         /**
7002          * Gets the target for the event.
7003          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7004          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7005                 search as a number or element (defaults to 10 || document.body)
7006          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7007          * @return {HTMLelement}
7008          */
7009         getTarget : function(selector, maxDepth, returnEl){
7010             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
7011         },
7012         /**
7013          * Gets the related target.
7014          * @return {HTMLElement}
7015          */
7016         getRelatedTarget : function(){
7017             if(this.browserEvent){
7018                 return E.getRelatedTarget(this.browserEvent);
7019             }
7020             return null;
7021         },
7022
7023         /**
7024          * Normalizes mouse wheel delta across browsers
7025          * @return {Number} The delta
7026          */
7027         getWheelDelta : function(){
7028             var e = this.browserEvent;
7029             var delta = 0;
7030             if(e.wheelDelta){ /* IE/Opera. */
7031                 delta = e.wheelDelta/120;
7032             }else if(e.detail){ /* Mozilla case. */
7033                 delta = -e.detail/3;
7034             }
7035             return delta;
7036         },
7037
7038         /**
7039          * Returns true if the control, meta, shift or alt key was pressed during this event.
7040          * @return {Boolean}
7041          */
7042         hasModifier : function(){
7043             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
7044         },
7045
7046         /**
7047          * Returns true if the target of this event equals el or is a child of el
7048          * @param {String/HTMLElement/Element} el
7049          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7050          * @return {Boolean}
7051          */
7052         within : function(el, related){
7053             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7054             return t && Roo.fly(el).contains(t);
7055         },
7056
7057         getPoint : function(){
7058             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7059         }
7060     };
7061
7062     return new Roo.EventObjectImpl();
7063 }();
7064             
7065     /*
7066  * Based on:
7067  * Ext JS Library 1.1.1
7068  * Copyright(c) 2006-2007, Ext JS, LLC.
7069  *
7070  * Originally Released Under LGPL - original licence link has changed is not relivant.
7071  *
7072  * Fork - LGPL
7073  * <script type="text/javascript">
7074  */
7075
7076  
7077 // was in Composite Element!??!?!
7078  
7079 (function(){
7080     var D = Roo.lib.Dom;
7081     var E = Roo.lib.Event;
7082     var A = Roo.lib.Anim;
7083
7084     // local style camelizing for speed
7085     var propCache = {};
7086     var camelRe = /(-[a-z])/gi;
7087     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7088     var view = document.defaultView;
7089
7090 /**
7091  * @class Roo.Element
7092  * Represents an Element in the DOM.<br><br>
7093  * Usage:<br>
7094 <pre><code>
7095 var el = Roo.get("my-div");
7096
7097 // or with getEl
7098 var el = getEl("my-div");
7099
7100 // or with a DOM element
7101 var el = Roo.get(myDivElement);
7102 </code></pre>
7103  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7104  * each call instead of constructing a new one.<br><br>
7105  * <b>Animations</b><br />
7106  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7107  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7108 <pre>
7109 Option    Default   Description
7110 --------- --------  ---------------------------------------------
7111 duration  .35       The duration of the animation in seconds
7112 easing    easeOut   The YUI easing method
7113 callback  none      A function to execute when the anim completes
7114 scope     this      The scope (this) of the callback function
7115 </pre>
7116 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7117 * manipulate the animation. Here's an example:
7118 <pre><code>
7119 var el = Roo.get("my-div");
7120
7121 // no animation
7122 el.setWidth(100);
7123
7124 // default animation
7125 el.setWidth(100, true);
7126
7127 // animation with some options set
7128 el.setWidth(100, {
7129     duration: 1,
7130     callback: this.foo,
7131     scope: this
7132 });
7133
7134 // using the "anim" property to get the Anim object
7135 var opt = {
7136     duration: 1,
7137     callback: this.foo,
7138     scope: this
7139 };
7140 el.setWidth(100, opt);
7141 ...
7142 if(opt.anim.isAnimated()){
7143     opt.anim.stop();
7144 }
7145 </code></pre>
7146 * <b> Composite (Collections of) Elements</b><br />
7147  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7148  * @constructor Create a new Element directly.
7149  * @param {String/HTMLElement} element
7150  * @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).
7151  */
7152     Roo.Element = function(element, forceNew)
7153     {
7154         var dom = typeof element == "string" ?
7155                 document.getElementById(element) : element;
7156         
7157         this.listeners = {};
7158         
7159         if(!dom){ // invalid id/element
7160             return null;
7161         }
7162         var id = dom.id;
7163         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7164             return Roo.Element.cache[id];
7165         }
7166
7167         /**
7168          * The DOM element
7169          * @type HTMLElement
7170          */
7171         this.dom = dom;
7172
7173         /**
7174          * The DOM element ID
7175          * @type String
7176          */
7177         this.id = id || Roo.id(dom);
7178         
7179         return this; // assumed for cctor?
7180     };
7181
7182     var El = Roo.Element;
7183
7184     El.prototype = {
7185         /**
7186          * The element's default display mode  (defaults to "") 
7187          * @type String
7188          */
7189         originalDisplay : "",
7190
7191         
7192         // note this is overridden in BS version..
7193         visibilityMode : 1, 
7194         /**
7195          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7196          * @type String
7197          */
7198         defaultUnit : "px",
7199         
7200         /**
7201          * Sets the element's visibility mode. When setVisible() is called it
7202          * will use this to determine whether to set the visibility or the display property.
7203          * @param visMode Element.VISIBILITY or Element.DISPLAY
7204          * @return {Roo.Element} this
7205          */
7206         setVisibilityMode : function(visMode){
7207             this.visibilityMode = visMode;
7208             return this;
7209         },
7210         /**
7211          * Convenience method for setVisibilityMode(Element.DISPLAY)
7212          * @param {String} display (optional) What to set display to when visible
7213          * @return {Roo.Element} this
7214          */
7215         enableDisplayMode : function(display){
7216             this.setVisibilityMode(El.DISPLAY);
7217             if(typeof display != "undefined") { this.originalDisplay = display; }
7218             return this;
7219         },
7220
7221         /**
7222          * 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)
7223          * @param {String} selector The simple selector to test
7224          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7225                 search as a number or element (defaults to 10 || document.body)
7226          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7227          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7228          */
7229         findParent : function(simpleSelector, maxDepth, returnEl){
7230             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7231             maxDepth = maxDepth || 50;
7232             if(typeof maxDepth != "number"){
7233                 stopEl = Roo.getDom(maxDepth);
7234                 maxDepth = 10;
7235             }
7236             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7237                 if(dq.is(p, simpleSelector)){
7238                     return returnEl ? Roo.get(p) : p;
7239                 }
7240                 depth++;
7241                 p = p.parentNode;
7242             }
7243             return null;
7244         },
7245
7246
7247         /**
7248          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7249          * @param {String} selector The simple selector to test
7250          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7251                 search as a number or element (defaults to 10 || document.body)
7252          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7253          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7254          */
7255         findParentNode : function(simpleSelector, maxDepth, returnEl){
7256             var p = Roo.fly(this.dom.parentNode, '_internal');
7257             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7258         },
7259         
7260         /**
7261          * Looks at  the scrollable parent element
7262          */
7263         findScrollableParent : function()
7264         {
7265             var overflowRegex = /(auto|scroll)/;
7266             
7267             if(this.getStyle('position') === 'fixed'){
7268                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7269             }
7270             
7271             var excludeStaticParent = this.getStyle('position') === "absolute";
7272             
7273             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7274                 
7275                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7276                     continue;
7277                 }
7278                 
7279                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7280                     return parent;
7281                 }
7282                 
7283                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7284                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7285                 }
7286             }
7287             
7288             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7289         },
7290
7291         /**
7292          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7293          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7294          * @param {String} selector The simple selector to test
7295          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7296                 search as a number or element (defaults to 10 || document.body)
7297          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7298          */
7299         up : function(simpleSelector, maxDepth){
7300             return this.findParentNode(simpleSelector, maxDepth, true);
7301         },
7302
7303
7304
7305         /**
7306          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7307          * @param {String} selector The simple selector to test
7308          * @return {Boolean} True if this element matches the selector, else false
7309          */
7310         is : function(simpleSelector){
7311             return Roo.DomQuery.is(this.dom, simpleSelector);
7312         },
7313
7314         /**
7315          * Perform animation on this element.
7316          * @param {Object} args The YUI animation control args
7317          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7318          * @param {Function} onComplete (optional) Function to call when animation completes
7319          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7320          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7321          * @return {Roo.Element} this
7322          */
7323         animate : function(args, duration, onComplete, easing, animType){
7324             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7325             return this;
7326         },
7327
7328         /*
7329          * @private Internal animation call
7330          */
7331         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7332             animType = animType || 'run';
7333             opt = opt || {};
7334             var anim = Roo.lib.Anim[animType](
7335                 this.dom, args,
7336                 (opt.duration || defaultDur) || .35,
7337                 (opt.easing || defaultEase) || 'easeOut',
7338                 function(){
7339                     Roo.callback(cb, this);
7340                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7341                 },
7342                 this
7343             );
7344             opt.anim = anim;
7345             return anim;
7346         },
7347
7348         // private legacy anim prep
7349         preanim : function(a, i){
7350             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7351         },
7352
7353         /**
7354          * Removes worthless text nodes
7355          * @param {Boolean} forceReclean (optional) By default the element
7356          * keeps track if it has been cleaned already so
7357          * you can call this over and over. However, if you update the element and
7358          * need to force a reclean, you can pass true.
7359          */
7360         clean : function(forceReclean){
7361             if(this.isCleaned && forceReclean !== true){
7362                 return this;
7363             }
7364             var ns = /\S/;
7365             var d = this.dom, n = d.firstChild, ni = -1;
7366             while(n){
7367                 var nx = n.nextSibling;
7368                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7369                     d.removeChild(n);
7370                 }else{
7371                     n.nodeIndex = ++ni;
7372                 }
7373                 n = nx;
7374             }
7375             this.isCleaned = true;
7376             return this;
7377         },
7378
7379         // private
7380         calcOffsetsTo : function(el){
7381             el = Roo.get(el);
7382             var d = el.dom;
7383             var restorePos = false;
7384             if(el.getStyle('position') == 'static'){
7385                 el.position('relative');
7386                 restorePos = true;
7387             }
7388             var x = 0, y =0;
7389             var op = this.dom;
7390             while(op && op != d && op.tagName != 'HTML'){
7391                 x+= op.offsetLeft;
7392                 y+= op.offsetTop;
7393                 op = op.offsetParent;
7394             }
7395             if(restorePos){
7396                 el.position('static');
7397             }
7398             return [x, y];
7399         },
7400
7401         /**
7402          * Scrolls this element into view within the passed container.
7403          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7404          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7405          * @return {Roo.Element} this
7406          */
7407         scrollIntoView : function(container, hscroll){
7408             var c = Roo.getDom(container) || document.body;
7409             var el = this.dom;
7410
7411             var o = this.calcOffsetsTo(c),
7412                 l = o[0],
7413                 t = o[1],
7414                 b = t+el.offsetHeight,
7415                 r = l+el.offsetWidth;
7416
7417             var ch = c.clientHeight;
7418             var ct = parseInt(c.scrollTop, 10);
7419             var cl = parseInt(c.scrollLeft, 10);
7420             var cb = ct + ch;
7421             var cr = cl + c.clientWidth;
7422
7423             if(t < ct){
7424                 c.scrollTop = t;
7425             }else if(b > cb){
7426                 c.scrollTop = b-ch;
7427             }
7428
7429             if(hscroll !== false){
7430                 if(l < cl){
7431                     c.scrollLeft = l;
7432                 }else if(r > cr){
7433                     c.scrollLeft = r-c.clientWidth;
7434                 }
7435             }
7436             return this;
7437         },
7438
7439         // private
7440         scrollChildIntoView : function(child, hscroll){
7441             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7442         },
7443
7444         /**
7445          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7446          * the new height may not be available immediately.
7447          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7448          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7449          * @param {Function} onComplete (optional) Function to call when animation completes
7450          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7451          * @return {Roo.Element} this
7452          */
7453         autoHeight : function(animate, duration, onComplete, easing){
7454             var oldHeight = this.getHeight();
7455             this.clip();
7456             this.setHeight(1); // force clipping
7457             setTimeout(function(){
7458                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7459                 if(!animate){
7460                     this.setHeight(height);
7461                     this.unclip();
7462                     if(typeof onComplete == "function"){
7463                         onComplete();
7464                     }
7465                 }else{
7466                     this.setHeight(oldHeight); // restore original height
7467                     this.setHeight(height, animate, duration, function(){
7468                         this.unclip();
7469                         if(typeof onComplete == "function") { onComplete(); }
7470                     }.createDelegate(this), easing);
7471                 }
7472             }.createDelegate(this), 0);
7473             return this;
7474         },
7475
7476         /**
7477          * Returns true if this element is an ancestor of the passed element
7478          * @param {HTMLElement/String} el The element to check
7479          * @return {Boolean} True if this element is an ancestor of el, else false
7480          */
7481         contains : function(el){
7482             if(!el){return false;}
7483             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7484         },
7485
7486         /**
7487          * Checks whether the element is currently visible using both visibility and display properties.
7488          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7489          * @return {Boolean} True if the element is currently visible, else false
7490          */
7491         isVisible : function(deep) {
7492             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7493             if(deep !== true || !vis){
7494                 return vis;
7495             }
7496             var p = this.dom.parentNode;
7497             while(p && p.tagName.toLowerCase() != "body"){
7498                 if(!Roo.fly(p, '_isVisible').isVisible()){
7499                     return false;
7500                 }
7501                 p = p.parentNode;
7502             }
7503             return true;
7504         },
7505
7506         /**
7507          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7508          * @param {String} selector The CSS selector
7509          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7510          * @return {CompositeElement/CompositeElementLite} The composite element
7511          */
7512         select : function(selector, unique){
7513             return El.select(selector, unique, this.dom);
7514         },
7515
7516         /**
7517          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7518          * @param {String} selector The CSS selector
7519          * @return {Array} An array of the matched nodes
7520          */
7521         query : function(selector, unique){
7522             return Roo.DomQuery.select(selector, this.dom);
7523         },
7524
7525         /**
7526          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7527          * @param {String} selector The CSS selector
7528          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7529          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7530          */
7531         child : function(selector, returnDom){
7532             var n = Roo.DomQuery.selectNode(selector, this.dom);
7533             return returnDom ? n : Roo.get(n);
7534         },
7535
7536         /**
7537          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7538          * @param {String} selector The CSS selector
7539          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7540          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7541          */
7542         down : function(selector, returnDom){
7543             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7544             return returnDom ? n : Roo.get(n);
7545         },
7546
7547         /**
7548          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7549          * @param {String} group The group the DD object is member of
7550          * @param {Object} config The DD config object
7551          * @param {Object} overrides An object containing methods to override/implement on the DD object
7552          * @return {Roo.dd.DD} The DD object
7553          */
7554         initDD : function(group, config, overrides){
7555             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7556             return Roo.apply(dd, overrides);
7557         },
7558
7559         /**
7560          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7561          * @param {String} group The group the DDProxy object is member of
7562          * @param {Object} config The DDProxy config object
7563          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7564          * @return {Roo.dd.DDProxy} The DDProxy object
7565          */
7566         initDDProxy : function(group, config, overrides){
7567             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7568             return Roo.apply(dd, overrides);
7569         },
7570
7571         /**
7572          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7573          * @param {String} group The group the DDTarget object is member of
7574          * @param {Object} config The DDTarget config object
7575          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7576          * @return {Roo.dd.DDTarget} The DDTarget object
7577          */
7578         initDDTarget : function(group, config, overrides){
7579             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7580             return Roo.apply(dd, overrides);
7581         },
7582
7583         /**
7584          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7585          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7586          * @param {Boolean} visible Whether the element is visible
7587          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7588          * @return {Roo.Element} this
7589          */
7590          setVisible : function(visible, animate){
7591             if(!animate || !A){
7592                 if(this.visibilityMode == El.DISPLAY){
7593                     this.setDisplayed(visible);
7594                 }else{
7595                     this.fixDisplay();
7596                     this.dom.style.visibility = visible ? "visible" : "hidden";
7597                 }
7598             }else{
7599                 // closure for composites
7600                 var dom = this.dom;
7601                 var visMode = this.visibilityMode;
7602                 if(visible){
7603                     this.setOpacity(.01);
7604                     this.setVisible(true);
7605                 }
7606                 this.anim({opacity: { to: (visible?1:0) }},
7607                       this.preanim(arguments, 1),
7608                       null, .35, 'easeIn', function(){
7609                          if(!visible){
7610                              if(visMode == El.DISPLAY){
7611                                  dom.style.display = "none";
7612                              }else{
7613                                  dom.style.visibility = "hidden";
7614                              }
7615                              Roo.get(dom).setOpacity(1);
7616                          }
7617                      });
7618             }
7619             return this;
7620         },
7621
7622         /**
7623          * Returns true if display is not "none"
7624          * @return {Boolean}
7625          */
7626         isDisplayed : function() {
7627             return this.getStyle("display") != "none";
7628         },
7629
7630         /**
7631          * Toggles the element's visibility or display, depending on visibility mode.
7632          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7633          * @return {Roo.Element} this
7634          */
7635         toggle : function(animate){
7636             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7637             return this;
7638         },
7639
7640         /**
7641          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7642          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7643          * @return {Roo.Element} this
7644          */
7645         setDisplayed : function(value) {
7646             if(typeof value == "boolean"){
7647                value = value ? this.originalDisplay : "none";
7648             }
7649             this.setStyle("display", value);
7650             return this;
7651         },
7652
7653         /**
7654          * Tries to focus the element. Any exceptions are caught and ignored.
7655          * @return {Roo.Element} this
7656          */
7657         focus : function() {
7658             try{
7659                 this.dom.focus();
7660             }catch(e){}
7661             return this;
7662         },
7663
7664         /**
7665          * Tries to blur the element. Any exceptions are caught and ignored.
7666          * @return {Roo.Element} this
7667          */
7668         blur : function() {
7669             try{
7670                 this.dom.blur();
7671             }catch(e){}
7672             return this;
7673         },
7674
7675         /**
7676          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7677          * @param {String/Array} className The CSS class to add, or an array of classes
7678          * @return {Roo.Element} this
7679          */
7680         addClass : function(className){
7681             if(className instanceof Array){
7682                 for(var i = 0, len = className.length; i < len; i++) {
7683                     this.addClass(className[i]);
7684                 }
7685             }else{
7686                 if(className && !this.hasClass(className)){
7687                     if (this.dom instanceof SVGElement) {
7688                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
7689                     } else {
7690                         this.dom.className = this.dom.className + " " + className;
7691                     }
7692                 }
7693             }
7694             return this;
7695         },
7696
7697         /**
7698          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7699          * @param {String/Array} className The CSS class to add, or an array of classes
7700          * @return {Roo.Element} this
7701          */
7702         radioClass : function(className){
7703             var siblings = this.dom.parentNode.childNodes;
7704             for(var i = 0; i < siblings.length; i++) {
7705                 var s = siblings[i];
7706                 if(s.nodeType == 1){
7707                     Roo.get(s).removeClass(className);
7708                 }
7709             }
7710             this.addClass(className);
7711             return this;
7712         },
7713
7714         /**
7715          * Removes one or more CSS classes from the element.
7716          * @param {String/Array} className The CSS class to remove, or an array of classes
7717          * @return {Roo.Element} this
7718          */
7719         removeClass : function(className){
7720             
7721             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
7722             if(!className || !cn){
7723                 return this;
7724             }
7725             if(className instanceof Array){
7726                 for(var i = 0, len = className.length; i < len; i++) {
7727                     this.removeClass(className[i]);
7728                 }
7729             }else{
7730                 if(this.hasClass(className)){
7731                     var re = this.classReCache[className];
7732                     if (!re) {
7733                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7734                        this.classReCache[className] = re;
7735                     }
7736                     if (this.dom instanceof SVGElement) {
7737                         this.dom.className.baseVal = cn.replace(re, " ");
7738                     } else {
7739                         this.dom.className = cn.replace(re, " ");
7740                     }
7741                 }
7742             }
7743             return this;
7744         },
7745
7746         // private
7747         classReCache: {},
7748
7749         /**
7750          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7751          * @param {String} className The CSS class to toggle
7752          * @return {Roo.Element} this
7753          */
7754         toggleClass : function(className){
7755             if(this.hasClass(className)){
7756                 this.removeClass(className);
7757             }else{
7758                 this.addClass(className);
7759             }
7760             return this;
7761         },
7762
7763         /**
7764          * Checks if the specified CSS class exists on this element's DOM node.
7765          * @param {String} className The CSS class to check for
7766          * @return {Boolean} True if the class exists, else false
7767          */
7768         hasClass : function(className){
7769             if (this.dom instanceof SVGElement) {
7770                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
7771             } 
7772             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7773         },
7774
7775         /**
7776          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7777          * @param {String} oldClassName The CSS class to replace
7778          * @param {String} newClassName The replacement CSS class
7779          * @return {Roo.Element} this
7780          */
7781         replaceClass : function(oldClassName, newClassName){
7782             this.removeClass(oldClassName);
7783             this.addClass(newClassName);
7784             return this;
7785         },
7786
7787         /**
7788          * Returns an object with properties matching the styles requested.
7789          * For example, el.getStyles('color', 'font-size', 'width') might return
7790          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7791          * @param {String} style1 A style name
7792          * @param {String} style2 A style name
7793          * @param {String} etc.
7794          * @return {Object} The style object
7795          */
7796         getStyles : function(){
7797             var a = arguments, len = a.length, r = {};
7798             for(var i = 0; i < len; i++){
7799                 r[a[i]] = this.getStyle(a[i]);
7800             }
7801             return r;
7802         },
7803
7804         /**
7805          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7806          * @param {String} property The style property whose value is returned.
7807          * @return {String} The current value of the style property for this element.
7808          */
7809         getStyle : function(){
7810             return view && view.getComputedStyle ?
7811                 function(prop){
7812                     var el = this.dom, v, cs, camel;
7813                     if(prop == 'float'){
7814                         prop = "cssFloat";
7815                     }
7816                     if(el.style && (v = el.style[prop])){
7817                         return v;
7818                     }
7819                     if(cs = view.getComputedStyle(el, "")){
7820                         if(!(camel = propCache[prop])){
7821                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7822                         }
7823                         return cs[camel];
7824                     }
7825                     return null;
7826                 } :
7827                 function(prop){
7828                     var el = this.dom, v, cs, camel;
7829                     if(prop == 'opacity'){
7830                         if(typeof el.style.filter == 'string'){
7831                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7832                             if(m){
7833                                 var fv = parseFloat(m[1]);
7834                                 if(!isNaN(fv)){
7835                                     return fv ? fv / 100 : 0;
7836                                 }
7837                             }
7838                         }
7839                         return 1;
7840                     }else if(prop == 'float'){
7841                         prop = "styleFloat";
7842                     }
7843                     if(!(camel = propCache[prop])){
7844                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7845                     }
7846                     if(v = el.style[camel]){
7847                         return v;
7848                     }
7849                     if(cs = el.currentStyle){
7850                         return cs[camel];
7851                     }
7852                     return null;
7853                 };
7854         }(),
7855
7856         /**
7857          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7858          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7859          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7860          * @return {Roo.Element} this
7861          */
7862         setStyle : function(prop, value){
7863             if(typeof prop == "string"){
7864                 
7865                 if (prop == 'float') {
7866                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7867                     return this;
7868                 }
7869                 
7870                 var camel;
7871                 if(!(camel = propCache[prop])){
7872                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7873                 }
7874                 
7875                 if(camel == 'opacity') {
7876                     this.setOpacity(value);
7877                 }else{
7878                     this.dom.style[camel] = value;
7879                 }
7880             }else{
7881                 for(var style in prop){
7882                     if(typeof prop[style] != "function"){
7883                        this.setStyle(style, prop[style]);
7884                     }
7885                 }
7886             }
7887             return this;
7888         },
7889
7890         /**
7891          * More flexible version of {@link #setStyle} for setting style properties.
7892          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7893          * a function which returns such a specification.
7894          * @return {Roo.Element} this
7895          */
7896         applyStyles : function(style){
7897             Roo.DomHelper.applyStyles(this.dom, style);
7898             return this;
7899         },
7900
7901         /**
7902           * 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).
7903           * @return {Number} The X position of the element
7904           */
7905         getX : function(){
7906             return D.getX(this.dom);
7907         },
7908
7909         /**
7910           * 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).
7911           * @return {Number} The Y position of the element
7912           */
7913         getY : function(){
7914             return D.getY(this.dom);
7915         },
7916
7917         /**
7918           * 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).
7919           * @return {Array} The XY position of the element
7920           */
7921         getXY : function(){
7922             return D.getXY(this.dom);
7923         },
7924
7925         /**
7926          * 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).
7927          * @param {Number} The X position of the element
7928          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7929          * @return {Roo.Element} this
7930          */
7931         setX : function(x, animate){
7932             if(!animate || !A){
7933                 D.setX(this.dom, x);
7934             }else{
7935                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7936             }
7937             return this;
7938         },
7939
7940         /**
7941          * 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).
7942          * @param {Number} The Y position of the element
7943          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7944          * @return {Roo.Element} this
7945          */
7946         setY : function(y, animate){
7947             if(!animate || !A){
7948                 D.setY(this.dom, y);
7949             }else{
7950                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7951             }
7952             return this;
7953         },
7954
7955         /**
7956          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7957          * @param {String} left The left CSS property value
7958          * @return {Roo.Element} this
7959          */
7960         setLeft : function(left){
7961             this.setStyle("left", this.addUnits(left));
7962             return this;
7963         },
7964
7965         /**
7966          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7967          * @param {String} top The top CSS property value
7968          * @return {Roo.Element} this
7969          */
7970         setTop : function(top){
7971             this.setStyle("top", this.addUnits(top));
7972             return this;
7973         },
7974
7975         /**
7976          * Sets the element's CSS right style.
7977          * @param {String} right The right CSS property value
7978          * @return {Roo.Element} this
7979          */
7980         setRight : function(right){
7981             this.setStyle("right", this.addUnits(right));
7982             return this;
7983         },
7984
7985         /**
7986          * Sets the element's CSS bottom style.
7987          * @param {String} bottom The bottom CSS property value
7988          * @return {Roo.Element} this
7989          */
7990         setBottom : function(bottom){
7991             this.setStyle("bottom", this.addUnits(bottom));
7992             return this;
7993         },
7994
7995         /**
7996          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7997          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7998          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7999          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8000          * @return {Roo.Element} this
8001          */
8002         setXY : function(pos, animate){
8003             if(!animate || !A){
8004                 D.setXY(this.dom, pos);
8005             }else{
8006                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
8007             }
8008             return this;
8009         },
8010
8011         /**
8012          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8013          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8014          * @param {Number} x X value for new position (coordinates are page-based)
8015          * @param {Number} y Y value for new position (coordinates are page-based)
8016          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8017          * @return {Roo.Element} this
8018          */
8019         setLocation : function(x, y, animate){
8020             this.setXY([x, y], this.preanim(arguments, 2));
8021             return this;
8022         },
8023
8024         /**
8025          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8026          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8027          * @param {Number} x X value for new position (coordinates are page-based)
8028          * @param {Number} y Y value for new position (coordinates are page-based)
8029          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8030          * @return {Roo.Element} this
8031          */
8032         moveTo : function(x, y, animate){
8033             this.setXY([x, y], this.preanim(arguments, 2));
8034             return this;
8035         },
8036
8037         /**
8038          * Returns the region of the given element.
8039          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8040          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
8041          */
8042         getRegion : function(){
8043             return D.getRegion(this.dom);
8044         },
8045
8046         /**
8047          * Returns the offset height of the element
8048          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8049          * @return {Number} The element's height
8050          */
8051         getHeight : function(contentHeight){
8052             var h = this.dom.offsetHeight || 0;
8053             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8054         },
8055
8056         /**
8057          * Returns the offset width of the element
8058          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8059          * @return {Number} The element's width
8060          */
8061         getWidth : function(contentWidth){
8062             var w = this.dom.offsetWidth || 0;
8063             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8064         },
8065
8066         /**
8067          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8068          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8069          * if a height has not been set using CSS.
8070          * @return {Number}
8071          */
8072         getComputedHeight : function(){
8073             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8074             if(!h){
8075                 h = parseInt(this.getStyle('height'), 10) || 0;
8076                 if(!this.isBorderBox()){
8077                     h += this.getFrameWidth('tb');
8078                 }
8079             }
8080             return h;
8081         },
8082
8083         /**
8084          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8085          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8086          * if a width has not been set using CSS.
8087          * @return {Number}
8088          */
8089         getComputedWidth : function(){
8090             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8091             if(!w){
8092                 w = parseInt(this.getStyle('width'), 10) || 0;
8093                 if(!this.isBorderBox()){
8094                     w += this.getFrameWidth('lr');
8095                 }
8096             }
8097             return w;
8098         },
8099
8100         /**
8101          * Returns the size of the element.
8102          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8103          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8104          */
8105         getSize : function(contentSize){
8106             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8107         },
8108
8109         /**
8110          * Returns the width and height of the viewport.
8111          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8112          */
8113         getViewSize : function(){
8114             var d = this.dom, doc = document, aw = 0, ah = 0;
8115             if(d == doc || d == doc.body){
8116                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8117             }else{
8118                 return {
8119                     width : d.clientWidth,
8120                     height: d.clientHeight
8121                 };
8122             }
8123         },
8124
8125         /**
8126          * Returns the value of the "value" attribute
8127          * @param {Boolean} asNumber true to parse the value as a number
8128          * @return {String/Number}
8129          */
8130         getValue : function(asNumber){
8131             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8132         },
8133
8134         // private
8135         adjustWidth : function(width){
8136             if(typeof width == "number"){
8137                 if(this.autoBoxAdjust && !this.isBorderBox()){
8138                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8139                 }
8140                 if(width < 0){
8141                     width = 0;
8142                 }
8143             }
8144             return width;
8145         },
8146
8147         // private
8148         adjustHeight : function(height){
8149             if(typeof height == "number"){
8150                if(this.autoBoxAdjust && !this.isBorderBox()){
8151                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8152                }
8153                if(height < 0){
8154                    height = 0;
8155                }
8156             }
8157             return height;
8158         },
8159
8160         /**
8161          * Set the width of the element
8162          * @param {Number} width The new width
8163          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8164          * @return {Roo.Element} this
8165          */
8166         setWidth : function(width, animate){
8167             width = this.adjustWidth(width);
8168             if(!animate || !A){
8169                 this.dom.style.width = this.addUnits(width);
8170             }else{
8171                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8172             }
8173             return this;
8174         },
8175
8176         /**
8177          * Set the height of the element
8178          * @param {Number} height The new height
8179          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8180          * @return {Roo.Element} this
8181          */
8182          setHeight : function(height, animate){
8183             height = this.adjustHeight(height);
8184             if(!animate || !A){
8185                 this.dom.style.height = this.addUnits(height);
8186             }else{
8187                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8188             }
8189             return this;
8190         },
8191
8192         /**
8193          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8194          * @param {Number} width The new width
8195          * @param {Number} height The new height
8196          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8197          * @return {Roo.Element} this
8198          */
8199          setSize : function(width, height, animate){
8200             if(typeof width == "object"){ // in case of object from getSize()
8201                 height = width.height; width = width.width;
8202             }
8203             width = this.adjustWidth(width); height = this.adjustHeight(height);
8204             if(!animate || !A){
8205                 this.dom.style.width = this.addUnits(width);
8206                 this.dom.style.height = this.addUnits(height);
8207             }else{
8208                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8209             }
8210             return this;
8211         },
8212
8213         /**
8214          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8215          * @param {Number} x X value for new position (coordinates are page-based)
8216          * @param {Number} y Y value for new position (coordinates are page-based)
8217          * @param {Number} width The new width
8218          * @param {Number} height The new height
8219          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8220          * @return {Roo.Element} this
8221          */
8222         setBounds : function(x, y, width, height, animate){
8223             if(!animate || !A){
8224                 this.setSize(width, height);
8225                 this.setLocation(x, y);
8226             }else{
8227                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8228                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8229                               this.preanim(arguments, 4), 'motion');
8230             }
8231             return this;
8232         },
8233
8234         /**
8235          * 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.
8236          * @param {Roo.lib.Region} region The region to fill
8237          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8238          * @return {Roo.Element} this
8239          */
8240         setRegion : function(region, animate){
8241             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8242             return this;
8243         },
8244
8245         /**
8246          * Appends an event handler
8247          *
8248          * @param {String}   eventName     The type of event to append
8249          * @param {Function} fn        The method the event invokes
8250          * @param {Object} scope       (optional) The scope (this object) of the fn
8251          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8252          */
8253         addListener : function(eventName, fn, scope, options)
8254         {
8255             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
8256                 this.addListener('touchstart', this.onTapHandler, this);
8257             }
8258             
8259             // we need to handle a special case where dom element is a svg element.
8260             // in this case we do not actua
8261             if (!this.dom) {
8262                 return;
8263             }
8264             
8265             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
8266                 if (typeof(this.listeners[eventName]) == 'undefined') {
8267                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
8268                 }
8269                 this.listeners[eventName].addListener(fn, scope, options);
8270                 return;
8271             }
8272             
8273                 
8274             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8275             
8276             
8277         },
8278         tapedTwice : false,
8279         onTapHandler : function(event)
8280         {
8281             if(!this.tapedTwice) {
8282                 this.tapedTwice = true;
8283                 var s = this;
8284                 setTimeout( function() {
8285                     s.tapedTwice = false;
8286                 }, 300 );
8287                 return;
8288             }
8289             event.preventDefault();
8290             var revent = new MouseEvent('dblclick',  {
8291                 view: window,
8292                 bubbles: true,
8293                 cancelable: true
8294             });
8295              
8296             this.dom.dispatchEvent(revent);
8297             //action on double tap goes below
8298              
8299         }, 
8300  
8301         /**
8302          * Removes an event handler from this element
8303          * @param {String} eventName the type of event to remove
8304          * @param {Function} fn the method the event invokes
8305          * @param {Function} scope (needed for svg fake listeners)
8306          * @return {Roo.Element} this
8307          */
8308         removeListener : function(eventName, fn, scope){
8309             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8310             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
8311                 return this;
8312             }
8313             this.listeners[eventName].removeListener(fn, scope);
8314             return this;
8315         },
8316
8317         /**
8318          * Removes all previous added listeners from this element
8319          * @return {Roo.Element} this
8320          */
8321         removeAllListeners : function(){
8322             E.purgeElement(this.dom);
8323             this.listeners = {};
8324             return this;
8325         },
8326
8327         relayEvent : function(eventName, observable){
8328             this.on(eventName, function(e){
8329                 observable.fireEvent(eventName, e);
8330             });
8331         },
8332
8333         
8334         /**
8335          * Set the opacity of the element
8336          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8337          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8338          * @return {Roo.Element} this
8339          */
8340          setOpacity : function(opacity, animate){
8341             if(!animate || !A){
8342                 var s = this.dom.style;
8343                 if(Roo.isIE){
8344                     s.zoom = 1;
8345                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8346                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8347                 }else{
8348                     s.opacity = opacity;
8349                 }
8350             }else{
8351                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8352             }
8353             return this;
8354         },
8355
8356         /**
8357          * Gets the left X coordinate
8358          * @param {Boolean} local True to get the local css position instead of page coordinate
8359          * @return {Number}
8360          */
8361         getLeft : function(local){
8362             if(!local){
8363                 return this.getX();
8364             }else{
8365                 return parseInt(this.getStyle("left"), 10) || 0;
8366             }
8367         },
8368
8369         /**
8370          * Gets the right X coordinate of the element (element X position + element width)
8371          * @param {Boolean} local True to get the local css position instead of page coordinate
8372          * @return {Number}
8373          */
8374         getRight : function(local){
8375             if(!local){
8376                 return this.getX() + this.getWidth();
8377             }else{
8378                 return (this.getLeft(true) + this.getWidth()) || 0;
8379             }
8380         },
8381
8382         /**
8383          * Gets the top Y coordinate
8384          * @param {Boolean} local True to get the local css position instead of page coordinate
8385          * @return {Number}
8386          */
8387         getTop : function(local) {
8388             if(!local){
8389                 return this.getY();
8390             }else{
8391                 return parseInt(this.getStyle("top"), 10) || 0;
8392             }
8393         },
8394
8395         /**
8396          * Gets the bottom Y coordinate of the element (element Y position + element height)
8397          * @param {Boolean} local True to get the local css position instead of page coordinate
8398          * @return {Number}
8399          */
8400         getBottom : function(local){
8401             if(!local){
8402                 return this.getY() + this.getHeight();
8403             }else{
8404                 return (this.getTop(true) + this.getHeight()) || 0;
8405             }
8406         },
8407
8408         /**
8409         * Initializes positioning on this element. If a desired position is not passed, it will make the
8410         * the element positioned relative IF it is not already positioned.
8411         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8412         * @param {Number} zIndex (optional) The zIndex to apply
8413         * @param {Number} x (optional) Set the page X position
8414         * @param {Number} y (optional) Set the page Y position
8415         */
8416         position : function(pos, zIndex, x, y){
8417             if(!pos){
8418                if(this.getStyle('position') == 'static'){
8419                    this.setStyle('position', 'relative');
8420                }
8421             }else{
8422                 this.setStyle("position", pos);
8423             }
8424             if(zIndex){
8425                 this.setStyle("z-index", zIndex);
8426             }
8427             if(x !== undefined && y !== undefined){
8428                 this.setXY([x, y]);
8429             }else if(x !== undefined){
8430                 this.setX(x);
8431             }else if(y !== undefined){
8432                 this.setY(y);
8433             }
8434         },
8435
8436         /**
8437         * Clear positioning back to the default when the document was loaded
8438         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8439         * @return {Roo.Element} this
8440          */
8441         clearPositioning : function(value){
8442             value = value ||'';
8443             this.setStyle({
8444                 "left": value,
8445                 "right": value,
8446                 "top": value,
8447                 "bottom": value,
8448                 "z-index": "",
8449                 "position" : "static"
8450             });
8451             return this;
8452         },
8453
8454         /**
8455         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8456         * snapshot before performing an update and then restoring the element.
8457         * @return {Object}
8458         */
8459         getPositioning : function(){
8460             var l = this.getStyle("left");
8461             var t = this.getStyle("top");
8462             return {
8463                 "position" : this.getStyle("position"),
8464                 "left" : l,
8465                 "right" : l ? "" : this.getStyle("right"),
8466                 "top" : t,
8467                 "bottom" : t ? "" : this.getStyle("bottom"),
8468                 "z-index" : this.getStyle("z-index")
8469             };
8470         },
8471
8472         /**
8473          * Gets the width of the border(s) for the specified side(s)
8474          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8475          * passing lr would get the border (l)eft width + the border (r)ight width.
8476          * @return {Number} The width of the sides passed added together
8477          */
8478         getBorderWidth : function(side){
8479             return this.addStyles(side, El.borders);
8480         },
8481
8482         /**
8483          * Gets the width of the padding(s) for the specified side(s)
8484          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8485          * passing lr would get the padding (l)eft + the padding (r)ight.
8486          * @return {Number} The padding of the sides passed added together
8487          */
8488         getPadding : function(side){
8489             return this.addStyles(side, El.paddings);
8490         },
8491
8492         /**
8493         * Set positioning with an object returned by getPositioning().
8494         * @param {Object} posCfg
8495         * @return {Roo.Element} this
8496          */
8497         setPositioning : function(pc){
8498             this.applyStyles(pc);
8499             if(pc.right == "auto"){
8500                 this.dom.style.right = "";
8501             }
8502             if(pc.bottom == "auto"){
8503                 this.dom.style.bottom = "";
8504             }
8505             return this;
8506         },
8507
8508         // private
8509         fixDisplay : function(){
8510             if(this.getStyle("display") == "none"){
8511                 this.setStyle("visibility", "hidden");
8512                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8513                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8514                     this.setStyle("display", "block");
8515                 }
8516             }
8517         },
8518
8519         /**
8520          * Quick set left and top adding default units
8521          * @param {String} left The left CSS property value
8522          * @param {String} top The top CSS property value
8523          * @return {Roo.Element} this
8524          */
8525          setLeftTop : function(left, top){
8526             this.dom.style.left = this.addUnits(left);
8527             this.dom.style.top = this.addUnits(top);
8528             return this;
8529         },
8530
8531         /**
8532          * Move this element relative to its current position.
8533          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8534          * @param {Number} distance How far to move the element in pixels
8535          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8536          * @return {Roo.Element} this
8537          */
8538          move : function(direction, distance, animate){
8539             var xy = this.getXY();
8540             direction = direction.toLowerCase();
8541             switch(direction){
8542                 case "l":
8543                 case "left":
8544                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8545                     break;
8546                case "r":
8547                case "right":
8548                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8549                     break;
8550                case "t":
8551                case "top":
8552                case "up":
8553                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8554                     break;
8555                case "b":
8556                case "bottom":
8557                case "down":
8558                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8559                     break;
8560             }
8561             return this;
8562         },
8563
8564         /**
8565          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8566          * @return {Roo.Element} this
8567          */
8568         clip : function(){
8569             if(!this.isClipped){
8570                this.isClipped = true;
8571                this.originalClip = {
8572                    "o": this.getStyle("overflow"),
8573                    "x": this.getStyle("overflow-x"),
8574                    "y": this.getStyle("overflow-y")
8575                };
8576                this.setStyle("overflow", "hidden");
8577                this.setStyle("overflow-x", "hidden");
8578                this.setStyle("overflow-y", "hidden");
8579             }
8580             return this;
8581         },
8582
8583         /**
8584          *  Return clipping (overflow) to original clipping before clip() was called
8585          * @return {Roo.Element} this
8586          */
8587         unclip : function(){
8588             if(this.isClipped){
8589                 this.isClipped = false;
8590                 var o = this.originalClip;
8591                 if(o.o){this.setStyle("overflow", o.o);}
8592                 if(o.x){this.setStyle("overflow-x", o.x);}
8593                 if(o.y){this.setStyle("overflow-y", o.y);}
8594             }
8595             return this;
8596         },
8597
8598
8599         /**
8600          * Gets the x,y coordinates specified by the anchor position on the element.
8601          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8602          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8603          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8604          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8605          * @return {Array} [x, y] An array containing the element's x and y coordinates
8606          */
8607         getAnchorXY : function(anchor, local, s){
8608             //Passing a different size is useful for pre-calculating anchors,
8609             //especially for anchored animations that change the el size.
8610
8611             var w, h, vp = false;
8612             if(!s){
8613                 var d = this.dom;
8614                 if(d == document.body || d == document){
8615                     vp = true;
8616                     w = D.getViewWidth(); h = D.getViewHeight();
8617                 }else{
8618                     w = this.getWidth(); h = this.getHeight();
8619                 }
8620             }else{
8621                 w = s.width;  h = s.height;
8622             }
8623             var x = 0, y = 0, r = Math.round;
8624             switch((anchor || "tl").toLowerCase()){
8625                 case "c":
8626                     x = r(w*.5);
8627                     y = r(h*.5);
8628                 break;
8629                 case "t":
8630                     x = r(w*.5);
8631                     y = 0;
8632                 break;
8633                 case "l":
8634                     x = 0;
8635                     y = r(h*.5);
8636                 break;
8637                 case "r":
8638                     x = w;
8639                     y = r(h*.5);
8640                 break;
8641                 case "b":
8642                     x = r(w*.5);
8643                     y = h;
8644                 break;
8645                 case "tl":
8646                     x = 0;
8647                     y = 0;
8648                 break;
8649                 case "bl":
8650                     x = 0;
8651                     y = h;
8652                 break;
8653                 case "br":
8654                     x = w;
8655                     y = h;
8656                 break;
8657                 case "tr":
8658                     x = w;
8659                     y = 0;
8660                 break;
8661             }
8662             if(local === true){
8663                 return [x, y];
8664             }
8665             if(vp){
8666                 var sc = this.getScroll();
8667                 return [x + sc.left, y + sc.top];
8668             }
8669             //Add the element's offset xy
8670             var o = this.getXY();
8671             return [x+o[0], y+o[1]];
8672         },
8673
8674         /**
8675          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8676          * supported position values.
8677          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8678          * @param {String} position The position to align to.
8679          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8680          * @return {Array} [x, y]
8681          */
8682         getAlignToXY : function(el, p, o)
8683         {
8684             el = Roo.get(el);
8685             var d = this.dom;
8686             if(!el.dom){
8687                 throw "Element.alignTo with an element that doesn't exist";
8688             }
8689             var c = false; //constrain to viewport
8690             var p1 = "", p2 = "";
8691             o = o || [0,0];
8692
8693             if(!p){
8694                 p = "tl-bl";
8695             }else if(p == "?"){
8696                 p = "tl-bl?";
8697             }else if(p.indexOf("-") == -1){
8698                 p = "tl-" + p;
8699             }
8700             p = p.toLowerCase();
8701             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8702             if(!m){
8703                throw "Element.alignTo with an invalid alignment " + p;
8704             }
8705             p1 = m[1]; p2 = m[2]; c = !!m[3];
8706
8707             //Subtract the aligned el's internal xy from the target's offset xy
8708             //plus custom offset to get the aligned el's new offset xy
8709             var a1 = this.getAnchorXY(p1, true);
8710             var a2 = el.getAnchorXY(p2, false);
8711             var x = a2[0] - a1[0] + o[0];
8712             var y = a2[1] - a1[1] + o[1];
8713             if(c){
8714                 //constrain the aligned el to viewport if necessary
8715                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8716                 // 5px of margin for ie
8717                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8718
8719                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8720                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8721                 //otherwise swap the aligned el to the opposite border of the target.
8722                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8723                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8724                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
8725                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8726
8727                var doc = document;
8728                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8729                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8730
8731                if((x+w) > dw + scrollX){
8732                     x = swapX ? r.left-w : dw+scrollX-w;
8733                 }
8734                if(x < scrollX){
8735                    x = swapX ? r.right : scrollX;
8736                }
8737                if((y+h) > dh + scrollY){
8738                     y = swapY ? r.top-h : dh+scrollY-h;
8739                 }
8740                if (y < scrollY){
8741                    y = swapY ? r.bottom : scrollY;
8742                }
8743             }
8744             return [x,y];
8745         },
8746
8747         // private
8748         getConstrainToXY : function(){
8749             var os = {top:0, left:0, bottom:0, right: 0};
8750
8751             return function(el, local, offsets, proposedXY){
8752                 el = Roo.get(el);
8753                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8754
8755                 var vw, vh, vx = 0, vy = 0;
8756                 if(el.dom == document.body || el.dom == document){
8757                     vw = Roo.lib.Dom.getViewWidth();
8758                     vh = Roo.lib.Dom.getViewHeight();
8759                 }else{
8760                     vw = el.dom.clientWidth;
8761                     vh = el.dom.clientHeight;
8762                     if(!local){
8763                         var vxy = el.getXY();
8764                         vx = vxy[0];
8765                         vy = vxy[1];
8766                     }
8767                 }
8768
8769                 var s = el.getScroll();
8770
8771                 vx += offsets.left + s.left;
8772                 vy += offsets.top + s.top;
8773
8774                 vw -= offsets.right;
8775                 vh -= offsets.bottom;
8776
8777                 var vr = vx+vw;
8778                 var vb = vy+vh;
8779
8780                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8781                 var x = xy[0], y = xy[1];
8782                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8783
8784                 // only move it if it needs it
8785                 var moved = false;
8786
8787                 // first validate right/bottom
8788                 if((x + w) > vr){
8789                     x = vr - w;
8790                     moved = true;
8791                 }
8792                 if((y + h) > vb){
8793                     y = vb - h;
8794                     moved = true;
8795                 }
8796                 // then make sure top/left isn't negative
8797                 if(x < vx){
8798                     x = vx;
8799                     moved = true;
8800                 }
8801                 if(y < vy){
8802                     y = vy;
8803                     moved = true;
8804                 }
8805                 return moved ? [x, y] : false;
8806             };
8807         }(),
8808
8809         // private
8810         adjustForConstraints : function(xy, parent, offsets){
8811             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8812         },
8813
8814         /**
8815          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8816          * document it aligns it to the viewport.
8817          * The position parameter is optional, and can be specified in any one of the following formats:
8818          * <ul>
8819          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8820          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8821          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8822          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8823          *   <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
8824          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8825          * </ul>
8826          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8827          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8828          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8829          * that specified in order to enforce the viewport constraints.
8830          * Following are all of the supported anchor positions:
8831     <pre>
8832     Value  Description
8833     -----  -----------------------------
8834     tl     The top left corner (default)
8835     t      The center of the top edge
8836     tr     The top right corner
8837     l      The center of the left edge
8838     c      In the center of the element
8839     r      The center of the right edge
8840     bl     The bottom left corner
8841     b      The center of the bottom edge
8842     br     The bottom right corner
8843     </pre>
8844     Example Usage:
8845     <pre><code>
8846     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8847     el.alignTo("other-el");
8848
8849     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8850     el.alignTo("other-el", "tr?");
8851
8852     // align the bottom right corner of el with the center left edge of other-el
8853     el.alignTo("other-el", "br-l?");
8854
8855     // align the center of el with the bottom left corner of other-el and
8856     // adjust the x position by -6 pixels (and the y position by 0)
8857     el.alignTo("other-el", "c-bl", [-6, 0]);
8858     </code></pre>
8859          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8860          * @param {String} position The position to align to.
8861          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8862          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8863          * @return {Roo.Element} this
8864          */
8865         alignTo : function(element, position, offsets, animate){
8866             var xy = this.getAlignToXY(element, position, offsets);
8867             this.setXY(xy, this.preanim(arguments, 3));
8868             return this;
8869         },
8870
8871         /**
8872          * Anchors an element to another element and realigns it when the window is resized.
8873          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8874          * @param {String} position The position to align to.
8875          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8876          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8877          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8878          * is a number, it is used as the buffer delay (defaults to 50ms).
8879          * @param {Function} callback The function to call after the animation finishes
8880          * @return {Roo.Element} this
8881          */
8882         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8883             var action = function(){
8884                 this.alignTo(el, alignment, offsets, animate);
8885                 Roo.callback(callback, this);
8886             };
8887             Roo.EventManager.onWindowResize(action, this);
8888             var tm = typeof monitorScroll;
8889             if(tm != 'undefined'){
8890                 Roo.EventManager.on(window, 'scroll', action, this,
8891                     {buffer: tm == 'number' ? monitorScroll : 50});
8892             }
8893             action.call(this); // align immediately
8894             return this;
8895         },
8896         /**
8897          * Clears any opacity settings from this element. Required in some cases for IE.
8898          * @return {Roo.Element} this
8899          */
8900         clearOpacity : function(){
8901             if (window.ActiveXObject) {
8902                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8903                     this.dom.style.filter = "";
8904                 }
8905             } else {
8906                 this.dom.style.opacity = "";
8907                 this.dom.style["-moz-opacity"] = "";
8908                 this.dom.style["-khtml-opacity"] = "";
8909             }
8910             return this;
8911         },
8912
8913         /**
8914          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8915          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8916          * @return {Roo.Element} this
8917          */
8918         hide : function(animate){
8919             this.setVisible(false, this.preanim(arguments, 0));
8920             return this;
8921         },
8922
8923         /**
8924         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8925         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8926          * @return {Roo.Element} this
8927          */
8928         show : function(animate){
8929             this.setVisible(true, this.preanim(arguments, 0));
8930             return this;
8931         },
8932
8933         /**
8934          * @private Test if size has a unit, otherwise appends the default
8935          */
8936         addUnits : function(size){
8937             return Roo.Element.addUnits(size, this.defaultUnit);
8938         },
8939
8940         /**
8941          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8942          * @return {Roo.Element} this
8943          */
8944         beginMeasure : function(){
8945             var el = this.dom;
8946             if(el.offsetWidth || el.offsetHeight){
8947                 return this; // offsets work already
8948             }
8949             var changed = [];
8950             var p = this.dom, b = document.body; // start with this element
8951             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8952                 var pe = Roo.get(p);
8953                 if(pe.getStyle('display') == 'none'){
8954                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8955                     p.style.visibility = "hidden";
8956                     p.style.display = "block";
8957                 }
8958                 p = p.parentNode;
8959             }
8960             this._measureChanged = changed;
8961             return this;
8962
8963         },
8964
8965         /**
8966          * Restores displays to before beginMeasure was called
8967          * @return {Roo.Element} this
8968          */
8969         endMeasure : function(){
8970             var changed = this._measureChanged;
8971             if(changed){
8972                 for(var i = 0, len = changed.length; i < len; i++) {
8973                     var r = changed[i];
8974                     r.el.style.visibility = r.visibility;
8975                     r.el.style.display = "none";
8976                 }
8977                 this._measureChanged = null;
8978             }
8979             return this;
8980         },
8981
8982         /**
8983         * Update the innerHTML of this element, optionally searching for and processing scripts
8984         * @param {String} html The new HTML
8985         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8986         * @param {Function} callback For async script loading you can be noticed when the update completes
8987         * @return {Roo.Element} this
8988          */
8989         update : function(html, loadScripts, callback){
8990             if(typeof html == "undefined"){
8991                 html = "";
8992             }
8993             if(loadScripts !== true){
8994                 this.dom.innerHTML = html;
8995                 if(typeof callback == "function"){
8996                     callback();
8997                 }
8998                 return this;
8999             }
9000             var id = Roo.id();
9001             var dom = this.dom;
9002
9003             html += '<span id="' + id + '"></span>';
9004
9005             E.onAvailable(id, function(){
9006                 var hd = document.getElementsByTagName("head")[0];
9007                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
9008                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
9009                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
9010
9011                 var match;
9012                 while(match = re.exec(html)){
9013                     var attrs = match[1];
9014                     var srcMatch = attrs ? attrs.match(srcRe) : false;
9015                     if(srcMatch && srcMatch[2]){
9016                        var s = document.createElement("script");
9017                        s.src = srcMatch[2];
9018                        var typeMatch = attrs.match(typeRe);
9019                        if(typeMatch && typeMatch[2]){
9020                            s.type = typeMatch[2];
9021                        }
9022                        hd.appendChild(s);
9023                     }else if(match[2] && match[2].length > 0){
9024                         if(window.execScript) {
9025                            window.execScript(match[2]);
9026                         } else {
9027                             /**
9028                              * eval:var:id
9029                              * eval:var:dom
9030                              * eval:var:html
9031                              * 
9032                              */
9033                            window.eval(match[2]);
9034                         }
9035                     }
9036                 }
9037                 var el = document.getElementById(id);
9038                 if(el){el.parentNode.removeChild(el);}
9039                 if(typeof callback == "function"){
9040                     callback();
9041                 }
9042             });
9043             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
9044             return this;
9045         },
9046
9047         /**
9048          * Direct access to the UpdateManager update() method (takes the same parameters).
9049          * @param {String/Function} url The url for this request or a function to call to get the url
9050          * @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}
9051          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9052          * @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.
9053          * @return {Roo.Element} this
9054          */
9055         load : function(){
9056             var um = this.getUpdateManager();
9057             um.update.apply(um, arguments);
9058             return this;
9059         },
9060
9061         /**
9062         * Gets this element's UpdateManager
9063         * @return {Roo.UpdateManager} The UpdateManager
9064         */
9065         getUpdateManager : function(){
9066             if(!this.updateManager){
9067                 this.updateManager = new Roo.UpdateManager(this);
9068             }
9069             return this.updateManager;
9070         },
9071
9072         /**
9073          * Disables text selection for this element (normalized across browsers)
9074          * @return {Roo.Element} this
9075          */
9076         unselectable : function(){
9077             this.dom.unselectable = "on";
9078             this.swallowEvent("selectstart", true);
9079             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
9080             this.addClass("x-unselectable");
9081             return this;
9082         },
9083
9084         /**
9085         * Calculates the x, y to center this element on the screen
9086         * @return {Array} The x, y values [x, y]
9087         */
9088         getCenterXY : function(){
9089             return this.getAlignToXY(document, 'c-c');
9090         },
9091
9092         /**
9093         * Centers the Element in either the viewport, or another Element.
9094         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9095         */
9096         center : function(centerIn){
9097             this.alignTo(centerIn || document, 'c-c');
9098             return this;
9099         },
9100
9101         /**
9102          * Tests various css rules/browsers to determine if this element uses a border box
9103          * @return {Boolean}
9104          */
9105         isBorderBox : function(){
9106             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9107         },
9108
9109         /**
9110          * Return a box {x, y, width, height} that can be used to set another elements
9111          * size/location to match this element.
9112          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9113          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9114          * @return {Object} box An object in the format {x, y, width, height}
9115          */
9116         getBox : function(contentBox, local){
9117             var xy;
9118             if(!local){
9119                 xy = this.getXY();
9120             }else{
9121                 var left = parseInt(this.getStyle("left"), 10) || 0;
9122                 var top = parseInt(this.getStyle("top"), 10) || 0;
9123                 xy = [left, top];
9124             }
9125             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9126             if(!contentBox){
9127                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9128             }else{
9129                 var l = this.getBorderWidth("l")+this.getPadding("l");
9130                 var r = this.getBorderWidth("r")+this.getPadding("r");
9131                 var t = this.getBorderWidth("t")+this.getPadding("t");
9132                 var b = this.getBorderWidth("b")+this.getPadding("b");
9133                 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)};
9134             }
9135             bx.right = bx.x + bx.width;
9136             bx.bottom = bx.y + bx.height;
9137             return bx;
9138         },
9139
9140         /**
9141          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9142          for more information about the sides.
9143          * @param {String} sides
9144          * @return {Number}
9145          */
9146         getFrameWidth : function(sides, onlyContentBox){
9147             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9148         },
9149
9150         /**
9151          * 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.
9152          * @param {Object} box The box to fill {x, y, width, height}
9153          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9154          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9155          * @return {Roo.Element} this
9156          */
9157         setBox : function(box, adjust, animate){
9158             var w = box.width, h = box.height;
9159             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9160                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9161                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9162             }
9163             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9164             return this;
9165         },
9166
9167         /**
9168          * Forces the browser to repaint this element
9169          * @return {Roo.Element} this
9170          */
9171          repaint : function(){
9172             var dom = this.dom;
9173             this.addClass("x-repaint");
9174             setTimeout(function(){
9175                 Roo.get(dom).removeClass("x-repaint");
9176             }, 1);
9177             return this;
9178         },
9179
9180         /**
9181          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9182          * then it returns the calculated width of the sides (see getPadding)
9183          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9184          * @return {Object/Number}
9185          */
9186         getMargins : function(side){
9187             if(!side){
9188                 return {
9189                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9190                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9191                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9192                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9193                 };
9194             }else{
9195                 return this.addStyles(side, El.margins);
9196              }
9197         },
9198
9199         // private
9200         addStyles : function(sides, styles){
9201             var val = 0, v, w;
9202             for(var i = 0, len = sides.length; i < len; i++){
9203                 v = this.getStyle(styles[sides.charAt(i)]);
9204                 if(v){
9205                      w = parseInt(v, 10);
9206                      if(w){ val += w; }
9207                 }
9208             }
9209             return val;
9210         },
9211
9212         /**
9213          * Creates a proxy element of this element
9214          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9215          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9216          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9217          * @return {Roo.Element} The new proxy element
9218          */
9219         createProxy : function(config, renderTo, matchBox){
9220             if(renderTo){
9221                 renderTo = Roo.getDom(renderTo);
9222             }else{
9223                 renderTo = document.body;
9224             }
9225             config = typeof config == "object" ?
9226                 config : {tag : "div", cls: config};
9227             var proxy = Roo.DomHelper.append(renderTo, config, true);
9228             if(matchBox){
9229                proxy.setBox(this.getBox());
9230             }
9231             return proxy;
9232         },
9233
9234         /**
9235          * Puts a mask over this element to disable user interaction. Requires core.css.
9236          * This method can only be applied to elements which accept child nodes.
9237          * @param {String} msg (optional) A message to display in the mask
9238          * @param {String} msgCls (optional) A css class to apply to the msg element
9239          * @return {Element} The mask  element
9240          */
9241         mask : function(msg, msgCls)
9242         {
9243             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9244                 this.setStyle("position", "relative");
9245             }
9246             if(!this._mask){
9247                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9248             }
9249             
9250             this.addClass("x-masked");
9251             this._mask.setDisplayed(true);
9252             
9253             // we wander
9254             var z = 0;
9255             var dom = this.dom;
9256             while (dom && dom.style) {
9257                 if (!isNaN(parseInt(dom.style.zIndex))) {
9258                     z = Math.max(z, parseInt(dom.style.zIndex));
9259                 }
9260                 dom = dom.parentNode;
9261             }
9262             // if we are masking the body - then it hides everything..
9263             if (this.dom == document.body) {
9264                 z = 1000000;
9265                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9266                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9267             }
9268            
9269             if(typeof msg == 'string'){
9270                 if(!this._maskMsg){
9271                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9272                         cls: "roo-el-mask-msg", 
9273                         cn: [
9274                             {
9275                                 tag: 'i',
9276                                 cls: 'fa fa-spinner fa-spin'
9277                             },
9278                             {
9279                                 tag: 'div'
9280                             }   
9281                         ]
9282                     }, true);
9283                 }
9284                 var mm = this._maskMsg;
9285                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9286                 if (mm.dom.lastChild) { // weird IE issue?
9287                     mm.dom.lastChild.innerHTML = msg;
9288                 }
9289                 mm.setDisplayed(true);
9290                 mm.center(this);
9291                 mm.setStyle('z-index', z + 102);
9292             }
9293             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9294                 this._mask.setHeight(this.getHeight());
9295             }
9296             this._mask.setStyle('z-index', z + 100);
9297             
9298             return this._mask;
9299         },
9300
9301         /**
9302          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9303          * it is cached for reuse.
9304          */
9305         unmask : function(removeEl){
9306             if(this._mask){
9307                 if(removeEl === true){
9308                     this._mask.remove();
9309                     delete this._mask;
9310                     if(this._maskMsg){
9311                         this._maskMsg.remove();
9312                         delete this._maskMsg;
9313                     }
9314                 }else{
9315                     this._mask.setDisplayed(false);
9316                     if(this._maskMsg){
9317                         this._maskMsg.setDisplayed(false);
9318                     }
9319                 }
9320             }
9321             this.removeClass("x-masked");
9322         },
9323
9324         /**
9325          * Returns true if this element is masked
9326          * @return {Boolean}
9327          */
9328         isMasked : function(){
9329             return this._mask && this._mask.isVisible();
9330         },
9331
9332         /**
9333          * Creates an iframe shim for this element to keep selects and other windowed objects from
9334          * showing through.
9335          * @return {Roo.Element} The new shim element
9336          */
9337         createShim : function(){
9338             var el = document.createElement('iframe');
9339             el.frameBorder = 'no';
9340             el.className = 'roo-shim';
9341             if(Roo.isIE && Roo.isSecure){
9342                 el.src = Roo.SSL_SECURE_URL;
9343             }
9344             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9345             shim.autoBoxAdjust = false;
9346             return shim;
9347         },
9348
9349         /**
9350          * Removes this element from the DOM and deletes it from the cache
9351          */
9352         remove : function(){
9353             if(this.dom.parentNode){
9354                 this.dom.parentNode.removeChild(this.dom);
9355             }
9356             delete El.cache[this.dom.id];
9357         },
9358
9359         /**
9360          * Sets up event handlers to add and remove a css class when the mouse is over this element
9361          * @param {String} className
9362          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9363          * mouseout events for children elements
9364          * @return {Roo.Element} this
9365          */
9366         addClassOnOver : function(className, preventFlicker){
9367             this.on("mouseover", function(){
9368                 Roo.fly(this, '_internal').addClass(className);
9369             }, this.dom);
9370             var removeFn = function(e){
9371                 if(preventFlicker !== true || !e.within(this, true)){
9372                     Roo.fly(this, '_internal').removeClass(className);
9373                 }
9374             };
9375             this.on("mouseout", removeFn, this.dom);
9376             return this;
9377         },
9378
9379         /**
9380          * Sets up event handlers to add and remove a css class when this element has the focus
9381          * @param {String} className
9382          * @return {Roo.Element} this
9383          */
9384         addClassOnFocus : function(className){
9385             this.on("focus", function(){
9386                 Roo.fly(this, '_internal').addClass(className);
9387             }, this.dom);
9388             this.on("blur", function(){
9389                 Roo.fly(this, '_internal').removeClass(className);
9390             }, this.dom);
9391             return this;
9392         },
9393         /**
9394          * 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)
9395          * @param {String} className
9396          * @return {Roo.Element} this
9397          */
9398         addClassOnClick : function(className){
9399             var dom = this.dom;
9400             this.on("mousedown", function(){
9401                 Roo.fly(dom, '_internal').addClass(className);
9402                 var d = Roo.get(document);
9403                 var fn = function(){
9404                     Roo.fly(dom, '_internal').removeClass(className);
9405                     d.removeListener("mouseup", fn);
9406                 };
9407                 d.on("mouseup", fn);
9408             });
9409             return this;
9410         },
9411
9412         /**
9413          * Stops the specified event from bubbling and optionally prevents the default action
9414          * @param {String} eventName
9415          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9416          * @return {Roo.Element} this
9417          */
9418         swallowEvent : function(eventName, preventDefault){
9419             var fn = function(e){
9420                 e.stopPropagation();
9421                 if(preventDefault){
9422                     e.preventDefault();
9423                 }
9424             };
9425             if(eventName instanceof Array){
9426                 for(var i = 0, len = eventName.length; i < len; i++){
9427                      this.on(eventName[i], fn);
9428                 }
9429                 return this;
9430             }
9431             this.on(eventName, fn);
9432             return this;
9433         },
9434
9435         /**
9436          * @private
9437          */
9438         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9439
9440         /**
9441          * Sizes this element to its parent element's dimensions performing
9442          * neccessary box adjustments.
9443          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9444          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9445          * @return {Roo.Element} this
9446          */
9447         fitToParent : function(monitorResize, targetParent) {
9448           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9449           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9450           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9451             return this;
9452           }
9453           var p = Roo.get(targetParent || this.dom.parentNode);
9454           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9455           if (monitorResize === true) {
9456             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9457             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9458           }
9459           return this;
9460         },
9461
9462         /**
9463          * Gets the next sibling, skipping text nodes
9464          * @return {HTMLElement} The next sibling or null
9465          */
9466         getNextSibling : function(){
9467             var n = this.dom.nextSibling;
9468             while(n && n.nodeType != 1){
9469                 n = n.nextSibling;
9470             }
9471             return n;
9472         },
9473
9474         /**
9475          * Gets the previous sibling, skipping text nodes
9476          * @return {HTMLElement} The previous sibling or null
9477          */
9478         getPrevSibling : function(){
9479             var n = this.dom.previousSibling;
9480             while(n && n.nodeType != 1){
9481                 n = n.previousSibling;
9482             }
9483             return n;
9484         },
9485
9486
9487         /**
9488          * Appends the passed element(s) to this element
9489          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9490          * @return {Roo.Element} this
9491          */
9492         appendChild: function(el){
9493             el = Roo.get(el);
9494             el.appendTo(this);
9495             return this;
9496         },
9497
9498         /**
9499          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9500          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9501          * automatically generated with the specified attributes.
9502          * @param {HTMLElement} insertBefore (optional) a child element of this element
9503          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9504          * @return {Roo.Element} The new child element
9505          */
9506         createChild: function(config, insertBefore, returnDom){
9507             config = config || {tag:'div'};
9508             if(insertBefore){
9509                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9510             }
9511             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9512         },
9513
9514         /**
9515          * Appends this element to the passed element
9516          * @param {String/HTMLElement/Element} el The new parent element
9517          * @return {Roo.Element} this
9518          */
9519         appendTo: function(el){
9520             el = Roo.getDom(el);
9521             el.appendChild(this.dom);
9522             return this;
9523         },
9524
9525         /**
9526          * Inserts this element before the passed element in the DOM
9527          * @param {String/HTMLElement/Element} el The element to insert before
9528          * @return {Roo.Element} this
9529          */
9530         insertBefore: function(el){
9531             el = Roo.getDom(el);
9532             el.parentNode.insertBefore(this.dom, el);
9533             return this;
9534         },
9535
9536         /**
9537          * Inserts this element after the passed element in the DOM
9538          * @param {String/HTMLElement/Element} el The element to insert after
9539          * @return {Roo.Element} this
9540          */
9541         insertAfter: function(el){
9542             el = Roo.getDom(el);
9543             el.parentNode.insertBefore(this.dom, el.nextSibling);
9544             return this;
9545         },
9546
9547         /**
9548          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9549          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9550          * @return {Roo.Element} The new child
9551          */
9552         insertFirst: function(el, returnDom){
9553             el = el || {};
9554             if(typeof el == 'object' && !el.nodeType){ // dh config
9555                 return this.createChild(el, this.dom.firstChild, returnDom);
9556             }else{
9557                 el = Roo.getDom(el);
9558                 this.dom.insertBefore(el, this.dom.firstChild);
9559                 return !returnDom ? Roo.get(el) : el;
9560             }
9561         },
9562
9563         /**
9564          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9565          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9566          * @param {String} where (optional) 'before' or 'after' defaults to before
9567          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9568          * @return {Roo.Element} the inserted Element
9569          */
9570         insertSibling: function(el, where, returnDom){
9571             where = where ? where.toLowerCase() : 'before';
9572             el = el || {};
9573             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9574
9575             if(typeof el == 'object' && !el.nodeType){ // dh config
9576                 if(where == 'after' && !this.dom.nextSibling){
9577                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9578                 }else{
9579                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9580                 }
9581
9582             }else{
9583                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9584                             where == 'before' ? this.dom : this.dom.nextSibling);
9585                 if(!returnDom){
9586                     rt = Roo.get(rt);
9587                 }
9588             }
9589             return rt;
9590         },
9591
9592         /**
9593          * Creates and wraps this element with another element
9594          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9595          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9596          * @return {HTMLElement/Element} The newly created wrapper element
9597          */
9598         wrap: function(config, returnDom){
9599             if(!config){
9600                 config = {tag: "div"};
9601             }
9602             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9603             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9604             return newEl;
9605         },
9606
9607         /**
9608          * Replaces the passed element with this element
9609          * @param {String/HTMLElement/Element} el The element to replace
9610          * @return {Roo.Element} this
9611          */
9612         replace: function(el){
9613             el = Roo.get(el);
9614             this.insertBefore(el);
9615             el.remove();
9616             return this;
9617         },
9618
9619         /**
9620          * Inserts an html fragment into this element
9621          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9622          * @param {String} html The HTML fragment
9623          * @param {Boolean} returnEl True to return an Roo.Element
9624          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9625          */
9626         insertHtml : function(where, html, returnEl){
9627             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9628             return returnEl ? Roo.get(el) : el;
9629         },
9630
9631         /**
9632          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9633          * @param {Object} o The object with the attributes
9634          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9635          * @return {Roo.Element} this
9636          */
9637         set : function(o, useSet){
9638             var el = this.dom;
9639             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9640             for(var attr in o){
9641                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9642                 if(attr=="cls"){
9643                     el.className = o["cls"];
9644                 }else{
9645                     if(useSet) {
9646                         el.setAttribute(attr, o[attr]);
9647                     } else {
9648                         el[attr] = o[attr];
9649                     }
9650                 }
9651             }
9652             if(o.style){
9653                 Roo.DomHelper.applyStyles(el, o.style);
9654             }
9655             return this;
9656         },
9657
9658         /**
9659          * Convenience method for constructing a KeyMap
9660          * @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:
9661          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9662          * @param {Function} fn The function to call
9663          * @param {Object} scope (optional) The scope of the function
9664          * @return {Roo.KeyMap} The KeyMap created
9665          */
9666         addKeyListener : function(key, fn, scope){
9667             var config;
9668             if(typeof key != "object" || key instanceof Array){
9669                 config = {
9670                     key: key,
9671                     fn: fn,
9672                     scope: scope
9673                 };
9674             }else{
9675                 config = {
9676                     key : key.key,
9677                     shift : key.shift,
9678                     ctrl : key.ctrl,
9679                     alt : key.alt,
9680                     fn: fn,
9681                     scope: scope
9682                 };
9683             }
9684             return new Roo.KeyMap(this, config);
9685         },
9686
9687         /**
9688          * Creates a KeyMap for this element
9689          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9690          * @return {Roo.KeyMap} The KeyMap created
9691          */
9692         addKeyMap : function(config){
9693             return new Roo.KeyMap(this, config);
9694         },
9695
9696         /**
9697          * Returns true if this element is scrollable.
9698          * @return {Boolean}
9699          */
9700          isScrollable : function(){
9701             var dom = this.dom;
9702             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9703         },
9704
9705         /**
9706          * 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().
9707          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9708          * @param {Number} value The new scroll value
9709          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9710          * @return {Element} this
9711          */
9712
9713         scrollTo : function(side, value, animate){
9714             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9715             if(!animate || !A){
9716                 this.dom[prop] = value;
9717             }else{
9718                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9719                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9720             }
9721             return this;
9722         },
9723
9724         /**
9725          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9726          * within this element's scrollable range.
9727          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9728          * @param {Number} distance How far to scroll the element in pixels
9729          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9730          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9731          * was scrolled as far as it could go.
9732          */
9733          scroll : function(direction, distance, animate){
9734              if(!this.isScrollable()){
9735                  return;
9736              }
9737              var el = this.dom;
9738              var l = el.scrollLeft, t = el.scrollTop;
9739              var w = el.scrollWidth, h = el.scrollHeight;
9740              var cw = el.clientWidth, ch = el.clientHeight;
9741              direction = direction.toLowerCase();
9742              var scrolled = false;
9743              var a = this.preanim(arguments, 2);
9744              switch(direction){
9745                  case "l":
9746                  case "left":
9747                      if(w - l > cw){
9748                          var v = Math.min(l + distance, w-cw);
9749                          this.scrollTo("left", v, a);
9750                          scrolled = true;
9751                      }
9752                      break;
9753                 case "r":
9754                 case "right":
9755                      if(l > 0){
9756                          var v = Math.max(l - distance, 0);
9757                          this.scrollTo("left", v, a);
9758                          scrolled = true;
9759                      }
9760                      break;
9761                 case "t":
9762                 case "top":
9763                 case "up":
9764                      if(t > 0){
9765                          var v = Math.max(t - distance, 0);
9766                          this.scrollTo("top", v, a);
9767                          scrolled = true;
9768                      }
9769                      break;
9770                 case "b":
9771                 case "bottom":
9772                 case "down":
9773                      if(h - t > ch){
9774                          var v = Math.min(t + distance, h-ch);
9775                          this.scrollTo("top", v, a);
9776                          scrolled = true;
9777                      }
9778                      break;
9779              }
9780              return scrolled;
9781         },
9782
9783         /**
9784          * Translates the passed page coordinates into left/top css values for this element
9785          * @param {Number/Array} x The page x or an array containing [x, y]
9786          * @param {Number} y The page y
9787          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9788          */
9789         translatePoints : function(x, y){
9790             if(typeof x == 'object' || x instanceof Array){
9791                 y = x[1]; x = x[0];
9792             }
9793             var p = this.getStyle('position');
9794             var o = this.getXY();
9795
9796             var l = parseInt(this.getStyle('left'), 10);
9797             var t = parseInt(this.getStyle('top'), 10);
9798
9799             if(isNaN(l)){
9800                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9801             }
9802             if(isNaN(t)){
9803                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9804             }
9805
9806             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9807         },
9808
9809         /**
9810          * Returns the current scroll position of the element.
9811          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9812          */
9813         getScroll : function(){
9814             var d = this.dom, doc = document;
9815             if(d == doc || d == doc.body){
9816                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9817                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9818                 return {left: l, top: t};
9819             }else{
9820                 return {left: d.scrollLeft, top: d.scrollTop};
9821             }
9822         },
9823
9824         /**
9825          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9826          * are convert to standard 6 digit hex color.
9827          * @param {String} attr The css attribute
9828          * @param {String} defaultValue The default value to use when a valid color isn't found
9829          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9830          * YUI color anims.
9831          */
9832         getColor : function(attr, defaultValue, prefix){
9833             var v = this.getStyle(attr);
9834             if(!v || v == "transparent" || v == "inherit") {
9835                 return defaultValue;
9836             }
9837             var color = typeof prefix == "undefined" ? "#" : prefix;
9838             if(v.substr(0, 4) == "rgb("){
9839                 var rvs = v.slice(4, v.length -1).split(",");
9840                 for(var i = 0; i < 3; i++){
9841                     var h = parseInt(rvs[i]).toString(16);
9842                     if(h < 16){
9843                         h = "0" + h;
9844                     }
9845                     color += h;
9846                 }
9847             } else {
9848                 if(v.substr(0, 1) == "#"){
9849                     if(v.length == 4) {
9850                         for(var i = 1; i < 4; i++){
9851                             var c = v.charAt(i);
9852                             color +=  c + c;
9853                         }
9854                     }else if(v.length == 7){
9855                         color += v.substr(1);
9856                     }
9857                 }
9858             }
9859             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9860         },
9861
9862         /**
9863          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9864          * gradient background, rounded corners and a 4-way shadow.
9865          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9866          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9867          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9868          * @return {Roo.Element} this
9869          */
9870         boxWrap : function(cls){
9871             cls = cls || 'x-box';
9872             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9873             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9874             return el;
9875         },
9876
9877         /**
9878          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9879          * @param {String} namespace The namespace in which to look for the attribute
9880          * @param {String} name The attribute name
9881          * @return {String} The attribute value
9882          */
9883         getAttributeNS : Roo.isIE ? function(ns, name){
9884             var d = this.dom;
9885             var type = typeof d[ns+":"+name];
9886             if(type != 'undefined' && type != 'unknown'){
9887                 return d[ns+":"+name];
9888             }
9889             return d[name];
9890         } : function(ns, name){
9891             var d = this.dom;
9892             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9893         },
9894         
9895         
9896         /**
9897          * Sets or Returns the value the dom attribute value
9898          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9899          * @param {String} value (optional) The value to set the attribute to
9900          * @return {String} The attribute value
9901          */
9902         attr : function(name){
9903             if (arguments.length > 1) {
9904                 this.dom.setAttribute(name, arguments[1]);
9905                 return arguments[1];
9906             }
9907             if (typeof(name) == 'object') {
9908                 for(var i in name) {
9909                     this.attr(i, name[i]);
9910                 }
9911                 return name;
9912             }
9913             
9914             
9915             if (!this.dom.hasAttribute(name)) {
9916                 return undefined;
9917             }
9918             return this.dom.getAttribute(name);
9919         }
9920         
9921         
9922         
9923     };
9924
9925     var ep = El.prototype;
9926
9927     /**
9928      * Appends an event handler (Shorthand for addListener)
9929      * @param {String}   eventName     The type of event to append
9930      * @param {Function} fn        The method the event invokes
9931      * @param {Object} scope       (optional) The scope (this object) of the fn
9932      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9933      * @method
9934      */
9935     ep.on = ep.addListener;
9936         // backwards compat
9937     ep.mon = ep.addListener;
9938
9939     /**
9940      * Removes an event handler from this element (shorthand for removeListener)
9941      * @param {String} eventName the type of event to remove
9942      * @param {Function} fn the method the event invokes
9943      * @return {Roo.Element} this
9944      * @method
9945      */
9946     ep.un = ep.removeListener;
9947
9948     /**
9949      * true to automatically adjust width and height settings for box-model issues (default to true)
9950      */
9951     ep.autoBoxAdjust = true;
9952
9953     // private
9954     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9955
9956     // private
9957     El.addUnits = function(v, defaultUnit){
9958         if(v === "" || v == "auto"){
9959             return v;
9960         }
9961         if(v === undefined){
9962             return '';
9963         }
9964         if(typeof v == "number" || !El.unitPattern.test(v)){
9965             return v + (defaultUnit || 'px');
9966         }
9967         return v;
9968     };
9969
9970     // special markup used throughout Roo when box wrapping elements
9971     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>';
9972     /**
9973      * Visibility mode constant - Use visibility to hide element
9974      * @static
9975      * @type Number
9976      */
9977     El.VISIBILITY = 1;
9978     /**
9979      * Visibility mode constant - Use display to hide element
9980      * @static
9981      * @type Number
9982      */
9983     El.DISPLAY = 2;
9984
9985     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9986     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9987     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9988
9989
9990
9991     /**
9992      * @private
9993      */
9994     El.cache = {};
9995
9996     var docEl;
9997
9998     /**
9999      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10000      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10001      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10002      * @return {Element} The Element object
10003      * @static
10004      */
10005     El.get = function(el){
10006         var ex, elm, id;
10007         if(!el){ return null; }
10008         if(typeof el == "string"){ // element id
10009             if(!(elm = document.getElementById(el))){
10010                 return null;
10011             }
10012             if(ex = El.cache[el]){
10013                 ex.dom = elm;
10014             }else{
10015                 ex = El.cache[el] = new El(elm);
10016             }
10017             return ex;
10018         }else if(el.tagName){ // dom element
10019             if(!(id = el.id)){
10020                 id = Roo.id(el);
10021             }
10022             if(ex = El.cache[id]){
10023                 ex.dom = el;
10024             }else{
10025                 ex = El.cache[id] = new El(el);
10026             }
10027             return ex;
10028         }else if(el instanceof El){
10029             if(el != docEl){
10030                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
10031                                                               // catch case where it hasn't been appended
10032                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
10033             }
10034             return el;
10035         }else if(el.isComposite){
10036             return el;
10037         }else if(el instanceof Array){
10038             return El.select(el);
10039         }else if(el == document){
10040             // create a bogus element object representing the document object
10041             if(!docEl){
10042                 var f = function(){};
10043                 f.prototype = El.prototype;
10044                 docEl = new f();
10045                 docEl.dom = document;
10046             }
10047             return docEl;
10048         }
10049         return null;
10050     };
10051
10052     // private
10053     El.uncache = function(el){
10054         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
10055             if(a[i]){
10056                 delete El.cache[a[i].id || a[i]];
10057             }
10058         }
10059     };
10060
10061     // private
10062     // Garbage collection - uncache elements/purge listeners on orphaned elements
10063     // so we don't hold a reference and cause the browser to retain them
10064     El.garbageCollect = function(){
10065         if(!Roo.enableGarbageCollector){
10066             clearInterval(El.collectorThread);
10067             return;
10068         }
10069         for(var eid in El.cache){
10070             var el = El.cache[eid], d = el.dom;
10071             // -------------------------------------------------------
10072             // Determining what is garbage:
10073             // -------------------------------------------------------
10074             // !d
10075             // dom node is null, definitely garbage
10076             // -------------------------------------------------------
10077             // !d.parentNode
10078             // no parentNode == direct orphan, definitely garbage
10079             // -------------------------------------------------------
10080             // !d.offsetParent && !document.getElementById(eid)
10081             // display none elements have no offsetParent so we will
10082             // also try to look it up by it's id. However, check
10083             // offsetParent first so we don't do unneeded lookups.
10084             // This enables collection of elements that are not orphans
10085             // directly, but somewhere up the line they have an orphan
10086             // parent.
10087             // -------------------------------------------------------
10088             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
10089                 delete El.cache[eid];
10090                 if(d && Roo.enableListenerCollection){
10091                     E.purgeElement(d);
10092                 }
10093             }
10094         }
10095     }
10096     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10097
10098
10099     // dom is optional
10100     El.Flyweight = function(dom){
10101         this.dom = dom;
10102     };
10103     El.Flyweight.prototype = El.prototype;
10104
10105     El._flyweights = {};
10106     /**
10107      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10108      * the dom node can be overwritten by other code.
10109      * @param {String/HTMLElement} el The dom node or id
10110      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10111      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10112      * @static
10113      * @return {Element} The shared Element object
10114      */
10115     El.fly = function(el, named){
10116         named = named || '_global';
10117         el = Roo.getDom(el);
10118         if(!el){
10119             return null;
10120         }
10121         if(!El._flyweights[named]){
10122             El._flyweights[named] = new El.Flyweight();
10123         }
10124         El._flyweights[named].dom = el;
10125         return El._flyweights[named];
10126     };
10127
10128     /**
10129      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10130      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10131      * Shorthand of {@link Roo.Element#get}
10132      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10133      * @return {Element} The Element object
10134      * @member Roo
10135      * @method get
10136      */
10137     Roo.get = El.get;
10138     /**
10139      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10140      * the dom node can be overwritten by other code.
10141      * Shorthand of {@link Roo.Element#fly}
10142      * @param {String/HTMLElement} el The dom node or id
10143      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10144      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10145      * @static
10146      * @return {Element} The shared Element object
10147      * @member Roo
10148      * @method fly
10149      */
10150     Roo.fly = El.fly;
10151
10152     // speedy lookup for elements never to box adjust
10153     var noBoxAdjust = Roo.isStrict ? {
10154         select:1
10155     } : {
10156         input:1, select:1, textarea:1
10157     };
10158     if(Roo.isIE || Roo.isGecko){
10159         noBoxAdjust['button'] = 1;
10160     }
10161
10162
10163     Roo.EventManager.on(window, 'unload', function(){
10164         delete El.cache;
10165         delete El._flyweights;
10166     });
10167 })();
10168
10169
10170
10171
10172 if(Roo.DomQuery){
10173     Roo.Element.selectorFunction = Roo.DomQuery.select;
10174 }
10175
10176 Roo.Element.select = function(selector, unique, root){
10177     var els;
10178     if(typeof selector == "string"){
10179         els = Roo.Element.selectorFunction(selector, root);
10180     }else if(selector.length !== undefined){
10181         els = selector;
10182     }else{
10183         throw "Invalid selector";
10184     }
10185     if(unique === true){
10186         return new Roo.CompositeElement(els);
10187     }else{
10188         return new Roo.CompositeElementLite(els);
10189     }
10190 };
10191 /**
10192  * Selects elements based on the passed CSS selector to enable working on them as 1.
10193  * @param {String/Array} selector The CSS selector or an array of elements
10194  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10195  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10196  * @return {CompositeElementLite/CompositeElement}
10197  * @member Roo
10198  * @method select
10199  */
10200 Roo.select = Roo.Element.select;
10201
10202
10203
10204
10205
10206
10207
10208
10209
10210
10211
10212
10213
10214
10215 /*
10216  * Based on:
10217  * Ext JS Library 1.1.1
10218  * Copyright(c) 2006-2007, Ext JS, LLC.
10219  *
10220  * Originally Released Under LGPL - original licence link has changed is not relivant.
10221  *
10222  * Fork - LGPL
10223  * <script type="text/javascript">
10224  */
10225
10226
10227
10228 //Notifies Element that fx methods are available
10229 Roo.enableFx = true;
10230
10231 /**
10232  * @class Roo.Fx
10233  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10234  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10235  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10236  * Element effects to work.</p><br/>
10237  *
10238  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10239  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10240  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10241  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10242  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10243  * expected results and should be done with care.</p><br/>
10244  *
10245  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10246  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10247 <pre>
10248 Value  Description
10249 -----  -----------------------------
10250 tl     The top left corner
10251 t      The center of the top edge
10252 tr     The top right corner
10253 l      The center of the left edge
10254 r      The center of the right edge
10255 bl     The bottom left corner
10256 b      The center of the bottom edge
10257 br     The bottom right corner
10258 </pre>
10259  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10260  * below are common options that can be passed to any Fx method.</b>
10261  * @cfg {Function} callback A function called when the effect is finished
10262  * @cfg {Object} scope The scope of the effect function
10263  * @cfg {String} easing A valid Easing value for the effect
10264  * @cfg {String} afterCls A css class to apply after the effect
10265  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10266  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10267  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10268  * effects that end with the element being visually hidden, ignored otherwise)
10269  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10270  * a function which returns such a specification that will be applied to the Element after the effect finishes
10271  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10272  * @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
10273  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10274  */
10275 Roo.Fx = {
10276         /**
10277          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10278          * origin for the slide effect.  This function automatically handles wrapping the element with
10279          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10280          * Usage:
10281          *<pre><code>
10282 // default: slide the element in from the top
10283 el.slideIn();
10284
10285 // custom: slide the element in from the right with a 2-second duration
10286 el.slideIn('r', { duration: 2 });
10287
10288 // common config options shown with default values
10289 el.slideIn('t', {
10290     easing: 'easeOut',
10291     duration: .5
10292 });
10293 </code></pre>
10294          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10295          * @param {Object} options (optional) Object literal with any of the Fx config options
10296          * @return {Roo.Element} The Element
10297          */
10298     slideIn : function(anchor, o){
10299         var el = this.getFxEl();
10300         o = o || {};
10301
10302         el.queueFx(o, function(){
10303
10304             anchor = anchor || "t";
10305
10306             // fix display to visibility
10307             this.fixDisplay();
10308
10309             // restore values after effect
10310             var r = this.getFxRestore();
10311             var b = this.getBox();
10312             // fixed size for slide
10313             this.setSize(b);
10314
10315             // wrap if needed
10316             var wrap = this.fxWrap(r.pos, o, "hidden");
10317
10318             var st = this.dom.style;
10319             st.visibility = "visible";
10320             st.position = "absolute";
10321
10322             // clear out temp styles after slide and unwrap
10323             var after = function(){
10324                 el.fxUnwrap(wrap, r.pos, o);
10325                 st.width = r.width;
10326                 st.height = r.height;
10327                 el.afterFx(o);
10328             };
10329             // time to calc the positions
10330             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10331
10332             switch(anchor.toLowerCase()){
10333                 case "t":
10334                     wrap.setSize(b.width, 0);
10335                     st.left = st.bottom = "0";
10336                     a = {height: bh};
10337                 break;
10338                 case "l":
10339                     wrap.setSize(0, b.height);
10340                     st.right = st.top = "0";
10341                     a = {width: bw};
10342                 break;
10343                 case "r":
10344                     wrap.setSize(0, b.height);
10345                     wrap.setX(b.right);
10346                     st.left = st.top = "0";
10347                     a = {width: bw, points: pt};
10348                 break;
10349                 case "b":
10350                     wrap.setSize(b.width, 0);
10351                     wrap.setY(b.bottom);
10352                     st.left = st.top = "0";
10353                     a = {height: bh, points: pt};
10354                 break;
10355                 case "tl":
10356                     wrap.setSize(0, 0);
10357                     st.right = st.bottom = "0";
10358                     a = {width: bw, height: bh};
10359                 break;
10360                 case "bl":
10361                     wrap.setSize(0, 0);
10362                     wrap.setY(b.y+b.height);
10363                     st.right = st.top = "0";
10364                     a = {width: bw, height: bh, points: pt};
10365                 break;
10366                 case "br":
10367                     wrap.setSize(0, 0);
10368                     wrap.setXY([b.right, b.bottom]);
10369                     st.left = st.top = "0";
10370                     a = {width: bw, height: bh, points: pt};
10371                 break;
10372                 case "tr":
10373                     wrap.setSize(0, 0);
10374                     wrap.setX(b.x+b.width);
10375                     st.left = st.bottom = "0";
10376                     a = {width: bw, height: bh, points: pt};
10377                 break;
10378             }
10379             this.dom.style.visibility = "visible";
10380             wrap.show();
10381
10382             arguments.callee.anim = wrap.fxanim(a,
10383                 o,
10384                 'motion',
10385                 .5,
10386                 'easeOut', after);
10387         });
10388         return this;
10389     },
10390     
10391         /**
10392          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10393          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10394          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10395          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10396          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10397          * Usage:
10398          *<pre><code>
10399 // default: slide the element out to the top
10400 el.slideOut();
10401
10402 // custom: slide the element out to the right with a 2-second duration
10403 el.slideOut('r', { duration: 2 });
10404
10405 // common config options shown with default values
10406 el.slideOut('t', {
10407     easing: 'easeOut',
10408     duration: .5,
10409     remove: false,
10410     useDisplay: false
10411 });
10412 </code></pre>
10413          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10414          * @param {Object} options (optional) Object literal with any of the Fx config options
10415          * @return {Roo.Element} The Element
10416          */
10417     slideOut : function(anchor, o){
10418         var el = this.getFxEl();
10419         o = o || {};
10420
10421         el.queueFx(o, function(){
10422
10423             anchor = anchor || "t";
10424
10425             // restore values after effect
10426             var r = this.getFxRestore();
10427             
10428             var b = this.getBox();
10429             // fixed size for slide
10430             this.setSize(b);
10431
10432             // wrap if needed
10433             var wrap = this.fxWrap(r.pos, o, "visible");
10434
10435             var st = this.dom.style;
10436             st.visibility = "visible";
10437             st.position = "absolute";
10438
10439             wrap.setSize(b);
10440
10441             var after = function(){
10442                 if(o.useDisplay){
10443                     el.setDisplayed(false);
10444                 }else{
10445                     el.hide();
10446                 }
10447
10448                 el.fxUnwrap(wrap, r.pos, o);
10449
10450                 st.width = r.width;
10451                 st.height = r.height;
10452
10453                 el.afterFx(o);
10454             };
10455
10456             var a, zero = {to: 0};
10457             switch(anchor.toLowerCase()){
10458                 case "t":
10459                     st.left = st.bottom = "0";
10460                     a = {height: zero};
10461                 break;
10462                 case "l":
10463                     st.right = st.top = "0";
10464                     a = {width: zero};
10465                 break;
10466                 case "r":
10467                     st.left = st.top = "0";
10468                     a = {width: zero, points: {to:[b.right, b.y]}};
10469                 break;
10470                 case "b":
10471                     st.left = st.top = "0";
10472                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10473                 break;
10474                 case "tl":
10475                     st.right = st.bottom = "0";
10476                     a = {width: zero, height: zero};
10477                 break;
10478                 case "bl":
10479                     st.right = st.top = "0";
10480                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10481                 break;
10482                 case "br":
10483                     st.left = st.top = "0";
10484                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10485                 break;
10486                 case "tr":
10487                     st.left = st.bottom = "0";
10488                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10489                 break;
10490             }
10491
10492             arguments.callee.anim = wrap.fxanim(a,
10493                 o,
10494                 'motion',
10495                 .5,
10496                 "easeOut", after);
10497         });
10498         return this;
10499     },
10500
10501         /**
10502          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10503          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10504          * The element must be removed from the DOM using the 'remove' config option if desired.
10505          * Usage:
10506          *<pre><code>
10507 // default
10508 el.puff();
10509
10510 // common config options shown with default values
10511 el.puff({
10512     easing: 'easeOut',
10513     duration: .5,
10514     remove: false,
10515     useDisplay: false
10516 });
10517 </code></pre>
10518          * @param {Object} options (optional) Object literal with any of the Fx config options
10519          * @return {Roo.Element} The Element
10520          */
10521     puff : function(o){
10522         var el = this.getFxEl();
10523         o = o || {};
10524
10525         el.queueFx(o, function(){
10526             this.clearOpacity();
10527             this.show();
10528
10529             // restore values after effect
10530             var r = this.getFxRestore();
10531             var st = this.dom.style;
10532
10533             var after = function(){
10534                 if(o.useDisplay){
10535                     el.setDisplayed(false);
10536                 }else{
10537                     el.hide();
10538                 }
10539
10540                 el.clearOpacity();
10541
10542                 el.setPositioning(r.pos);
10543                 st.width = r.width;
10544                 st.height = r.height;
10545                 st.fontSize = '';
10546                 el.afterFx(o);
10547             };
10548
10549             var width = this.getWidth();
10550             var height = this.getHeight();
10551
10552             arguments.callee.anim = this.fxanim({
10553                     width : {to: this.adjustWidth(width * 2)},
10554                     height : {to: this.adjustHeight(height * 2)},
10555                     points : {by: [-(width * .5), -(height * .5)]},
10556                     opacity : {to: 0},
10557                     fontSize: {to:200, unit: "%"}
10558                 },
10559                 o,
10560                 'motion',
10561                 .5,
10562                 "easeOut", after);
10563         });
10564         return this;
10565     },
10566
10567         /**
10568          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10569          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10570          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10571          * Usage:
10572          *<pre><code>
10573 // default
10574 el.switchOff();
10575
10576 // all config options shown with default values
10577 el.switchOff({
10578     easing: 'easeIn',
10579     duration: .3,
10580     remove: false,
10581     useDisplay: false
10582 });
10583 </code></pre>
10584          * @param {Object} options (optional) Object literal with any of the Fx config options
10585          * @return {Roo.Element} The Element
10586          */
10587     switchOff : function(o){
10588         var el = this.getFxEl();
10589         o = o || {};
10590
10591         el.queueFx(o, function(){
10592             this.clearOpacity();
10593             this.clip();
10594
10595             // restore values after effect
10596             var r = this.getFxRestore();
10597             var st = this.dom.style;
10598
10599             var after = function(){
10600                 if(o.useDisplay){
10601                     el.setDisplayed(false);
10602                 }else{
10603                     el.hide();
10604                 }
10605
10606                 el.clearOpacity();
10607                 el.setPositioning(r.pos);
10608                 st.width = r.width;
10609                 st.height = r.height;
10610
10611                 el.afterFx(o);
10612             };
10613
10614             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10615                 this.clearOpacity();
10616                 (function(){
10617                     this.fxanim({
10618                         height:{to:1},
10619                         points:{by:[0, this.getHeight() * .5]}
10620                     }, o, 'motion', 0.3, 'easeIn', after);
10621                 }).defer(100, this);
10622             });
10623         });
10624         return this;
10625     },
10626
10627     /**
10628      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10629      * changed using the "attr" config option) and then fading back to the original color. If no original
10630      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10631      * Usage:
10632 <pre><code>
10633 // default: highlight background to yellow
10634 el.highlight();
10635
10636 // custom: highlight foreground text to blue for 2 seconds
10637 el.highlight("0000ff", { attr: 'color', duration: 2 });
10638
10639 // common config options shown with default values
10640 el.highlight("ffff9c", {
10641     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10642     endColor: (current color) or "ffffff",
10643     easing: 'easeIn',
10644     duration: 1
10645 });
10646 </code></pre>
10647      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10648      * @param {Object} options (optional) Object literal with any of the Fx config options
10649      * @return {Roo.Element} The Element
10650      */ 
10651     highlight : function(color, o){
10652         var el = this.getFxEl();
10653         o = o || {};
10654
10655         el.queueFx(o, function(){
10656             color = color || "ffff9c";
10657             attr = o.attr || "backgroundColor";
10658
10659             this.clearOpacity();
10660             this.show();
10661
10662             var origColor = this.getColor(attr);
10663             var restoreColor = this.dom.style[attr];
10664             endColor = (o.endColor || origColor) || "ffffff";
10665
10666             var after = function(){
10667                 el.dom.style[attr] = restoreColor;
10668                 el.afterFx(o);
10669             };
10670
10671             var a = {};
10672             a[attr] = {from: color, to: endColor};
10673             arguments.callee.anim = this.fxanim(a,
10674                 o,
10675                 'color',
10676                 1,
10677                 'easeIn', after);
10678         });
10679         return this;
10680     },
10681
10682    /**
10683     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10684     * Usage:
10685 <pre><code>
10686 // default: a single light blue ripple
10687 el.frame();
10688
10689 // custom: 3 red ripples lasting 3 seconds total
10690 el.frame("ff0000", 3, { duration: 3 });
10691
10692 // common config options shown with default values
10693 el.frame("C3DAF9", 1, {
10694     duration: 1 //duration of entire animation (not each individual ripple)
10695     // Note: Easing is not configurable and will be ignored if included
10696 });
10697 </code></pre>
10698     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10699     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10700     * @param {Object} options (optional) Object literal with any of the Fx config options
10701     * @return {Roo.Element} The Element
10702     */
10703     frame : function(color, count, o){
10704         var el = this.getFxEl();
10705         o = o || {};
10706
10707         el.queueFx(o, function(){
10708             color = color || "#C3DAF9";
10709             if(color.length == 6){
10710                 color = "#" + color;
10711             }
10712             count = count || 1;
10713             duration = o.duration || 1;
10714             this.show();
10715
10716             var b = this.getBox();
10717             var animFn = function(){
10718                 var proxy = this.createProxy({
10719
10720                      style:{
10721                         visbility:"hidden",
10722                         position:"absolute",
10723                         "z-index":"35000", // yee haw
10724                         border:"0px solid " + color
10725                      }
10726                   });
10727                 var scale = Roo.isBorderBox ? 2 : 1;
10728                 proxy.animate({
10729                     top:{from:b.y, to:b.y - 20},
10730                     left:{from:b.x, to:b.x - 20},
10731                     borderWidth:{from:0, to:10},
10732                     opacity:{from:1, to:0},
10733                     height:{from:b.height, to:(b.height + (20*scale))},
10734                     width:{from:b.width, to:(b.width + (20*scale))}
10735                 }, duration, function(){
10736                     proxy.remove();
10737                 });
10738                 if(--count > 0){
10739                      animFn.defer((duration/2)*1000, this);
10740                 }else{
10741                     el.afterFx(o);
10742                 }
10743             };
10744             animFn.call(this);
10745         });
10746         return this;
10747     },
10748
10749    /**
10750     * Creates a pause before any subsequent queued effects begin.  If there are
10751     * no effects queued after the pause it will have no effect.
10752     * Usage:
10753 <pre><code>
10754 el.pause(1);
10755 </code></pre>
10756     * @param {Number} seconds The length of time to pause (in seconds)
10757     * @return {Roo.Element} The Element
10758     */
10759     pause : function(seconds){
10760         var el = this.getFxEl();
10761         var o = {};
10762
10763         el.queueFx(o, function(){
10764             setTimeout(function(){
10765                 el.afterFx(o);
10766             }, seconds * 1000);
10767         });
10768         return this;
10769     },
10770
10771    /**
10772     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10773     * using the "endOpacity" config option.
10774     * Usage:
10775 <pre><code>
10776 // default: fade in from opacity 0 to 100%
10777 el.fadeIn();
10778
10779 // custom: fade in from opacity 0 to 75% over 2 seconds
10780 el.fadeIn({ endOpacity: .75, duration: 2});
10781
10782 // common config options shown with default values
10783 el.fadeIn({
10784     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10785     easing: 'easeOut',
10786     duration: .5
10787 });
10788 </code></pre>
10789     * @param {Object} options (optional) Object literal with any of the Fx config options
10790     * @return {Roo.Element} The Element
10791     */
10792     fadeIn : function(o){
10793         var el = this.getFxEl();
10794         o = o || {};
10795         el.queueFx(o, function(){
10796             this.setOpacity(0);
10797             this.fixDisplay();
10798             this.dom.style.visibility = 'visible';
10799             var to = o.endOpacity || 1;
10800             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10801                 o, null, .5, "easeOut", function(){
10802                 if(to == 1){
10803                     this.clearOpacity();
10804                 }
10805                 el.afterFx(o);
10806             });
10807         });
10808         return this;
10809     },
10810
10811    /**
10812     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10813     * using the "endOpacity" config option.
10814     * Usage:
10815 <pre><code>
10816 // default: fade out from the element's current opacity to 0
10817 el.fadeOut();
10818
10819 // custom: fade out from the element's current opacity to 25% over 2 seconds
10820 el.fadeOut({ endOpacity: .25, duration: 2});
10821
10822 // common config options shown with default values
10823 el.fadeOut({
10824     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10825     easing: 'easeOut',
10826     duration: .5
10827     remove: false,
10828     useDisplay: false
10829 });
10830 </code></pre>
10831     * @param {Object} options (optional) Object literal with any of the Fx config options
10832     * @return {Roo.Element} The Element
10833     */
10834     fadeOut : function(o){
10835         var el = this.getFxEl();
10836         o = o || {};
10837         el.queueFx(o, function(){
10838             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10839                 o, null, .5, "easeOut", function(){
10840                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10841                      this.dom.style.display = "none";
10842                 }else{
10843                      this.dom.style.visibility = "hidden";
10844                 }
10845                 this.clearOpacity();
10846                 el.afterFx(o);
10847             });
10848         });
10849         return this;
10850     },
10851
10852    /**
10853     * Animates the transition of an element's dimensions from a starting height/width
10854     * to an ending height/width.
10855     * Usage:
10856 <pre><code>
10857 // change height and width to 100x100 pixels
10858 el.scale(100, 100);
10859
10860 // common config options shown with default values.  The height and width will default to
10861 // the element's existing values if passed as null.
10862 el.scale(
10863     [element's width],
10864     [element's height], {
10865     easing: 'easeOut',
10866     duration: .35
10867 });
10868 </code></pre>
10869     * @param {Number} width  The new width (pass undefined to keep the original width)
10870     * @param {Number} height  The new height (pass undefined to keep the original height)
10871     * @param {Object} options (optional) Object literal with any of the Fx config options
10872     * @return {Roo.Element} The Element
10873     */
10874     scale : function(w, h, o){
10875         this.shift(Roo.apply({}, o, {
10876             width: w,
10877             height: h
10878         }));
10879         return this;
10880     },
10881
10882    /**
10883     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10884     * Any of these properties not specified in the config object will not be changed.  This effect 
10885     * requires that at least one new dimension, position or opacity setting must be passed in on
10886     * the config object in order for the function to have any effect.
10887     * Usage:
10888 <pre><code>
10889 // slide the element horizontally to x position 200 while changing the height and opacity
10890 el.shift({ x: 200, height: 50, opacity: .8 });
10891
10892 // common config options shown with default values.
10893 el.shift({
10894     width: [element's width],
10895     height: [element's height],
10896     x: [element's x position],
10897     y: [element's y position],
10898     opacity: [element's opacity],
10899     easing: 'easeOut',
10900     duration: .35
10901 });
10902 </code></pre>
10903     * @param {Object} options  Object literal with any of the Fx config options
10904     * @return {Roo.Element} The Element
10905     */
10906     shift : function(o){
10907         var el = this.getFxEl();
10908         o = o || {};
10909         el.queueFx(o, function(){
10910             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10911             if(w !== undefined){
10912                 a.width = {to: this.adjustWidth(w)};
10913             }
10914             if(h !== undefined){
10915                 a.height = {to: this.adjustHeight(h)};
10916             }
10917             if(x !== undefined || y !== undefined){
10918                 a.points = {to: [
10919                     x !== undefined ? x : this.getX(),
10920                     y !== undefined ? y : this.getY()
10921                 ]};
10922             }
10923             if(op !== undefined){
10924                 a.opacity = {to: op};
10925             }
10926             if(o.xy !== undefined){
10927                 a.points = {to: o.xy};
10928             }
10929             arguments.callee.anim = this.fxanim(a,
10930                 o, 'motion', .35, "easeOut", function(){
10931                 el.afterFx(o);
10932             });
10933         });
10934         return this;
10935     },
10936
10937         /**
10938          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10939          * ending point of the effect.
10940          * Usage:
10941          *<pre><code>
10942 // default: slide the element downward while fading out
10943 el.ghost();
10944
10945 // custom: slide the element out to the right with a 2-second duration
10946 el.ghost('r', { duration: 2 });
10947
10948 // common config options shown with default values
10949 el.ghost('b', {
10950     easing: 'easeOut',
10951     duration: .5
10952     remove: false,
10953     useDisplay: false
10954 });
10955 </code></pre>
10956          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10957          * @param {Object} options (optional) Object literal with any of the Fx config options
10958          * @return {Roo.Element} The Element
10959          */
10960     ghost : function(anchor, o){
10961         var el = this.getFxEl();
10962         o = o || {};
10963
10964         el.queueFx(o, function(){
10965             anchor = anchor || "b";
10966
10967             // restore values after effect
10968             var r = this.getFxRestore();
10969             var w = this.getWidth(),
10970                 h = this.getHeight();
10971
10972             var st = this.dom.style;
10973
10974             var after = function(){
10975                 if(o.useDisplay){
10976                     el.setDisplayed(false);
10977                 }else{
10978                     el.hide();
10979                 }
10980
10981                 el.clearOpacity();
10982                 el.setPositioning(r.pos);
10983                 st.width = r.width;
10984                 st.height = r.height;
10985
10986                 el.afterFx(o);
10987             };
10988
10989             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10990             switch(anchor.toLowerCase()){
10991                 case "t":
10992                     pt.by = [0, -h];
10993                 break;
10994                 case "l":
10995                     pt.by = [-w, 0];
10996                 break;
10997                 case "r":
10998                     pt.by = [w, 0];
10999                 break;
11000                 case "b":
11001                     pt.by = [0, h];
11002                 break;
11003                 case "tl":
11004                     pt.by = [-w, -h];
11005                 break;
11006                 case "bl":
11007                     pt.by = [-w, h];
11008                 break;
11009                 case "br":
11010                     pt.by = [w, h];
11011                 break;
11012                 case "tr":
11013                     pt.by = [w, -h];
11014                 break;
11015             }
11016
11017             arguments.callee.anim = this.fxanim(a,
11018                 o,
11019                 'motion',
11020                 .5,
11021                 "easeOut", after);
11022         });
11023         return this;
11024     },
11025
11026         /**
11027          * Ensures that all effects queued after syncFx is called on the element are
11028          * run concurrently.  This is the opposite of {@link #sequenceFx}.
11029          * @return {Roo.Element} The Element
11030          */
11031     syncFx : function(){
11032         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11033             block : false,
11034             concurrent : true,
11035             stopFx : false
11036         });
11037         return this;
11038     },
11039
11040         /**
11041          * Ensures that all effects queued after sequenceFx is called on the element are
11042          * run in sequence.  This is the opposite of {@link #syncFx}.
11043          * @return {Roo.Element} The Element
11044          */
11045     sequenceFx : function(){
11046         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11047             block : false,
11048             concurrent : false,
11049             stopFx : false
11050         });
11051         return this;
11052     },
11053
11054         /* @private */
11055     nextFx : function(){
11056         var ef = this.fxQueue[0];
11057         if(ef){
11058             ef.call(this);
11059         }
11060     },
11061
11062         /**
11063          * Returns true if the element has any effects actively running or queued, else returns false.
11064          * @return {Boolean} True if element has active effects, else false
11065          */
11066     hasActiveFx : function(){
11067         return this.fxQueue && this.fxQueue[0];
11068     },
11069
11070         /**
11071          * Stops any running effects and clears the element's internal effects queue if it contains
11072          * any additional effects that haven't started yet.
11073          * @return {Roo.Element} The Element
11074          */
11075     stopFx : function(){
11076         if(this.hasActiveFx()){
11077             var cur = this.fxQueue[0];
11078             if(cur && cur.anim && cur.anim.isAnimated()){
11079                 this.fxQueue = [cur]; // clear out others
11080                 cur.anim.stop(true);
11081             }
11082         }
11083         return this;
11084     },
11085
11086         /* @private */
11087     beforeFx : function(o){
11088         if(this.hasActiveFx() && !o.concurrent){
11089            if(o.stopFx){
11090                this.stopFx();
11091                return true;
11092            }
11093            return false;
11094         }
11095         return true;
11096     },
11097
11098         /**
11099          * Returns true if the element is currently blocking so that no other effect can be queued
11100          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11101          * used to ensure that an effect initiated by a user action runs to completion prior to the
11102          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11103          * @return {Boolean} True if blocking, else false
11104          */
11105     hasFxBlock : function(){
11106         var q = this.fxQueue;
11107         return q && q[0] && q[0].block;
11108     },
11109
11110         /* @private */
11111     queueFx : function(o, fn){
11112         if(!this.fxQueue){
11113             this.fxQueue = [];
11114         }
11115         if(!this.hasFxBlock()){
11116             Roo.applyIf(o, this.fxDefaults);
11117             if(!o.concurrent){
11118                 var run = this.beforeFx(o);
11119                 fn.block = o.block;
11120                 this.fxQueue.push(fn);
11121                 if(run){
11122                     this.nextFx();
11123                 }
11124             }else{
11125                 fn.call(this);
11126             }
11127         }
11128         return this;
11129     },
11130
11131         /* @private */
11132     fxWrap : function(pos, o, vis){
11133         var wrap;
11134         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11135             var wrapXY;
11136             if(o.fixPosition){
11137                 wrapXY = this.getXY();
11138             }
11139             var div = document.createElement("div");
11140             div.style.visibility = vis;
11141             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11142             wrap.setPositioning(pos);
11143             if(wrap.getStyle("position") == "static"){
11144                 wrap.position("relative");
11145             }
11146             this.clearPositioning('auto');
11147             wrap.clip();
11148             wrap.dom.appendChild(this.dom);
11149             if(wrapXY){
11150                 wrap.setXY(wrapXY);
11151             }
11152         }
11153         return wrap;
11154     },
11155
11156         /* @private */
11157     fxUnwrap : function(wrap, pos, o){
11158         this.clearPositioning();
11159         this.setPositioning(pos);
11160         if(!o.wrap){
11161             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11162             wrap.remove();
11163         }
11164     },
11165
11166         /* @private */
11167     getFxRestore : function(){
11168         var st = this.dom.style;
11169         return {pos: this.getPositioning(), width: st.width, height : st.height};
11170     },
11171
11172         /* @private */
11173     afterFx : function(o){
11174         if(o.afterStyle){
11175             this.applyStyles(o.afterStyle);
11176         }
11177         if(o.afterCls){
11178             this.addClass(o.afterCls);
11179         }
11180         if(o.remove === true){
11181             this.remove();
11182         }
11183         Roo.callback(o.callback, o.scope, [this]);
11184         if(!o.concurrent){
11185             this.fxQueue.shift();
11186             this.nextFx();
11187         }
11188     },
11189
11190         /* @private */
11191     getFxEl : function(){ // support for composite element fx
11192         return Roo.get(this.dom);
11193     },
11194
11195         /* @private */
11196     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11197         animType = animType || 'run';
11198         opt = opt || {};
11199         var anim = Roo.lib.Anim[animType](
11200             this.dom, args,
11201             (opt.duration || defaultDur) || .35,
11202             (opt.easing || defaultEase) || 'easeOut',
11203             function(){
11204                 Roo.callback(cb, this);
11205             },
11206             this
11207         );
11208         opt.anim = anim;
11209         return anim;
11210     }
11211 };
11212
11213 // backwords compat
11214 Roo.Fx.resize = Roo.Fx.scale;
11215
11216 //When included, Roo.Fx is automatically applied to Element so that all basic
11217 //effects are available directly via the Element API
11218 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11219  * Based on:
11220  * Ext JS Library 1.1.1
11221  * Copyright(c) 2006-2007, Ext JS, LLC.
11222  *
11223  * Originally Released Under LGPL - original licence link has changed is not relivant.
11224  *
11225  * Fork - LGPL
11226  * <script type="text/javascript">
11227  */
11228
11229
11230 /**
11231  * @class Roo.CompositeElement
11232  * Standard composite class. Creates a Roo.Element for every element in the collection.
11233  * <br><br>
11234  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11235  * actions will be performed on all the elements in this collection.</b>
11236  * <br><br>
11237  * All methods return <i>this</i> and can be chained.
11238  <pre><code>
11239  var els = Roo.select("#some-el div.some-class", true);
11240  // or select directly from an existing element
11241  var el = Roo.get('some-el');
11242  el.select('div.some-class', true);
11243
11244  els.setWidth(100); // all elements become 100 width
11245  els.hide(true); // all elements fade out and hide
11246  // or
11247  els.setWidth(100).hide(true);
11248  </code></pre>
11249  */
11250 Roo.CompositeElement = function(els){
11251     this.elements = [];
11252     this.addElements(els);
11253 };
11254 Roo.CompositeElement.prototype = {
11255     isComposite: true,
11256     addElements : function(els){
11257         if(!els) {
11258             return this;
11259         }
11260         if(typeof els == "string"){
11261             els = Roo.Element.selectorFunction(els);
11262         }
11263         var yels = this.elements;
11264         var index = yels.length-1;
11265         for(var i = 0, len = els.length; i < len; i++) {
11266                 yels[++index] = Roo.get(els[i]);
11267         }
11268         return this;
11269     },
11270
11271     /**
11272     * Clears this composite and adds the elements returned by the passed selector.
11273     * @param {String/Array} els A string CSS selector, an array of elements or an element
11274     * @return {CompositeElement} this
11275     */
11276     fill : function(els){
11277         this.elements = [];
11278         this.add(els);
11279         return this;
11280     },
11281
11282     /**
11283     * Filters this composite to only elements that match the passed selector.
11284     * @param {String} selector A string CSS selector
11285     * @param {Boolean} inverse return inverse filter (not matches)
11286     * @return {CompositeElement} this
11287     */
11288     filter : function(selector, inverse){
11289         var els = [];
11290         inverse = inverse || false;
11291         this.each(function(el){
11292             var match = inverse ? !el.is(selector) : el.is(selector);
11293             if(match){
11294                 els[els.length] = el.dom;
11295             }
11296         });
11297         this.fill(els);
11298         return this;
11299     },
11300
11301     invoke : function(fn, args){
11302         var els = this.elements;
11303         for(var i = 0, len = els.length; i < len; i++) {
11304                 Roo.Element.prototype[fn].apply(els[i], args);
11305         }
11306         return this;
11307     },
11308     /**
11309     * Adds elements to this composite.
11310     * @param {String/Array} els A string CSS selector, an array of elements or an element
11311     * @return {CompositeElement} this
11312     */
11313     add : function(els){
11314         if(typeof els == "string"){
11315             this.addElements(Roo.Element.selectorFunction(els));
11316         }else if(els.length !== undefined){
11317             this.addElements(els);
11318         }else{
11319             this.addElements([els]);
11320         }
11321         return this;
11322     },
11323     /**
11324     * Calls the passed function passing (el, this, index) for each element in this composite.
11325     * @param {Function} fn The function to call
11326     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11327     * @return {CompositeElement} this
11328     */
11329     each : function(fn, scope){
11330         var els = this.elements;
11331         for(var i = 0, len = els.length; i < len; i++){
11332             if(fn.call(scope || els[i], els[i], this, i) === false) {
11333                 break;
11334             }
11335         }
11336         return this;
11337     },
11338
11339     /**
11340      * Returns the Element object at the specified index
11341      * @param {Number} index
11342      * @return {Roo.Element}
11343      */
11344     item : function(index){
11345         return this.elements[index] || null;
11346     },
11347
11348     /**
11349      * Returns the first Element
11350      * @return {Roo.Element}
11351      */
11352     first : function(){
11353         return this.item(0);
11354     },
11355
11356     /**
11357      * Returns the last Element
11358      * @return {Roo.Element}
11359      */
11360     last : function(){
11361         return this.item(this.elements.length-1);
11362     },
11363
11364     /**
11365      * Returns the number of elements in this composite
11366      * @return Number
11367      */
11368     getCount : function(){
11369         return this.elements.length;
11370     },
11371
11372     /**
11373      * Returns true if this composite contains the passed element
11374      * @return Boolean
11375      */
11376     contains : function(el){
11377         return this.indexOf(el) !== -1;
11378     },
11379
11380     /**
11381      * Returns true if this composite contains the passed element
11382      * @return Boolean
11383      */
11384     indexOf : function(el){
11385         return this.elements.indexOf(Roo.get(el));
11386     },
11387
11388
11389     /**
11390     * Removes the specified element(s).
11391     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11392     * or an array of any of those.
11393     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11394     * @return {CompositeElement} this
11395     */
11396     removeElement : function(el, removeDom){
11397         if(el instanceof Array){
11398             for(var i = 0, len = el.length; i < len; i++){
11399                 this.removeElement(el[i]);
11400             }
11401             return this;
11402         }
11403         var index = typeof el == 'number' ? el : this.indexOf(el);
11404         if(index !== -1){
11405             if(removeDom){
11406                 var d = this.elements[index];
11407                 if(d.dom){
11408                     d.remove();
11409                 }else{
11410                     d.parentNode.removeChild(d);
11411                 }
11412             }
11413             this.elements.splice(index, 1);
11414         }
11415         return this;
11416     },
11417
11418     /**
11419     * Replaces the specified element with the passed element.
11420     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11421     * to replace.
11422     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11423     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11424     * @return {CompositeElement} this
11425     */
11426     replaceElement : function(el, replacement, domReplace){
11427         var index = typeof el == 'number' ? el : this.indexOf(el);
11428         if(index !== -1){
11429             if(domReplace){
11430                 this.elements[index].replaceWith(replacement);
11431             }else{
11432                 this.elements.splice(index, 1, Roo.get(replacement))
11433             }
11434         }
11435         return this;
11436     },
11437
11438     /**
11439      * Removes all elements.
11440      */
11441     clear : function(){
11442         this.elements = [];
11443     }
11444 };
11445 (function(){
11446     Roo.CompositeElement.createCall = function(proto, fnName){
11447         if(!proto[fnName]){
11448             proto[fnName] = function(){
11449                 return this.invoke(fnName, arguments);
11450             };
11451         }
11452     };
11453     for(var fnName in Roo.Element.prototype){
11454         if(typeof Roo.Element.prototype[fnName] == "function"){
11455             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11456         }
11457     };
11458 })();
11459 /*
11460  * Based on:
11461  * Ext JS Library 1.1.1
11462  * Copyright(c) 2006-2007, Ext JS, LLC.
11463  *
11464  * Originally Released Under LGPL - original licence link has changed is not relivant.
11465  *
11466  * Fork - LGPL
11467  * <script type="text/javascript">
11468  */
11469
11470 /**
11471  * @class Roo.CompositeElementLite
11472  * @extends Roo.CompositeElement
11473  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11474  <pre><code>
11475  var els = Roo.select("#some-el div.some-class");
11476  // or select directly from an existing element
11477  var el = Roo.get('some-el');
11478  el.select('div.some-class');
11479
11480  els.setWidth(100); // all elements become 100 width
11481  els.hide(true); // all elements fade out and hide
11482  // or
11483  els.setWidth(100).hide(true);
11484  </code></pre><br><br>
11485  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11486  * actions will be performed on all the elements in this collection.</b>
11487  */
11488 Roo.CompositeElementLite = function(els){
11489     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11490     this.el = new Roo.Element.Flyweight();
11491 };
11492 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11493     addElements : function(els){
11494         if(els){
11495             if(els instanceof Array){
11496                 this.elements = this.elements.concat(els);
11497             }else{
11498                 var yels = this.elements;
11499                 var index = yels.length-1;
11500                 for(var i = 0, len = els.length; i < len; i++) {
11501                     yels[++index] = els[i];
11502                 }
11503             }
11504         }
11505         return this;
11506     },
11507     invoke : function(fn, args){
11508         var els = this.elements;
11509         var el = this.el;
11510         for(var i = 0, len = els.length; i < len; i++) {
11511             el.dom = els[i];
11512                 Roo.Element.prototype[fn].apply(el, args);
11513         }
11514         return this;
11515     },
11516     /**
11517      * Returns a flyweight Element of the dom element object at the specified index
11518      * @param {Number} index
11519      * @return {Roo.Element}
11520      */
11521     item : function(index){
11522         if(!this.elements[index]){
11523             return null;
11524         }
11525         this.el.dom = this.elements[index];
11526         return this.el;
11527     },
11528
11529     // fixes scope with flyweight
11530     addListener : function(eventName, handler, scope, opt){
11531         var els = this.elements;
11532         for(var i = 0, len = els.length; i < len; i++) {
11533             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11534         }
11535         return this;
11536     },
11537
11538     /**
11539     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11540     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11541     * a reference to the dom node, use el.dom.</b>
11542     * @param {Function} fn The function to call
11543     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11544     * @return {CompositeElement} this
11545     */
11546     each : function(fn, scope){
11547         var els = this.elements;
11548         var el = this.el;
11549         for(var i = 0, len = els.length; i < len; i++){
11550             el.dom = els[i];
11551                 if(fn.call(scope || el, el, this, i) === false){
11552                 break;
11553             }
11554         }
11555         return this;
11556     },
11557
11558     indexOf : function(el){
11559         return this.elements.indexOf(Roo.getDom(el));
11560     },
11561
11562     replaceElement : function(el, replacement, domReplace){
11563         var index = typeof el == 'number' ? el : this.indexOf(el);
11564         if(index !== -1){
11565             replacement = Roo.getDom(replacement);
11566             if(domReplace){
11567                 var d = this.elements[index];
11568                 d.parentNode.insertBefore(replacement, d);
11569                 d.parentNode.removeChild(d);
11570             }
11571             this.elements.splice(index, 1, replacement);
11572         }
11573         return this;
11574     }
11575 });
11576 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11577
11578 /*
11579  * Based on:
11580  * Ext JS Library 1.1.1
11581  * Copyright(c) 2006-2007, Ext JS, LLC.
11582  *
11583  * Originally Released Under LGPL - original licence link has changed is not relivant.
11584  *
11585  * Fork - LGPL
11586  * <script type="text/javascript">
11587  */
11588
11589  
11590
11591 /**
11592  * @class Roo.data.Connection
11593  * @extends Roo.util.Observable
11594  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11595  * either to a configured URL, or to a URL specified at request time. 
11596  * 
11597  * Requests made by this class are asynchronous, and will return immediately. No data from
11598  * the server will be available to the statement immediately following the {@link #request} call.
11599  * To process returned data, use a callback in the request options object, or an event listener.
11600  * 
11601  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11602  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11603  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11604  * property and, if present, the IFRAME's XML document as the responseXML property.
11605  * 
11606  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11607  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11608  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11609  * standard DOM methods.
11610  * @constructor
11611  * @param {Object} config a configuration object.
11612  */
11613 Roo.data.Connection = function(config){
11614     Roo.apply(this, config);
11615     this.addEvents({
11616         /**
11617          * @event beforerequest
11618          * Fires before a network request is made to retrieve a data object.
11619          * @param {Connection} conn This Connection object.
11620          * @param {Object} options The options config object passed to the {@link #request} method.
11621          */
11622         "beforerequest" : true,
11623         /**
11624          * @event requestcomplete
11625          * Fires if the request was successfully completed.
11626          * @param {Connection} conn This Connection object.
11627          * @param {Object} response The XHR object containing the response data.
11628          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11629          * @param {Object} options The options config object passed to the {@link #request} method.
11630          */
11631         "requestcomplete" : true,
11632         /**
11633          * @event requestexception
11634          * Fires if an error HTTP status was returned from the server.
11635          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11636          * @param {Connection} conn This Connection object.
11637          * @param {Object} response The XHR object containing the response data.
11638          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11639          * @param {Object} options The options config object passed to the {@link #request} method.
11640          */
11641         "requestexception" : true
11642     });
11643     Roo.data.Connection.superclass.constructor.call(this);
11644 };
11645
11646 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11647     /**
11648      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11649      */
11650     /**
11651      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11652      * extra parameters to each request made by this object. (defaults to undefined)
11653      */
11654     /**
11655      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11656      *  to each request made by this object. (defaults to undefined)
11657      */
11658     /**
11659      * @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)
11660      */
11661     /**
11662      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11663      */
11664     timeout : 30000,
11665     /**
11666      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11667      * @type Boolean
11668      */
11669     autoAbort:false,
11670
11671     /**
11672      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11673      * @type Boolean
11674      */
11675     disableCaching: true,
11676
11677     /**
11678      * Sends an HTTP request to a remote server.
11679      * @param {Object} options An object which may contain the following properties:<ul>
11680      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11681      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11682      * request, a url encoded string or a function to call to get either.</li>
11683      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11684      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11685      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11686      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11687      * <li>options {Object} The parameter to the request call.</li>
11688      * <li>success {Boolean} True if the request succeeded.</li>
11689      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11690      * </ul></li>
11691      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11692      * The callback is passed the following parameters:<ul>
11693      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11694      * <li>options {Object} The parameter to the request call.</li>
11695      * </ul></li>
11696      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11697      * The callback is passed the following parameters:<ul>
11698      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11699      * <li>options {Object} The parameter to the request call.</li>
11700      * </ul></li>
11701      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11702      * for the callback function. Defaults to the browser window.</li>
11703      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11704      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11705      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11706      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11707      * params for the post data. Any params will be appended to the URL.</li>
11708      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11709      * </ul>
11710      * @return {Number} transactionId
11711      */
11712     request : function(o){
11713         if(this.fireEvent("beforerequest", this, o) !== false){
11714             var p = o.params;
11715
11716             if(typeof p == "function"){
11717                 p = p.call(o.scope||window, o);
11718             }
11719             if(typeof p == "object"){
11720                 p = Roo.urlEncode(o.params);
11721             }
11722             if(this.extraParams){
11723                 var extras = Roo.urlEncode(this.extraParams);
11724                 p = p ? (p + '&' + extras) : extras;
11725             }
11726
11727             var url = o.url || this.url;
11728             if(typeof url == 'function'){
11729                 url = url.call(o.scope||window, o);
11730             }
11731
11732             if(o.form){
11733                 var form = Roo.getDom(o.form);
11734                 url = url || form.action;
11735
11736                 var enctype = form.getAttribute("enctype");
11737                 
11738                 if (o.formData) {
11739                     return this.doFormDataUpload(o, url);
11740                 }
11741                 
11742                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11743                     return this.doFormUpload(o, p, url);
11744                 }
11745                 var f = Roo.lib.Ajax.serializeForm(form);
11746                 p = p ? (p + '&' + f) : f;
11747             }
11748             
11749             if (!o.form && o.formData) {
11750                 o.formData = o.formData === true ? new FormData() : o.formData;
11751                 for (var k in o.params) {
11752                     o.formData.append(k,o.params[k]);
11753                 }
11754                     
11755                 return this.doFormDataUpload(o, url);
11756             }
11757             
11758
11759             var hs = o.headers;
11760             if(this.defaultHeaders){
11761                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11762                 if(!o.headers){
11763                     o.headers = hs;
11764                 }
11765             }
11766
11767             var cb = {
11768                 success: this.handleResponse,
11769                 failure: this.handleFailure,
11770                 scope: this,
11771                 argument: {options: o},
11772                 timeout : o.timeout || this.timeout
11773             };
11774
11775             var method = o.method||this.method||(p ? "POST" : "GET");
11776
11777             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11778                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11779             }
11780
11781             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11782                 if(o.autoAbort){
11783                     this.abort();
11784                 }
11785             }else if(this.autoAbort !== false){
11786                 this.abort();
11787             }
11788
11789             if((method == 'GET' && p) || o.xmlData){
11790                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11791                 p = '';
11792             }
11793             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
11794             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11795             Roo.lib.Ajax.useDefaultHeader == true;
11796             return this.transId;
11797         }else{
11798             Roo.callback(o.callback, o.scope, [o, null, null]);
11799             return null;
11800         }
11801     },
11802
11803     /**
11804      * Determine whether this object has a request outstanding.
11805      * @param {Number} transactionId (Optional) defaults to the last transaction
11806      * @return {Boolean} True if there is an outstanding request.
11807      */
11808     isLoading : function(transId){
11809         if(transId){
11810             return Roo.lib.Ajax.isCallInProgress(transId);
11811         }else{
11812             return this.transId ? true : false;
11813         }
11814     },
11815
11816     /**
11817      * Aborts any outstanding request.
11818      * @param {Number} transactionId (Optional) defaults to the last transaction
11819      */
11820     abort : function(transId){
11821         if(transId || this.isLoading()){
11822             Roo.lib.Ajax.abort(transId || this.transId);
11823         }
11824     },
11825
11826     // private
11827     handleResponse : function(response){
11828         this.transId = false;
11829         var options = response.argument.options;
11830         response.argument = options ? options.argument : null;
11831         this.fireEvent("requestcomplete", this, response, options);
11832         Roo.callback(options.success, options.scope, [response, options]);
11833         Roo.callback(options.callback, options.scope, [options, true, response]);
11834     },
11835
11836     // private
11837     handleFailure : function(response, e){
11838         this.transId = false;
11839         var options = response.argument.options;
11840         response.argument = options ? options.argument : null;
11841         this.fireEvent("requestexception", this, response, options, e);
11842         Roo.callback(options.failure, options.scope, [response, options]);
11843         Roo.callback(options.callback, options.scope, [options, false, response]);
11844     },
11845
11846     // private
11847     doFormUpload : function(o, ps, url){
11848         var id = Roo.id();
11849         var frame = document.createElement('iframe');
11850         frame.id = id;
11851         frame.name = id;
11852         frame.className = 'x-hidden';
11853         if(Roo.isIE){
11854             frame.src = Roo.SSL_SECURE_URL;
11855         }
11856         document.body.appendChild(frame);
11857
11858         if(Roo.isIE){
11859            document.frames[id].name = id;
11860         }
11861
11862         var form = Roo.getDom(o.form);
11863         form.target = id;
11864         form.method = 'POST';
11865         form.enctype = form.encoding = 'multipart/form-data';
11866         if(url){
11867             form.action = url;
11868         }
11869
11870         var hiddens, hd;
11871         if(ps){ // add dynamic params
11872             hiddens = [];
11873             ps = Roo.urlDecode(ps, false);
11874             for(var k in ps){
11875                 if(ps.hasOwnProperty(k)){
11876                     hd = document.createElement('input');
11877                     hd.type = 'hidden';
11878                     hd.name = k;
11879                     hd.value = ps[k];
11880                     form.appendChild(hd);
11881                     hiddens.push(hd);
11882                 }
11883             }
11884         }
11885
11886         function cb(){
11887             var r = {  // bogus response object
11888                 responseText : '',
11889                 responseXML : null
11890             };
11891
11892             r.argument = o ? o.argument : null;
11893
11894             try { //
11895                 var doc;
11896                 if(Roo.isIE){
11897                     doc = frame.contentWindow.document;
11898                 }else {
11899                     doc = (frame.contentDocument || window.frames[id].document);
11900                 }
11901                 if(doc && doc.body){
11902                     r.responseText = doc.body.innerHTML;
11903                 }
11904                 if(doc && doc.XMLDocument){
11905                     r.responseXML = doc.XMLDocument;
11906                 }else {
11907                     r.responseXML = doc;
11908                 }
11909             }
11910             catch(e) {
11911                 // ignore
11912             }
11913
11914             Roo.EventManager.removeListener(frame, 'load', cb, this);
11915
11916             this.fireEvent("requestcomplete", this, r, o);
11917             Roo.callback(o.success, o.scope, [r, o]);
11918             Roo.callback(o.callback, o.scope, [o, true, r]);
11919
11920             setTimeout(function(){document.body.removeChild(frame);}, 100);
11921         }
11922
11923         Roo.EventManager.on(frame, 'load', cb, this);
11924         form.submit();
11925
11926         if(hiddens){ // remove dynamic params
11927             for(var i = 0, len = hiddens.length; i < len; i++){
11928                 form.removeChild(hiddens[i]);
11929             }
11930         }
11931     },
11932     // this is a 'formdata version???'
11933     
11934     
11935     doFormDataUpload : function(o,  url)
11936     {
11937         var formData;
11938         if (o.form) {
11939             var form =  Roo.getDom(o.form);
11940             form.enctype = form.encoding = 'multipart/form-data';
11941             formData = o.formData === true ? new FormData(form) : o.formData;
11942         } else {
11943             formData = o.formData === true ? new FormData() : o.formData;
11944         }
11945         
11946       
11947         var cb = {
11948             success: this.handleResponse,
11949             failure: this.handleFailure,
11950             scope: this,
11951             argument: {options: o},
11952             timeout : o.timeout || this.timeout
11953         };
11954  
11955         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11956             if(o.autoAbort){
11957                 this.abort();
11958             }
11959         }else if(this.autoAbort !== false){
11960             this.abort();
11961         }
11962
11963         //Roo.lib.Ajax.defaultPostHeader = null;
11964         Roo.lib.Ajax.useDefaultHeader = false;
11965         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
11966         Roo.lib.Ajax.useDefaultHeader = true;
11967  
11968          
11969     }
11970     
11971 });
11972 /*
11973  * Based on:
11974  * Ext JS Library 1.1.1
11975  * Copyright(c) 2006-2007, Ext JS, LLC.
11976  *
11977  * Originally Released Under LGPL - original licence link has changed is not relivant.
11978  *
11979  * Fork - LGPL
11980  * <script type="text/javascript">
11981  */
11982  
11983 /**
11984  * Global Ajax request class.
11985  * 
11986  * @class Roo.Ajax
11987  * @extends Roo.data.Connection
11988  * @static
11989  * 
11990  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11991  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11992  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11993  * @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)
11994  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11995  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11996  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11997  */
11998 Roo.Ajax = new Roo.data.Connection({
11999     // fix up the docs
12000     /**
12001      * @scope Roo.Ajax
12002      * @type {Boolear} 
12003      */
12004     autoAbort : false,
12005
12006     /**
12007      * Serialize the passed form into a url encoded string
12008      * @scope Roo.Ajax
12009      * @param {String/HTMLElement} form
12010      * @return {String}
12011      */
12012     serializeForm : function(form){
12013         return Roo.lib.Ajax.serializeForm(form);
12014     }
12015 });/*
12016  * Based on:
12017  * Ext JS Library 1.1.1
12018  * Copyright(c) 2006-2007, Ext JS, LLC.
12019  *
12020  * Originally Released Under LGPL - original licence link has changed is not relivant.
12021  *
12022  * Fork - LGPL
12023  * <script type="text/javascript">
12024  */
12025
12026  
12027 /**
12028  * @class Roo.UpdateManager
12029  * @extends Roo.util.Observable
12030  * Provides AJAX-style update for Element object.<br><br>
12031  * Usage:<br>
12032  * <pre><code>
12033  * // Get it from a Roo.Element object
12034  * var el = Roo.get("foo");
12035  * var mgr = el.getUpdateManager();
12036  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
12037  * ...
12038  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
12039  * <br>
12040  * // or directly (returns the same UpdateManager instance)
12041  * var mgr = new Roo.UpdateManager("myElementId");
12042  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
12043  * mgr.on("update", myFcnNeedsToKnow);
12044  * <br>
12045    // short handed call directly from the element object
12046    Roo.get("foo").load({
12047         url: "bar.php",
12048         scripts:true,
12049         params: "for=bar",
12050         text: "Loading Foo..."
12051    });
12052  * </code></pre>
12053  * @constructor
12054  * Create new UpdateManager directly.
12055  * @param {String/HTMLElement/Roo.Element} el The element to update
12056  * @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).
12057  */
12058 Roo.UpdateManager = function(el, forceNew){
12059     el = Roo.get(el);
12060     if(!forceNew && el.updateManager){
12061         return el.updateManager;
12062     }
12063     /**
12064      * The Element object
12065      * @type Roo.Element
12066      */
12067     this.el = el;
12068     /**
12069      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
12070      * @type String
12071      */
12072     this.defaultUrl = null;
12073
12074     this.addEvents({
12075         /**
12076          * @event beforeupdate
12077          * Fired before an update is made, return false from your handler and the update is cancelled.
12078          * @param {Roo.Element} el
12079          * @param {String/Object/Function} url
12080          * @param {String/Object} params
12081          */
12082         "beforeupdate": true,
12083         /**
12084          * @event update
12085          * Fired after successful update is made.
12086          * @param {Roo.Element} el
12087          * @param {Object} oResponseObject The response Object
12088          */
12089         "update": true,
12090         /**
12091          * @event failure
12092          * Fired on update failure.
12093          * @param {Roo.Element} el
12094          * @param {Object} oResponseObject The response Object
12095          */
12096         "failure": true
12097     });
12098     var d = Roo.UpdateManager.defaults;
12099     /**
12100      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12101      * @type String
12102      */
12103     this.sslBlankUrl = d.sslBlankUrl;
12104     /**
12105      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12106      * @type Boolean
12107      */
12108     this.disableCaching = d.disableCaching;
12109     /**
12110      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12111      * @type String
12112      */
12113     this.indicatorText = d.indicatorText;
12114     /**
12115      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12116      * @type String
12117      */
12118     this.showLoadIndicator = d.showLoadIndicator;
12119     /**
12120      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12121      * @type Number
12122      */
12123     this.timeout = d.timeout;
12124
12125     /**
12126      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12127      * @type Boolean
12128      */
12129     this.loadScripts = d.loadScripts;
12130
12131     /**
12132      * Transaction object of current executing transaction
12133      */
12134     this.transaction = null;
12135
12136     /**
12137      * @private
12138      */
12139     this.autoRefreshProcId = null;
12140     /**
12141      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12142      * @type Function
12143      */
12144     this.refreshDelegate = this.refresh.createDelegate(this);
12145     /**
12146      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12147      * @type Function
12148      */
12149     this.updateDelegate = this.update.createDelegate(this);
12150     /**
12151      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12152      * @type Function
12153      */
12154     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12155     /**
12156      * @private
12157      */
12158     this.successDelegate = this.processSuccess.createDelegate(this);
12159     /**
12160      * @private
12161      */
12162     this.failureDelegate = this.processFailure.createDelegate(this);
12163
12164     if(!this.renderer){
12165      /**
12166       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12167       */
12168     this.renderer = new Roo.UpdateManager.BasicRenderer();
12169     }
12170     
12171     Roo.UpdateManager.superclass.constructor.call(this);
12172 };
12173
12174 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12175     /**
12176      * Get the Element this UpdateManager is bound to
12177      * @return {Roo.Element} The element
12178      */
12179     getEl : function(){
12180         return this.el;
12181     },
12182     /**
12183      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12184      * @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:
12185 <pre><code>
12186 um.update({<br/>
12187     url: "your-url.php",<br/>
12188     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12189     callback: yourFunction,<br/>
12190     scope: yourObject, //(optional scope)  <br/>
12191     discardUrl: false, <br/>
12192     nocache: false,<br/>
12193     text: "Loading...",<br/>
12194     timeout: 30,<br/>
12195     scripts: false<br/>
12196 });
12197 </code></pre>
12198      * The only required property is url. The optional properties nocache, text and scripts
12199      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12200      * @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}
12201      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12202      * @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.
12203      */
12204     update : function(url, params, callback, discardUrl){
12205         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12206             var method = this.method,
12207                 cfg;
12208             if(typeof url == "object"){ // must be config object
12209                 cfg = url;
12210                 url = cfg.url;
12211                 params = params || cfg.params;
12212                 callback = callback || cfg.callback;
12213                 discardUrl = discardUrl || cfg.discardUrl;
12214                 if(callback && cfg.scope){
12215                     callback = callback.createDelegate(cfg.scope);
12216                 }
12217                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12218                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12219                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12220                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12221                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12222             }
12223             this.showLoading();
12224             if(!discardUrl){
12225                 this.defaultUrl = url;
12226             }
12227             if(typeof url == "function"){
12228                 url = url.call(this);
12229             }
12230
12231             method = method || (params ? "POST" : "GET");
12232             if(method == "GET"){
12233                 url = this.prepareUrl(url);
12234             }
12235
12236             var o = Roo.apply(cfg ||{}, {
12237                 url : url,
12238                 params: params,
12239                 success: this.successDelegate,
12240                 failure: this.failureDelegate,
12241                 callback: undefined,
12242                 timeout: (this.timeout*1000),
12243                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12244             });
12245             Roo.log("updated manager called with timeout of " + o.timeout);
12246             this.transaction = Roo.Ajax.request(o);
12247         }
12248     },
12249
12250     /**
12251      * 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.
12252      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12253      * @param {String/HTMLElement} form The form Id or form element
12254      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12255      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12256      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12257      */
12258     formUpdate : function(form, url, reset, callback){
12259         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12260             if(typeof url == "function"){
12261                 url = url.call(this);
12262             }
12263             form = Roo.getDom(form);
12264             this.transaction = Roo.Ajax.request({
12265                 form: form,
12266                 url:url,
12267                 success: this.successDelegate,
12268                 failure: this.failureDelegate,
12269                 timeout: (this.timeout*1000),
12270                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12271             });
12272             this.showLoading.defer(1, this);
12273         }
12274     },
12275
12276     /**
12277      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12278      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12279      */
12280     refresh : function(callback){
12281         if(this.defaultUrl == null){
12282             return;
12283         }
12284         this.update(this.defaultUrl, null, callback, true);
12285     },
12286
12287     /**
12288      * Set this element to auto refresh.
12289      * @param {Number} interval How often to update (in seconds).
12290      * @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)
12291      * @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}
12292      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12293      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12294      */
12295     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12296         if(refreshNow){
12297             this.update(url || this.defaultUrl, params, callback, true);
12298         }
12299         if(this.autoRefreshProcId){
12300             clearInterval(this.autoRefreshProcId);
12301         }
12302         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12303     },
12304
12305     /**
12306      * Stop auto refresh on this element.
12307      */
12308      stopAutoRefresh : function(){
12309         if(this.autoRefreshProcId){
12310             clearInterval(this.autoRefreshProcId);
12311             delete this.autoRefreshProcId;
12312         }
12313     },
12314
12315     isAutoRefreshing : function(){
12316        return this.autoRefreshProcId ? true : false;
12317     },
12318     /**
12319      * Called to update the element to "Loading" state. Override to perform custom action.
12320      */
12321     showLoading : function(){
12322         if(this.showLoadIndicator){
12323             this.el.update(this.indicatorText);
12324         }
12325     },
12326
12327     /**
12328      * Adds unique parameter to query string if disableCaching = true
12329      * @private
12330      */
12331     prepareUrl : function(url){
12332         if(this.disableCaching){
12333             var append = "_dc=" + (new Date().getTime());
12334             if(url.indexOf("?") !== -1){
12335                 url += "&" + append;
12336             }else{
12337                 url += "?" + append;
12338             }
12339         }
12340         return url;
12341     },
12342
12343     /**
12344      * @private
12345      */
12346     processSuccess : function(response){
12347         this.transaction = null;
12348         if(response.argument.form && response.argument.reset){
12349             try{ // put in try/catch since some older FF releases had problems with this
12350                 response.argument.form.reset();
12351             }catch(e){}
12352         }
12353         if(this.loadScripts){
12354             this.renderer.render(this.el, response, this,
12355                 this.updateComplete.createDelegate(this, [response]));
12356         }else{
12357             this.renderer.render(this.el, response, this);
12358             this.updateComplete(response);
12359         }
12360     },
12361
12362     updateComplete : function(response){
12363         this.fireEvent("update", this.el, response);
12364         if(typeof response.argument.callback == "function"){
12365             response.argument.callback(this.el, true, response);
12366         }
12367     },
12368
12369     /**
12370      * @private
12371      */
12372     processFailure : function(response){
12373         this.transaction = null;
12374         this.fireEvent("failure", this.el, response);
12375         if(typeof response.argument.callback == "function"){
12376             response.argument.callback(this.el, false, response);
12377         }
12378     },
12379
12380     /**
12381      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12382      * @param {Object} renderer The object implementing the render() method
12383      */
12384     setRenderer : function(renderer){
12385         this.renderer = renderer;
12386     },
12387
12388     getRenderer : function(){
12389        return this.renderer;
12390     },
12391
12392     /**
12393      * Set the defaultUrl used for updates
12394      * @param {String/Function} defaultUrl The url or a function to call to get the url
12395      */
12396     setDefaultUrl : function(defaultUrl){
12397         this.defaultUrl = defaultUrl;
12398     },
12399
12400     /**
12401      * Aborts the executing transaction
12402      */
12403     abort : function(){
12404         if(this.transaction){
12405             Roo.Ajax.abort(this.transaction);
12406         }
12407     },
12408
12409     /**
12410      * Returns true if an update is in progress
12411      * @return {Boolean}
12412      */
12413     isUpdating : function(){
12414         if(this.transaction){
12415             return Roo.Ajax.isLoading(this.transaction);
12416         }
12417         return false;
12418     }
12419 });
12420
12421 /**
12422  * @class Roo.UpdateManager.defaults
12423  * @static (not really - but it helps the doc tool)
12424  * The defaults collection enables customizing the default properties of UpdateManager
12425  */
12426    Roo.UpdateManager.defaults = {
12427        /**
12428          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12429          * @type Number
12430          */
12431          timeout : 30,
12432
12433          /**
12434          * True to process scripts by default (Defaults to false).
12435          * @type Boolean
12436          */
12437         loadScripts : false,
12438
12439         /**
12440         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12441         * @type String
12442         */
12443         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12444         /**
12445          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12446          * @type Boolean
12447          */
12448         disableCaching : false,
12449         /**
12450          * Whether to show indicatorText when loading (Defaults to true).
12451          * @type Boolean
12452          */
12453         showLoadIndicator : true,
12454         /**
12455          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12456          * @type String
12457          */
12458         indicatorText : '<div class="loading-indicator">Loading...</div>'
12459    };
12460
12461 /**
12462  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12463  *Usage:
12464  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12465  * @param {String/HTMLElement/Roo.Element} el The element to update
12466  * @param {String} url The url
12467  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12468  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12469  * @static
12470  * @deprecated
12471  * @member Roo.UpdateManager
12472  */
12473 Roo.UpdateManager.updateElement = function(el, url, params, options){
12474     var um = Roo.get(el, true).getUpdateManager();
12475     Roo.apply(um, options);
12476     um.update(url, params, options ? options.callback : null);
12477 };
12478 // alias for backwards compat
12479 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12480 /**
12481  * @class Roo.UpdateManager.BasicRenderer
12482  * Default Content renderer. Updates the elements innerHTML with the responseText.
12483  */
12484 Roo.UpdateManager.BasicRenderer = function(){};
12485
12486 Roo.UpdateManager.BasicRenderer.prototype = {
12487     /**
12488      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12489      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12490      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12491      * @param {Roo.Element} el The element being rendered
12492      * @param {Object} response The YUI Connect response object
12493      * @param {UpdateManager} updateManager The calling update manager
12494      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12495      */
12496      render : function(el, response, updateManager, callback){
12497         el.update(response.responseText, updateManager.loadScripts, callback);
12498     }
12499 };
12500 /*
12501  * Based on:
12502  * Roo JS
12503  * (c)) Alan Knowles
12504  * Licence : LGPL
12505  */
12506
12507
12508 /**
12509  * @class Roo.DomTemplate
12510  * @extends Roo.Template
12511  * An effort at a dom based template engine..
12512  *
12513  * Similar to XTemplate, except it uses dom parsing to create the template..
12514  *
12515  * Supported features:
12516  *
12517  *  Tags:
12518
12519 <pre><code>
12520       {a_variable} - output encoded.
12521       {a_variable.format:("Y-m-d")} - call a method on the variable
12522       {a_variable:raw} - unencoded output
12523       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12524       {a_variable:this.method_on_template(...)} - call a method on the template object.
12525  
12526 </code></pre>
12527  *  The tpl tag:
12528 <pre><code>
12529         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12530         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12531         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12532         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12533   
12534 </code></pre>
12535  *      
12536  */
12537 Roo.DomTemplate = function()
12538 {
12539      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12540      if (this.html) {
12541         this.compile();
12542      }
12543 };
12544
12545
12546 Roo.extend(Roo.DomTemplate, Roo.Template, {
12547     /**
12548      * id counter for sub templates.
12549      */
12550     id : 0,
12551     /**
12552      * flag to indicate if dom parser is inside a pre,
12553      * it will strip whitespace if not.
12554      */
12555     inPre : false,
12556     
12557     /**
12558      * The various sub templates
12559      */
12560     tpls : false,
12561     
12562     
12563     
12564     /**
12565      *
12566      * basic tag replacing syntax
12567      * WORD:WORD()
12568      *
12569      * // you can fake an object call by doing this
12570      *  x.t:(test,tesT) 
12571      * 
12572      */
12573     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12574     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12575     
12576     iterChild : function (node, method) {
12577         
12578         var oldPre = this.inPre;
12579         if (node.tagName == 'PRE') {
12580             this.inPre = true;
12581         }
12582         for( var i = 0; i < node.childNodes.length; i++) {
12583             method.call(this, node.childNodes[i]);
12584         }
12585         this.inPre = oldPre;
12586     },
12587     
12588     
12589     
12590     /**
12591      * compile the template
12592      *
12593      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12594      *
12595      */
12596     compile: function()
12597     {
12598         var s = this.html;
12599         
12600         // covert the html into DOM...
12601         var doc = false;
12602         var div =false;
12603         try {
12604             doc = document.implementation.createHTMLDocument("");
12605             doc.documentElement.innerHTML =   this.html  ;
12606             div = doc.documentElement;
12607         } catch (e) {
12608             // old IE... - nasty -- it causes all sorts of issues.. with
12609             // images getting pulled from server..
12610             div = document.createElement('div');
12611             div.innerHTML = this.html;
12612         }
12613         //doc.documentElement.innerHTML = htmlBody
12614          
12615         
12616         
12617         this.tpls = [];
12618         var _t = this;
12619         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12620         
12621         var tpls = this.tpls;
12622         
12623         // create a top level template from the snippet..
12624         
12625         //Roo.log(div.innerHTML);
12626         
12627         var tpl = {
12628             uid : 'master',
12629             id : this.id++,
12630             attr : false,
12631             value : false,
12632             body : div.innerHTML,
12633             
12634             forCall : false,
12635             execCall : false,
12636             dom : div,
12637             isTop : true
12638             
12639         };
12640         tpls.unshift(tpl);
12641         
12642         
12643         // compile them...
12644         this.tpls = [];
12645         Roo.each(tpls, function(tp){
12646             this.compileTpl(tp);
12647             this.tpls[tp.id] = tp;
12648         }, this);
12649         
12650         this.master = tpls[0];
12651         return this;
12652         
12653         
12654     },
12655     
12656     compileNode : function(node, istop) {
12657         // test for
12658         //Roo.log(node);
12659         
12660         
12661         // skip anything not a tag..
12662         if (node.nodeType != 1) {
12663             if (node.nodeType == 3 && !this.inPre) {
12664                 // reduce white space..
12665                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12666                 
12667             }
12668             return;
12669         }
12670         
12671         var tpl = {
12672             uid : false,
12673             id : false,
12674             attr : false,
12675             value : false,
12676             body : '',
12677             
12678             forCall : false,
12679             execCall : false,
12680             dom : false,
12681             isTop : istop
12682             
12683             
12684         };
12685         
12686         
12687         switch(true) {
12688             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12689             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12690             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12691             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12692             // no default..
12693         }
12694         
12695         
12696         if (!tpl.attr) {
12697             // just itterate children..
12698             this.iterChild(node,this.compileNode);
12699             return;
12700         }
12701         tpl.uid = this.id++;
12702         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12703         node.removeAttribute('roo-'+ tpl.attr);
12704         if (tpl.attr != 'name') {
12705             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12706             node.parentNode.replaceChild(placeholder,  node);
12707         } else {
12708             
12709             var placeholder =  document.createElement('span');
12710             placeholder.className = 'roo-tpl-' + tpl.value;
12711             node.parentNode.replaceChild(placeholder,  node);
12712         }
12713         
12714         // parent now sees '{domtplXXXX}
12715         this.iterChild(node,this.compileNode);
12716         
12717         // we should now have node body...
12718         var div = document.createElement('div');
12719         div.appendChild(node);
12720         tpl.dom = node;
12721         // this has the unfortunate side effect of converting tagged attributes
12722         // eg. href="{...}" into %7C...%7D
12723         // this has been fixed by searching for those combo's although it's a bit hacky..
12724         
12725         
12726         tpl.body = div.innerHTML;
12727         
12728         
12729          
12730         tpl.id = tpl.uid;
12731         switch(tpl.attr) {
12732             case 'for' :
12733                 switch (tpl.value) {
12734                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12735                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12736                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12737                 }
12738                 break;
12739             
12740             case 'exec':
12741                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12742                 break;
12743             
12744             case 'if':     
12745                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12746                 break;
12747             
12748             case 'name':
12749                 tpl.id  = tpl.value; // replace non characters???
12750                 break;
12751             
12752         }
12753         
12754         
12755         this.tpls.push(tpl);
12756         
12757         
12758         
12759     },
12760     
12761     
12762     
12763     
12764     /**
12765      * Compile a segment of the template into a 'sub-template'
12766      *
12767      * 
12768      * 
12769      *
12770      */
12771     compileTpl : function(tpl)
12772     {
12773         var fm = Roo.util.Format;
12774         var useF = this.disableFormats !== true;
12775         
12776         var sep = Roo.isGecko ? "+\n" : ",\n";
12777         
12778         var undef = function(str) {
12779             Roo.debug && Roo.log("Property not found :"  + str);
12780             return '';
12781         };
12782           
12783         //Roo.log(tpl.body);
12784         
12785         
12786         
12787         var fn = function(m, lbrace, name, format, args)
12788         {
12789             //Roo.log("ARGS");
12790             //Roo.log(arguments);
12791             args = args ? args.replace(/\\'/g,"'") : args;
12792             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12793             if (typeof(format) == 'undefined') {
12794                 format =  'htmlEncode'; 
12795             }
12796             if (format == 'raw' ) {
12797                 format = false;
12798             }
12799             
12800             if(name.substr(0, 6) == 'domtpl'){
12801                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12802             }
12803             
12804             // build an array of options to determine if value is undefined..
12805             
12806             // basically get 'xxxx.yyyy' then do
12807             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12808             //    (function () { Roo.log("Property not found"); return ''; })() :
12809             //    ......
12810             
12811             var udef_ar = [];
12812             var lookfor = '';
12813             Roo.each(name.split('.'), function(st) {
12814                 lookfor += (lookfor.length ? '.': '') + st;
12815                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12816             });
12817             
12818             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12819             
12820             
12821             if(format && useF){
12822                 
12823                 args = args ? ',' + args : "";
12824                  
12825                 if(format.substr(0, 5) != "this."){
12826                     format = "fm." + format + '(';
12827                 }else{
12828                     format = 'this.call("'+ format.substr(5) + '", ';
12829                     args = ", values";
12830                 }
12831                 
12832                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12833             }
12834              
12835             if (args && args.length) {
12836                 // called with xxyx.yuu:(test,test)
12837                 // change to ()
12838                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12839             }
12840             // raw.. - :raw modifier..
12841             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12842             
12843         };
12844         var body;
12845         // branched to use + in gecko and [].join() in others
12846         if(Roo.isGecko){
12847             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12848                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12849                     "';};};";
12850         }else{
12851             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12852             body.push(tpl.body.replace(/(\r\n|\n)/g,
12853                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12854             body.push("'].join('');};};");
12855             body = body.join('');
12856         }
12857         
12858         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12859        
12860         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12861         eval(body);
12862         
12863         return this;
12864     },
12865      
12866     /**
12867      * same as applyTemplate, except it's done to one of the subTemplates
12868      * when using named templates, you can do:
12869      *
12870      * var str = pl.applySubTemplate('your-name', values);
12871      *
12872      * 
12873      * @param {Number} id of the template
12874      * @param {Object} values to apply to template
12875      * @param {Object} parent (normaly the instance of this object)
12876      */
12877     applySubTemplate : function(id, values, parent)
12878     {
12879         
12880         
12881         var t = this.tpls[id];
12882         
12883         
12884         try { 
12885             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12886                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12887                 return '';
12888             }
12889         } catch(e) {
12890             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12891             Roo.log(values);
12892           
12893             return '';
12894         }
12895         try { 
12896             
12897             if(t.execCall && t.execCall.call(this, values, parent)){
12898                 return '';
12899             }
12900         } catch(e) {
12901             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12902             Roo.log(values);
12903             return '';
12904         }
12905         
12906         try {
12907             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12908             parent = t.target ? values : parent;
12909             if(t.forCall && vs instanceof Array){
12910                 var buf = [];
12911                 for(var i = 0, len = vs.length; i < len; i++){
12912                     try {
12913                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12914                     } catch (e) {
12915                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12916                         Roo.log(e.body);
12917                         //Roo.log(t.compiled);
12918                         Roo.log(vs[i]);
12919                     }   
12920                 }
12921                 return buf.join('');
12922             }
12923         } catch (e) {
12924             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12925             Roo.log(values);
12926             return '';
12927         }
12928         try {
12929             return t.compiled.call(this, vs, parent);
12930         } catch (e) {
12931             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12932             Roo.log(e.body);
12933             //Roo.log(t.compiled);
12934             Roo.log(values);
12935             return '';
12936         }
12937     },
12938
12939    
12940
12941     applyTemplate : function(values){
12942         return this.master.compiled.call(this, values, {});
12943         //var s = this.subs;
12944     },
12945
12946     apply : function(){
12947         return this.applyTemplate.apply(this, arguments);
12948     }
12949
12950  });
12951
12952 Roo.DomTemplate.from = function(el){
12953     el = Roo.getDom(el);
12954     return new Roo.Domtemplate(el.value || el.innerHTML);
12955 };/*
12956  * Based on:
12957  * Ext JS Library 1.1.1
12958  * Copyright(c) 2006-2007, Ext JS, LLC.
12959  *
12960  * Originally Released Under LGPL - original licence link has changed is not relivant.
12961  *
12962  * Fork - LGPL
12963  * <script type="text/javascript">
12964  */
12965
12966 /**
12967  * @class Roo.util.DelayedTask
12968  * Provides a convenient method of performing setTimeout where a new
12969  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12970  * You can use this class to buffer
12971  * the keypress events for a certain number of milliseconds, and perform only if they stop
12972  * for that amount of time.
12973  * @constructor The parameters to this constructor serve as defaults and are not required.
12974  * @param {Function} fn (optional) The default function to timeout
12975  * @param {Object} scope (optional) The default scope of that timeout
12976  * @param {Array} args (optional) The default Array of arguments
12977  */
12978 Roo.util.DelayedTask = function(fn, scope, args){
12979     var id = null, d, t;
12980
12981     var call = function(){
12982         var now = new Date().getTime();
12983         if(now - t >= d){
12984             clearInterval(id);
12985             id = null;
12986             fn.apply(scope, args || []);
12987         }
12988     };
12989     /**
12990      * Cancels any pending timeout and queues a new one
12991      * @param {Number} delay The milliseconds to delay
12992      * @param {Function} newFn (optional) Overrides function passed to constructor
12993      * @param {Object} newScope (optional) Overrides scope passed to constructor
12994      * @param {Array} newArgs (optional) Overrides args passed to constructor
12995      */
12996     this.delay = function(delay, newFn, newScope, newArgs){
12997         if(id && delay != d){
12998             this.cancel();
12999         }
13000         d = delay;
13001         t = new Date().getTime();
13002         fn = newFn || fn;
13003         scope = newScope || scope;
13004         args = newArgs || args;
13005         if(!id){
13006             id = setInterval(call, d);
13007         }
13008     };
13009
13010     /**
13011      * Cancel the last queued timeout
13012      */
13013     this.cancel = function(){
13014         if(id){
13015             clearInterval(id);
13016             id = null;
13017         }
13018     };
13019 };/*
13020  * Based on:
13021  * Ext JS Library 1.1.1
13022  * Copyright(c) 2006-2007, Ext JS, LLC.
13023  *
13024  * Originally Released Under LGPL - original licence link has changed is not relivant.
13025  *
13026  * Fork - LGPL
13027  * <script type="text/javascript">
13028  */
13029  
13030  
13031 Roo.util.TaskRunner = function(interval){
13032     interval = interval || 10;
13033     var tasks = [], removeQueue = [];
13034     var id = 0;
13035     var running = false;
13036
13037     var stopThread = function(){
13038         running = false;
13039         clearInterval(id);
13040         id = 0;
13041     };
13042
13043     var startThread = function(){
13044         if(!running){
13045             running = true;
13046             id = setInterval(runTasks, interval);
13047         }
13048     };
13049
13050     var removeTask = function(task){
13051         removeQueue.push(task);
13052         if(task.onStop){
13053             task.onStop();
13054         }
13055     };
13056
13057     var runTasks = function(){
13058         if(removeQueue.length > 0){
13059             for(var i = 0, len = removeQueue.length; i < len; i++){
13060                 tasks.remove(removeQueue[i]);
13061             }
13062             removeQueue = [];
13063             if(tasks.length < 1){
13064                 stopThread();
13065                 return;
13066             }
13067         }
13068         var now = new Date().getTime();
13069         for(var i = 0, len = tasks.length; i < len; ++i){
13070             var t = tasks[i];
13071             var itime = now - t.taskRunTime;
13072             if(t.interval <= itime){
13073                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
13074                 t.taskRunTime = now;
13075                 if(rt === false || t.taskRunCount === t.repeat){
13076                     removeTask(t);
13077                     return;
13078                 }
13079             }
13080             if(t.duration && t.duration <= (now - t.taskStartTime)){
13081                 removeTask(t);
13082             }
13083         }
13084     };
13085
13086     /**
13087      * Queues a new task.
13088      * @param {Object} task
13089      */
13090     this.start = function(task){
13091         tasks.push(task);
13092         task.taskStartTime = new Date().getTime();
13093         task.taskRunTime = 0;
13094         task.taskRunCount = 0;
13095         startThread();
13096         return task;
13097     };
13098
13099     this.stop = function(task){
13100         removeTask(task);
13101         return task;
13102     };
13103
13104     this.stopAll = function(){
13105         stopThread();
13106         for(var i = 0, len = tasks.length; i < len; i++){
13107             if(tasks[i].onStop){
13108                 tasks[i].onStop();
13109             }
13110         }
13111         tasks = [];
13112         removeQueue = [];
13113     };
13114 };
13115
13116 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13117  * Based on:
13118  * Ext JS Library 1.1.1
13119  * Copyright(c) 2006-2007, Ext JS, LLC.
13120  *
13121  * Originally Released Under LGPL - original licence link has changed is not relivant.
13122  *
13123  * Fork - LGPL
13124  * <script type="text/javascript">
13125  */
13126
13127  
13128 /**
13129  * @class Roo.util.MixedCollection
13130  * @extends Roo.util.Observable
13131  * A Collection class that maintains both numeric indexes and keys and exposes events.
13132  * @constructor
13133  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13134  * collection (defaults to false)
13135  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13136  * and return the key value for that item.  This is used when available to look up the key on items that
13137  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13138  * equivalent to providing an implementation for the {@link #getKey} method.
13139  */
13140 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13141     this.items = [];
13142     this.map = {};
13143     this.keys = [];
13144     this.length = 0;
13145     this.addEvents({
13146         /**
13147          * @event clear
13148          * Fires when the collection is cleared.
13149          */
13150         "clear" : true,
13151         /**
13152          * @event add
13153          * Fires when an item is added to the collection.
13154          * @param {Number} index The index at which the item was added.
13155          * @param {Object} o The item added.
13156          * @param {String} key The key associated with the added item.
13157          */
13158         "add" : true,
13159         /**
13160          * @event replace
13161          * Fires when an item is replaced in the collection.
13162          * @param {String} key he key associated with the new added.
13163          * @param {Object} old The item being replaced.
13164          * @param {Object} new The new item.
13165          */
13166         "replace" : true,
13167         /**
13168          * @event remove
13169          * Fires when an item is removed from the collection.
13170          * @param {Object} o The item being removed.
13171          * @param {String} key (optional) The key associated with the removed item.
13172          */
13173         "remove" : true,
13174         "sort" : true
13175     });
13176     this.allowFunctions = allowFunctions === true;
13177     if(keyFn){
13178         this.getKey = keyFn;
13179     }
13180     Roo.util.MixedCollection.superclass.constructor.call(this);
13181 };
13182
13183 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13184     allowFunctions : false,
13185     
13186 /**
13187  * Adds an item to the collection.
13188  * @param {String} key The key to associate with the item
13189  * @param {Object} o The item to add.
13190  * @return {Object} The item added.
13191  */
13192     add : function(key, o){
13193         if(arguments.length == 1){
13194             o = arguments[0];
13195             key = this.getKey(o);
13196         }
13197         if(typeof key == "undefined" || key === null){
13198             this.length++;
13199             this.items.push(o);
13200             this.keys.push(null);
13201         }else{
13202             var old = this.map[key];
13203             if(old){
13204                 return this.replace(key, o);
13205             }
13206             this.length++;
13207             this.items.push(o);
13208             this.map[key] = o;
13209             this.keys.push(key);
13210         }
13211         this.fireEvent("add", this.length-1, o, key);
13212         return o;
13213     },
13214        
13215 /**
13216   * MixedCollection has a generic way to fetch keys if you implement getKey.
13217 <pre><code>
13218 // normal way
13219 var mc = new Roo.util.MixedCollection();
13220 mc.add(someEl.dom.id, someEl);
13221 mc.add(otherEl.dom.id, otherEl);
13222 //and so on
13223
13224 // using getKey
13225 var mc = new Roo.util.MixedCollection();
13226 mc.getKey = function(el){
13227    return el.dom.id;
13228 };
13229 mc.add(someEl);
13230 mc.add(otherEl);
13231
13232 // or via the constructor
13233 var mc = new Roo.util.MixedCollection(false, function(el){
13234    return el.dom.id;
13235 });
13236 mc.add(someEl);
13237 mc.add(otherEl);
13238 </code></pre>
13239  * @param o {Object} The item for which to find the key.
13240  * @return {Object} The key for the passed item.
13241  */
13242     getKey : function(o){
13243          return o.id; 
13244     },
13245    
13246 /**
13247  * Replaces an item in the collection.
13248  * @param {String} key The key associated with the item to replace, or the item to replace.
13249  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13250  * @return {Object}  The new item.
13251  */
13252     replace : function(key, o){
13253         if(arguments.length == 1){
13254             o = arguments[0];
13255             key = this.getKey(o);
13256         }
13257         var old = this.item(key);
13258         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13259              return this.add(key, o);
13260         }
13261         var index = this.indexOfKey(key);
13262         this.items[index] = o;
13263         this.map[key] = o;
13264         this.fireEvent("replace", key, old, o);
13265         return o;
13266     },
13267    
13268 /**
13269  * Adds all elements of an Array or an Object to the collection.
13270  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13271  * an Array of values, each of which are added to the collection.
13272  */
13273     addAll : function(objs){
13274         if(arguments.length > 1 || objs instanceof Array){
13275             var args = arguments.length > 1 ? arguments : objs;
13276             for(var i = 0, len = args.length; i < len; i++){
13277                 this.add(args[i]);
13278             }
13279         }else{
13280             for(var key in objs){
13281                 if(this.allowFunctions || typeof objs[key] != "function"){
13282                     this.add(key, objs[key]);
13283                 }
13284             }
13285         }
13286     },
13287    
13288 /**
13289  * Executes the specified function once for every item in the collection, passing each
13290  * item as the first and only parameter. returning false from the function will stop the iteration.
13291  * @param {Function} fn The function to execute for each item.
13292  * @param {Object} scope (optional) The scope in which to execute the function.
13293  */
13294     each : function(fn, scope){
13295         var items = [].concat(this.items); // each safe for removal
13296         for(var i = 0, len = items.length; i < len; i++){
13297             if(fn.call(scope || items[i], items[i], i, len) === false){
13298                 break;
13299             }
13300         }
13301     },
13302    
13303 /**
13304  * Executes the specified function once for every key in the collection, passing each
13305  * key, and its associated item as the first two parameters.
13306  * @param {Function} fn The function to execute for each item.
13307  * @param {Object} scope (optional) The scope in which to execute the function.
13308  */
13309     eachKey : function(fn, scope){
13310         for(var i = 0, len = this.keys.length; i < len; i++){
13311             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13312         }
13313     },
13314    
13315 /**
13316  * Returns the first item in the collection which elicits a true return value from the
13317  * passed selection function.
13318  * @param {Function} fn The selection function to execute for each item.
13319  * @param {Object} scope (optional) The scope in which to execute the function.
13320  * @return {Object} The first item in the collection which returned true from the selection function.
13321  */
13322     find : function(fn, scope){
13323         for(var i = 0, len = this.items.length; i < len; i++){
13324             if(fn.call(scope || window, this.items[i], this.keys[i])){
13325                 return this.items[i];
13326             }
13327         }
13328         return null;
13329     },
13330    
13331 /**
13332  * Inserts an item at the specified index in the collection.
13333  * @param {Number} index The index to insert the item at.
13334  * @param {String} key The key to associate with the new item, or the item itself.
13335  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13336  * @return {Object} The item inserted.
13337  */
13338     insert : function(index, key, o){
13339         if(arguments.length == 2){
13340             o = arguments[1];
13341             key = this.getKey(o);
13342         }
13343         if(index >= this.length){
13344             return this.add(key, o);
13345         }
13346         this.length++;
13347         this.items.splice(index, 0, o);
13348         if(typeof key != "undefined" && key != null){
13349             this.map[key] = o;
13350         }
13351         this.keys.splice(index, 0, key);
13352         this.fireEvent("add", index, o, key);
13353         return o;
13354     },
13355    
13356 /**
13357  * Removed an item from the collection.
13358  * @param {Object} o The item to remove.
13359  * @return {Object} The item removed.
13360  */
13361     remove : function(o){
13362         return this.removeAt(this.indexOf(o));
13363     },
13364    
13365 /**
13366  * Remove an item from a specified index in the collection.
13367  * @param {Number} index The index within the collection of the item to remove.
13368  */
13369     removeAt : function(index){
13370         if(index < this.length && index >= 0){
13371             this.length--;
13372             var o = this.items[index];
13373             this.items.splice(index, 1);
13374             var key = this.keys[index];
13375             if(typeof key != "undefined"){
13376                 delete this.map[key];
13377             }
13378             this.keys.splice(index, 1);
13379             this.fireEvent("remove", o, key);
13380         }
13381     },
13382    
13383 /**
13384  * Removed an item associated with the passed key fom the collection.
13385  * @param {String} key The key of the item to remove.
13386  */
13387     removeKey : function(key){
13388         return this.removeAt(this.indexOfKey(key));
13389     },
13390    
13391 /**
13392  * Returns the number of items in the collection.
13393  * @return {Number} the number of items in the collection.
13394  */
13395     getCount : function(){
13396         return this.length; 
13397     },
13398    
13399 /**
13400  * Returns index within the collection of the passed Object.
13401  * @param {Object} o The item to find the index of.
13402  * @return {Number} index of the item.
13403  */
13404     indexOf : function(o){
13405         if(!this.items.indexOf){
13406             for(var i = 0, len = this.items.length; i < len; i++){
13407                 if(this.items[i] == o) {
13408                     return i;
13409                 }
13410             }
13411             return -1;
13412         }else{
13413             return this.items.indexOf(o);
13414         }
13415     },
13416    
13417 /**
13418  * Returns index within the collection of the passed key.
13419  * @param {String} key The key to find the index of.
13420  * @return {Number} index of the key.
13421  */
13422     indexOfKey : function(key){
13423         if(!this.keys.indexOf){
13424             for(var i = 0, len = this.keys.length; i < len; i++){
13425                 if(this.keys[i] == key) {
13426                     return i;
13427                 }
13428             }
13429             return -1;
13430         }else{
13431             return this.keys.indexOf(key);
13432         }
13433     },
13434    
13435 /**
13436  * Returns the item associated with the passed key OR index. Key has priority over index.
13437  * @param {String/Number} key The key or index of the item.
13438  * @return {Object} The item associated with the passed key.
13439  */
13440     item : function(key){
13441         if (key === 'length') {
13442             return null;
13443         }
13444         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13445         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13446     },
13447     
13448 /**
13449  * Returns the item at the specified index.
13450  * @param {Number} index The index of the item.
13451  * @return {Object}
13452  */
13453     itemAt : function(index){
13454         return this.items[index];
13455     },
13456     
13457 /**
13458  * Returns the item associated with the passed key.
13459  * @param {String/Number} key The key of the item.
13460  * @return {Object} The item associated with the passed key.
13461  */
13462     key : function(key){
13463         return this.map[key];
13464     },
13465    
13466 /**
13467  * Returns true if the collection contains the passed Object as an item.
13468  * @param {Object} o  The Object to look for in the collection.
13469  * @return {Boolean} True if the collection contains the Object as an item.
13470  */
13471     contains : function(o){
13472         return this.indexOf(o) != -1;
13473     },
13474    
13475 /**
13476  * Returns true if the collection contains the passed Object as a key.
13477  * @param {String} key The key to look for in the collection.
13478  * @return {Boolean} True if the collection contains the Object as a key.
13479  */
13480     containsKey : function(key){
13481         return typeof this.map[key] != "undefined";
13482     },
13483    
13484 /**
13485  * Removes all items from the collection.
13486  */
13487     clear : function(){
13488         this.length = 0;
13489         this.items = [];
13490         this.keys = [];
13491         this.map = {};
13492         this.fireEvent("clear");
13493     },
13494    
13495 /**
13496  * Returns the first item in the collection.
13497  * @return {Object} the first item in the collection..
13498  */
13499     first : function(){
13500         return this.items[0]; 
13501     },
13502    
13503 /**
13504  * Returns the last item in the collection.
13505  * @return {Object} the last item in the collection..
13506  */
13507     last : function(){
13508         return this.items[this.length-1];   
13509     },
13510     
13511     _sort : function(property, dir, fn){
13512         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13513         fn = fn || function(a, b){
13514             return a-b;
13515         };
13516         var c = [], k = this.keys, items = this.items;
13517         for(var i = 0, len = items.length; i < len; i++){
13518             c[c.length] = {key: k[i], value: items[i], index: i};
13519         }
13520         c.sort(function(a, b){
13521             var v = fn(a[property], b[property]) * dsc;
13522             if(v == 0){
13523                 v = (a.index < b.index ? -1 : 1);
13524             }
13525             return v;
13526         });
13527         for(var i = 0, len = c.length; i < len; i++){
13528             items[i] = c[i].value;
13529             k[i] = c[i].key;
13530         }
13531         this.fireEvent("sort", this);
13532     },
13533     
13534     /**
13535      * Sorts this collection with the passed comparison function
13536      * @param {String} direction (optional) "ASC" or "DESC"
13537      * @param {Function} fn (optional) comparison function
13538      */
13539     sort : function(dir, fn){
13540         this._sort("value", dir, fn);
13541     },
13542     
13543     /**
13544      * Sorts this collection by keys
13545      * @param {String} direction (optional) "ASC" or "DESC"
13546      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13547      */
13548     keySort : function(dir, fn){
13549         this._sort("key", dir, fn || function(a, b){
13550             return String(a).toUpperCase()-String(b).toUpperCase();
13551         });
13552     },
13553     
13554     /**
13555      * Returns a range of items in this collection
13556      * @param {Number} startIndex (optional) defaults to 0
13557      * @param {Number} endIndex (optional) default to the last item
13558      * @return {Array} An array of items
13559      */
13560     getRange : function(start, end){
13561         var items = this.items;
13562         if(items.length < 1){
13563             return [];
13564         }
13565         start = start || 0;
13566         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13567         var r = [];
13568         if(start <= end){
13569             for(var i = start; i <= end; i++) {
13570                     r[r.length] = items[i];
13571             }
13572         }else{
13573             for(var i = start; i >= end; i--) {
13574                     r[r.length] = items[i];
13575             }
13576         }
13577         return r;
13578     },
13579         
13580     /**
13581      * Filter the <i>objects</i> in this collection by a specific property. 
13582      * Returns a new collection that has been filtered.
13583      * @param {String} property A property on your objects
13584      * @param {String/RegExp} value Either string that the property values 
13585      * should start with or a RegExp to test against the property
13586      * @return {MixedCollection} The new filtered collection
13587      */
13588     filter : function(property, value){
13589         if(!value.exec){ // not a regex
13590             value = String(value);
13591             if(value.length == 0){
13592                 return this.clone();
13593             }
13594             value = new RegExp("^" + Roo.escapeRe(value), "i");
13595         }
13596         return this.filterBy(function(o){
13597             return o && value.test(o[property]);
13598         });
13599         },
13600     
13601     /**
13602      * Filter by a function. * Returns a new collection that has been filtered.
13603      * The passed function will be called with each 
13604      * object in the collection. If the function returns true, the value is included 
13605      * otherwise it is filtered.
13606      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13607      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13608      * @return {MixedCollection} The new filtered collection
13609      */
13610     filterBy : function(fn, scope){
13611         var r = new Roo.util.MixedCollection();
13612         r.getKey = this.getKey;
13613         var k = this.keys, it = this.items;
13614         for(var i = 0, len = it.length; i < len; i++){
13615             if(fn.call(scope||this, it[i], k[i])){
13616                                 r.add(k[i], it[i]);
13617                         }
13618         }
13619         return r;
13620     },
13621     
13622     /**
13623      * Creates a duplicate of this collection
13624      * @return {MixedCollection}
13625      */
13626     clone : function(){
13627         var r = new Roo.util.MixedCollection();
13628         var k = this.keys, it = this.items;
13629         for(var i = 0, len = it.length; i < len; i++){
13630             r.add(k[i], it[i]);
13631         }
13632         r.getKey = this.getKey;
13633         return r;
13634     }
13635 });
13636 /**
13637  * Returns the item associated with the passed key or index.
13638  * @method
13639  * @param {String/Number} key The key or index of the item.
13640  * @return {Object} The item associated with the passed key.
13641  */
13642 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13643  * Based on:
13644  * Ext JS Library 1.1.1
13645  * Copyright(c) 2006-2007, Ext JS, LLC.
13646  *
13647  * Originally Released Under LGPL - original licence link has changed is not relivant.
13648  *
13649  * Fork - LGPL
13650  * <script type="text/javascript">
13651  */
13652 /**
13653  * @class Roo.util.JSON
13654  * Modified version of Douglas Crockford"s json.js that doesn"t
13655  * mess with the Object prototype 
13656  * http://www.json.org/js.html
13657  * @singleton
13658  */
13659 Roo.util.JSON = new (function(){
13660     var useHasOwn = {}.hasOwnProperty ? true : false;
13661     
13662     // crashes Safari in some instances
13663     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13664     
13665     var pad = function(n) {
13666         return n < 10 ? "0" + n : n;
13667     };
13668     
13669     var m = {
13670         "\b": '\\b',
13671         "\t": '\\t',
13672         "\n": '\\n',
13673         "\f": '\\f',
13674         "\r": '\\r',
13675         '"' : '\\"',
13676         "\\": '\\\\'
13677     };
13678
13679     var encodeString = function(s){
13680         if (/["\\\x00-\x1f]/.test(s)) {
13681             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13682                 var c = m[b];
13683                 if(c){
13684                     return c;
13685                 }
13686                 c = b.charCodeAt();
13687                 return "\\u00" +
13688                     Math.floor(c / 16).toString(16) +
13689                     (c % 16).toString(16);
13690             }) + '"';
13691         }
13692         return '"' + s + '"';
13693     };
13694     
13695     var encodeArray = function(o){
13696         var a = ["["], b, i, l = o.length, v;
13697             for (i = 0; i < l; i += 1) {
13698                 v = o[i];
13699                 switch (typeof v) {
13700                     case "undefined":
13701                     case "function":
13702                     case "unknown":
13703                         break;
13704                     default:
13705                         if (b) {
13706                             a.push(',');
13707                         }
13708                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13709                         b = true;
13710                 }
13711             }
13712             a.push("]");
13713             return a.join("");
13714     };
13715     
13716     var encodeDate = function(o){
13717         return '"' + o.getFullYear() + "-" +
13718                 pad(o.getMonth() + 1) + "-" +
13719                 pad(o.getDate()) + "T" +
13720                 pad(o.getHours()) + ":" +
13721                 pad(o.getMinutes()) + ":" +
13722                 pad(o.getSeconds()) + '"';
13723     };
13724     
13725     /**
13726      * Encodes an Object, Array or other value
13727      * @param {Mixed} o The variable to encode
13728      * @return {String} The JSON string
13729      */
13730     this.encode = function(o)
13731     {
13732         // should this be extended to fully wrap stringify..
13733         
13734         if(typeof o == "undefined" || o === null){
13735             return "null";
13736         }else if(o instanceof Array){
13737             return encodeArray(o);
13738         }else if(o instanceof Date){
13739             return encodeDate(o);
13740         }else if(typeof o == "string"){
13741             return encodeString(o);
13742         }else if(typeof o == "number"){
13743             return isFinite(o) ? String(o) : "null";
13744         }else if(typeof o == "boolean"){
13745             return String(o);
13746         }else {
13747             var a = ["{"], b, i, v;
13748             for (i in o) {
13749                 if(!useHasOwn || o.hasOwnProperty(i)) {
13750                     v = o[i];
13751                     switch (typeof v) {
13752                     case "undefined":
13753                     case "function":
13754                     case "unknown":
13755                         break;
13756                     default:
13757                         if(b){
13758                             a.push(',');
13759                         }
13760                         a.push(this.encode(i), ":",
13761                                 v === null ? "null" : this.encode(v));
13762                         b = true;
13763                     }
13764                 }
13765             }
13766             a.push("}");
13767             return a.join("");
13768         }
13769     };
13770     
13771     /**
13772      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13773      * @param {String} json The JSON string
13774      * @return {Object} The resulting object
13775      */
13776     this.decode = function(json){
13777         
13778         return  /** eval:var:json */ eval("(" + json + ')');
13779     };
13780 })();
13781 /** 
13782  * Shorthand for {@link Roo.util.JSON#encode}
13783  * @member Roo encode 
13784  * @method */
13785 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13786 /** 
13787  * Shorthand for {@link Roo.util.JSON#decode}
13788  * @member Roo decode 
13789  * @method */
13790 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13791 /*
13792  * Based on:
13793  * Ext JS Library 1.1.1
13794  * Copyright(c) 2006-2007, Ext JS, LLC.
13795  *
13796  * Originally Released Under LGPL - original licence link has changed is not relivant.
13797  *
13798  * Fork - LGPL
13799  * <script type="text/javascript">
13800  */
13801  
13802 /**
13803  * @class Roo.util.Format
13804  * Reusable data formatting functions
13805  * @singleton
13806  */
13807 Roo.util.Format = function(){
13808     var trimRe = /^\s+|\s+$/g;
13809     return {
13810         /**
13811          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13812          * @param {String} value The string to truncate
13813          * @param {Number} length The maximum length to allow before truncating
13814          * @return {String} The converted text
13815          */
13816         ellipsis : function(value, len){
13817             if(value && value.length > len){
13818                 return value.substr(0, len-3)+"...";
13819             }
13820             return value;
13821         },
13822
13823         /**
13824          * Checks a reference and converts it to empty string if it is undefined
13825          * @param {Mixed} value Reference to check
13826          * @return {Mixed} Empty string if converted, otherwise the original value
13827          */
13828         undef : function(value){
13829             return typeof value != "undefined" ? value : "";
13830         },
13831
13832         /**
13833          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13834          * @param {String} value The string to encode
13835          * @return {String} The encoded text
13836          */
13837         htmlEncode : function(value){
13838             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13839         },
13840
13841         /**
13842          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13843          * @param {String} value The string to decode
13844          * @return {String} The decoded text
13845          */
13846         htmlDecode : function(value){
13847             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13848         },
13849
13850         /**
13851          * Trims any whitespace from either side of a string
13852          * @param {String} value The text to trim
13853          * @return {String} The trimmed text
13854          */
13855         trim : function(value){
13856             return String(value).replace(trimRe, "");
13857         },
13858
13859         /**
13860          * Returns a substring from within an original string
13861          * @param {String} value The original text
13862          * @param {Number} start The start index of the substring
13863          * @param {Number} length The length of the substring
13864          * @return {String} The substring
13865          */
13866         substr : function(value, start, length){
13867             return String(value).substr(start, length);
13868         },
13869
13870         /**
13871          * Converts a string to all lower case letters
13872          * @param {String} value The text to convert
13873          * @return {String} The converted text
13874          */
13875         lowercase : function(value){
13876             return String(value).toLowerCase();
13877         },
13878
13879         /**
13880          * Converts a string to all upper case letters
13881          * @param {String} value The text to convert
13882          * @return {String} The converted text
13883          */
13884         uppercase : function(value){
13885             return String(value).toUpperCase();
13886         },
13887
13888         /**
13889          * Converts the first character only of a string to upper case
13890          * @param {String} value The text to convert
13891          * @return {String} The converted text
13892          */
13893         capitalize : function(value){
13894             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13895         },
13896
13897         // private
13898         call : function(value, fn){
13899             if(arguments.length > 2){
13900                 var args = Array.prototype.slice.call(arguments, 2);
13901                 args.unshift(value);
13902                  
13903                 return /** eval:var:value */  eval(fn).apply(window, args);
13904             }else{
13905                 /** eval:var:value */
13906                 return /** eval:var:value */ eval(fn).call(window, value);
13907             }
13908         },
13909
13910        
13911         /**
13912          * safer version of Math.toFixed..??/
13913          * @param {Number/String} value The numeric value to format
13914          * @param {Number/String} value Decimal places 
13915          * @return {String} The formatted currency string
13916          */
13917         toFixed : function(v, n)
13918         {
13919             // why not use to fixed - precision is buggered???
13920             if (!n) {
13921                 return Math.round(v-0);
13922             }
13923             var fact = Math.pow(10,n+1);
13924             v = (Math.round((v-0)*fact))/fact;
13925             var z = (''+fact).substring(2);
13926             if (v == Math.floor(v)) {
13927                 return Math.floor(v) + '.' + z;
13928             }
13929             
13930             // now just padd decimals..
13931             var ps = String(v).split('.');
13932             var fd = (ps[1] + z);
13933             var r = fd.substring(0,n); 
13934             var rm = fd.substring(n); 
13935             if (rm < 5) {
13936                 return ps[0] + '.' + r;
13937             }
13938             r*=1; // turn it into a number;
13939             r++;
13940             if (String(r).length != n) {
13941                 ps[0]*=1;
13942                 ps[0]++;
13943                 r = String(r).substring(1); // chop the end off.
13944             }
13945             
13946             return ps[0] + '.' + r;
13947              
13948         },
13949         
13950         /**
13951          * Format a number as US currency
13952          * @param {Number/String} value The numeric value to format
13953          * @return {String} The formatted currency string
13954          */
13955         usMoney : function(v){
13956             return '$' + Roo.util.Format.number(v);
13957         },
13958         
13959         /**
13960          * Format a number
13961          * eventually this should probably emulate php's number_format
13962          * @param {Number/String} value The numeric value to format
13963          * @param {Number} decimals number of decimal places
13964          * @param {String} delimiter for thousands (default comma)
13965          * @return {String} The formatted currency string
13966          */
13967         number : function(v, decimals, thousandsDelimiter)
13968         {
13969             // multiply and round.
13970             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13971             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
13972             
13973             var mul = Math.pow(10, decimals);
13974             var zero = String(mul).substring(1);
13975             v = (Math.round((v-0)*mul))/mul;
13976             
13977             // if it's '0' number.. then
13978             
13979             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13980             v = String(v);
13981             var ps = v.split('.');
13982             var whole = ps[0];
13983             
13984             var r = /(\d+)(\d{3})/;
13985             // add comma's
13986             
13987             if(thousandsDelimiter.length != 0) {
13988                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
13989             } 
13990             
13991             var sub = ps[1] ?
13992                     // has decimals..
13993                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13994                     // does not have decimals
13995                     (decimals ? ('.' + zero) : '');
13996             
13997             
13998             return whole + sub ;
13999         },
14000         
14001         /**
14002          * Parse a value into a formatted date using the specified format pattern.
14003          * @param {Mixed} value The value to format
14004          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
14005          * @return {String} The formatted date string
14006          */
14007         date : function(v, format){
14008             if(!v){
14009                 return "";
14010             }
14011             if(!(v instanceof Date)){
14012                 v = new Date(Date.parse(v));
14013             }
14014             return v.dateFormat(format || Roo.util.Format.defaults.date);
14015         },
14016
14017         /**
14018          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
14019          * @param {String} format Any valid date format string
14020          * @return {Function} The date formatting function
14021          */
14022         dateRenderer : function(format){
14023             return function(v){
14024                 return Roo.util.Format.date(v, format);  
14025             };
14026         },
14027
14028         // private
14029         stripTagsRE : /<\/?[^>]+>/gi,
14030         
14031         /**
14032          * Strips all HTML tags
14033          * @param {Mixed} value The text from which to strip tags
14034          * @return {String} The stripped text
14035          */
14036         stripTags : function(v){
14037             return !v ? v : String(v).replace(this.stripTagsRE, "");
14038         },
14039         
14040         /**
14041          * Size in Mb,Gb etc.
14042          * @param {Number} value The number to be formated
14043          * @param {number} decimals how many decimal places
14044          * @return {String} the formated string
14045          */
14046         size : function(value, decimals)
14047         {
14048             var sizes = ['b', 'k', 'M', 'G', 'T'];
14049             if (value == 0) {
14050                 return 0;
14051             }
14052             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
14053             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
14054         }
14055         
14056         
14057         
14058     };
14059 }();
14060 Roo.util.Format.defaults = {
14061     date : 'd/M/Y'
14062 };/*
14063  * Based on:
14064  * Ext JS Library 1.1.1
14065  * Copyright(c) 2006-2007, Ext JS, LLC.
14066  *
14067  * Originally Released Under LGPL - original licence link has changed is not relivant.
14068  *
14069  * Fork - LGPL
14070  * <script type="text/javascript">
14071  */
14072
14073
14074  
14075
14076 /**
14077  * @class Roo.MasterTemplate
14078  * @extends Roo.Template
14079  * Provides a template that can have child templates. The syntax is:
14080 <pre><code>
14081 var t = new Roo.MasterTemplate(
14082         '&lt;select name="{name}"&gt;',
14083                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
14084         '&lt;/select&gt;'
14085 );
14086 t.add('options', {value: 'foo', text: 'bar'});
14087 // or you can add multiple child elements in one shot
14088 t.addAll('options', [
14089     {value: 'foo', text: 'bar'},
14090     {value: 'foo2', text: 'bar2'},
14091     {value: 'foo3', text: 'bar3'}
14092 ]);
14093 // then append, applying the master template values
14094 t.append('my-form', {name: 'my-select'});
14095 </code></pre>
14096 * A name attribute for the child template is not required if you have only one child
14097 * template or you want to refer to them by index.
14098  */
14099 Roo.MasterTemplate = function(){
14100     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
14101     this.originalHtml = this.html;
14102     var st = {};
14103     var m, re = this.subTemplateRe;
14104     re.lastIndex = 0;
14105     var subIndex = 0;
14106     while(m = re.exec(this.html)){
14107         var name = m[1], content = m[2];
14108         st[subIndex] = {
14109             name: name,
14110             index: subIndex,
14111             buffer: [],
14112             tpl : new Roo.Template(content)
14113         };
14114         if(name){
14115             st[name] = st[subIndex];
14116         }
14117         st[subIndex].tpl.compile();
14118         st[subIndex].tpl.call = this.call.createDelegate(this);
14119         subIndex++;
14120     }
14121     this.subCount = subIndex;
14122     this.subs = st;
14123 };
14124 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14125     /**
14126     * The regular expression used to match sub templates
14127     * @type RegExp
14128     * @property
14129     */
14130     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
14131
14132     /**
14133      * Applies the passed values to a child template.
14134      * @param {String/Number} name (optional) The name or index of the child template
14135      * @param {Array/Object} values The values to be applied to the template
14136      * @return {MasterTemplate} this
14137      */
14138      add : function(name, values){
14139         if(arguments.length == 1){
14140             values = arguments[0];
14141             name = 0;
14142         }
14143         var s = this.subs[name];
14144         s.buffer[s.buffer.length] = s.tpl.apply(values);
14145         return this;
14146     },
14147
14148     /**
14149      * Applies all the passed values to a child template.
14150      * @param {String/Number} name (optional) The name or index of the child template
14151      * @param {Array} values The values to be applied to the template, this should be an array of objects.
14152      * @param {Boolean} reset (optional) True to reset the template first
14153      * @return {MasterTemplate} this
14154      */
14155     fill : function(name, values, reset){
14156         var a = arguments;
14157         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14158             values = a[0];
14159             name = 0;
14160             reset = a[1];
14161         }
14162         if(reset){
14163             this.reset();
14164         }
14165         for(var i = 0, len = values.length; i < len; i++){
14166             this.add(name, values[i]);
14167         }
14168         return this;
14169     },
14170
14171     /**
14172      * Resets the template for reuse
14173      * @return {MasterTemplate} this
14174      */
14175      reset : function(){
14176         var s = this.subs;
14177         for(var i = 0; i < this.subCount; i++){
14178             s[i].buffer = [];
14179         }
14180         return this;
14181     },
14182
14183     applyTemplate : function(values){
14184         var s = this.subs;
14185         var replaceIndex = -1;
14186         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14187             return s[++replaceIndex].buffer.join("");
14188         });
14189         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14190     },
14191
14192     apply : function(){
14193         return this.applyTemplate.apply(this, arguments);
14194     },
14195
14196     compile : function(){return this;}
14197 });
14198
14199 /**
14200  * Alias for fill().
14201  * @method
14202  */
14203 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14204  /**
14205  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14206  * var tpl = Roo.MasterTemplate.from('element-id');
14207  * @param {String/HTMLElement} el
14208  * @param {Object} config
14209  * @static
14210  */
14211 Roo.MasterTemplate.from = function(el, config){
14212     el = Roo.getDom(el);
14213     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14214 };/*
14215  * Based on:
14216  * Ext JS Library 1.1.1
14217  * Copyright(c) 2006-2007, Ext JS, LLC.
14218  *
14219  * Originally Released Under LGPL - original licence link has changed is not relivant.
14220  *
14221  * Fork - LGPL
14222  * <script type="text/javascript">
14223  */
14224
14225  
14226 /**
14227  * @class Roo.util.CSS
14228  * Utility class for manipulating CSS rules
14229  * @singleton
14230  */
14231 Roo.util.CSS = function(){
14232         var rules = null;
14233         var doc = document;
14234
14235     var camelRe = /(-[a-z])/gi;
14236     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14237
14238    return {
14239    /**
14240     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14241     * tag and appended to the HEAD of the document.
14242     * @param {String|Object} cssText The text containing the css rules
14243     * @param {String} id An id to add to the stylesheet for later removal
14244     * @return {StyleSheet}
14245     */
14246     createStyleSheet : function(cssText, id){
14247         var ss;
14248         var head = doc.getElementsByTagName("head")[0];
14249         var nrules = doc.createElement("style");
14250         nrules.setAttribute("type", "text/css");
14251         if(id){
14252             nrules.setAttribute("id", id);
14253         }
14254         if (typeof(cssText) != 'string') {
14255             // support object maps..
14256             // not sure if this a good idea.. 
14257             // perhaps it should be merged with the general css handling
14258             // and handle js style props.
14259             var cssTextNew = [];
14260             for(var n in cssText) {
14261                 var citems = [];
14262                 for(var k in cssText[n]) {
14263                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14264                 }
14265                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14266                 
14267             }
14268             cssText = cssTextNew.join("\n");
14269             
14270         }
14271        
14272        
14273        if(Roo.isIE){
14274            head.appendChild(nrules);
14275            ss = nrules.styleSheet;
14276            ss.cssText = cssText;
14277        }else{
14278            try{
14279                 nrules.appendChild(doc.createTextNode(cssText));
14280            }catch(e){
14281                nrules.cssText = cssText; 
14282            }
14283            head.appendChild(nrules);
14284            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14285        }
14286        this.cacheStyleSheet(ss);
14287        return ss;
14288    },
14289
14290    /**
14291     * Removes a style or link tag by id
14292     * @param {String} id The id of the tag
14293     */
14294    removeStyleSheet : function(id){
14295        var existing = doc.getElementById(id);
14296        if(existing){
14297            existing.parentNode.removeChild(existing);
14298        }
14299    },
14300
14301    /**
14302     * Dynamically swaps an existing stylesheet reference for a new one
14303     * @param {String} id The id of an existing link tag to remove
14304     * @param {String} url The href of the new stylesheet to include
14305     */
14306    swapStyleSheet : function(id, url){
14307        this.removeStyleSheet(id);
14308        var ss = doc.createElement("link");
14309        ss.setAttribute("rel", "stylesheet");
14310        ss.setAttribute("type", "text/css");
14311        ss.setAttribute("id", id);
14312        ss.setAttribute("href", url);
14313        doc.getElementsByTagName("head")[0].appendChild(ss);
14314    },
14315    
14316    /**
14317     * Refresh the rule cache if you have dynamically added stylesheets
14318     * @return {Object} An object (hash) of rules indexed by selector
14319     */
14320    refreshCache : function(){
14321        return this.getRules(true);
14322    },
14323
14324    // private
14325    cacheStyleSheet : function(stylesheet){
14326        if(!rules){
14327            rules = {};
14328        }
14329        try{// try catch for cross domain access issue
14330            var ssRules = stylesheet.cssRules || stylesheet.rules;
14331            for(var j = ssRules.length-1; j >= 0; --j){
14332                rules[ssRules[j].selectorText] = ssRules[j];
14333            }
14334        }catch(e){}
14335    },
14336    
14337    /**
14338     * Gets all css rules for the document
14339     * @param {Boolean} refreshCache true to refresh the internal cache
14340     * @return {Object} An object (hash) of rules indexed by selector
14341     */
14342    getRules : function(refreshCache){
14343                 if(rules == null || refreshCache){
14344                         rules = {};
14345                         var ds = doc.styleSheets;
14346                         for(var i =0, len = ds.length; i < len; i++){
14347                             try{
14348                         this.cacheStyleSheet(ds[i]);
14349                     }catch(e){} 
14350                 }
14351                 }
14352                 return rules;
14353         },
14354         
14355         /**
14356     * Gets an an individual CSS rule by selector(s)
14357     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14358     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14359     * @return {CSSRule} The CSS rule or null if one is not found
14360     */
14361    getRule : function(selector, refreshCache){
14362                 var rs = this.getRules(refreshCache);
14363                 if(!(selector instanceof Array)){
14364                     return rs[selector];
14365                 }
14366                 for(var i = 0; i < selector.length; i++){
14367                         if(rs[selector[i]]){
14368                                 return rs[selector[i]];
14369                         }
14370                 }
14371                 return null;
14372         },
14373         
14374         
14375         /**
14376     * Updates a rule property
14377     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14378     * @param {String} property The css property
14379     * @param {String} value The new value for the property
14380     * @return {Boolean} true If a rule was found and updated
14381     */
14382    updateRule : function(selector, property, value){
14383                 if(!(selector instanceof Array)){
14384                         var rule = this.getRule(selector);
14385                         if(rule){
14386                                 rule.style[property.replace(camelRe, camelFn)] = value;
14387                                 return true;
14388                         }
14389                 }else{
14390                         for(var i = 0; i < selector.length; i++){
14391                                 if(this.updateRule(selector[i], property, value)){
14392                                         return true;
14393                                 }
14394                         }
14395                 }
14396                 return false;
14397         }
14398    };   
14399 }();/*
14400  * Based on:
14401  * Ext JS Library 1.1.1
14402  * Copyright(c) 2006-2007, Ext JS, LLC.
14403  *
14404  * Originally Released Under LGPL - original licence link has changed is not relivant.
14405  *
14406  * Fork - LGPL
14407  * <script type="text/javascript">
14408  */
14409
14410  
14411
14412 /**
14413  * @class Roo.util.ClickRepeater
14414  * @extends Roo.util.Observable
14415  * 
14416  * A wrapper class which can be applied to any element. Fires a "click" event while the
14417  * mouse is pressed. The interval between firings may be specified in the config but
14418  * defaults to 10 milliseconds.
14419  * 
14420  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14421  * 
14422  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14423  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14424  * Similar to an autorepeat key delay.
14425  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14426  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14427  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14428  *           "interval" and "delay" are ignored. "immediate" is honored.
14429  * @cfg {Boolean} preventDefault True to prevent the default click event
14430  * @cfg {Boolean} stopDefault True to stop the default click event
14431  * 
14432  * @history
14433  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14434  *     2007-02-02 jvs Renamed to ClickRepeater
14435  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14436  *
14437  *  @constructor
14438  * @param {String/HTMLElement/Element} el The element to listen on
14439  * @param {Object} config
14440  **/
14441 Roo.util.ClickRepeater = function(el, config)
14442 {
14443     this.el = Roo.get(el);
14444     this.el.unselectable();
14445
14446     Roo.apply(this, config);
14447
14448     this.addEvents({
14449     /**
14450      * @event mousedown
14451      * Fires when the mouse button is depressed.
14452      * @param {Roo.util.ClickRepeater} this
14453      */
14454         "mousedown" : true,
14455     /**
14456      * @event click
14457      * Fires on a specified interval during the time the element is pressed.
14458      * @param {Roo.util.ClickRepeater} this
14459      */
14460         "click" : true,
14461     /**
14462      * @event mouseup
14463      * Fires when the mouse key is released.
14464      * @param {Roo.util.ClickRepeater} this
14465      */
14466         "mouseup" : true
14467     });
14468
14469     this.el.on("mousedown", this.handleMouseDown, this);
14470     if(this.preventDefault || this.stopDefault){
14471         this.el.on("click", function(e){
14472             if(this.preventDefault){
14473                 e.preventDefault();
14474             }
14475             if(this.stopDefault){
14476                 e.stopEvent();
14477             }
14478         }, this);
14479     }
14480
14481     // allow inline handler
14482     if(this.handler){
14483         this.on("click", this.handler,  this.scope || this);
14484     }
14485
14486     Roo.util.ClickRepeater.superclass.constructor.call(this);
14487 };
14488
14489 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14490     interval : 20,
14491     delay: 250,
14492     preventDefault : true,
14493     stopDefault : false,
14494     timer : 0,
14495
14496     // private
14497     handleMouseDown : function(){
14498         clearTimeout(this.timer);
14499         this.el.blur();
14500         if(this.pressClass){
14501             this.el.addClass(this.pressClass);
14502         }
14503         this.mousedownTime = new Date();
14504
14505         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14506         this.el.on("mouseout", this.handleMouseOut, this);
14507
14508         this.fireEvent("mousedown", this);
14509         this.fireEvent("click", this);
14510         
14511         this.timer = this.click.defer(this.delay || this.interval, this);
14512     },
14513
14514     // private
14515     click : function(){
14516         this.fireEvent("click", this);
14517         this.timer = this.click.defer(this.getInterval(), this);
14518     },
14519
14520     // private
14521     getInterval: function(){
14522         if(!this.accelerate){
14523             return this.interval;
14524         }
14525         var pressTime = this.mousedownTime.getElapsed();
14526         if(pressTime < 500){
14527             return 400;
14528         }else if(pressTime < 1700){
14529             return 320;
14530         }else if(pressTime < 2600){
14531             return 250;
14532         }else if(pressTime < 3500){
14533             return 180;
14534         }else if(pressTime < 4400){
14535             return 140;
14536         }else if(pressTime < 5300){
14537             return 80;
14538         }else if(pressTime < 6200){
14539             return 50;
14540         }else{
14541             return 10;
14542         }
14543     },
14544
14545     // private
14546     handleMouseOut : function(){
14547         clearTimeout(this.timer);
14548         if(this.pressClass){
14549             this.el.removeClass(this.pressClass);
14550         }
14551         this.el.on("mouseover", this.handleMouseReturn, this);
14552     },
14553
14554     // private
14555     handleMouseReturn : function(){
14556         this.el.un("mouseover", this.handleMouseReturn);
14557         if(this.pressClass){
14558             this.el.addClass(this.pressClass);
14559         }
14560         this.click();
14561     },
14562
14563     // private
14564     handleMouseUp : function(){
14565         clearTimeout(this.timer);
14566         this.el.un("mouseover", this.handleMouseReturn);
14567         this.el.un("mouseout", this.handleMouseOut);
14568         Roo.get(document).un("mouseup", this.handleMouseUp);
14569         this.el.removeClass(this.pressClass);
14570         this.fireEvent("mouseup", this);
14571     }
14572 });/**
14573  * @class Roo.util.Clipboard
14574  * @static
14575  * 
14576  * Clipboard UTILS
14577  * 
14578  **/
14579 Roo.util.Clipboard = {
14580     /**
14581      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
14582      * @param {String} text to copy to clipboard
14583      */
14584     write : function(text) {
14585         // navigator clipboard api needs a secure context (https)
14586         if (navigator.clipboard && window.isSecureContext) {
14587             // navigator clipboard api method'
14588             navigator.clipboard.writeText(text);
14589             return ;
14590         } 
14591         // text area method
14592         var ta = document.createElement("textarea");
14593         ta.value = text;
14594         // make the textarea out of viewport
14595         ta.style.position = "fixed";
14596         ta.style.left = "-999999px";
14597         ta.style.top = "-999999px";
14598         document.body.appendChild(ta);
14599         ta.focus();
14600         ta.select();
14601         document.execCommand('copy');
14602         (function() {
14603             ta.remove();
14604         }).defer(100);
14605         
14606     }
14607         
14608 }
14609     /*
14610  * Based on:
14611  * Ext JS Library 1.1.1
14612  * Copyright(c) 2006-2007, Ext JS, LLC.
14613  *
14614  * Originally Released Under LGPL - original licence link has changed is not relivant.
14615  *
14616  * Fork - LGPL
14617  * <script type="text/javascript">
14618  */
14619
14620  
14621 /**
14622  * @class Roo.KeyNav
14623  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14624  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14625  * way to implement custom navigation schemes for any UI component.</p>
14626  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14627  * pageUp, pageDown, del, home, end.  Usage:</p>
14628  <pre><code>
14629 var nav = new Roo.KeyNav("my-element", {
14630     "left" : function(e){
14631         this.moveLeft(e.ctrlKey);
14632     },
14633     "right" : function(e){
14634         this.moveRight(e.ctrlKey);
14635     },
14636     "enter" : function(e){
14637         this.save();
14638     },
14639     scope : this
14640 });
14641 </code></pre>
14642  * @constructor
14643  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14644  * @param {Object} config The config
14645  */
14646 Roo.KeyNav = function(el, config){
14647     this.el = Roo.get(el);
14648     Roo.apply(this, config);
14649     if(!this.disabled){
14650         this.disabled = true;
14651         this.enable();
14652     }
14653 };
14654
14655 Roo.KeyNav.prototype = {
14656     /**
14657      * @cfg {Boolean} disabled
14658      * True to disable this KeyNav instance (defaults to false)
14659      */
14660     disabled : false,
14661     /**
14662      * @cfg {String} defaultEventAction
14663      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14664      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14665      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14666      */
14667     defaultEventAction: "stopEvent",
14668     /**
14669      * @cfg {Boolean} forceKeyDown
14670      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14671      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14672      * handle keydown instead of keypress.
14673      */
14674     forceKeyDown : false,
14675
14676     // private
14677     prepareEvent : function(e){
14678         var k = e.getKey();
14679         var h = this.keyToHandler[k];
14680         //if(h && this[h]){
14681         //    e.stopPropagation();
14682         //}
14683         if(Roo.isSafari && h && k >= 37 && k <= 40){
14684             e.stopEvent();
14685         }
14686     },
14687
14688     // private
14689     relay : function(e){
14690         var k = e.getKey();
14691         var h = this.keyToHandler[k];
14692         if(h && this[h]){
14693             if(this.doRelay(e, this[h], h) !== true){
14694                 e[this.defaultEventAction]();
14695             }
14696         }
14697     },
14698
14699     // private
14700     doRelay : function(e, h, hname){
14701         return h.call(this.scope || this, e);
14702     },
14703
14704     // possible handlers
14705     enter : false,
14706     left : false,
14707     right : false,
14708     up : false,
14709     down : false,
14710     tab : false,
14711     esc : false,
14712     pageUp : false,
14713     pageDown : false,
14714     del : false,
14715     home : false,
14716     end : false,
14717
14718     // quick lookup hash
14719     keyToHandler : {
14720         37 : "left",
14721         39 : "right",
14722         38 : "up",
14723         40 : "down",
14724         33 : "pageUp",
14725         34 : "pageDown",
14726         46 : "del",
14727         36 : "home",
14728         35 : "end",
14729         13 : "enter",
14730         27 : "esc",
14731         9  : "tab"
14732     },
14733
14734         /**
14735          * Enable this KeyNav
14736          */
14737         enable: function(){
14738                 if(this.disabled){
14739             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14740             // the EventObject will normalize Safari automatically
14741             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14742                 this.el.on("keydown", this.relay,  this);
14743             }else{
14744                 this.el.on("keydown", this.prepareEvent,  this);
14745                 this.el.on("keypress", this.relay,  this);
14746             }
14747                     this.disabled = false;
14748                 }
14749         },
14750
14751         /**
14752          * Disable this KeyNav
14753          */
14754         disable: function(){
14755                 if(!this.disabled){
14756                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14757                 this.el.un("keydown", this.relay);
14758             }else{
14759                 this.el.un("keydown", this.prepareEvent);
14760                 this.el.un("keypress", this.relay);
14761             }
14762                     this.disabled = true;
14763                 }
14764         }
14765 };/*
14766  * Based on:
14767  * Ext JS Library 1.1.1
14768  * Copyright(c) 2006-2007, Ext JS, LLC.
14769  *
14770  * Originally Released Under LGPL - original licence link has changed is not relivant.
14771  *
14772  * Fork - LGPL
14773  * <script type="text/javascript">
14774  */
14775
14776  
14777 /**
14778  * @class Roo.KeyMap
14779  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14780  * The constructor accepts the same config object as defined by {@link #addBinding}.
14781  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14782  * combination it will call the function with this signature (if the match is a multi-key
14783  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14784  * A KeyMap can also handle a string representation of keys.<br />
14785  * Usage:
14786  <pre><code>
14787 // map one key by key code
14788 var map = new Roo.KeyMap("my-element", {
14789     key: 13, // or Roo.EventObject.ENTER
14790     fn: myHandler,
14791     scope: myObject
14792 });
14793
14794 // map multiple keys to one action by string
14795 var map = new Roo.KeyMap("my-element", {
14796     key: "a\r\n\t",
14797     fn: myHandler,
14798     scope: myObject
14799 });
14800
14801 // map multiple keys to multiple actions by strings and array of codes
14802 var map = new Roo.KeyMap("my-element", [
14803     {
14804         key: [10,13],
14805         fn: function(){ alert("Return was pressed"); }
14806     }, {
14807         key: "abc",
14808         fn: function(){ alert('a, b or c was pressed'); }
14809     }, {
14810         key: "\t",
14811         ctrl:true,
14812         shift:true,
14813         fn: function(){ alert('Control + shift + tab was pressed.'); }
14814     }
14815 ]);
14816 </code></pre>
14817  * <b>Note: A KeyMap starts enabled</b>
14818  * @constructor
14819  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14820  * @param {Object} config The config (see {@link #addBinding})
14821  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14822  */
14823 Roo.KeyMap = function(el, config, eventName){
14824     this.el  = Roo.get(el);
14825     this.eventName = eventName || "keydown";
14826     this.bindings = [];
14827     if(config){
14828         this.addBinding(config);
14829     }
14830     this.enable();
14831 };
14832
14833 Roo.KeyMap.prototype = {
14834     /**
14835      * True to stop the event from bubbling and prevent the default browser action if the
14836      * key was handled by the KeyMap (defaults to false)
14837      * @type Boolean
14838      */
14839     stopEvent : false,
14840
14841     /**
14842      * Add a new binding to this KeyMap. The following config object properties are supported:
14843      * <pre>
14844 Property    Type             Description
14845 ----------  ---------------  ----------------------------------------------------------------------
14846 key         String/Array     A single keycode or an array of keycodes to handle
14847 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14848 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14849 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14850 fn          Function         The function to call when KeyMap finds the expected key combination
14851 scope       Object           The scope of the callback function
14852 </pre>
14853      *
14854      * Usage:
14855      * <pre><code>
14856 // Create a KeyMap
14857 var map = new Roo.KeyMap(document, {
14858     key: Roo.EventObject.ENTER,
14859     fn: handleKey,
14860     scope: this
14861 });
14862
14863 //Add a new binding to the existing KeyMap later
14864 map.addBinding({
14865     key: 'abc',
14866     shift: true,
14867     fn: handleKey,
14868     scope: this
14869 });
14870 </code></pre>
14871      * @param {Object/Array} config A single KeyMap config or an array of configs
14872      */
14873         addBinding : function(config){
14874         if(config instanceof Array){
14875             for(var i = 0, len = config.length; i < len; i++){
14876                 this.addBinding(config[i]);
14877             }
14878             return;
14879         }
14880         var keyCode = config.key,
14881             shift = config.shift, 
14882             ctrl = config.ctrl, 
14883             alt = config.alt,
14884             fn = config.fn,
14885             scope = config.scope;
14886         if(typeof keyCode == "string"){
14887             var ks = [];
14888             var keyString = keyCode.toUpperCase();
14889             for(var j = 0, len = keyString.length; j < len; j++){
14890                 ks.push(keyString.charCodeAt(j));
14891             }
14892             keyCode = ks;
14893         }
14894         var keyArray = keyCode instanceof Array;
14895         var handler = function(e){
14896             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14897                 var k = e.getKey();
14898                 if(keyArray){
14899                     for(var i = 0, len = keyCode.length; i < len; i++){
14900                         if(keyCode[i] == k){
14901                           if(this.stopEvent){
14902                               e.stopEvent();
14903                           }
14904                           fn.call(scope || window, k, e);
14905                           return;
14906                         }
14907                     }
14908                 }else{
14909                     if(k == keyCode){
14910                         if(this.stopEvent){
14911                            e.stopEvent();
14912                         }
14913                         fn.call(scope || window, k, e);
14914                     }
14915                 }
14916             }
14917         };
14918         this.bindings.push(handler);  
14919         },
14920
14921     /**
14922      * Shorthand for adding a single key listener
14923      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14924      * following options:
14925      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14926      * @param {Function} fn The function to call
14927      * @param {Object} scope (optional) The scope of the function
14928      */
14929     on : function(key, fn, scope){
14930         var keyCode, shift, ctrl, alt;
14931         if(typeof key == "object" && !(key instanceof Array)){
14932             keyCode = key.key;
14933             shift = key.shift;
14934             ctrl = key.ctrl;
14935             alt = key.alt;
14936         }else{
14937             keyCode = key;
14938         }
14939         this.addBinding({
14940             key: keyCode,
14941             shift: shift,
14942             ctrl: ctrl,
14943             alt: alt,
14944             fn: fn,
14945             scope: scope
14946         })
14947     },
14948
14949     // private
14950     handleKeyDown : function(e){
14951             if(this.enabled){ //just in case
14952             var b = this.bindings;
14953             for(var i = 0, len = b.length; i < len; i++){
14954                 b[i].call(this, e);
14955             }
14956             }
14957         },
14958         
14959         /**
14960          * Returns true if this KeyMap is enabled
14961          * @return {Boolean} 
14962          */
14963         isEnabled : function(){
14964             return this.enabled;  
14965         },
14966         
14967         /**
14968          * Enables this KeyMap
14969          */
14970         enable: function(){
14971                 if(!this.enabled){
14972                     this.el.on(this.eventName, this.handleKeyDown, this);
14973                     this.enabled = true;
14974                 }
14975         },
14976
14977         /**
14978          * Disable this KeyMap
14979          */
14980         disable: function(){
14981                 if(this.enabled){
14982                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14983                     this.enabled = false;
14984                 }
14985         }
14986 };/*
14987  * Based on:
14988  * Ext JS Library 1.1.1
14989  * Copyright(c) 2006-2007, Ext JS, LLC.
14990  *
14991  * Originally Released Under LGPL - original licence link has changed is not relivant.
14992  *
14993  * Fork - LGPL
14994  * <script type="text/javascript">
14995  */
14996
14997  
14998 /**
14999  * @class Roo.util.TextMetrics
15000  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
15001  * wide, in pixels, a given block of text will be.
15002  * @singleton
15003  */
15004 Roo.util.TextMetrics = function(){
15005     var shared;
15006     return {
15007         /**
15008          * Measures the size of the specified text
15009          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
15010          * that can affect the size of the rendered text
15011          * @param {String} text The text to measure
15012          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15013          * in order to accurately measure the text height
15014          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15015          */
15016         measure : function(el, text, fixedWidth){
15017             if(!shared){
15018                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
15019             }
15020             shared.bind(el);
15021             shared.setFixedWidth(fixedWidth || 'auto');
15022             return shared.getSize(text);
15023         },
15024
15025         /**
15026          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
15027          * the overhead of multiple calls to initialize the style properties on each measurement.
15028          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
15029          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15030          * in order to accurately measure the text height
15031          * @return {Roo.util.TextMetrics.Instance} instance The new instance
15032          */
15033         createInstance : function(el, fixedWidth){
15034             return Roo.util.TextMetrics.Instance(el, fixedWidth);
15035         }
15036     };
15037 }();
15038
15039  
15040
15041 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
15042     var ml = new Roo.Element(document.createElement('div'));
15043     document.body.appendChild(ml.dom);
15044     ml.position('absolute');
15045     ml.setLeftTop(-1000, -1000);
15046     ml.hide();
15047
15048     if(fixedWidth){
15049         ml.setWidth(fixedWidth);
15050     }
15051      
15052     var instance = {
15053         /**
15054          * Returns the size of the specified text based on the internal element's style and width properties
15055          * @memberOf Roo.util.TextMetrics.Instance#
15056          * @param {String} text The text to measure
15057          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15058          */
15059         getSize : function(text){
15060             ml.update(text);
15061             var s = ml.getSize();
15062             ml.update('');
15063             return s;
15064         },
15065
15066         /**
15067          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
15068          * that can affect the size of the rendered text
15069          * @memberOf Roo.util.TextMetrics.Instance#
15070          * @param {String/HTMLElement} el The element, dom node or id
15071          */
15072         bind : function(el){
15073             ml.setStyle(
15074                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
15075             );
15076         },
15077
15078         /**
15079          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
15080          * to set a fixed width in order to accurately measure the text height.
15081          * @memberOf Roo.util.TextMetrics.Instance#
15082          * @param {Number} width The width to set on the element
15083          */
15084         setFixedWidth : function(width){
15085             ml.setWidth(width);
15086         },
15087
15088         /**
15089          * Returns the measured width of the specified text
15090          * @memberOf Roo.util.TextMetrics.Instance#
15091          * @param {String} text The text to measure
15092          * @return {Number} width The width in pixels
15093          */
15094         getWidth : function(text){
15095             ml.dom.style.width = 'auto';
15096             return this.getSize(text).width;
15097         },
15098
15099         /**
15100          * Returns the measured height of the specified text.  For multiline text, be sure to call
15101          * {@link #setFixedWidth} if necessary.
15102          * @memberOf Roo.util.TextMetrics.Instance#
15103          * @param {String} text The text to measure
15104          * @return {Number} height The height in pixels
15105          */
15106         getHeight : function(text){
15107             return this.getSize(text).height;
15108         }
15109     };
15110
15111     instance.bind(bindTo);
15112
15113     return instance;
15114 };
15115
15116 // backwards compat
15117 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
15118  * Based on:
15119  * Ext JS Library 1.1.1
15120  * Copyright(c) 2006-2007, Ext JS, LLC.
15121  *
15122  * Originally Released Under LGPL - original licence link has changed is not relivant.
15123  *
15124  * Fork - LGPL
15125  * <script type="text/javascript">
15126  */
15127
15128 /**
15129  * @class Roo.state.Provider
15130  * Abstract base class for state provider implementations. This class provides methods
15131  * for encoding and decoding <b>typed</b> variables including dates and defines the 
15132  * Provider interface.
15133  */
15134 Roo.state.Provider = function(){
15135     /**
15136      * @event statechange
15137      * Fires when a state change occurs.
15138      * @param {Provider} this This state provider
15139      * @param {String} key The state key which was changed
15140      * @param {String} value The encoded value for the state
15141      */
15142     this.addEvents({
15143         "statechange": true
15144     });
15145     this.state = {};
15146     Roo.state.Provider.superclass.constructor.call(this);
15147 };
15148 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
15149     /**
15150      * Returns the current value for a key
15151      * @param {String} name The key name
15152      * @param {Mixed} defaultValue A default value to return if the key's value is not found
15153      * @return {Mixed} The state data
15154      */
15155     get : function(name, defaultValue){
15156         return typeof this.state[name] == "undefined" ?
15157             defaultValue : this.state[name];
15158     },
15159     
15160     /**
15161      * Clears a value from the state
15162      * @param {String} name The key name
15163      */
15164     clear : function(name){
15165         delete this.state[name];
15166         this.fireEvent("statechange", this, name, null);
15167     },
15168     
15169     /**
15170      * Sets the value for a key
15171      * @param {String} name The key name
15172      * @param {Mixed} value The value to set
15173      */
15174     set : function(name, value){
15175         this.state[name] = value;
15176         this.fireEvent("statechange", this, name, value);
15177     },
15178     
15179     /**
15180      * Decodes a string previously encoded with {@link #encodeValue}.
15181      * @param {String} value The value to decode
15182      * @return {Mixed} The decoded value
15183      */
15184     decodeValue : function(cookie){
15185         var re = /^(a|n|d|b|s|o)\:(.*)$/;
15186         var matches = re.exec(unescape(cookie));
15187         if(!matches || !matches[1]) {
15188             return; // non state cookie
15189         }
15190         var type = matches[1];
15191         var v = matches[2];
15192         switch(type){
15193             case "n":
15194                 return parseFloat(v);
15195             case "d":
15196                 return new Date(Date.parse(v));
15197             case "b":
15198                 return (v == "1");
15199             case "a":
15200                 var all = [];
15201                 var values = v.split("^");
15202                 for(var i = 0, len = values.length; i < len; i++){
15203                     all.push(this.decodeValue(values[i]));
15204                 }
15205                 return all;
15206            case "o":
15207                 var all = {};
15208                 var values = v.split("^");
15209                 for(var i = 0, len = values.length; i < len; i++){
15210                     var kv = values[i].split("=");
15211                     all[kv[0]] = this.decodeValue(kv[1]);
15212                 }
15213                 return all;
15214            default:
15215                 return v;
15216         }
15217     },
15218     
15219     /**
15220      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15221      * @param {Mixed} value The value to encode
15222      * @return {String} The encoded value
15223      */
15224     encodeValue : function(v){
15225         var enc;
15226         if(typeof v == "number"){
15227             enc = "n:" + v;
15228         }else if(typeof v == "boolean"){
15229             enc = "b:" + (v ? "1" : "0");
15230         }else if(v instanceof Date){
15231             enc = "d:" + v.toGMTString();
15232         }else if(v instanceof Array){
15233             var flat = "";
15234             for(var i = 0, len = v.length; i < len; i++){
15235                 flat += this.encodeValue(v[i]);
15236                 if(i != len-1) {
15237                     flat += "^";
15238                 }
15239             }
15240             enc = "a:" + flat;
15241         }else if(typeof v == "object"){
15242             var flat = "";
15243             for(var key in v){
15244                 if(typeof v[key] != "function"){
15245                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15246                 }
15247             }
15248             enc = "o:" + flat.substring(0, flat.length-1);
15249         }else{
15250             enc = "s:" + v;
15251         }
15252         return escape(enc);        
15253     }
15254 });
15255
15256 /*
15257  * Based on:
15258  * Ext JS Library 1.1.1
15259  * Copyright(c) 2006-2007, Ext JS, LLC.
15260  *
15261  * Originally Released Under LGPL - original licence link has changed is not relivant.
15262  *
15263  * Fork - LGPL
15264  * <script type="text/javascript">
15265  */
15266 /**
15267  * @class Roo.state.Manager
15268  * This is the global state manager. By default all components that are "state aware" check this class
15269  * for state information if you don't pass them a custom state provider. In order for this class
15270  * to be useful, it must be initialized with a provider when your application initializes.
15271  <pre><code>
15272 // in your initialization function
15273 init : function(){
15274    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15275    ...
15276    // supposed you have a {@link Roo.BorderLayout}
15277    var layout = new Roo.BorderLayout(...);
15278    layout.restoreState();
15279    // or a {Roo.BasicDialog}
15280    var dialog = new Roo.BasicDialog(...);
15281    dialog.restoreState();
15282  </code></pre>
15283  * @singleton
15284  */
15285 Roo.state.Manager = function(){
15286     var provider = new Roo.state.Provider();
15287     
15288     return {
15289         /**
15290          * Configures the default state provider for your application
15291          * @param {Provider} stateProvider The state provider to set
15292          */
15293         setProvider : function(stateProvider){
15294             provider = stateProvider;
15295         },
15296         
15297         /**
15298          * Returns the current value for a key
15299          * @param {String} name The key name
15300          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15301          * @return {Mixed} The state data
15302          */
15303         get : function(key, defaultValue){
15304             return provider.get(key, defaultValue);
15305         },
15306         
15307         /**
15308          * Sets the value for a key
15309          * @param {String} name The key name
15310          * @param {Mixed} value The state data
15311          */
15312          set : function(key, value){
15313             provider.set(key, value);
15314         },
15315         
15316         /**
15317          * Clears a value from the state
15318          * @param {String} name The key name
15319          */
15320         clear : function(key){
15321             provider.clear(key);
15322         },
15323         
15324         /**
15325          * Gets the currently configured state provider
15326          * @return {Provider} The state provider
15327          */
15328         getProvider : function(){
15329             return provider;
15330         }
15331     };
15332 }();
15333 /*
15334  * Based on:
15335  * Ext JS Library 1.1.1
15336  * Copyright(c) 2006-2007, Ext JS, LLC.
15337  *
15338  * Originally Released Under LGPL - original licence link has changed is not relivant.
15339  *
15340  * Fork - LGPL
15341  * <script type="text/javascript">
15342  */
15343 /**
15344  * @class Roo.state.CookieProvider
15345  * @extends Roo.state.Provider
15346  * The default Provider implementation which saves state via cookies.
15347  * <br />Usage:
15348  <pre><code>
15349    var cp = new Roo.state.CookieProvider({
15350        path: "/cgi-bin/",
15351        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15352        domain: "roojs.com"
15353    })
15354    Roo.state.Manager.setProvider(cp);
15355  </code></pre>
15356  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15357  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15358  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15359  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15360  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15361  * domain the page is running on including the 'www' like 'www.roojs.com')
15362  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15363  * @constructor
15364  * Create a new CookieProvider
15365  * @param {Object} config The configuration object
15366  */
15367 Roo.state.CookieProvider = function(config){
15368     Roo.state.CookieProvider.superclass.constructor.call(this);
15369     this.path = "/";
15370     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15371     this.domain = null;
15372     this.secure = false;
15373     Roo.apply(this, config);
15374     this.state = this.readCookies();
15375 };
15376
15377 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15378     // private
15379     set : function(name, value){
15380         if(typeof value == "undefined" || value === null){
15381             this.clear(name);
15382             return;
15383         }
15384         this.setCookie(name, value);
15385         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15386     },
15387
15388     // private
15389     clear : function(name){
15390         this.clearCookie(name);
15391         Roo.state.CookieProvider.superclass.clear.call(this, name);
15392     },
15393
15394     // private
15395     readCookies : function(){
15396         var cookies = {};
15397         var c = document.cookie + ";";
15398         var re = /\s?(.*?)=(.*?);/g;
15399         var matches;
15400         while((matches = re.exec(c)) != null){
15401             var name = matches[1];
15402             var value = matches[2];
15403             if(name && name.substring(0,3) == "ys-"){
15404                 cookies[name.substr(3)] = this.decodeValue(value);
15405             }
15406         }
15407         return cookies;
15408     },
15409
15410     // private
15411     setCookie : function(name, value){
15412         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15413            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15414            ((this.path == null) ? "" : ("; path=" + this.path)) +
15415            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15416            ((this.secure == true) ? "; secure" : "");
15417     },
15418
15419     // private
15420     clearCookie : function(name){
15421         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15422            ((this.path == null) ? "" : ("; path=" + this.path)) +
15423            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15424            ((this.secure == true) ? "; secure" : "");
15425     }
15426 });/*
15427  * Based on:
15428  * Ext JS Library 1.1.1
15429  * Copyright(c) 2006-2007, Ext JS, LLC.
15430  *
15431  * Originally Released Under LGPL - original licence link has changed is not relivant.
15432  *
15433  * Fork - LGPL
15434  * <script type="text/javascript">
15435  */
15436  
15437
15438 /**
15439  * @class Roo.ComponentMgr
15440  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15441  * @singleton
15442  */
15443 Roo.ComponentMgr = function(){
15444     var all = new Roo.util.MixedCollection();
15445
15446     return {
15447         /**
15448          * Registers a component.
15449          * @param {Roo.Component} c The component
15450          */
15451         register : function(c){
15452             all.add(c);
15453         },
15454
15455         /**
15456          * Unregisters a component.
15457          * @param {Roo.Component} c The component
15458          */
15459         unregister : function(c){
15460             all.remove(c);
15461         },
15462
15463         /**
15464          * Returns a component by id
15465          * @param {String} id The component id
15466          */
15467         get : function(id){
15468             return all.get(id);
15469         },
15470
15471         /**
15472          * Registers a function that will be called when a specified component is added to ComponentMgr
15473          * @param {String} id The component id
15474          * @param {Funtction} fn The callback function
15475          * @param {Object} scope The scope of the callback
15476          */
15477         onAvailable : function(id, fn, scope){
15478             all.on("add", function(index, o){
15479                 if(o.id == id){
15480                     fn.call(scope || o, o);
15481                     all.un("add", fn, scope);
15482                 }
15483             });
15484         }
15485     };
15486 }();/*
15487  * Based on:
15488  * Ext JS Library 1.1.1
15489  * Copyright(c) 2006-2007, Ext JS, LLC.
15490  *
15491  * Originally Released Under LGPL - original licence link has changed is not relivant.
15492  *
15493  * Fork - LGPL
15494  * <script type="text/javascript">
15495  */
15496  
15497 /**
15498  * @class Roo.Component
15499  * @extends Roo.util.Observable
15500  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15501  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15502  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15503  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15504  * All visual components (widgets) that require rendering into a layout should subclass Component.
15505  * @constructor
15506  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15507  * 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
15508  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15509  */
15510 Roo.Component = function(config){
15511     config = config || {};
15512     if(config.tagName || config.dom || typeof config == "string"){ // element object
15513         config = {el: config, id: config.id || config};
15514     }
15515     this.initialConfig = config;
15516
15517     Roo.apply(this, config);
15518     this.addEvents({
15519         /**
15520          * @event disable
15521          * Fires after the component is disabled.
15522              * @param {Roo.Component} this
15523              */
15524         disable : true,
15525         /**
15526          * @event enable
15527          * Fires after the component is enabled.
15528              * @param {Roo.Component} this
15529              */
15530         enable : true,
15531         /**
15532          * @event beforeshow
15533          * Fires before the component is shown.  Return false to stop the show.
15534              * @param {Roo.Component} this
15535              */
15536         beforeshow : true,
15537         /**
15538          * @event show
15539          * Fires after the component is shown.
15540              * @param {Roo.Component} this
15541              */
15542         show : true,
15543         /**
15544          * @event beforehide
15545          * Fires before the component is hidden. Return false to stop the hide.
15546              * @param {Roo.Component} this
15547              */
15548         beforehide : true,
15549         /**
15550          * @event hide
15551          * Fires after the component is hidden.
15552              * @param {Roo.Component} this
15553              */
15554         hide : true,
15555         /**
15556          * @event beforerender
15557          * Fires before the component is rendered. Return false to stop the render.
15558              * @param {Roo.Component} this
15559              */
15560         beforerender : true,
15561         /**
15562          * @event render
15563          * Fires after the component is rendered.
15564              * @param {Roo.Component} this
15565              */
15566         render : true,
15567         /**
15568          * @event beforedestroy
15569          * Fires before the component is destroyed. Return false to stop the destroy.
15570              * @param {Roo.Component} this
15571              */
15572         beforedestroy : true,
15573         /**
15574          * @event destroy
15575          * Fires after the component is destroyed.
15576              * @param {Roo.Component} this
15577              */
15578         destroy : true
15579     });
15580     if(!this.id){
15581         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15582     }
15583     Roo.ComponentMgr.register(this);
15584     Roo.Component.superclass.constructor.call(this);
15585     this.initComponent();
15586     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15587         this.render(this.renderTo);
15588         delete this.renderTo;
15589     }
15590 };
15591
15592 /** @private */
15593 Roo.Component.AUTO_ID = 1000;
15594
15595 Roo.extend(Roo.Component, Roo.util.Observable, {
15596     /**
15597      * @scope Roo.Component.prototype
15598      * @type {Boolean}
15599      * true if this component is hidden. Read-only.
15600      */
15601     hidden : false,
15602     /**
15603      * @type {Boolean}
15604      * true if this component is disabled. Read-only.
15605      */
15606     disabled : false,
15607     /**
15608      * @type {Boolean}
15609      * true if this component has been rendered. Read-only.
15610      */
15611     rendered : false,
15612     
15613     /** @cfg {String} disableClass
15614      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15615      */
15616     disabledClass : "x-item-disabled",
15617         /** @cfg {Boolean} allowDomMove
15618          * Whether the component can move the Dom node when rendering (defaults to true).
15619          */
15620     allowDomMove : true,
15621     /** @cfg {String} hideMode (display|visibility)
15622      * How this component should hidden. Supported values are
15623      * "visibility" (css visibility), "offsets" (negative offset position) and
15624      * "display" (css display) - defaults to "display".
15625      */
15626     hideMode: 'display',
15627
15628     /** @private */
15629     ctype : "Roo.Component",
15630
15631     /**
15632      * @cfg {String} actionMode 
15633      * which property holds the element that used for  hide() / show() / disable() / enable()
15634      * default is 'el' for forms you probably want to set this to fieldEl 
15635      */
15636     actionMode : "el",
15637
15638     /** @private */
15639     getActionEl : function(){
15640         return this[this.actionMode];
15641     },
15642
15643     initComponent : Roo.emptyFn,
15644     /**
15645      * If this is a lazy rendering component, render it to its container element.
15646      * @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.
15647      */
15648     render : function(container, position){
15649         
15650         if(this.rendered){
15651             return this;
15652         }
15653         
15654         if(this.fireEvent("beforerender", this) === false){
15655             return false;
15656         }
15657         
15658         if(!container && this.el){
15659             this.el = Roo.get(this.el);
15660             container = this.el.dom.parentNode;
15661             this.allowDomMove = false;
15662         }
15663         this.container = Roo.get(container);
15664         this.rendered = true;
15665         if(position !== undefined){
15666             if(typeof position == 'number'){
15667                 position = this.container.dom.childNodes[position];
15668             }else{
15669                 position = Roo.getDom(position);
15670             }
15671         }
15672         this.onRender(this.container, position || null);
15673         if(this.cls){
15674             this.el.addClass(this.cls);
15675             delete this.cls;
15676         }
15677         if(this.style){
15678             this.el.applyStyles(this.style);
15679             delete this.style;
15680         }
15681         this.fireEvent("render", this);
15682         this.afterRender(this.container);
15683         if(this.hidden){
15684             this.hide();
15685         }
15686         if(this.disabled){
15687             this.disable();
15688         }
15689
15690         return this;
15691         
15692     },
15693
15694     /** @private */
15695     // default function is not really useful
15696     onRender : function(ct, position){
15697         if(this.el){
15698             this.el = Roo.get(this.el);
15699             if(this.allowDomMove !== false){
15700                 ct.dom.insertBefore(this.el.dom, position);
15701             }
15702         }
15703     },
15704
15705     /** @private */
15706     getAutoCreate : function(){
15707         var cfg = typeof this.autoCreate == "object" ?
15708                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15709         if(this.id && !cfg.id){
15710             cfg.id = this.id;
15711         }
15712         return cfg;
15713     },
15714
15715     /** @private */
15716     afterRender : Roo.emptyFn,
15717
15718     /**
15719      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15720      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15721      */
15722     destroy : function(){
15723         if(this.fireEvent("beforedestroy", this) !== false){
15724             this.purgeListeners();
15725             this.beforeDestroy();
15726             if(this.rendered){
15727                 this.el.removeAllListeners();
15728                 this.el.remove();
15729                 if(this.actionMode == "container"){
15730                     this.container.remove();
15731                 }
15732             }
15733             this.onDestroy();
15734             Roo.ComponentMgr.unregister(this);
15735             this.fireEvent("destroy", this);
15736         }
15737     },
15738
15739         /** @private */
15740     beforeDestroy : function(){
15741
15742     },
15743
15744         /** @private */
15745         onDestroy : function(){
15746
15747     },
15748
15749     /**
15750      * Returns the underlying {@link Roo.Element}.
15751      * @return {Roo.Element} The element
15752      */
15753     getEl : function(){
15754         return this.el;
15755     },
15756
15757     /**
15758      * Returns the id of this component.
15759      * @return {String}
15760      */
15761     getId : function(){
15762         return this.id;
15763     },
15764
15765     /**
15766      * Try to focus this component.
15767      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15768      * @return {Roo.Component} this
15769      */
15770     focus : function(selectText){
15771         if(this.rendered){
15772             this.el.focus();
15773             if(selectText === true){
15774                 this.el.dom.select();
15775             }
15776         }
15777         return this;
15778     },
15779
15780     /** @private */
15781     blur : function(){
15782         if(this.rendered){
15783             this.el.blur();
15784         }
15785         return this;
15786     },
15787
15788     /**
15789      * Disable this component.
15790      * @return {Roo.Component} this
15791      */
15792     disable : function(){
15793         if(this.rendered){
15794             this.onDisable();
15795         }
15796         this.disabled = true;
15797         this.fireEvent("disable", this);
15798         return this;
15799     },
15800
15801         // private
15802     onDisable : function(){
15803         this.getActionEl().addClass(this.disabledClass);
15804         this.el.dom.disabled = true;
15805     },
15806
15807     /**
15808      * Enable this component.
15809      * @return {Roo.Component} this
15810      */
15811     enable : function(){
15812         if(this.rendered){
15813             this.onEnable();
15814         }
15815         this.disabled = false;
15816         this.fireEvent("enable", this);
15817         return this;
15818     },
15819
15820         // private
15821     onEnable : function(){
15822         this.getActionEl().removeClass(this.disabledClass);
15823         this.el.dom.disabled = false;
15824     },
15825
15826     /**
15827      * Convenience function for setting disabled/enabled by boolean.
15828      * @param {Boolean} disabled
15829      */
15830     setDisabled : function(disabled){
15831         this[disabled ? "disable" : "enable"]();
15832     },
15833
15834     /**
15835      * Show this component.
15836      * @return {Roo.Component} this
15837      */
15838     show: function(){
15839         if(this.fireEvent("beforeshow", this) !== false){
15840             this.hidden = false;
15841             if(this.rendered){
15842                 this.onShow();
15843             }
15844             this.fireEvent("show", this);
15845         }
15846         return this;
15847     },
15848
15849     // private
15850     onShow : function(){
15851         var ae = this.getActionEl();
15852         if(this.hideMode == 'visibility'){
15853             ae.dom.style.visibility = "visible";
15854         }else if(this.hideMode == 'offsets'){
15855             ae.removeClass('x-hidden');
15856         }else{
15857             ae.dom.style.display = "";
15858         }
15859     },
15860
15861     /**
15862      * Hide this component.
15863      * @return {Roo.Component} this
15864      */
15865     hide: function(){
15866         if(this.fireEvent("beforehide", this) !== false){
15867             this.hidden = true;
15868             if(this.rendered){
15869                 this.onHide();
15870             }
15871             this.fireEvent("hide", this);
15872         }
15873         return this;
15874     },
15875
15876     // private
15877     onHide : function(){
15878         var ae = this.getActionEl();
15879         if(this.hideMode == 'visibility'){
15880             ae.dom.style.visibility = "hidden";
15881         }else if(this.hideMode == 'offsets'){
15882             ae.addClass('x-hidden');
15883         }else{
15884             ae.dom.style.display = "none";
15885         }
15886     },
15887
15888     /**
15889      * Convenience function to hide or show this component by boolean.
15890      * @param {Boolean} visible True to show, false to hide
15891      * @return {Roo.Component} this
15892      */
15893     setVisible: function(visible){
15894         if(visible) {
15895             this.show();
15896         }else{
15897             this.hide();
15898         }
15899         return this;
15900     },
15901
15902     /**
15903      * Returns true if this component is visible.
15904      */
15905     isVisible : function(){
15906         return this.getActionEl().isVisible();
15907     },
15908
15909     cloneConfig : function(overrides){
15910         overrides = overrides || {};
15911         var id = overrides.id || Roo.id();
15912         var cfg = Roo.applyIf(overrides, this.initialConfig);
15913         cfg.id = id; // prevent dup id
15914         return new this.constructor(cfg);
15915     }
15916 });/*
15917  * Based on:
15918  * Ext JS Library 1.1.1
15919  * Copyright(c) 2006-2007, Ext JS, LLC.
15920  *
15921  * Originally Released Under LGPL - original licence link has changed is not relivant.
15922  *
15923  * Fork - LGPL
15924  * <script type="text/javascript">
15925  */
15926
15927 /**
15928  * @class Roo.BoxComponent
15929  * @extends Roo.Component
15930  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15931  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15932  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
15933  * layout containers.
15934  * @constructor
15935  * @param {Roo.Element/String/Object} config The configuration options.
15936  */
15937 Roo.BoxComponent = function(config){
15938     Roo.Component.call(this, config);
15939     this.addEvents({
15940         /**
15941          * @event resize
15942          * Fires after the component is resized.
15943              * @param {Roo.Component} this
15944              * @param {Number} adjWidth The box-adjusted width that was set
15945              * @param {Number} adjHeight The box-adjusted height that was set
15946              * @param {Number} rawWidth The width that was originally specified
15947              * @param {Number} rawHeight The height that was originally specified
15948              */
15949         resize : true,
15950         /**
15951          * @event move
15952          * Fires after the component is moved.
15953              * @param {Roo.Component} this
15954              * @param {Number} x The new x position
15955              * @param {Number} y The new y position
15956              */
15957         move : true
15958     });
15959 };
15960
15961 Roo.extend(Roo.BoxComponent, Roo.Component, {
15962     // private, set in afterRender to signify that the component has been rendered
15963     boxReady : false,
15964     // private, used to defer height settings to subclasses
15965     deferHeight: false,
15966     /** @cfg {Number} width
15967      * width (optional) size of component
15968      */
15969      /** @cfg {Number} height
15970      * height (optional) size of component
15971      */
15972      
15973     /**
15974      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15975      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15976      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15977      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15978      * @return {Roo.BoxComponent} this
15979      */
15980     setSize : function(w, h){
15981         // support for standard size objects
15982         if(typeof w == 'object'){
15983             h = w.height;
15984             w = w.width;
15985         }
15986         // not rendered
15987         if(!this.boxReady){
15988             this.width = w;
15989             this.height = h;
15990             return this;
15991         }
15992
15993         // prevent recalcs when not needed
15994         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15995             return this;
15996         }
15997         this.lastSize = {width: w, height: h};
15998
15999         var adj = this.adjustSize(w, h);
16000         var aw = adj.width, ah = adj.height;
16001         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
16002             var rz = this.getResizeEl();
16003             if(!this.deferHeight && aw !== undefined && ah !== undefined){
16004                 rz.setSize(aw, ah);
16005             }else if(!this.deferHeight && ah !== undefined){
16006                 rz.setHeight(ah);
16007             }else if(aw !== undefined){
16008                 rz.setWidth(aw);
16009             }
16010             this.onResize(aw, ah, w, h);
16011             this.fireEvent('resize', this, aw, ah, w, h);
16012         }
16013         return this;
16014     },
16015
16016     /**
16017      * Gets the current size of the component's underlying element.
16018      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
16019      */
16020     getSize : function(){
16021         return this.el.getSize();
16022     },
16023
16024     /**
16025      * Gets the current XY position of the component's underlying element.
16026      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16027      * @return {Array} The XY position of the element (e.g., [100, 200])
16028      */
16029     getPosition : function(local){
16030         if(local === true){
16031             return [this.el.getLeft(true), this.el.getTop(true)];
16032         }
16033         return this.xy || this.el.getXY();
16034     },
16035
16036     /**
16037      * Gets the current box measurements of the component's underlying element.
16038      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16039      * @returns {Object} box An object in the format {x, y, width, height}
16040      */
16041     getBox : function(local){
16042         var s = this.el.getSize();
16043         if(local){
16044             s.x = this.el.getLeft(true);
16045             s.y = this.el.getTop(true);
16046         }else{
16047             var xy = this.xy || this.el.getXY();
16048             s.x = xy[0];
16049             s.y = xy[1];
16050         }
16051         return s;
16052     },
16053
16054     /**
16055      * Sets the current box measurements of the component's underlying element.
16056      * @param {Object} box An object in the format {x, y, width, height}
16057      * @returns {Roo.BoxComponent} this
16058      */
16059     updateBox : function(box){
16060         this.setSize(box.width, box.height);
16061         this.setPagePosition(box.x, box.y);
16062         return this;
16063     },
16064
16065     // protected
16066     getResizeEl : function(){
16067         return this.resizeEl || this.el;
16068     },
16069
16070     // protected
16071     getPositionEl : function(){
16072         return this.positionEl || this.el;
16073     },
16074
16075     /**
16076      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
16077      * This method fires the move event.
16078      * @param {Number} left The new left
16079      * @param {Number} top The new top
16080      * @returns {Roo.BoxComponent} this
16081      */
16082     setPosition : function(x, y){
16083         this.x = x;
16084         this.y = y;
16085         if(!this.boxReady){
16086             return this;
16087         }
16088         var adj = this.adjustPosition(x, y);
16089         var ax = adj.x, ay = adj.y;
16090
16091         var el = this.getPositionEl();
16092         if(ax !== undefined || ay !== undefined){
16093             if(ax !== undefined && ay !== undefined){
16094                 el.setLeftTop(ax, ay);
16095             }else if(ax !== undefined){
16096                 el.setLeft(ax);
16097             }else if(ay !== undefined){
16098                 el.setTop(ay);
16099             }
16100             this.onPosition(ax, ay);
16101             this.fireEvent('move', this, ax, ay);
16102         }
16103         return this;
16104     },
16105
16106     /**
16107      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
16108      * This method fires the move event.
16109      * @param {Number} x The new x position
16110      * @param {Number} y The new y position
16111      * @returns {Roo.BoxComponent} this
16112      */
16113     setPagePosition : function(x, y){
16114         this.pageX = x;
16115         this.pageY = y;
16116         if(!this.boxReady){
16117             return;
16118         }
16119         if(x === undefined || y === undefined){ // cannot translate undefined points
16120             return;
16121         }
16122         var p = this.el.translatePoints(x, y);
16123         this.setPosition(p.left, p.top);
16124         return this;
16125     },
16126
16127     // private
16128     onRender : function(ct, position){
16129         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
16130         if(this.resizeEl){
16131             this.resizeEl = Roo.get(this.resizeEl);
16132         }
16133         if(this.positionEl){
16134             this.positionEl = Roo.get(this.positionEl);
16135         }
16136     },
16137
16138     // private
16139     afterRender : function(){
16140         Roo.BoxComponent.superclass.afterRender.call(this);
16141         this.boxReady = true;
16142         this.setSize(this.width, this.height);
16143         if(this.x || this.y){
16144             this.setPosition(this.x, this.y);
16145         }
16146         if(this.pageX || this.pageY){
16147             this.setPagePosition(this.pageX, this.pageY);
16148         }
16149     },
16150
16151     /**
16152      * Force the component's size to recalculate based on the underlying element's current height and width.
16153      * @returns {Roo.BoxComponent} this
16154      */
16155     syncSize : function(){
16156         delete this.lastSize;
16157         this.setSize(this.el.getWidth(), this.el.getHeight());
16158         return this;
16159     },
16160
16161     /**
16162      * Called after the component is resized, this method is empty by default but can be implemented by any
16163      * subclass that needs to perform custom logic after a resize occurs.
16164      * @param {Number} adjWidth The box-adjusted width that was set
16165      * @param {Number} adjHeight The box-adjusted height that was set
16166      * @param {Number} rawWidth The width that was originally specified
16167      * @param {Number} rawHeight The height that was originally specified
16168      */
16169     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
16170
16171     },
16172
16173     /**
16174      * Called after the component is moved, this method is empty by default but can be implemented by any
16175      * subclass that needs to perform custom logic after a move occurs.
16176      * @param {Number} x The new x position
16177      * @param {Number} y The new y position
16178      */
16179     onPosition : function(x, y){
16180
16181     },
16182
16183     // private
16184     adjustSize : function(w, h){
16185         if(this.autoWidth){
16186             w = 'auto';
16187         }
16188         if(this.autoHeight){
16189             h = 'auto';
16190         }
16191         return {width : w, height: h};
16192     },
16193
16194     // private
16195     adjustPosition : function(x, y){
16196         return {x : x, y: y};
16197     }
16198 });/*
16199  * Based on:
16200  * Ext JS Library 1.1.1
16201  * Copyright(c) 2006-2007, Ext JS, LLC.
16202  *
16203  * Originally Released Under LGPL - original licence link has changed is not relivant.
16204  *
16205  * Fork - LGPL
16206  * <script type="text/javascript">
16207  */
16208  (function(){ 
16209 /**
16210  * @class Roo.Layer
16211  * @extends Roo.Element
16212  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
16213  * automatic maintaining of shadow/shim positions.
16214  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
16215  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
16216  * you can pass a string with a CSS class name. False turns off the shadow.
16217  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
16218  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
16219  * @cfg {String} cls CSS class to add to the element
16220  * @cfg {Number} zindex Starting z-index (defaults to 11000)
16221  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
16222  * @constructor
16223  * @param {Object} config An object with config options.
16224  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
16225  */
16226
16227 Roo.Layer = function(config, existingEl){
16228     config = config || {};
16229     var dh = Roo.DomHelper;
16230     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
16231     if(existingEl){
16232         this.dom = Roo.getDom(existingEl);
16233     }
16234     if(!this.dom){
16235         var o = config.dh || {tag: "div", cls: "x-layer"};
16236         this.dom = dh.append(pel, o);
16237     }
16238     if(config.cls){
16239         this.addClass(config.cls);
16240     }
16241     this.constrain = config.constrain !== false;
16242     this.visibilityMode = Roo.Element.VISIBILITY;
16243     if(config.id){
16244         this.id = this.dom.id = config.id;
16245     }else{
16246         this.id = Roo.id(this.dom);
16247     }
16248     this.zindex = config.zindex || this.getZIndex();
16249     this.position("absolute", this.zindex);
16250     if(config.shadow){
16251         this.shadowOffset = config.shadowOffset || 4;
16252         this.shadow = new Roo.Shadow({
16253             offset : this.shadowOffset,
16254             mode : config.shadow
16255         });
16256     }else{
16257         this.shadowOffset = 0;
16258     }
16259     this.useShim = config.shim !== false && Roo.useShims;
16260     this.useDisplay = config.useDisplay;
16261     this.hide();
16262 };
16263
16264 var supr = Roo.Element.prototype;
16265
16266 // shims are shared among layer to keep from having 100 iframes
16267 var shims = [];
16268
16269 Roo.extend(Roo.Layer, Roo.Element, {
16270
16271     getZIndex : function(){
16272         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
16273     },
16274
16275     getShim : function(){
16276         if(!this.useShim){
16277             return null;
16278         }
16279         if(this.shim){
16280             return this.shim;
16281         }
16282         var shim = shims.shift();
16283         if(!shim){
16284             shim = this.createShim();
16285             shim.enableDisplayMode('block');
16286             shim.dom.style.display = 'none';
16287             shim.dom.style.visibility = 'visible';
16288         }
16289         var pn = this.dom.parentNode;
16290         if(shim.dom.parentNode != pn){
16291             pn.insertBefore(shim.dom, this.dom);
16292         }
16293         shim.setStyle('z-index', this.getZIndex()-2);
16294         this.shim = shim;
16295         return shim;
16296     },
16297
16298     hideShim : function(){
16299         if(this.shim){
16300             this.shim.setDisplayed(false);
16301             shims.push(this.shim);
16302             delete this.shim;
16303         }
16304     },
16305
16306     disableShadow : function(){
16307         if(this.shadow){
16308             this.shadowDisabled = true;
16309             this.shadow.hide();
16310             this.lastShadowOffset = this.shadowOffset;
16311             this.shadowOffset = 0;
16312         }
16313     },
16314
16315     enableShadow : function(show){
16316         if(this.shadow){
16317             this.shadowDisabled = false;
16318             this.shadowOffset = this.lastShadowOffset;
16319             delete this.lastShadowOffset;
16320             if(show){
16321                 this.sync(true);
16322             }
16323         }
16324     },
16325
16326     // private
16327     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
16328     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
16329     sync : function(doShow){
16330         var sw = this.shadow;
16331         if(!this.updating && this.isVisible() && (sw || this.useShim)){
16332             var sh = this.getShim();
16333
16334             var w = this.getWidth(),
16335                 h = this.getHeight();
16336
16337             var l = this.getLeft(true),
16338                 t = this.getTop(true);
16339
16340             if(sw && !this.shadowDisabled){
16341                 if(doShow && !sw.isVisible()){
16342                     sw.show(this);
16343                 }else{
16344                     sw.realign(l, t, w, h);
16345                 }
16346                 if(sh){
16347                     if(doShow){
16348                        sh.show();
16349                     }
16350                     // fit the shim behind the shadow, so it is shimmed too
16351                     var a = sw.adjusts, s = sh.dom.style;
16352                     s.left = (Math.min(l, l+a.l))+"px";
16353                     s.top = (Math.min(t, t+a.t))+"px";
16354                     s.width = (w+a.w)+"px";
16355                     s.height = (h+a.h)+"px";
16356                 }
16357             }else if(sh){
16358                 if(doShow){
16359                    sh.show();
16360                 }
16361                 sh.setSize(w, h);
16362                 sh.setLeftTop(l, t);
16363             }
16364             
16365         }
16366     },
16367
16368     // private
16369     destroy : function(){
16370         this.hideShim();
16371         if(this.shadow){
16372             this.shadow.hide();
16373         }
16374         this.removeAllListeners();
16375         var pn = this.dom.parentNode;
16376         if(pn){
16377             pn.removeChild(this.dom);
16378         }
16379         Roo.Element.uncache(this.id);
16380     },
16381
16382     remove : function(){
16383         this.destroy();
16384     },
16385
16386     // private
16387     beginUpdate : function(){
16388         this.updating = true;
16389     },
16390
16391     // private
16392     endUpdate : function(){
16393         this.updating = false;
16394         this.sync(true);
16395     },
16396
16397     // private
16398     hideUnders : function(negOffset){
16399         if(this.shadow){
16400             this.shadow.hide();
16401         }
16402         this.hideShim();
16403     },
16404
16405     // private
16406     constrainXY : function(){
16407         if(this.constrain){
16408             var vw = Roo.lib.Dom.getViewWidth(),
16409                 vh = Roo.lib.Dom.getViewHeight();
16410             var s = Roo.get(document).getScroll();
16411
16412             var xy = this.getXY();
16413             var x = xy[0], y = xy[1];   
16414             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
16415             // only move it if it needs it
16416             var moved = false;
16417             // first validate right/bottom
16418             if((x + w) > vw+s.left){
16419                 x = vw - w - this.shadowOffset;
16420                 moved = true;
16421             }
16422             if((y + h) > vh+s.top){
16423                 y = vh - h - this.shadowOffset;
16424                 moved = true;
16425             }
16426             // then make sure top/left isn't negative
16427             if(x < s.left){
16428                 x = s.left;
16429                 moved = true;
16430             }
16431             if(y < s.top){
16432                 y = s.top;
16433                 moved = true;
16434             }
16435             if(moved){
16436                 if(this.avoidY){
16437                     var ay = this.avoidY;
16438                     if(y <= ay && (y+h) >= ay){
16439                         y = ay-h-5;   
16440                     }
16441                 }
16442                 xy = [x, y];
16443                 this.storeXY(xy);
16444                 supr.setXY.call(this, xy);
16445                 this.sync();
16446             }
16447         }
16448     },
16449
16450     isVisible : function(){
16451         return this.visible;    
16452     },
16453
16454     // private
16455     showAction : function(){
16456         this.visible = true; // track visibility to prevent getStyle calls
16457         if(this.useDisplay === true){
16458             this.setDisplayed("");
16459         }else if(this.lastXY){
16460             supr.setXY.call(this, this.lastXY);
16461         }else if(this.lastLT){
16462             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
16463         }
16464     },
16465
16466     // private
16467     hideAction : function(){
16468         this.visible = false;
16469         if(this.useDisplay === true){
16470             this.setDisplayed(false);
16471         }else{
16472             this.setLeftTop(-10000,-10000);
16473         }
16474     },
16475
16476     // overridden Element method
16477     setVisible : function(v, a, d, c, e){
16478         if(v){
16479             this.showAction();
16480         }
16481         if(a && v){
16482             var cb = function(){
16483                 this.sync(true);
16484                 if(c){
16485                     c();
16486                 }
16487             }.createDelegate(this);
16488             supr.setVisible.call(this, true, true, d, cb, e);
16489         }else{
16490             if(!v){
16491                 this.hideUnders(true);
16492             }
16493             var cb = c;
16494             if(a){
16495                 cb = function(){
16496                     this.hideAction();
16497                     if(c){
16498                         c();
16499                     }
16500                 }.createDelegate(this);
16501             }
16502             supr.setVisible.call(this, v, a, d, cb, e);
16503             if(v){
16504                 this.sync(true);
16505             }else if(!a){
16506                 this.hideAction();
16507             }
16508         }
16509     },
16510
16511     storeXY : function(xy){
16512         delete this.lastLT;
16513         this.lastXY = xy;
16514     },
16515
16516     storeLeftTop : function(left, top){
16517         delete this.lastXY;
16518         this.lastLT = [left, top];
16519     },
16520
16521     // private
16522     beforeFx : function(){
16523         this.beforeAction();
16524         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
16525     },
16526
16527     // private
16528     afterFx : function(){
16529         Roo.Layer.superclass.afterFx.apply(this, arguments);
16530         this.sync(this.isVisible());
16531     },
16532
16533     // private
16534     beforeAction : function(){
16535         if(!this.updating && this.shadow){
16536             this.shadow.hide();
16537         }
16538     },
16539
16540     // overridden Element method
16541     setLeft : function(left){
16542         this.storeLeftTop(left, this.getTop(true));
16543         supr.setLeft.apply(this, arguments);
16544         this.sync();
16545     },
16546
16547     setTop : function(top){
16548         this.storeLeftTop(this.getLeft(true), top);
16549         supr.setTop.apply(this, arguments);
16550         this.sync();
16551     },
16552
16553     setLeftTop : function(left, top){
16554         this.storeLeftTop(left, top);
16555         supr.setLeftTop.apply(this, arguments);
16556         this.sync();
16557     },
16558
16559     setXY : function(xy, a, d, c, e){
16560         this.fixDisplay();
16561         this.beforeAction();
16562         this.storeXY(xy);
16563         var cb = this.createCB(c);
16564         supr.setXY.call(this, xy, a, d, cb, e);
16565         if(!a){
16566             cb();
16567         }
16568     },
16569
16570     // private
16571     createCB : function(c){
16572         var el = this;
16573         return function(){
16574             el.constrainXY();
16575             el.sync(true);
16576             if(c){
16577                 c();
16578             }
16579         };
16580     },
16581
16582     // overridden Element method
16583     setX : function(x, a, d, c, e){
16584         this.setXY([x, this.getY()], a, d, c, e);
16585     },
16586
16587     // overridden Element method
16588     setY : function(y, a, d, c, e){
16589         this.setXY([this.getX(), y], a, d, c, e);
16590     },
16591
16592     // overridden Element method
16593     setSize : function(w, h, a, d, c, e){
16594         this.beforeAction();
16595         var cb = this.createCB(c);
16596         supr.setSize.call(this, w, h, a, d, cb, e);
16597         if(!a){
16598             cb();
16599         }
16600     },
16601
16602     // overridden Element method
16603     setWidth : function(w, a, d, c, e){
16604         this.beforeAction();
16605         var cb = this.createCB(c);
16606         supr.setWidth.call(this, w, a, d, cb, e);
16607         if(!a){
16608             cb();
16609         }
16610     },
16611
16612     // overridden Element method
16613     setHeight : function(h, a, d, c, e){
16614         this.beforeAction();
16615         var cb = this.createCB(c);
16616         supr.setHeight.call(this, h, a, d, cb, e);
16617         if(!a){
16618             cb();
16619         }
16620     },
16621
16622     // overridden Element method
16623     setBounds : function(x, y, w, h, a, d, c, e){
16624         this.beforeAction();
16625         var cb = this.createCB(c);
16626         if(!a){
16627             this.storeXY([x, y]);
16628             supr.setXY.call(this, [x, y]);
16629             supr.setSize.call(this, w, h, a, d, cb, e);
16630             cb();
16631         }else{
16632             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
16633         }
16634         return this;
16635     },
16636     
16637     /**
16638      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
16639      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
16640      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
16641      * @param {Number} zindex The new z-index to set
16642      * @return {this} The Layer
16643      */
16644     setZIndex : function(zindex){
16645         this.zindex = zindex;
16646         this.setStyle("z-index", zindex + 2);
16647         if(this.shadow){
16648             this.shadow.setZIndex(zindex + 1);
16649         }
16650         if(this.shim){
16651             this.shim.setStyle("z-index", zindex);
16652         }
16653     }
16654 });
16655 })();/*
16656  * Original code for Roojs - LGPL
16657  * <script type="text/javascript">
16658  */
16659  
16660 /**
16661  * @class Roo.XComponent
16662  * A delayed Element creator...
16663  * Or a way to group chunks of interface together.
16664  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
16665  *  used in conjunction with XComponent.build() it will create an instance of each element,
16666  *  then call addxtype() to build the User interface.
16667  * 
16668  * Mypart.xyx = new Roo.XComponent({
16669
16670     parent : 'Mypart.xyz', // empty == document.element.!!
16671     order : '001',
16672     name : 'xxxx'
16673     region : 'xxxx'
16674     disabled : function() {} 
16675      
16676     tree : function() { // return an tree of xtype declared components
16677         var MODULE = this;
16678         return 
16679         {
16680             xtype : 'NestedLayoutPanel',
16681             // technicall
16682         }
16683      ]
16684  *})
16685  *
16686  *
16687  * It can be used to build a big heiracy, with parent etc.
16688  * or you can just use this to render a single compoent to a dom element
16689  * MYPART.render(Roo.Element | String(id) | dom_element )
16690  *
16691  *
16692  * Usage patterns.
16693  *
16694  * Classic Roo
16695  *
16696  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16697  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16698  *
16699  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16700  *
16701  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16702  * - if mulitple topModules exist, the last one is defined as the top module.
16703  *
16704  * Embeded Roo
16705  * 
16706  * When the top level or multiple modules are to embedded into a existing HTML page,
16707  * the parent element can container '#id' of the element where the module will be drawn.
16708  *
16709  * Bootstrap Roo
16710  *
16711  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16712  * it relies more on a include mechanism, where sub modules are included into an outer page.
16713  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16714  * 
16715  * Bootstrap Roo Included elements
16716  *
16717  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16718  * hence confusing the component builder as it thinks there are multiple top level elements. 
16719  *
16720  * String Over-ride & Translations
16721  *
16722  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16723  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16724  * are needed. @see Roo.XComponent.overlayString  
16725  * 
16726  * 
16727  * 
16728  * @extends Roo.util.Observable
16729  * @constructor
16730  * @param cfg {Object} configuration of component
16731  * 
16732  */
16733 Roo.XComponent = function(cfg) {
16734     Roo.apply(this, cfg);
16735     this.addEvents({ 
16736         /**
16737              * @event built
16738              * Fires when this the componnt is built
16739              * @param {Roo.XComponent} c the component
16740              */
16741         'built' : true
16742         
16743     });
16744     this.region = this.region || 'center'; // default..
16745     Roo.XComponent.register(this);
16746     this.modules = false;
16747     this.el = false; // where the layout goes..
16748     
16749     
16750 }
16751 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16752     /**
16753      * @property el
16754      * The created element (with Roo.factory())
16755      * @type {Roo.Layout}
16756      */
16757     el  : false,
16758     
16759     /**
16760      * @property el
16761      * for BC  - use el in new code
16762      * @type {Roo.Layout}
16763      */
16764     panel : false,
16765     
16766     /**
16767      * @property layout
16768      * for BC  - use el in new code
16769      * @type {Roo.Layout}
16770      */
16771     layout : false,
16772     
16773      /**
16774      * @cfg {Function|boolean} disabled
16775      * If this module is disabled by some rule, return true from the funtion
16776      */
16777     disabled : false,
16778     
16779     /**
16780      * @cfg {String} parent 
16781      * Name of parent element which it get xtype added to..
16782      */
16783     parent: false,
16784     
16785     /**
16786      * @cfg {String} order
16787      * Used to set the order in which elements are created (usefull for multiple tabs)
16788      */
16789     
16790     order : false,
16791     /**
16792      * @cfg {String} name
16793      * String to display while loading.
16794      */
16795     name : false,
16796     /**
16797      * @cfg {String} region
16798      * Region to render component to (defaults to center)
16799      */
16800     region : 'center',
16801     
16802     /**
16803      * @cfg {Array} items
16804      * A single item array - the first element is the root of the tree..
16805      * It's done this way to stay compatible with the Xtype system...
16806      */
16807     items : false,
16808     
16809     /**
16810      * @property _tree
16811      * The method that retuns the tree of parts that make up this compoennt 
16812      * @type {function}
16813      */
16814     _tree  : false,
16815     
16816      /**
16817      * render
16818      * render element to dom or tree
16819      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16820      */
16821     
16822     render : function(el)
16823     {
16824         
16825         el = el || false;
16826         var hp = this.parent ? 1 : 0;
16827         Roo.debug &&  Roo.log(this);
16828         
16829         var tree = this._tree ? this._tree() : this.tree();
16830
16831         
16832         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16833             // if parent is a '#.....' string, then let's use that..
16834             var ename = this.parent.substr(1);
16835             this.parent = false;
16836             Roo.debug && Roo.log(ename);
16837             switch (ename) {
16838                 case 'bootstrap-body':
16839                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16840                         // this is the BorderLayout standard?
16841                        this.parent = { el : true };
16842                        break;
16843                     }
16844                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16845                         // need to insert stuff...
16846                         this.parent =  {
16847                              el : new Roo.bootstrap.layout.Border({
16848                                  el : document.body, 
16849                      
16850                                  center: {
16851                                     titlebar: false,
16852                                     autoScroll:false,
16853                                     closeOnTab: true,
16854                                     tabPosition: 'top',
16855                                       //resizeTabs: true,
16856                                     alwaysShowTabs: true,
16857                                     hideTabs: false
16858                                      //minTabWidth: 140
16859                                  }
16860                              })
16861                         
16862                          };
16863                          break;
16864                     }
16865                          
16866                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16867                         this.parent = { el :  new  Roo.bootstrap.Body() };
16868                         Roo.debug && Roo.log("setting el to doc body");
16869                          
16870                     } else {
16871                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16872                     }
16873                     break;
16874                 case 'bootstrap':
16875                     this.parent = { el : true};
16876                     // fall through
16877                 default:
16878                     el = Roo.get(ename);
16879                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16880                         this.parent = { el : true};
16881                     }
16882                     
16883                     break;
16884             }
16885                 
16886             
16887             if (!el && !this.parent) {
16888                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16889                 return;
16890             }
16891         }
16892         
16893         Roo.debug && Roo.log("EL:");
16894         Roo.debug && Roo.log(el);
16895         Roo.debug && Roo.log("this.parent.el:");
16896         Roo.debug && Roo.log(this.parent.el);
16897         
16898
16899         // altertive root elements ??? - we need a better way to indicate these.
16900         var is_alt = Roo.XComponent.is_alt ||
16901                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16902                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16903                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16904         
16905         
16906         
16907         if (!this.parent && is_alt) {
16908             //el = Roo.get(document.body);
16909             this.parent = { el : true };
16910         }
16911             
16912             
16913         
16914         if (!this.parent) {
16915             
16916             Roo.debug && Roo.log("no parent - creating one");
16917             
16918             el = el ? Roo.get(el) : false;      
16919             
16920             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16921                 
16922                 this.parent =  {
16923                     el : new Roo.bootstrap.layout.Border({
16924                         el: el || document.body,
16925                     
16926                         center: {
16927                             titlebar: false,
16928                             autoScroll:false,
16929                             closeOnTab: true,
16930                             tabPosition: 'top',
16931                              //resizeTabs: true,
16932                             alwaysShowTabs: false,
16933                             hideTabs: true,
16934                             minTabWidth: 140,
16935                             overflow: 'visible'
16936                          }
16937                      })
16938                 };
16939             } else {
16940             
16941                 // it's a top level one..
16942                 this.parent =  {
16943                     el : new Roo.BorderLayout(el || document.body, {
16944                         center: {
16945                             titlebar: false,
16946                             autoScroll:false,
16947                             closeOnTab: true,
16948                             tabPosition: 'top',
16949                              //resizeTabs: true,
16950                             alwaysShowTabs: el && hp? false :  true,
16951                             hideTabs: el || !hp ? true :  false,
16952                             minTabWidth: 140
16953                          }
16954                     })
16955                 };
16956             }
16957         }
16958         
16959         if (!this.parent.el) {
16960                 // probably an old style ctor, which has been disabled.
16961                 return;
16962
16963         }
16964                 // The 'tree' method is  '_tree now' 
16965             
16966         tree.region = tree.region || this.region;
16967         var is_body = false;
16968         if (this.parent.el === true) {
16969             // bootstrap... - body..
16970             if (el) {
16971                 tree.el = el;
16972             }
16973             this.parent.el = Roo.factory(tree);
16974             is_body = true;
16975         }
16976         
16977         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16978         this.fireEvent('built', this);
16979         
16980         this.panel = this.el;
16981         this.layout = this.panel.layout;
16982         this.parentLayout = this.parent.layout  || false;  
16983          
16984     }
16985     
16986 });
16987
16988 Roo.apply(Roo.XComponent, {
16989     /**
16990      * @property  hideProgress
16991      * true to disable the building progress bar.. usefull on single page renders.
16992      * @type Boolean
16993      */
16994     hideProgress : false,
16995     /**
16996      * @property  buildCompleted
16997      * True when the builder has completed building the interface.
16998      * @type Boolean
16999      */
17000     buildCompleted : false,
17001      
17002     /**
17003      * @property  topModule
17004      * the upper most module - uses document.element as it's constructor.
17005      * @type Object
17006      */
17007      
17008     topModule  : false,
17009       
17010     /**
17011      * @property  modules
17012      * array of modules to be created by registration system.
17013      * @type {Array} of Roo.XComponent
17014      */
17015     
17016     modules : [],
17017     /**
17018      * @property  elmodules
17019      * array of modules to be created by which use #ID 
17020      * @type {Array} of Roo.XComponent
17021      */
17022      
17023     elmodules : [],
17024
17025      /**
17026      * @property  is_alt
17027      * Is an alternative Root - normally used by bootstrap or other systems,
17028      *    where the top element in the tree can wrap 'body' 
17029      * @type {boolean}  (default false)
17030      */
17031      
17032     is_alt : false,
17033     /**
17034      * @property  build_from_html
17035      * Build elements from html - used by bootstrap HTML stuff 
17036      *    - this is cleared after build is completed
17037      * @type {boolean}    (default false)
17038      */
17039      
17040     build_from_html : false,
17041     /**
17042      * Register components to be built later.
17043      *
17044      * This solves the following issues
17045      * - Building is not done on page load, but after an authentication process has occured.
17046      * - Interface elements are registered on page load
17047      * - Parent Interface elements may not be loaded before child, so this handles that..
17048      * 
17049      *
17050      * example:
17051      * 
17052      * MyApp.register({
17053           order : '000001',
17054           module : 'Pman.Tab.projectMgr',
17055           region : 'center',
17056           parent : 'Pman.layout',
17057           disabled : false,  // or use a function..
17058         })
17059      
17060      * * @param {Object} details about module
17061      */
17062     register : function(obj) {
17063                 
17064         Roo.XComponent.event.fireEvent('register', obj);
17065         switch(typeof(obj.disabled) ) {
17066                 
17067             case 'undefined':
17068                 break;
17069             
17070             case 'function':
17071                 if ( obj.disabled() ) {
17072                         return;
17073                 }
17074                 break;
17075             
17076             default:
17077                 if (obj.disabled || obj.region == '#disabled') {
17078                         return;
17079                 }
17080                 break;
17081         }
17082                 
17083         this.modules.push(obj);
17084          
17085     },
17086     /**
17087      * convert a string to an object..
17088      * eg. 'AAA.BBB' -> finds AAA.BBB
17089
17090      */
17091     
17092     toObject : function(str)
17093     {
17094         if (!str || typeof(str) == 'object') {
17095             return str;
17096         }
17097         if (str.substring(0,1) == '#') {
17098             return str;
17099         }
17100
17101         var ar = str.split('.');
17102         var rt, o;
17103         rt = ar.shift();
17104             /** eval:var:o */
17105         try {
17106             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
17107         } catch (e) {
17108             throw "Module not found : " + str;
17109         }
17110         
17111         if (o === false) {
17112             throw "Module not found : " + str;
17113         }
17114         Roo.each(ar, function(e) {
17115             if (typeof(o[e]) == 'undefined') {
17116                 throw "Module not found : " + str;
17117             }
17118             o = o[e];
17119         });
17120         
17121         return o;
17122         
17123     },
17124     
17125     
17126     /**
17127      * move modules into their correct place in the tree..
17128      * 
17129      */
17130     preBuild : function ()
17131     {
17132         var _t = this;
17133         Roo.each(this.modules , function (obj)
17134         {
17135             Roo.XComponent.event.fireEvent('beforebuild', obj);
17136             
17137             var opar = obj.parent;
17138             try { 
17139                 obj.parent = this.toObject(opar);
17140             } catch(e) {
17141                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
17142                 return;
17143             }
17144             
17145             if (!obj.parent) {
17146                 Roo.debug && Roo.log("GOT top level module");
17147                 Roo.debug && Roo.log(obj);
17148                 obj.modules = new Roo.util.MixedCollection(false, 
17149                     function(o) { return o.order + '' }
17150                 );
17151                 this.topModule = obj;
17152                 return;
17153             }
17154                         // parent is a string (usually a dom element name..)
17155             if (typeof(obj.parent) == 'string') {
17156                 this.elmodules.push(obj);
17157                 return;
17158             }
17159             if (obj.parent.constructor != Roo.XComponent) {
17160                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
17161             }
17162             if (!obj.parent.modules) {
17163                 obj.parent.modules = new Roo.util.MixedCollection(false, 
17164                     function(o) { return o.order + '' }
17165                 );
17166             }
17167             if (obj.parent.disabled) {
17168                 obj.disabled = true;
17169             }
17170             obj.parent.modules.add(obj);
17171         }, this);
17172     },
17173     
17174      /**
17175      * make a list of modules to build.
17176      * @return {Array} list of modules. 
17177      */ 
17178     
17179     buildOrder : function()
17180     {
17181         var _this = this;
17182         var cmp = function(a,b) {   
17183             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
17184         };
17185         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
17186             throw "No top level modules to build";
17187         }
17188         
17189         // make a flat list in order of modules to build.
17190         var mods = this.topModule ? [ this.topModule ] : [];
17191                 
17192         
17193         // elmodules (is a list of DOM based modules )
17194         Roo.each(this.elmodules, function(e) {
17195             mods.push(e);
17196             if (!this.topModule &&
17197                 typeof(e.parent) == 'string' &&
17198                 e.parent.substring(0,1) == '#' &&
17199                 Roo.get(e.parent.substr(1))
17200                ) {
17201                 
17202                 _this.topModule = e;
17203             }
17204             
17205         });
17206
17207         
17208         // add modules to their parents..
17209         var addMod = function(m) {
17210             Roo.debug && Roo.log("build Order: add: " + m.name);
17211                 
17212             mods.push(m);
17213             if (m.modules && !m.disabled) {
17214                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
17215                 m.modules.keySort('ASC',  cmp );
17216                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
17217     
17218                 m.modules.each(addMod);
17219             } else {
17220                 Roo.debug && Roo.log("build Order: no child modules");
17221             }
17222             // not sure if this is used any more..
17223             if (m.finalize) {
17224                 m.finalize.name = m.name + " (clean up) ";
17225                 mods.push(m.finalize);
17226             }
17227             
17228         }
17229         if (this.topModule && this.topModule.modules) { 
17230             this.topModule.modules.keySort('ASC',  cmp );
17231             this.topModule.modules.each(addMod);
17232         } 
17233         return mods;
17234     },
17235     
17236      /**
17237      * Build the registered modules.
17238      * @param {Object} parent element.
17239      * @param {Function} optional method to call after module has been added.
17240      * 
17241      */ 
17242    
17243     build : function(opts) 
17244     {
17245         
17246         if (typeof(opts) != 'undefined') {
17247             Roo.apply(this,opts);
17248         }
17249         
17250         this.preBuild();
17251         var mods = this.buildOrder();
17252       
17253         //this.allmods = mods;
17254         //Roo.debug && Roo.log(mods);
17255         //return;
17256         if (!mods.length) { // should not happen
17257             throw "NO modules!!!";
17258         }
17259         
17260         
17261         var msg = "Building Interface...";
17262         // flash it up as modal - so we store the mask!?
17263         if (!this.hideProgress && Roo.MessageBox) {
17264             Roo.MessageBox.show({ title: 'loading' });
17265             Roo.MessageBox.show({
17266                title: "Please wait...",
17267                msg: msg,
17268                width:450,
17269                progress:true,
17270                buttons : false,
17271                closable:false,
17272                modal: false
17273               
17274             });
17275         }
17276         var total = mods.length;
17277         
17278         var _this = this;
17279         var progressRun = function() {
17280             if (!mods.length) {
17281                 Roo.debug && Roo.log('hide?');
17282                 if (!this.hideProgress && Roo.MessageBox) {
17283                     Roo.MessageBox.hide();
17284                 }
17285                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
17286                 
17287                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
17288                 
17289                 // THE END...
17290                 return false;   
17291             }
17292             
17293             var m = mods.shift();
17294             
17295             
17296             Roo.debug && Roo.log(m);
17297             // not sure if this is supported any more.. - modules that are are just function
17298             if (typeof(m) == 'function') { 
17299                 m.call(this);
17300                 return progressRun.defer(10, _this);
17301             } 
17302             
17303             
17304             msg = "Building Interface " + (total  - mods.length) + 
17305                     " of " + total + 
17306                     (m.name ? (' - ' + m.name) : '');
17307                         Roo.debug && Roo.log(msg);
17308             if (!_this.hideProgress &&  Roo.MessageBox) { 
17309                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
17310             }
17311             
17312          
17313             // is the module disabled?
17314             var disabled = (typeof(m.disabled) == 'function') ?
17315                 m.disabled.call(m.module.disabled) : m.disabled;    
17316             
17317             
17318             if (disabled) {
17319                 return progressRun(); // we do not update the display!
17320             }
17321             
17322             // now build 
17323             
17324                         
17325                         
17326             m.render();
17327             // it's 10 on top level, and 1 on others??? why...
17328             return progressRun.defer(10, _this);
17329              
17330         }
17331         progressRun.defer(1, _this);
17332      
17333         
17334         
17335     },
17336     /**
17337      * Overlay a set of modified strings onto a component
17338      * This is dependant on our builder exporting the strings and 'named strings' elements.
17339      * 
17340      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
17341      * @param {Object} associative array of 'named' string and it's new value.
17342      * 
17343      */
17344         overlayStrings : function( component, strings )
17345     {
17346         if (typeof(component['_named_strings']) == 'undefined') {
17347             throw "ERROR: component does not have _named_strings";
17348         }
17349         for ( var k in strings ) {
17350             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
17351             if (md !== false) {
17352                 component['_strings'][md] = strings[k];
17353             } else {
17354                 Roo.log('could not find named string: ' + k + ' in');
17355                 Roo.log(component);
17356             }
17357             
17358         }
17359         
17360     },
17361     
17362         
17363         /**
17364          * Event Object.
17365          *
17366          *
17367          */
17368         event: false, 
17369     /**
17370          * wrapper for event.on - aliased later..  
17371          * Typically use to register a event handler for register:
17372          *
17373          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
17374          *
17375          */
17376     on : false
17377    
17378     
17379     
17380 });
17381
17382 Roo.XComponent.event = new Roo.util.Observable({
17383                 events : { 
17384                         /**
17385                          * @event register
17386                          * Fires when an Component is registered,
17387                          * set the disable property on the Component to stop registration.
17388                          * @param {Roo.XComponent} c the component being registerd.
17389                          * 
17390                          */
17391                         'register' : true,
17392             /**
17393                          * @event beforebuild
17394                          * Fires before each Component is built
17395                          * can be used to apply permissions.
17396                          * @param {Roo.XComponent} c the component being registerd.
17397                          * 
17398                          */
17399                         'beforebuild' : true,
17400                         /**
17401                          * @event buildcomplete
17402                          * Fires on the top level element when all elements have been built
17403                          * @param {Roo.XComponent} the top level component.
17404                          */
17405                         'buildcomplete' : true
17406                         
17407                 }
17408 });
17409
17410 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
17411  //
17412  /**
17413  * marked - a markdown parser
17414  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
17415  * https://github.com/chjj/marked
17416  */
17417
17418
17419 /**
17420  *
17421  * Roo.Markdown - is a very crude wrapper around marked..
17422  *
17423  * usage:
17424  * 
17425  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
17426  * 
17427  * Note: move the sample code to the bottom of this
17428  * file before uncommenting it.
17429  *
17430  */
17431
17432 Roo.Markdown = {};
17433 Roo.Markdown.toHtml = function(text) {
17434     
17435     var c = new Roo.Markdown.marked.setOptions({
17436             renderer: new Roo.Markdown.marked.Renderer(),
17437             gfm: true,
17438             tables: true,
17439             breaks: false,
17440             pedantic: false,
17441             sanitize: false,
17442             smartLists: true,
17443             smartypants: false
17444           });
17445     // A FEW HACKS!!?
17446     
17447     text = text.replace(/\\\n/g,' ');
17448     return Roo.Markdown.marked(text);
17449 };
17450 //
17451 // converter
17452 //
17453 // Wraps all "globals" so that the only thing
17454 // exposed is makeHtml().
17455 //
17456 (function() {
17457     
17458      /**
17459          * eval:var:escape
17460          * eval:var:unescape
17461          * eval:var:replace
17462          */
17463       
17464     /**
17465      * Helpers
17466      */
17467     
17468     var escape = function (html, encode) {
17469       return html
17470         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17471         .replace(/</g, '&lt;')
17472         .replace(/>/g, '&gt;')
17473         .replace(/"/g, '&quot;')
17474         .replace(/'/g, '&#39;');
17475     }
17476     
17477     var unescape = function (html) {
17478         // explicitly match decimal, hex, and named HTML entities 
17479       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17480         n = n.toLowerCase();
17481         if (n === 'colon') { return ':'; }
17482         if (n.charAt(0) === '#') {
17483           return n.charAt(1) === 'x'
17484             ? String.fromCharCode(parseInt(n.substring(2), 16))
17485             : String.fromCharCode(+n.substring(1));
17486         }
17487         return '';
17488       });
17489     }
17490     
17491     var replace = function (regex, opt) {
17492       regex = regex.source;
17493       opt = opt || '';
17494       return function self(name, val) {
17495         if (!name) { return new RegExp(regex, opt); }
17496         val = val.source || val;
17497         val = val.replace(/(^|[^\[])\^/g, '$1');
17498         regex = regex.replace(name, val);
17499         return self;
17500       };
17501     }
17502
17503
17504          /**
17505          * eval:var:noop
17506     */
17507     var noop = function () {}
17508     noop.exec = noop;
17509     
17510          /**
17511          * eval:var:merge
17512     */
17513     var merge = function (obj) {
17514       var i = 1
17515         , target
17516         , key;
17517     
17518       for (; i < arguments.length; i++) {
17519         target = arguments[i];
17520         for (key in target) {
17521           if (Object.prototype.hasOwnProperty.call(target, key)) {
17522             obj[key] = target[key];
17523           }
17524         }
17525       }
17526     
17527       return obj;
17528     }
17529     
17530     
17531     /**
17532      * Block-Level Grammar
17533      */
17534     
17535     
17536     
17537     
17538     var block = {
17539       newline: /^\n+/,
17540       code: /^( {4}[^\n]+\n*)+/,
17541       fences: noop,
17542       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
17543       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
17544       nptable: noop,
17545       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
17546       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
17547       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
17548       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
17549       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
17550       table: noop,
17551       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
17552       text: /^[^\n]+/
17553     };
17554     
17555     block.bullet = /(?:[*+-]|\d+\.)/;
17556     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
17557     block.item = replace(block.item, 'gm')
17558       (/bull/g, block.bullet)
17559       ();
17560     
17561     block.list = replace(block.list)
17562       (/bull/g, block.bullet)
17563       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
17564       ('def', '\\n+(?=' + block.def.source + ')')
17565       ();
17566     
17567     block.blockquote = replace(block.blockquote)
17568       ('def', block.def)
17569       ();
17570     
17571     block._tag = '(?!(?:'
17572       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
17573       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
17574       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
17575     
17576     block.html = replace(block.html)
17577       ('comment', /<!--[\s\S]*?-->/)
17578       ('closed', /<(tag)[\s\S]+?<\/\1>/)
17579       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
17580       (/tag/g, block._tag)
17581       ();
17582     
17583     block.paragraph = replace(block.paragraph)
17584       ('hr', block.hr)
17585       ('heading', block.heading)
17586       ('lheading', block.lheading)
17587       ('blockquote', block.blockquote)
17588       ('tag', '<' + block._tag)
17589       ('def', block.def)
17590       ();
17591     
17592     /**
17593      * Normal Block Grammar
17594      */
17595     
17596     block.normal = merge({}, block);
17597     
17598     /**
17599      * GFM Block Grammar
17600      */
17601     
17602     block.gfm = merge({}, block.normal, {
17603       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
17604       paragraph: /^/,
17605       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
17606     });
17607     
17608     block.gfm.paragraph = replace(block.paragraph)
17609       ('(?!', '(?!'
17610         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
17611         + block.list.source.replace('\\1', '\\3') + '|')
17612       ();
17613     
17614     /**
17615      * GFM + Tables Block Grammar
17616      */
17617     
17618     block.tables = merge({}, block.gfm, {
17619       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
17620       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
17621     });
17622     
17623     /**
17624      * Block Lexer
17625      */
17626     
17627     var Lexer = function (options) {
17628       this.tokens = [];
17629       this.tokens.links = {};
17630       this.options = options || marked.defaults;
17631       this.rules = block.normal;
17632     
17633       if (this.options.gfm) {
17634         if (this.options.tables) {
17635           this.rules = block.tables;
17636         } else {
17637           this.rules = block.gfm;
17638         }
17639       }
17640     }
17641     
17642     /**
17643      * Expose Block Rules
17644      */
17645     
17646     Lexer.rules = block;
17647     
17648     /**
17649      * Static Lex Method
17650      */
17651     
17652     Lexer.lex = function(src, options) {
17653       var lexer = new Lexer(options);
17654       return lexer.lex(src);
17655     };
17656     
17657     /**
17658      * Preprocessing
17659      */
17660     
17661     Lexer.prototype.lex = function(src) {
17662       src = src
17663         .replace(/\r\n|\r/g, '\n')
17664         .replace(/\t/g, '    ')
17665         .replace(/\u00a0/g, ' ')
17666         .replace(/\u2424/g, '\n');
17667     
17668       return this.token(src, true);
17669     };
17670     
17671     /**
17672      * Lexing
17673      */
17674     
17675     Lexer.prototype.token = function(src, top, bq) {
17676       var src = src.replace(/^ +$/gm, '')
17677         , next
17678         , loose
17679         , cap
17680         , bull
17681         , b
17682         , item
17683         , space
17684         , i
17685         , l;
17686     
17687       while (src) {
17688         // newline
17689         if (cap = this.rules.newline.exec(src)) {
17690           src = src.substring(cap[0].length);
17691           if (cap[0].length > 1) {
17692             this.tokens.push({
17693               type: 'space'
17694             });
17695           }
17696         }
17697     
17698         // code
17699         if (cap = this.rules.code.exec(src)) {
17700           src = src.substring(cap[0].length);
17701           cap = cap[0].replace(/^ {4}/gm, '');
17702           this.tokens.push({
17703             type: 'code',
17704             text: !this.options.pedantic
17705               ? cap.replace(/\n+$/, '')
17706               : cap
17707           });
17708           continue;
17709         }
17710     
17711         // fences (gfm)
17712         if (cap = this.rules.fences.exec(src)) {
17713           src = src.substring(cap[0].length);
17714           this.tokens.push({
17715             type: 'code',
17716             lang: cap[2],
17717             text: cap[3] || ''
17718           });
17719           continue;
17720         }
17721     
17722         // heading
17723         if (cap = this.rules.heading.exec(src)) {
17724           src = src.substring(cap[0].length);
17725           this.tokens.push({
17726             type: 'heading',
17727             depth: cap[1].length,
17728             text: cap[2]
17729           });
17730           continue;
17731         }
17732     
17733         // table no leading pipe (gfm)
17734         if (top && (cap = this.rules.nptable.exec(src))) {
17735           src = src.substring(cap[0].length);
17736     
17737           item = {
17738             type: 'table',
17739             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17740             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17741             cells: cap[3].replace(/\n$/, '').split('\n')
17742           };
17743     
17744           for (i = 0; i < item.align.length; i++) {
17745             if (/^ *-+: *$/.test(item.align[i])) {
17746               item.align[i] = 'right';
17747             } else if (/^ *:-+: *$/.test(item.align[i])) {
17748               item.align[i] = 'center';
17749             } else if (/^ *:-+ *$/.test(item.align[i])) {
17750               item.align[i] = 'left';
17751             } else {
17752               item.align[i] = null;
17753             }
17754           }
17755     
17756           for (i = 0; i < item.cells.length; i++) {
17757             item.cells[i] = item.cells[i].split(/ *\| */);
17758           }
17759     
17760           this.tokens.push(item);
17761     
17762           continue;
17763         }
17764     
17765         // lheading
17766         if (cap = this.rules.lheading.exec(src)) {
17767           src = src.substring(cap[0].length);
17768           this.tokens.push({
17769             type: 'heading',
17770             depth: cap[2] === '=' ? 1 : 2,
17771             text: cap[1]
17772           });
17773           continue;
17774         }
17775     
17776         // hr
17777         if (cap = this.rules.hr.exec(src)) {
17778           src = src.substring(cap[0].length);
17779           this.tokens.push({
17780             type: 'hr'
17781           });
17782           continue;
17783         }
17784     
17785         // blockquote
17786         if (cap = this.rules.blockquote.exec(src)) {
17787           src = src.substring(cap[0].length);
17788     
17789           this.tokens.push({
17790             type: 'blockquote_start'
17791           });
17792     
17793           cap = cap[0].replace(/^ *> ?/gm, '');
17794     
17795           // Pass `top` to keep the current
17796           // "toplevel" state. This is exactly
17797           // how markdown.pl works.
17798           this.token(cap, top, true);
17799     
17800           this.tokens.push({
17801             type: 'blockquote_end'
17802           });
17803     
17804           continue;
17805         }
17806     
17807         // list
17808         if (cap = this.rules.list.exec(src)) {
17809           src = src.substring(cap[0].length);
17810           bull = cap[2];
17811     
17812           this.tokens.push({
17813             type: 'list_start',
17814             ordered: bull.length > 1
17815           });
17816     
17817           // Get each top-level item.
17818           cap = cap[0].match(this.rules.item);
17819     
17820           next = false;
17821           l = cap.length;
17822           i = 0;
17823     
17824           for (; i < l; i++) {
17825             item = cap[i];
17826     
17827             // Remove the list item's bullet
17828             // so it is seen as the next token.
17829             space = item.length;
17830             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17831     
17832             // Outdent whatever the
17833             // list item contains. Hacky.
17834             if (~item.indexOf('\n ')) {
17835               space -= item.length;
17836               item = !this.options.pedantic
17837                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17838                 : item.replace(/^ {1,4}/gm, '');
17839             }
17840     
17841             // Determine whether the next list item belongs here.
17842             // Backpedal if it does not belong in this list.
17843             if (this.options.smartLists && i !== l - 1) {
17844               b = block.bullet.exec(cap[i + 1])[0];
17845               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17846                 src = cap.slice(i + 1).join('\n') + src;
17847                 i = l - 1;
17848               }
17849             }
17850     
17851             // Determine whether item is loose or not.
17852             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17853             // for discount behavior.
17854             loose = next || /\n\n(?!\s*$)/.test(item);
17855             if (i !== l - 1) {
17856               next = item.charAt(item.length - 1) === '\n';
17857               if (!loose) { loose = next; }
17858             }
17859     
17860             this.tokens.push({
17861               type: loose
17862                 ? 'loose_item_start'
17863                 : 'list_item_start'
17864             });
17865     
17866             // Recurse.
17867             this.token(item, false, bq);
17868     
17869             this.tokens.push({
17870               type: 'list_item_end'
17871             });
17872           }
17873     
17874           this.tokens.push({
17875             type: 'list_end'
17876           });
17877     
17878           continue;
17879         }
17880     
17881         // html
17882         if (cap = this.rules.html.exec(src)) {
17883           src = src.substring(cap[0].length);
17884           this.tokens.push({
17885             type: this.options.sanitize
17886               ? 'paragraph'
17887               : 'html',
17888             pre: !this.options.sanitizer
17889               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17890             text: cap[0]
17891           });
17892           continue;
17893         }
17894     
17895         // def
17896         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17897           src = src.substring(cap[0].length);
17898           this.tokens.links[cap[1].toLowerCase()] = {
17899             href: cap[2],
17900             title: cap[3]
17901           };
17902           continue;
17903         }
17904     
17905         // table (gfm)
17906         if (top && (cap = this.rules.table.exec(src))) {
17907           src = src.substring(cap[0].length);
17908     
17909           item = {
17910             type: 'table',
17911             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17912             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17913             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17914           };
17915     
17916           for (i = 0; i < item.align.length; i++) {
17917             if (/^ *-+: *$/.test(item.align[i])) {
17918               item.align[i] = 'right';
17919             } else if (/^ *:-+: *$/.test(item.align[i])) {
17920               item.align[i] = 'center';
17921             } else if (/^ *:-+ *$/.test(item.align[i])) {
17922               item.align[i] = 'left';
17923             } else {
17924               item.align[i] = null;
17925             }
17926           }
17927     
17928           for (i = 0; i < item.cells.length; i++) {
17929             item.cells[i] = item.cells[i]
17930               .replace(/^ *\| *| *\| *$/g, '')
17931               .split(/ *\| */);
17932           }
17933     
17934           this.tokens.push(item);
17935     
17936           continue;
17937         }
17938     
17939         // top-level paragraph
17940         if (top && (cap = this.rules.paragraph.exec(src))) {
17941           src = src.substring(cap[0].length);
17942           this.tokens.push({
17943             type: 'paragraph',
17944             text: cap[1].charAt(cap[1].length - 1) === '\n'
17945               ? cap[1].slice(0, -1)
17946               : cap[1]
17947           });
17948           continue;
17949         }
17950     
17951         // text
17952         if (cap = this.rules.text.exec(src)) {
17953           // Top-level should never reach here.
17954           src = src.substring(cap[0].length);
17955           this.tokens.push({
17956             type: 'text',
17957             text: cap[0]
17958           });
17959           continue;
17960         }
17961     
17962         if (src) {
17963           throw new
17964             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17965         }
17966       }
17967     
17968       return this.tokens;
17969     };
17970     
17971     /**
17972      * Inline-Level Grammar
17973      */
17974     
17975     var inline = {
17976       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17977       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17978       url: noop,
17979       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17980       link: /^!?\[(inside)\]\(href\)/,
17981       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17982       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17983       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17984       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17985       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17986       br: /^ {2,}\n(?!\s*$)/,
17987       del: noop,
17988       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17989     };
17990     
17991     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17992     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17993     
17994     inline.link = replace(inline.link)
17995       ('inside', inline._inside)
17996       ('href', inline._href)
17997       ();
17998     
17999     inline.reflink = replace(inline.reflink)
18000       ('inside', inline._inside)
18001       ();
18002     
18003     /**
18004      * Normal Inline Grammar
18005      */
18006     
18007     inline.normal = merge({}, inline);
18008     
18009     /**
18010      * Pedantic Inline Grammar
18011      */
18012     
18013     inline.pedantic = merge({}, inline.normal, {
18014       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18015       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18016     });
18017     
18018     /**
18019      * GFM Inline Grammar
18020      */
18021     
18022     inline.gfm = merge({}, inline.normal, {
18023       escape: replace(inline.escape)('])', '~|])')(),
18024       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18025       del: /^~~(?=\S)([\s\S]*?\S)~~/,
18026       text: replace(inline.text)
18027         (']|', '~]|')
18028         ('|', '|https?://|')
18029         ()
18030     });
18031     
18032     /**
18033      * GFM + Line Breaks Inline Grammar
18034      */
18035     
18036     inline.breaks = merge({}, inline.gfm, {
18037       br: replace(inline.br)('{2,}', '*')(),
18038       text: replace(inline.gfm.text)('{2,}', '*')()
18039     });
18040     
18041     /**
18042      * Inline Lexer & Compiler
18043      */
18044     
18045     var InlineLexer  = function (links, options) {
18046       this.options = options || marked.defaults;
18047       this.links = links;
18048       this.rules = inline.normal;
18049       this.renderer = this.options.renderer || new Renderer;
18050       this.renderer.options = this.options;
18051     
18052       if (!this.links) {
18053         throw new
18054           Error('Tokens array requires a `links` property.');
18055       }
18056     
18057       if (this.options.gfm) {
18058         if (this.options.breaks) {
18059           this.rules = inline.breaks;
18060         } else {
18061           this.rules = inline.gfm;
18062         }
18063       } else if (this.options.pedantic) {
18064         this.rules = inline.pedantic;
18065       }
18066     }
18067     
18068     /**
18069      * Expose Inline Rules
18070      */
18071     
18072     InlineLexer.rules = inline;
18073     
18074     /**
18075      * Static Lexing/Compiling Method
18076      */
18077     
18078     InlineLexer.output = function(src, links, options) {
18079       var inline = new InlineLexer(links, options);
18080       return inline.output(src);
18081     };
18082     
18083     /**
18084      * Lexing/Compiling
18085      */
18086     
18087     InlineLexer.prototype.output = function(src) {
18088       var out = ''
18089         , link
18090         , text
18091         , href
18092         , cap;
18093     
18094       while (src) {
18095         // escape
18096         if (cap = this.rules.escape.exec(src)) {
18097           src = src.substring(cap[0].length);
18098           out += cap[1];
18099           continue;
18100         }
18101     
18102         // autolink
18103         if (cap = this.rules.autolink.exec(src)) {
18104           src = src.substring(cap[0].length);
18105           if (cap[2] === '@') {
18106             text = cap[1].charAt(6) === ':'
18107               ? this.mangle(cap[1].substring(7))
18108               : this.mangle(cap[1]);
18109             href = this.mangle('mailto:') + text;
18110           } else {
18111             text = escape(cap[1]);
18112             href = text;
18113           }
18114           out += this.renderer.link(href, null, text);
18115           continue;
18116         }
18117     
18118         // url (gfm)
18119         if (!this.inLink && (cap = this.rules.url.exec(src))) {
18120           src = src.substring(cap[0].length);
18121           text = escape(cap[1]);
18122           href = text;
18123           out += this.renderer.link(href, null, text);
18124           continue;
18125         }
18126     
18127         // tag
18128         if (cap = this.rules.tag.exec(src)) {
18129           if (!this.inLink && /^<a /i.test(cap[0])) {
18130             this.inLink = true;
18131           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
18132             this.inLink = false;
18133           }
18134           src = src.substring(cap[0].length);
18135           out += this.options.sanitize
18136             ? this.options.sanitizer
18137               ? this.options.sanitizer(cap[0])
18138               : escape(cap[0])
18139             : cap[0];
18140           continue;
18141         }
18142     
18143         // link
18144         if (cap = this.rules.link.exec(src)) {
18145           src = src.substring(cap[0].length);
18146           this.inLink = true;
18147           out += this.outputLink(cap, {
18148             href: cap[2],
18149             title: cap[3]
18150           });
18151           this.inLink = false;
18152           continue;
18153         }
18154     
18155         // reflink, nolink
18156         if ((cap = this.rules.reflink.exec(src))
18157             || (cap = this.rules.nolink.exec(src))) {
18158           src = src.substring(cap[0].length);
18159           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18160           link = this.links[link.toLowerCase()];
18161           if (!link || !link.href) {
18162             out += cap[0].charAt(0);
18163             src = cap[0].substring(1) + src;
18164             continue;
18165           }
18166           this.inLink = true;
18167           out += this.outputLink(cap, link);
18168           this.inLink = false;
18169           continue;
18170         }
18171     
18172         // strong
18173         if (cap = this.rules.strong.exec(src)) {
18174           src = src.substring(cap[0].length);
18175           out += this.renderer.strong(this.output(cap[2] || cap[1]));
18176           continue;
18177         }
18178     
18179         // em
18180         if (cap = this.rules.em.exec(src)) {
18181           src = src.substring(cap[0].length);
18182           out += this.renderer.em(this.output(cap[2] || cap[1]));
18183           continue;
18184         }
18185     
18186         // code
18187         if (cap = this.rules.code.exec(src)) {
18188           src = src.substring(cap[0].length);
18189           out += this.renderer.codespan(escape(cap[2], true));
18190           continue;
18191         }
18192     
18193         // br
18194         if (cap = this.rules.br.exec(src)) {
18195           src = src.substring(cap[0].length);
18196           out += this.renderer.br();
18197           continue;
18198         }
18199     
18200         // del (gfm)
18201         if (cap = this.rules.del.exec(src)) {
18202           src = src.substring(cap[0].length);
18203           out += this.renderer.del(this.output(cap[1]));
18204           continue;
18205         }
18206     
18207         // text
18208         if (cap = this.rules.text.exec(src)) {
18209           src = src.substring(cap[0].length);
18210           out += this.renderer.text(escape(this.smartypants(cap[0])));
18211           continue;
18212         }
18213     
18214         if (src) {
18215           throw new
18216             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18217         }
18218       }
18219     
18220       return out;
18221     };
18222     
18223     /**
18224      * Compile Link
18225      */
18226     
18227     InlineLexer.prototype.outputLink = function(cap, link) {
18228       var href = escape(link.href)
18229         , title = link.title ? escape(link.title) : null;
18230     
18231       return cap[0].charAt(0) !== '!'
18232         ? this.renderer.link(href, title, this.output(cap[1]))
18233         : this.renderer.image(href, title, escape(cap[1]));
18234     };
18235     
18236     /**
18237      * Smartypants Transformations
18238      */
18239     
18240     InlineLexer.prototype.smartypants = function(text) {
18241       if (!this.options.smartypants)  { return text; }
18242       return text
18243         // em-dashes
18244         .replace(/---/g, '\u2014')
18245         // en-dashes
18246         .replace(/--/g, '\u2013')
18247         // opening singles
18248         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
18249         // closing singles & apostrophes
18250         .replace(/'/g, '\u2019')
18251         // opening doubles
18252         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
18253         // closing doubles
18254         .replace(/"/g, '\u201d')
18255         // ellipses
18256         .replace(/\.{3}/g, '\u2026');
18257     };
18258     
18259     /**
18260      * Mangle Links
18261      */
18262     
18263     InlineLexer.prototype.mangle = function(text) {
18264       if (!this.options.mangle) { return text; }
18265       var out = ''
18266         , l = text.length
18267         , i = 0
18268         , ch;
18269     
18270       for (; i < l; i++) {
18271         ch = text.charCodeAt(i);
18272         if (Math.random() > 0.5) {
18273           ch = 'x' + ch.toString(16);
18274         }
18275         out += '&#' + ch + ';';
18276       }
18277     
18278       return out;
18279     };
18280     
18281     /**
18282      * Renderer
18283      */
18284     
18285      /**
18286          * eval:var:Renderer
18287     */
18288     
18289     var Renderer   = function (options) {
18290       this.options = options || {};
18291     }
18292     
18293     Renderer.prototype.code = function(code, lang, escaped) {
18294       if (this.options.highlight) {
18295         var out = this.options.highlight(code, lang);
18296         if (out != null && out !== code) {
18297           escaped = true;
18298           code = out;
18299         }
18300       } else {
18301             // hack!!! - it's already escapeD?
18302             escaped = true;
18303       }
18304     
18305       if (!lang) {
18306         return '<pre><code>'
18307           + (escaped ? code : escape(code, true))
18308           + '\n</code></pre>';
18309       }
18310     
18311       return '<pre><code class="'
18312         + this.options.langPrefix
18313         + escape(lang, true)
18314         + '">'
18315         + (escaped ? code : escape(code, true))
18316         + '\n</code></pre>\n';
18317     };
18318     
18319     Renderer.prototype.blockquote = function(quote) {
18320       return '<blockquote>\n' + quote + '</blockquote>\n';
18321     };
18322     
18323     Renderer.prototype.html = function(html) {
18324       return html;
18325     };
18326     
18327     Renderer.prototype.heading = function(text, level, raw) {
18328       return '<h'
18329         + level
18330         + ' id="'
18331         + this.options.headerPrefix
18332         + raw.toLowerCase().replace(/[^\w]+/g, '-')
18333         + '">'
18334         + text
18335         + '</h'
18336         + level
18337         + '>\n';
18338     };
18339     
18340     Renderer.prototype.hr = function() {
18341       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
18342     };
18343     
18344     Renderer.prototype.list = function(body, ordered) {
18345       var type = ordered ? 'ol' : 'ul';
18346       return '<' + type + '>\n' + body + '</' + type + '>\n';
18347     };
18348     
18349     Renderer.prototype.listitem = function(text) {
18350       return '<li>' + text + '</li>\n';
18351     };
18352     
18353     Renderer.prototype.paragraph = function(text) {
18354       return '<p>' + text + '</p>\n';
18355     };
18356     
18357     Renderer.prototype.table = function(header, body) {
18358       return '<table class="table table-striped">\n'
18359         + '<thead>\n'
18360         + header
18361         + '</thead>\n'
18362         + '<tbody>\n'
18363         + body
18364         + '</tbody>\n'
18365         + '</table>\n';
18366     };
18367     
18368     Renderer.prototype.tablerow = function(content) {
18369       return '<tr>\n' + content + '</tr>\n';
18370     };
18371     
18372     Renderer.prototype.tablecell = function(content, flags) {
18373       var type = flags.header ? 'th' : 'td';
18374       var tag = flags.align
18375         ? '<' + type + ' style="text-align:' + flags.align + '">'
18376         : '<' + type + '>';
18377       return tag + content + '</' + type + '>\n';
18378     };
18379     
18380     // span level renderer
18381     Renderer.prototype.strong = function(text) {
18382       return '<strong>' + text + '</strong>';
18383     };
18384     
18385     Renderer.prototype.em = function(text) {
18386       return '<em>' + text + '</em>';
18387     };
18388     
18389     Renderer.prototype.codespan = function(text) {
18390       return '<code>' + text + '</code>';
18391     };
18392     
18393     Renderer.prototype.br = function() {
18394       return this.options.xhtml ? '<br/>' : '<br>';
18395     };
18396     
18397     Renderer.prototype.del = function(text) {
18398       return '<del>' + text + '</del>';
18399     };
18400     
18401     Renderer.prototype.link = function(href, title, text) {
18402       if (this.options.sanitize) {
18403         try {
18404           var prot = decodeURIComponent(unescape(href))
18405             .replace(/[^\w:]/g, '')
18406             .toLowerCase();
18407         } catch (e) {
18408           return '';
18409         }
18410         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
18411           return '';
18412         }
18413       }
18414       var out = '<a href="' + href + '"';
18415       if (title) {
18416         out += ' title="' + title + '"';
18417       }
18418       out += '>' + text + '</a>';
18419       return out;
18420     };
18421     
18422     Renderer.prototype.image = function(href, title, text) {
18423       var out = '<img src="' + href + '" alt="' + text + '"';
18424       if (title) {
18425         out += ' title="' + title + '"';
18426       }
18427       out += this.options.xhtml ? '/>' : '>';
18428       return out;
18429     };
18430     
18431     Renderer.prototype.text = function(text) {
18432       return text;
18433     };
18434     
18435     /**
18436      * Parsing & Compiling
18437      */
18438          /**
18439          * eval:var:Parser
18440     */
18441     
18442     var Parser= function (options) {
18443       this.tokens = [];
18444       this.token = null;
18445       this.options = options || marked.defaults;
18446       this.options.renderer = this.options.renderer || new Renderer;
18447       this.renderer = this.options.renderer;
18448       this.renderer.options = this.options;
18449     }
18450     
18451     /**
18452      * Static Parse Method
18453      */
18454     
18455     Parser.parse = function(src, options, renderer) {
18456       var parser = new Parser(options, renderer);
18457       return parser.parse(src);
18458     };
18459     
18460     /**
18461      * Parse Loop
18462      */
18463     
18464     Parser.prototype.parse = function(src) {
18465       this.inline = new InlineLexer(src.links, this.options, this.renderer);
18466       this.tokens = src.reverse();
18467     
18468       var out = '';
18469       while (this.next()) {
18470         out += this.tok();
18471       }
18472     
18473       return out;
18474     };
18475     
18476     /**
18477      * Next Token
18478      */
18479     
18480     Parser.prototype.next = function() {
18481       return this.token = this.tokens.pop();
18482     };
18483     
18484     /**
18485      * Preview Next Token
18486      */
18487     
18488     Parser.prototype.peek = function() {
18489       return this.tokens[this.tokens.length - 1] || 0;
18490     };
18491     
18492     /**
18493      * Parse Text Tokens
18494      */
18495     
18496     Parser.prototype.parseText = function() {
18497       var body = this.token.text;
18498     
18499       while (this.peek().type === 'text') {
18500         body += '\n' + this.next().text;
18501       }
18502     
18503       return this.inline.output(body);
18504     };
18505     
18506     /**
18507      * Parse Current Token
18508      */
18509     
18510     Parser.prototype.tok = function() {
18511       switch (this.token.type) {
18512         case 'space': {
18513           return '';
18514         }
18515         case 'hr': {
18516           return this.renderer.hr();
18517         }
18518         case 'heading': {
18519           return this.renderer.heading(
18520             this.inline.output(this.token.text),
18521             this.token.depth,
18522             this.token.text);
18523         }
18524         case 'code': {
18525           return this.renderer.code(this.token.text,
18526             this.token.lang,
18527             this.token.escaped);
18528         }
18529         case 'table': {
18530           var header = ''
18531             , body = ''
18532             , i
18533             , row
18534             , cell
18535             , flags
18536             , j;
18537     
18538           // header
18539           cell = '';
18540           for (i = 0; i < this.token.header.length; i++) {
18541             flags = { header: true, align: this.token.align[i] };
18542             cell += this.renderer.tablecell(
18543               this.inline.output(this.token.header[i]),
18544               { header: true, align: this.token.align[i] }
18545             );
18546           }
18547           header += this.renderer.tablerow(cell);
18548     
18549           for (i = 0; i < this.token.cells.length; i++) {
18550             row = this.token.cells[i];
18551     
18552             cell = '';
18553             for (j = 0; j < row.length; j++) {
18554               cell += this.renderer.tablecell(
18555                 this.inline.output(row[j]),
18556                 { header: false, align: this.token.align[j] }
18557               );
18558             }
18559     
18560             body += this.renderer.tablerow(cell);
18561           }
18562           return this.renderer.table(header, body);
18563         }
18564         case 'blockquote_start': {
18565           var body = '';
18566     
18567           while (this.next().type !== 'blockquote_end') {
18568             body += this.tok();
18569           }
18570     
18571           return this.renderer.blockquote(body);
18572         }
18573         case 'list_start': {
18574           var body = ''
18575             , ordered = this.token.ordered;
18576     
18577           while (this.next().type !== 'list_end') {
18578             body += this.tok();
18579           }
18580     
18581           return this.renderer.list(body, ordered);
18582         }
18583         case 'list_item_start': {
18584           var body = '';
18585     
18586           while (this.next().type !== 'list_item_end') {
18587             body += this.token.type === 'text'
18588               ? this.parseText()
18589               : this.tok();
18590           }
18591     
18592           return this.renderer.listitem(body);
18593         }
18594         case 'loose_item_start': {
18595           var body = '';
18596     
18597           while (this.next().type !== 'list_item_end') {
18598             body += this.tok();
18599           }
18600     
18601           return this.renderer.listitem(body);
18602         }
18603         case 'html': {
18604           var html = !this.token.pre && !this.options.pedantic
18605             ? this.inline.output(this.token.text)
18606             : this.token.text;
18607           return this.renderer.html(html);
18608         }
18609         case 'paragraph': {
18610           return this.renderer.paragraph(this.inline.output(this.token.text));
18611         }
18612         case 'text': {
18613           return this.renderer.paragraph(this.parseText());
18614         }
18615       }
18616     };
18617   
18618     
18619     /**
18620      * Marked
18621      */
18622          /**
18623          * eval:var:marked
18624     */
18625     var marked = function (src, opt, callback) {
18626       if (callback || typeof opt === 'function') {
18627         if (!callback) {
18628           callback = opt;
18629           opt = null;
18630         }
18631     
18632         opt = merge({}, marked.defaults, opt || {});
18633     
18634         var highlight = opt.highlight
18635           , tokens
18636           , pending
18637           , i = 0;
18638     
18639         try {
18640           tokens = Lexer.lex(src, opt)
18641         } catch (e) {
18642           return callback(e);
18643         }
18644     
18645         pending = tokens.length;
18646          /**
18647          * eval:var:done
18648     */
18649         var done = function(err) {
18650           if (err) {
18651             opt.highlight = highlight;
18652             return callback(err);
18653           }
18654     
18655           var out;
18656     
18657           try {
18658             out = Parser.parse(tokens, opt);
18659           } catch (e) {
18660             err = e;
18661           }
18662     
18663           opt.highlight = highlight;
18664     
18665           return err
18666             ? callback(err)
18667             : callback(null, out);
18668         };
18669     
18670         if (!highlight || highlight.length < 3) {
18671           return done();
18672         }
18673     
18674         delete opt.highlight;
18675     
18676         if (!pending) { return done(); }
18677     
18678         for (; i < tokens.length; i++) {
18679           (function(token) {
18680             if (token.type !== 'code') {
18681               return --pending || done();
18682             }
18683             return highlight(token.text, token.lang, function(err, code) {
18684               if (err) { return done(err); }
18685               if (code == null || code === token.text) {
18686                 return --pending || done();
18687               }
18688               token.text = code;
18689               token.escaped = true;
18690               --pending || done();
18691             });
18692           })(tokens[i]);
18693         }
18694     
18695         return;
18696       }
18697       try {
18698         if (opt) { opt = merge({}, marked.defaults, opt); }
18699         return Parser.parse(Lexer.lex(src, opt), opt);
18700       } catch (e) {
18701         e.message += '\nPlease report this to https://github.com/chjj/marked.';
18702         if ((opt || marked.defaults).silent) {
18703           return '<p>An error occured:</p><pre>'
18704             + escape(e.message + '', true)
18705             + '</pre>';
18706         }
18707         throw e;
18708       }
18709     }
18710     
18711     /**
18712      * Options
18713      */
18714     
18715     marked.options =
18716     marked.setOptions = function(opt) {
18717       merge(marked.defaults, opt);
18718       return marked;
18719     };
18720     
18721     marked.defaults = {
18722       gfm: true,
18723       tables: true,
18724       breaks: false,
18725       pedantic: false,
18726       sanitize: false,
18727       sanitizer: null,
18728       mangle: true,
18729       smartLists: false,
18730       silent: false,
18731       highlight: null,
18732       langPrefix: 'lang-',
18733       smartypants: false,
18734       headerPrefix: '',
18735       renderer: new Renderer,
18736       xhtml: false
18737     };
18738     
18739     /**
18740      * Expose
18741      */
18742     
18743     marked.Parser = Parser;
18744     marked.parser = Parser.parse;
18745     
18746     marked.Renderer = Renderer;
18747     
18748     marked.Lexer = Lexer;
18749     marked.lexer = Lexer.lex;
18750     
18751     marked.InlineLexer = InlineLexer;
18752     marked.inlineLexer = InlineLexer.output;
18753     
18754     marked.parse = marked;
18755     
18756     Roo.Markdown.marked = marked;
18757
18758 })();/*
18759  * Based on:
18760  * Ext JS Library 1.1.1
18761  * Copyright(c) 2006-2007, Ext JS, LLC.
18762  *
18763  * Originally Released Under LGPL - original licence link has changed is not relivant.
18764  *
18765  * Fork - LGPL
18766  * <script type="text/javascript">
18767  */
18768
18769
18770
18771 /*
18772  * These classes are derivatives of the similarly named classes in the YUI Library.
18773  * The original license:
18774  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18775  * Code licensed under the BSD License:
18776  * http://developer.yahoo.net/yui/license.txt
18777  */
18778
18779 (function() {
18780
18781 var Event=Roo.EventManager;
18782 var Dom=Roo.lib.Dom;
18783
18784 /**
18785  * @class Roo.dd.DragDrop
18786  * @extends Roo.util.Observable
18787  * Defines the interface and base operation of items that that can be
18788  * dragged or can be drop targets.  It was designed to be extended, overriding
18789  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18790  * Up to three html elements can be associated with a DragDrop instance:
18791  * <ul>
18792  * <li>linked element: the element that is passed into the constructor.
18793  * This is the element which defines the boundaries for interaction with
18794  * other DragDrop objects.</li>
18795  * <li>handle element(s): The drag operation only occurs if the element that
18796  * was clicked matches a handle element.  By default this is the linked
18797  * element, but there are times that you will want only a portion of the
18798  * linked element to initiate the drag operation, and the setHandleElId()
18799  * method provides a way to define this.</li>
18800  * <li>drag element: this represents the element that would be moved along
18801  * with the cursor during a drag operation.  By default, this is the linked
18802  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18803  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18804  * </li>
18805  * </ul>
18806  * This class should not be instantiated until the onload event to ensure that
18807  * the associated elements are available.
18808  * The following would define a DragDrop obj that would interact with any
18809  * other DragDrop obj in the "group1" group:
18810  * <pre>
18811  *  dd = new Roo.dd.DragDrop("div1", "group1");
18812  * </pre>
18813  * Since none of the event handlers have been implemented, nothing would
18814  * actually happen if you were to run the code above.  Normally you would
18815  * override this class or one of the default implementations, but you can
18816  * also override the methods you want on an instance of the class...
18817  * <pre>
18818  *  dd.onDragDrop = function(e, id) {
18819  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18820  *  }
18821  * </pre>
18822  * @constructor
18823  * @param {String} id of the element that is linked to this instance
18824  * @param {String} sGroup the group of related DragDrop objects
18825  * @param {object} config an object containing configurable attributes
18826  *                Valid properties for DragDrop:
18827  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18828  */
18829 Roo.dd.DragDrop = function(id, sGroup, config) {
18830     if (id) {
18831         this.init(id, sGroup, config);
18832     }
18833     
18834 };
18835
18836 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18837
18838     /**
18839      * The id of the element associated with this object.  This is what we
18840      * refer to as the "linked element" because the size and position of
18841      * this element is used to determine when the drag and drop objects have
18842      * interacted.
18843      * @property id
18844      * @type String
18845      */
18846     id: null,
18847
18848     /**
18849      * Configuration attributes passed into the constructor
18850      * @property config
18851      * @type object
18852      */
18853     config: null,
18854
18855     /**
18856      * The id of the element that will be dragged.  By default this is same
18857      * as the linked element , but could be changed to another element. Ex:
18858      * Roo.dd.DDProxy
18859      * @property dragElId
18860      * @type String
18861      * @private
18862      */
18863     dragElId: null,
18864
18865     /**
18866      * the id of the element that initiates the drag operation.  By default
18867      * this is the linked element, but could be changed to be a child of this
18868      * element.  This lets us do things like only starting the drag when the
18869      * header element within the linked html element is clicked.
18870      * @property handleElId
18871      * @type String
18872      * @private
18873      */
18874     handleElId: null,
18875
18876     /**
18877      * An associative array of HTML tags that will be ignored if clicked.
18878      * @property invalidHandleTypes
18879      * @type {string: string}
18880      */
18881     invalidHandleTypes: null,
18882
18883     /**
18884      * An associative array of ids for elements that will be ignored if clicked
18885      * @property invalidHandleIds
18886      * @type {string: string}
18887      */
18888     invalidHandleIds: null,
18889
18890     /**
18891      * An indexted array of css class names for elements that will be ignored
18892      * if clicked.
18893      * @property invalidHandleClasses
18894      * @type string[]
18895      */
18896     invalidHandleClasses: null,
18897
18898     /**
18899      * The linked element's absolute X position at the time the drag was
18900      * started
18901      * @property startPageX
18902      * @type int
18903      * @private
18904      */
18905     startPageX: 0,
18906
18907     /**
18908      * The linked element's absolute X position at the time the drag was
18909      * started
18910      * @property startPageY
18911      * @type int
18912      * @private
18913      */
18914     startPageY: 0,
18915
18916     /**
18917      * The group defines a logical collection of DragDrop objects that are
18918      * related.  Instances only get events when interacting with other
18919      * DragDrop object in the same group.  This lets us define multiple
18920      * groups using a single DragDrop subclass if we want.
18921      * @property groups
18922      * @type {string: string}
18923      */
18924     groups: null,
18925
18926     /**
18927      * Individual drag/drop instances can be locked.  This will prevent
18928      * onmousedown start drag.
18929      * @property locked
18930      * @type boolean
18931      * @private
18932      */
18933     locked: false,
18934
18935     /**
18936      * Lock this instance
18937      * @method lock
18938      */
18939     lock: function() { this.locked = true; },
18940
18941     /**
18942      * Unlock this instace
18943      * @method unlock
18944      */
18945     unlock: function() { this.locked = false; },
18946
18947     /**
18948      * By default, all insances can be a drop target.  This can be disabled by
18949      * setting isTarget to false.
18950      * @method isTarget
18951      * @type boolean
18952      */
18953     isTarget: true,
18954
18955     /**
18956      * The padding configured for this drag and drop object for calculating
18957      * the drop zone intersection with this object.
18958      * @method padding
18959      * @type int[]
18960      */
18961     padding: null,
18962
18963     /**
18964      * Cached reference to the linked element
18965      * @property _domRef
18966      * @private
18967      */
18968     _domRef: null,
18969
18970     /**
18971      * Internal typeof flag
18972      * @property __ygDragDrop
18973      * @private
18974      */
18975     __ygDragDrop: true,
18976
18977     /**
18978      * Set to true when horizontal contraints are applied
18979      * @property constrainX
18980      * @type boolean
18981      * @private
18982      */
18983     constrainX: false,
18984
18985     /**
18986      * Set to true when vertical contraints are applied
18987      * @property constrainY
18988      * @type boolean
18989      * @private
18990      */
18991     constrainY: false,
18992
18993     /**
18994      * The left constraint
18995      * @property minX
18996      * @type int
18997      * @private
18998      */
18999     minX: 0,
19000
19001     /**
19002      * The right constraint
19003      * @property maxX
19004      * @type int
19005      * @private
19006      */
19007     maxX: 0,
19008
19009     /**
19010      * The up constraint
19011      * @property minY
19012      * @type int
19013      * @type int
19014      * @private
19015      */
19016     minY: 0,
19017
19018     /**
19019      * The down constraint
19020      * @property maxY
19021      * @type int
19022      * @private
19023      */
19024     maxY: 0,
19025
19026     /**
19027      * Maintain offsets when we resetconstraints.  Set to true when you want
19028      * the position of the element relative to its parent to stay the same
19029      * when the page changes
19030      *
19031      * @property maintainOffset
19032      * @type boolean
19033      */
19034     maintainOffset: false,
19035
19036     /**
19037      * Array of pixel locations the element will snap to if we specified a
19038      * horizontal graduation/interval.  This array is generated automatically
19039      * when you define a tick interval.
19040      * @property xTicks
19041      * @type int[]
19042      */
19043     xTicks: null,
19044
19045     /**
19046      * Array of pixel locations the element will snap to if we specified a
19047      * vertical graduation/interval.  This array is generated automatically
19048      * when you define a tick interval.
19049      * @property yTicks
19050      * @type int[]
19051      */
19052     yTicks: null,
19053
19054     /**
19055      * By default the drag and drop instance will only respond to the primary
19056      * button click (left button for a right-handed mouse).  Set to true to
19057      * allow drag and drop to start with any mouse click that is propogated
19058      * by the browser
19059      * @property primaryButtonOnly
19060      * @type boolean
19061      */
19062     primaryButtonOnly: true,
19063
19064     /**
19065      * The availabe property is false until the linked dom element is accessible.
19066      * @property available
19067      * @type boolean
19068      */
19069     available: false,
19070
19071     /**
19072      * By default, drags can only be initiated if the mousedown occurs in the
19073      * region the linked element is.  This is done in part to work around a
19074      * bug in some browsers that mis-report the mousedown if the previous
19075      * mouseup happened outside of the window.  This property is set to true
19076      * if outer handles are defined.
19077      *
19078      * @property hasOuterHandles
19079      * @type boolean
19080      * @default false
19081      */
19082     hasOuterHandles: false,
19083
19084     /**
19085      * Code that executes immediately before the startDrag event
19086      * @method b4StartDrag
19087      * @private
19088      */
19089     b4StartDrag: function(x, y) { },
19090
19091     /**
19092      * Abstract method called after a drag/drop object is clicked
19093      * and the drag or mousedown time thresholds have beeen met.
19094      * @method startDrag
19095      * @param {int} X click location
19096      * @param {int} Y click location
19097      */
19098     startDrag: function(x, y) { /* override this */ },
19099
19100     /**
19101      * Code that executes immediately before the onDrag event
19102      * @method b4Drag
19103      * @private
19104      */
19105     b4Drag: function(e) { },
19106
19107     /**
19108      * Abstract method called during the onMouseMove event while dragging an
19109      * object.
19110      * @method onDrag
19111      * @param {Event} e the mousemove event
19112      */
19113     onDrag: function(e) { /* override this */ },
19114
19115     /**
19116      * Abstract method called when this element fist begins hovering over
19117      * another DragDrop obj
19118      * @method onDragEnter
19119      * @param {Event} e the mousemove event
19120      * @param {String|DragDrop[]} id In POINT mode, the element
19121      * id this is hovering over.  In INTERSECT mode, an array of one or more
19122      * dragdrop items being hovered over.
19123      */
19124     onDragEnter: function(e, id) { /* override this */ },
19125
19126     /**
19127      * Code that executes immediately before the onDragOver event
19128      * @method b4DragOver
19129      * @private
19130      */
19131     b4DragOver: function(e) { },
19132
19133     /**
19134      * Abstract method called when this element is hovering over another
19135      * DragDrop obj
19136      * @method onDragOver
19137      * @param {Event} e the mousemove event
19138      * @param {String|DragDrop[]} id In POINT mode, the element
19139      * id this is hovering over.  In INTERSECT mode, an array of dd items
19140      * being hovered over.
19141      */
19142     onDragOver: function(e, id) { /* override this */ },
19143
19144     /**
19145      * Code that executes immediately before the onDragOut event
19146      * @method b4DragOut
19147      * @private
19148      */
19149     b4DragOut: function(e) { },
19150
19151     /**
19152      * Abstract method called when we are no longer hovering over an element
19153      * @method onDragOut
19154      * @param {Event} e the mousemove event
19155      * @param {String|DragDrop[]} id In POINT mode, the element
19156      * id this was hovering over.  In INTERSECT mode, an array of dd items
19157      * that the mouse is no longer over.
19158      */
19159     onDragOut: function(e, id) { /* override this */ },
19160
19161     /**
19162      * Code that executes immediately before the onDragDrop event
19163      * @method b4DragDrop
19164      * @private
19165      */
19166     b4DragDrop: function(e) { },
19167
19168     /**
19169      * Abstract method called when this item is dropped on another DragDrop
19170      * obj
19171      * @method onDragDrop
19172      * @param {Event} e the mouseup event
19173      * @param {String|DragDrop[]} id In POINT mode, the element
19174      * id this was dropped on.  In INTERSECT mode, an array of dd items this
19175      * was dropped on.
19176      */
19177     onDragDrop: function(e, id) { /* override this */ },
19178
19179     /**
19180      * Abstract method called when this item is dropped on an area with no
19181      * drop target
19182      * @method onInvalidDrop
19183      * @param {Event} e the mouseup event
19184      */
19185     onInvalidDrop: function(e) { /* override this */ },
19186
19187     /**
19188      * Code that executes immediately before the endDrag event
19189      * @method b4EndDrag
19190      * @private
19191      */
19192     b4EndDrag: function(e) { },
19193
19194     /**
19195      * Fired when we are done dragging the object
19196      * @method endDrag
19197      * @param {Event} e the mouseup event
19198      */
19199     endDrag: function(e) { /* override this */ },
19200
19201     /**
19202      * Code executed immediately before the onMouseDown event
19203      * @method b4MouseDown
19204      * @param {Event} e the mousedown event
19205      * @private
19206      */
19207     b4MouseDown: function(e) {  },
19208
19209     /**
19210      * Event handler that fires when a drag/drop obj gets a mousedown
19211      * @method onMouseDown
19212      * @param {Event} e the mousedown event
19213      */
19214     onMouseDown: function(e) { /* override this */ },
19215
19216     /**
19217      * Event handler that fires when a drag/drop obj gets a mouseup
19218      * @method onMouseUp
19219      * @param {Event} e the mouseup event
19220      */
19221     onMouseUp: function(e) { /* override this */ },
19222
19223     /**
19224      * Override the onAvailable method to do what is needed after the initial
19225      * position was determined.
19226      * @method onAvailable
19227      */
19228     onAvailable: function () {
19229     },
19230
19231     /*
19232      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
19233      * @type Object
19234      */
19235     defaultPadding : {left:0, right:0, top:0, bottom:0},
19236
19237     /*
19238      * Initializes the drag drop object's constraints to restrict movement to a certain element.
19239  *
19240  * Usage:
19241  <pre><code>
19242  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
19243                 { dragElId: "existingProxyDiv" });
19244  dd.startDrag = function(){
19245      this.constrainTo("parent-id");
19246  };
19247  </code></pre>
19248  * Or you can initalize it using the {@link Roo.Element} object:
19249  <pre><code>
19250  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
19251      startDrag : function(){
19252          this.constrainTo("parent-id");
19253      }
19254  });
19255  </code></pre>
19256      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
19257      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
19258      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
19259      * an object containing the sides to pad. For example: {right:10, bottom:10}
19260      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
19261      */
19262     constrainTo : function(constrainTo, pad, inContent){
19263         if(typeof pad == "number"){
19264             pad = {left: pad, right:pad, top:pad, bottom:pad};
19265         }
19266         pad = pad || this.defaultPadding;
19267         var b = Roo.get(this.getEl()).getBox();
19268         var ce = Roo.get(constrainTo);
19269         var s = ce.getScroll();
19270         var c, cd = ce.dom;
19271         if(cd == document.body){
19272             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
19273         }else{
19274             xy = ce.getXY();
19275             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
19276         }
19277
19278
19279         var topSpace = b.y - c.y;
19280         var leftSpace = b.x - c.x;
19281
19282         this.resetConstraints();
19283         this.setXConstraint(leftSpace - (pad.left||0), // left
19284                 c.width - leftSpace - b.width - (pad.right||0) //right
19285         );
19286         this.setYConstraint(topSpace - (pad.top||0), //top
19287                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
19288         );
19289     },
19290
19291     /**
19292      * Returns a reference to the linked element
19293      * @method getEl
19294      * @return {HTMLElement} the html element
19295      */
19296     getEl: function() {
19297         if (!this._domRef) {
19298             this._domRef = Roo.getDom(this.id);
19299         }
19300
19301         return this._domRef;
19302     },
19303
19304     /**
19305      * Returns a reference to the actual element to drag.  By default this is
19306      * the same as the html element, but it can be assigned to another
19307      * element. An example of this can be found in Roo.dd.DDProxy
19308      * @method getDragEl
19309      * @return {HTMLElement} the html element
19310      */
19311     getDragEl: function() {
19312         return Roo.getDom(this.dragElId);
19313     },
19314
19315     /**
19316      * Sets up the DragDrop object.  Must be called in the constructor of any
19317      * Roo.dd.DragDrop subclass
19318      * @method init
19319      * @param id the id of the linked element
19320      * @param {String} sGroup the group of related items
19321      * @param {object} config configuration attributes
19322      */
19323     init: function(id, sGroup, config) {
19324         this.initTarget(id, sGroup, config);
19325         if (!Roo.isTouch) {
19326             Event.on(this.id, "mousedown", this.handleMouseDown, this);
19327         }
19328         Event.on(this.id, "touchstart", this.handleMouseDown, this);
19329         // Event.on(this.id, "selectstart", Event.preventDefault);
19330     },
19331
19332     /**
19333      * Initializes Targeting functionality only... the object does not
19334      * get a mousedown handler.
19335      * @method initTarget
19336      * @param id the id of the linked element
19337      * @param {String} sGroup the group of related items
19338      * @param {object} config configuration attributes
19339      */
19340     initTarget: function(id, sGroup, config) {
19341
19342         // configuration attributes
19343         this.config = config || {};
19344
19345         // create a local reference to the drag and drop manager
19346         this.DDM = Roo.dd.DDM;
19347         // initialize the groups array
19348         this.groups = {};
19349
19350         // assume that we have an element reference instead of an id if the
19351         // parameter is not a string
19352         if (typeof id !== "string") {
19353             id = Roo.id(id);
19354         }
19355
19356         // set the id
19357         this.id = id;
19358
19359         // add to an interaction group
19360         this.addToGroup((sGroup) ? sGroup : "default");
19361
19362         // We don't want to register this as the handle with the manager
19363         // so we just set the id rather than calling the setter.
19364         this.handleElId = id;
19365
19366         // the linked element is the element that gets dragged by default
19367         this.setDragElId(id);
19368
19369         // by default, clicked anchors will not start drag operations.
19370         this.invalidHandleTypes = { A: "A" };
19371         this.invalidHandleIds = {};
19372         this.invalidHandleClasses = [];
19373
19374         this.applyConfig();
19375
19376         this.handleOnAvailable();
19377     },
19378
19379     /**
19380      * Applies the configuration parameters that were passed into the constructor.
19381      * This is supposed to happen at each level through the inheritance chain.  So
19382      * a DDProxy implentation will execute apply config on DDProxy, DD, and
19383      * DragDrop in order to get all of the parameters that are available in
19384      * each object.
19385      * @method applyConfig
19386      */
19387     applyConfig: function() {
19388
19389         // configurable properties:
19390         //    padding, isTarget, maintainOffset, primaryButtonOnly
19391         this.padding           = this.config.padding || [0, 0, 0, 0];
19392         this.isTarget          = (this.config.isTarget !== false);
19393         this.maintainOffset    = (this.config.maintainOffset);
19394         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
19395
19396     },
19397
19398     /**
19399      * Executed when the linked element is available
19400      * @method handleOnAvailable
19401      * @private
19402      */
19403     handleOnAvailable: function() {
19404         this.available = true;
19405         this.resetConstraints();
19406         this.onAvailable();
19407     },
19408
19409      /**
19410      * Configures the padding for the target zone in px.  Effectively expands
19411      * (or reduces) the virtual object size for targeting calculations.
19412      * Supports css-style shorthand; if only one parameter is passed, all sides
19413      * will have that padding, and if only two are passed, the top and bottom
19414      * will have the first param, the left and right the second.
19415      * @method setPadding
19416      * @param {int} iTop    Top pad
19417      * @param {int} iRight  Right pad
19418      * @param {int} iBot    Bot pad
19419      * @param {int} iLeft   Left pad
19420      */
19421     setPadding: function(iTop, iRight, iBot, iLeft) {
19422         // this.padding = [iLeft, iRight, iTop, iBot];
19423         if (!iRight && 0 !== iRight) {
19424             this.padding = [iTop, iTop, iTop, iTop];
19425         } else if (!iBot && 0 !== iBot) {
19426             this.padding = [iTop, iRight, iTop, iRight];
19427         } else {
19428             this.padding = [iTop, iRight, iBot, iLeft];
19429         }
19430     },
19431
19432     /**
19433      * Stores the initial placement of the linked element.
19434      * @method setInitialPosition
19435      * @param {int} diffX   the X offset, default 0
19436      * @param {int} diffY   the Y offset, default 0
19437      */
19438     setInitPosition: function(diffX, diffY) {
19439         var el = this.getEl();
19440
19441         if (!this.DDM.verifyEl(el)) {
19442             return;
19443         }
19444
19445         var dx = diffX || 0;
19446         var dy = diffY || 0;
19447
19448         var p = Dom.getXY( el );
19449
19450         this.initPageX = p[0] - dx;
19451         this.initPageY = p[1] - dy;
19452
19453         this.lastPageX = p[0];
19454         this.lastPageY = p[1];
19455
19456
19457         this.setStartPosition(p);
19458     },
19459
19460     /**
19461      * Sets the start position of the element.  This is set when the obj
19462      * is initialized, the reset when a drag is started.
19463      * @method setStartPosition
19464      * @param pos current position (from previous lookup)
19465      * @private
19466      */
19467     setStartPosition: function(pos) {
19468         var p = pos || Dom.getXY( this.getEl() );
19469         this.deltaSetXY = null;
19470
19471         this.startPageX = p[0];
19472         this.startPageY = p[1];
19473     },
19474
19475     /**
19476      * Add this instance to a group of related drag/drop objects.  All
19477      * instances belong to at least one group, and can belong to as many
19478      * groups as needed.
19479      * @method addToGroup
19480      * @param sGroup {string} the name of the group
19481      */
19482     addToGroup: function(sGroup) {
19483         this.groups[sGroup] = true;
19484         this.DDM.regDragDrop(this, sGroup);
19485     },
19486
19487     /**
19488      * Remove's this instance from the supplied interaction group
19489      * @method removeFromGroup
19490      * @param {string}  sGroup  The group to drop
19491      */
19492     removeFromGroup: function(sGroup) {
19493         if (this.groups[sGroup]) {
19494             delete this.groups[sGroup];
19495         }
19496
19497         this.DDM.removeDDFromGroup(this, sGroup);
19498     },
19499
19500     /**
19501      * Allows you to specify that an element other than the linked element
19502      * will be moved with the cursor during a drag
19503      * @method setDragElId
19504      * @param id {string} the id of the element that will be used to initiate the drag
19505      */
19506     setDragElId: function(id) {
19507         this.dragElId = id;
19508     },
19509
19510     /**
19511      * Allows you to specify a child of the linked element that should be
19512      * used to initiate the drag operation.  An example of this would be if
19513      * you have a content div with text and links.  Clicking anywhere in the
19514      * content area would normally start the drag operation.  Use this method
19515      * to specify that an element inside of the content div is the element
19516      * that starts the drag operation.
19517      * @method setHandleElId
19518      * @param id {string} the id of the element that will be used to
19519      * initiate the drag.
19520      */
19521     setHandleElId: function(id) {
19522         if (typeof id !== "string") {
19523             id = Roo.id(id);
19524         }
19525         this.handleElId = id;
19526         this.DDM.regHandle(this.id, id);
19527     },
19528
19529     /**
19530      * Allows you to set an element outside of the linked element as a drag
19531      * handle
19532      * @method setOuterHandleElId
19533      * @param id the id of the element that will be used to initiate the drag
19534      */
19535     setOuterHandleElId: function(id) {
19536         if (typeof id !== "string") {
19537             id = Roo.id(id);
19538         }
19539         Event.on(id, "mousedown",
19540                 this.handleMouseDown, this);
19541         this.setHandleElId(id);
19542
19543         this.hasOuterHandles = true;
19544     },
19545
19546     /**
19547      * Remove all drag and drop hooks for this element
19548      * @method unreg
19549      */
19550     unreg: function() {
19551         Event.un(this.id, "mousedown",
19552                 this.handleMouseDown);
19553         Event.un(this.id, "touchstart",
19554                 this.handleMouseDown);
19555         this._domRef = null;
19556         this.DDM._remove(this);
19557     },
19558
19559     destroy : function(){
19560         this.unreg();
19561     },
19562
19563     /**
19564      * Returns true if this instance is locked, or the drag drop mgr is locked
19565      * (meaning that all drag/drop is disabled on the page.)
19566      * @method isLocked
19567      * @return {boolean} true if this obj or all drag/drop is locked, else
19568      * false
19569      */
19570     isLocked: function() {
19571         return (this.DDM.isLocked() || this.locked);
19572     },
19573
19574     /**
19575      * Fired when this object is clicked
19576      * @method handleMouseDown
19577      * @param {Event} e
19578      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
19579      * @private
19580      */
19581     handleMouseDown: function(e, oDD){
19582      
19583         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
19584             //Roo.log('not touch/ button !=0');
19585             return;
19586         }
19587         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
19588             return; // double touch..
19589         }
19590         
19591
19592         if (this.isLocked()) {
19593             //Roo.log('locked');
19594             return;
19595         }
19596
19597         this.DDM.refreshCache(this.groups);
19598 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
19599         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
19600         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
19601             //Roo.log('no outer handes or not over target');
19602                 // do nothing.
19603         } else {
19604 //            Roo.log('check validator');
19605             if (this.clickValidator(e)) {
19606 //                Roo.log('validate success');
19607                 // set the initial element position
19608                 this.setStartPosition();
19609
19610
19611                 this.b4MouseDown(e);
19612                 this.onMouseDown(e);
19613
19614                 this.DDM.handleMouseDown(e, this);
19615
19616                 this.DDM.stopEvent(e);
19617             } else {
19618
19619
19620             }
19621         }
19622     },
19623
19624     clickValidator: function(e) {
19625         var target = e.getTarget();
19626         return ( this.isValidHandleChild(target) &&
19627                     (this.id == this.handleElId ||
19628                         this.DDM.handleWasClicked(target, this.id)) );
19629     },
19630
19631     /**
19632      * Allows you to specify a tag name that should not start a drag operation
19633      * when clicked.  This is designed to facilitate embedding links within a
19634      * drag handle that do something other than start the drag.
19635      * @method addInvalidHandleType
19636      * @param {string} tagName the type of element to exclude
19637      */
19638     addInvalidHandleType: function(tagName) {
19639         var type = tagName.toUpperCase();
19640         this.invalidHandleTypes[type] = type;
19641     },
19642
19643     /**
19644      * Lets you to specify an element id for a child of a drag handle
19645      * that should not initiate a drag
19646      * @method addInvalidHandleId
19647      * @param {string} id the element id of the element you wish to ignore
19648      */
19649     addInvalidHandleId: function(id) {
19650         if (typeof id !== "string") {
19651             id = Roo.id(id);
19652         }
19653         this.invalidHandleIds[id] = id;
19654     },
19655
19656     /**
19657      * Lets you specify a css class of elements that will not initiate a drag
19658      * @method addInvalidHandleClass
19659      * @param {string} cssClass the class of the elements you wish to ignore
19660      */
19661     addInvalidHandleClass: function(cssClass) {
19662         this.invalidHandleClasses.push(cssClass);
19663     },
19664
19665     /**
19666      * Unsets an excluded tag name set by addInvalidHandleType
19667      * @method removeInvalidHandleType
19668      * @param {string} tagName the type of element to unexclude
19669      */
19670     removeInvalidHandleType: function(tagName) {
19671         var type = tagName.toUpperCase();
19672         // this.invalidHandleTypes[type] = null;
19673         delete this.invalidHandleTypes[type];
19674     },
19675
19676     /**
19677      * Unsets an invalid handle id
19678      * @method removeInvalidHandleId
19679      * @param {string} id the id of the element to re-enable
19680      */
19681     removeInvalidHandleId: function(id) {
19682         if (typeof id !== "string") {
19683             id = Roo.id(id);
19684         }
19685         delete this.invalidHandleIds[id];
19686     },
19687
19688     /**
19689      * Unsets an invalid css class
19690      * @method removeInvalidHandleClass
19691      * @param {string} cssClass the class of the element(s) you wish to
19692      * re-enable
19693      */
19694     removeInvalidHandleClass: function(cssClass) {
19695         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
19696             if (this.invalidHandleClasses[i] == cssClass) {
19697                 delete this.invalidHandleClasses[i];
19698             }
19699         }
19700     },
19701
19702     /**
19703      * Checks the tag exclusion list to see if this click should be ignored
19704      * @method isValidHandleChild
19705      * @param {HTMLElement} node the HTMLElement to evaluate
19706      * @return {boolean} true if this is a valid tag type, false if not
19707      */
19708     isValidHandleChild: function(node) {
19709
19710         var valid = true;
19711         // var n = (node.nodeName == "#text") ? node.parentNode : node;
19712         var nodeName;
19713         try {
19714             nodeName = node.nodeName.toUpperCase();
19715         } catch(e) {
19716             nodeName = node.nodeName;
19717         }
19718         valid = valid && !this.invalidHandleTypes[nodeName];
19719         valid = valid && !this.invalidHandleIds[node.id];
19720
19721         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19722             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19723         }
19724
19725
19726         return valid;
19727
19728     },
19729
19730     /**
19731      * Create the array of horizontal tick marks if an interval was specified
19732      * in setXConstraint().
19733      * @method setXTicks
19734      * @private
19735      */
19736     setXTicks: function(iStartX, iTickSize) {
19737         this.xTicks = [];
19738         this.xTickSize = iTickSize;
19739
19740         var tickMap = {};
19741
19742         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19743             if (!tickMap[i]) {
19744                 this.xTicks[this.xTicks.length] = i;
19745                 tickMap[i] = true;
19746             }
19747         }
19748
19749         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19750             if (!tickMap[i]) {
19751                 this.xTicks[this.xTicks.length] = i;
19752                 tickMap[i] = true;
19753             }
19754         }
19755
19756         this.xTicks.sort(this.DDM.numericSort) ;
19757     },
19758
19759     /**
19760      * Create the array of vertical tick marks if an interval was specified in
19761      * setYConstraint().
19762      * @method setYTicks
19763      * @private
19764      */
19765     setYTicks: function(iStartY, iTickSize) {
19766         this.yTicks = [];
19767         this.yTickSize = iTickSize;
19768
19769         var tickMap = {};
19770
19771         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19772             if (!tickMap[i]) {
19773                 this.yTicks[this.yTicks.length] = i;
19774                 tickMap[i] = true;
19775             }
19776         }
19777
19778         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19779             if (!tickMap[i]) {
19780                 this.yTicks[this.yTicks.length] = i;
19781                 tickMap[i] = true;
19782             }
19783         }
19784
19785         this.yTicks.sort(this.DDM.numericSort) ;
19786     },
19787
19788     /**
19789      * By default, the element can be dragged any place on the screen.  Use
19790      * this method to limit the horizontal travel of the element.  Pass in
19791      * 0,0 for the parameters if you want to lock the drag to the y axis.
19792      * @method setXConstraint
19793      * @param {int} iLeft the number of pixels the element can move to the left
19794      * @param {int} iRight the number of pixels the element can move to the
19795      * right
19796      * @param {int} iTickSize optional parameter for specifying that the
19797      * element
19798      * should move iTickSize pixels at a time.
19799      */
19800     setXConstraint: function(iLeft, iRight, iTickSize) {
19801         this.leftConstraint = iLeft;
19802         this.rightConstraint = iRight;
19803
19804         this.minX = this.initPageX - iLeft;
19805         this.maxX = this.initPageX + iRight;
19806         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19807
19808         this.constrainX = true;
19809     },
19810
19811     /**
19812      * Clears any constraints applied to this instance.  Also clears ticks
19813      * since they can't exist independent of a constraint at this time.
19814      * @method clearConstraints
19815      */
19816     clearConstraints: function() {
19817         this.constrainX = false;
19818         this.constrainY = false;
19819         this.clearTicks();
19820     },
19821
19822     /**
19823      * Clears any tick interval defined for this instance
19824      * @method clearTicks
19825      */
19826     clearTicks: function() {
19827         this.xTicks = null;
19828         this.yTicks = null;
19829         this.xTickSize = 0;
19830         this.yTickSize = 0;
19831     },
19832
19833     /**
19834      * By default, the element can be dragged any place on the screen.  Set
19835      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19836      * parameters if you want to lock the drag to the x axis.
19837      * @method setYConstraint
19838      * @param {int} iUp the number of pixels the element can move up
19839      * @param {int} iDown the number of pixels the element can move down
19840      * @param {int} iTickSize optional parameter for specifying that the
19841      * element should move iTickSize pixels at a time.
19842      */
19843     setYConstraint: function(iUp, iDown, iTickSize) {
19844         this.topConstraint = iUp;
19845         this.bottomConstraint = iDown;
19846
19847         this.minY = this.initPageY - iUp;
19848         this.maxY = this.initPageY + iDown;
19849         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19850
19851         this.constrainY = true;
19852
19853     },
19854
19855     /**
19856      * resetConstraints must be called if you manually reposition a dd element.
19857      * @method resetConstraints
19858      * @param {boolean} maintainOffset
19859      */
19860     resetConstraints: function() {
19861
19862
19863         // Maintain offsets if necessary
19864         if (this.initPageX || this.initPageX === 0) {
19865             // figure out how much this thing has moved
19866             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19867             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19868
19869             this.setInitPosition(dx, dy);
19870
19871         // This is the first time we have detected the element's position
19872         } else {
19873             this.setInitPosition();
19874         }
19875
19876         if (this.constrainX) {
19877             this.setXConstraint( this.leftConstraint,
19878                                  this.rightConstraint,
19879                                  this.xTickSize        );
19880         }
19881
19882         if (this.constrainY) {
19883             this.setYConstraint( this.topConstraint,
19884                                  this.bottomConstraint,
19885                                  this.yTickSize         );
19886         }
19887     },
19888
19889     /**
19890      * Normally the drag element is moved pixel by pixel, but we can specify
19891      * that it move a number of pixels at a time.  This method resolves the
19892      * location when we have it set up like this.
19893      * @method getTick
19894      * @param {int} val where we want to place the object
19895      * @param {int[]} tickArray sorted array of valid points
19896      * @return {int} the closest tick
19897      * @private
19898      */
19899     getTick: function(val, tickArray) {
19900
19901         if (!tickArray) {
19902             // If tick interval is not defined, it is effectively 1 pixel,
19903             // so we return the value passed to us.
19904             return val;
19905         } else if (tickArray[0] >= val) {
19906             // The value is lower than the first tick, so we return the first
19907             // tick.
19908             return tickArray[0];
19909         } else {
19910             for (var i=0, len=tickArray.length; i<len; ++i) {
19911                 var next = i + 1;
19912                 if (tickArray[next] && tickArray[next] >= val) {
19913                     var diff1 = val - tickArray[i];
19914                     var diff2 = tickArray[next] - val;
19915                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19916                 }
19917             }
19918
19919             // The value is larger than the last tick, so we return the last
19920             // tick.
19921             return tickArray[tickArray.length - 1];
19922         }
19923     },
19924
19925     /**
19926      * toString method
19927      * @method toString
19928      * @return {string} string representation of the dd obj
19929      */
19930     toString: function() {
19931         return ("DragDrop " + this.id);
19932     }
19933
19934 });
19935
19936 })();
19937 /*
19938  * Based on:
19939  * Ext JS Library 1.1.1
19940  * Copyright(c) 2006-2007, Ext JS, LLC.
19941  *
19942  * Originally Released Under LGPL - original licence link has changed is not relivant.
19943  *
19944  * Fork - LGPL
19945  * <script type="text/javascript">
19946  */
19947
19948
19949 /**
19950  * The drag and drop utility provides a framework for building drag and drop
19951  * applications.  In addition to enabling drag and drop for specific elements,
19952  * the drag and drop elements are tracked by the manager class, and the
19953  * interactions between the various elements are tracked during the drag and
19954  * the implementing code is notified about these important moments.
19955  */
19956
19957 // Only load the library once.  Rewriting the manager class would orphan
19958 // existing drag and drop instances.
19959 if (!Roo.dd.DragDropMgr) {
19960
19961 /**
19962  * @class Roo.dd.DragDropMgr
19963  * DragDropMgr is a singleton that tracks the element interaction for
19964  * all DragDrop items in the window.  Generally, you will not call
19965  * this class directly, but it does have helper methods that could
19966  * be useful in your DragDrop implementations.
19967  * @singleton
19968  */
19969 Roo.dd.DragDropMgr = function() {
19970
19971     var Event = Roo.EventManager;
19972
19973     return {
19974
19975         /**
19976          * Two dimensional Array of registered DragDrop objects.  The first
19977          * dimension is the DragDrop item group, the second the DragDrop
19978          * object.
19979          * @property ids
19980          * @type {string: string}
19981          * @private
19982          * @static
19983          */
19984         ids: {},
19985
19986         /**
19987          * Array of element ids defined as drag handles.  Used to determine
19988          * if the element that generated the mousedown event is actually the
19989          * handle and not the html element itself.
19990          * @property handleIds
19991          * @type {string: string}
19992          * @private
19993          * @static
19994          */
19995         handleIds: {},
19996
19997         /**
19998          * the DragDrop object that is currently being dragged
19999          * @property dragCurrent
20000          * @type DragDrop
20001          * @private
20002          * @static
20003          **/
20004         dragCurrent: null,
20005
20006         /**
20007          * the DragDrop object(s) that are being hovered over
20008          * @property dragOvers
20009          * @type Array
20010          * @private
20011          * @static
20012          */
20013         dragOvers: {},
20014
20015         /**
20016          * the X distance between the cursor and the object being dragged
20017          * @property deltaX
20018          * @type int
20019          * @private
20020          * @static
20021          */
20022         deltaX: 0,
20023
20024         /**
20025          * the Y distance between the cursor and the object being dragged
20026          * @property deltaY
20027          * @type int
20028          * @private
20029          * @static
20030          */
20031         deltaY: 0,
20032
20033         /**
20034          * Flag to determine if we should prevent the default behavior of the
20035          * events we define. By default this is true, but this can be set to
20036          * false if you need the default behavior (not recommended)
20037          * @property preventDefault
20038          * @type boolean
20039          * @static
20040          */
20041         preventDefault: true,
20042
20043         /**
20044          * Flag to determine if we should stop the propagation of the events
20045          * we generate. This is true by default but you may want to set it to
20046          * false if the html element contains other features that require the
20047          * mouse click.
20048          * @property stopPropagation
20049          * @type boolean
20050          * @static
20051          */
20052         stopPropagation: true,
20053
20054         /**
20055          * Internal flag that is set to true when drag and drop has been
20056          * intialized
20057          * @property initialized
20058          * @private
20059          * @static
20060          */
20061         initalized: false,
20062
20063         /**
20064          * All drag and drop can be disabled.
20065          * @property locked
20066          * @private
20067          * @static
20068          */
20069         locked: false,
20070
20071         /**
20072          * Called the first time an element is registered.
20073          * @method init
20074          * @private
20075          * @static
20076          */
20077         init: function() {
20078             this.initialized = true;
20079         },
20080
20081         /**
20082          * In point mode, drag and drop interaction is defined by the
20083          * location of the cursor during the drag/drop
20084          * @property POINT
20085          * @type int
20086          * @static
20087          */
20088         POINT: 0,
20089
20090         /**
20091          * In intersect mode, drag and drop interactio nis defined by the
20092          * overlap of two or more drag and drop objects.
20093          * @property INTERSECT
20094          * @type int
20095          * @static
20096          */
20097         INTERSECT: 1,
20098
20099         /**
20100          * The current drag and drop mode.  Default: POINT
20101          * @property mode
20102          * @type int
20103          * @static
20104          */
20105         mode: 0,
20106
20107         /**
20108          * Runs method on all drag and drop objects
20109          * @method _execOnAll
20110          * @private
20111          * @static
20112          */
20113         _execOnAll: function(sMethod, args) {
20114             for (var i in this.ids) {
20115                 for (var j in this.ids[i]) {
20116                     var oDD = this.ids[i][j];
20117                     if (! this.isTypeOfDD(oDD)) {
20118                         continue;
20119                     }
20120                     oDD[sMethod].apply(oDD, args);
20121                 }
20122             }
20123         },
20124
20125         /**
20126          * Drag and drop initialization.  Sets up the global event handlers
20127          * @method _onLoad
20128          * @private
20129          * @static
20130          */
20131         _onLoad: function() {
20132
20133             this.init();
20134
20135             if (!Roo.isTouch) {
20136                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
20137                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
20138             }
20139             Event.on(document, "touchend",   this.handleMouseUp, this, true);
20140             Event.on(document, "touchmove", this.handleMouseMove, this, true);
20141             
20142             Event.on(window,   "unload",    this._onUnload, this, true);
20143             Event.on(window,   "resize",    this._onResize, this, true);
20144             // Event.on(window,   "mouseout",    this._test);
20145
20146         },
20147
20148         /**
20149          * Reset constraints on all drag and drop objs
20150          * @method _onResize
20151          * @private
20152          * @static
20153          */
20154         _onResize: function(e) {
20155             this._execOnAll("resetConstraints", []);
20156         },
20157
20158         /**
20159          * Lock all drag and drop functionality
20160          * @method lock
20161          * @static
20162          */
20163         lock: function() { this.locked = true; },
20164
20165         /**
20166          * Unlock all drag and drop functionality
20167          * @method unlock
20168          * @static
20169          */
20170         unlock: function() { this.locked = false; },
20171
20172         /**
20173          * Is drag and drop locked?
20174          * @method isLocked
20175          * @return {boolean} True if drag and drop is locked, false otherwise.
20176          * @static
20177          */
20178         isLocked: function() { return this.locked; },
20179
20180         /**
20181          * Location cache that is set for all drag drop objects when a drag is
20182          * initiated, cleared when the drag is finished.
20183          * @property locationCache
20184          * @private
20185          * @static
20186          */
20187         locationCache: {},
20188
20189         /**
20190          * Set useCache to false if you want to force object the lookup of each
20191          * drag and drop linked element constantly during a drag.
20192          * @property useCache
20193          * @type boolean
20194          * @static
20195          */
20196         useCache: true,
20197
20198         /**
20199          * The number of pixels that the mouse needs to move after the
20200          * mousedown before the drag is initiated.  Default=3;
20201          * @property clickPixelThresh
20202          * @type int
20203          * @static
20204          */
20205         clickPixelThresh: 3,
20206
20207         /**
20208          * The number of milliseconds after the mousedown event to initiate the
20209          * drag if we don't get a mouseup event. Default=1000
20210          * @property clickTimeThresh
20211          * @type int
20212          * @static
20213          */
20214         clickTimeThresh: 350,
20215
20216         /**
20217          * Flag that indicates that either the drag pixel threshold or the
20218          * mousdown time threshold has been met
20219          * @property dragThreshMet
20220          * @type boolean
20221          * @private
20222          * @static
20223          */
20224         dragThreshMet: false,
20225
20226         /**
20227          * Timeout used for the click time threshold
20228          * @property clickTimeout
20229          * @type Object
20230          * @private
20231          * @static
20232          */
20233         clickTimeout: null,
20234
20235         /**
20236          * The X position of the mousedown event stored for later use when a
20237          * drag threshold is met.
20238          * @property startX
20239          * @type int
20240          * @private
20241          * @static
20242          */
20243         startX: 0,
20244
20245         /**
20246          * The Y position of the mousedown event stored for later use when a
20247          * drag threshold is met.
20248          * @property startY
20249          * @type int
20250          * @private
20251          * @static
20252          */
20253         startY: 0,
20254
20255         /**
20256          * Each DragDrop instance must be registered with the DragDropMgr.
20257          * This is executed in DragDrop.init()
20258          * @method regDragDrop
20259          * @param {DragDrop} oDD the DragDrop object to register
20260          * @param {String} sGroup the name of the group this element belongs to
20261          * @static
20262          */
20263         regDragDrop: function(oDD, sGroup) {
20264             if (!this.initialized) { this.init(); }
20265
20266             if (!this.ids[sGroup]) {
20267                 this.ids[sGroup] = {};
20268             }
20269             this.ids[sGroup][oDD.id] = oDD;
20270         },
20271
20272         /**
20273          * Removes the supplied dd instance from the supplied group. Executed
20274          * by DragDrop.removeFromGroup, so don't call this function directly.
20275          * @method removeDDFromGroup
20276          * @private
20277          * @static
20278          */
20279         removeDDFromGroup: function(oDD, sGroup) {
20280             if (!this.ids[sGroup]) {
20281                 this.ids[sGroup] = {};
20282             }
20283
20284             var obj = this.ids[sGroup];
20285             if (obj && obj[oDD.id]) {
20286                 delete obj[oDD.id];
20287             }
20288         },
20289
20290         /**
20291          * Unregisters a drag and drop item.  This is executed in
20292          * DragDrop.unreg, use that method instead of calling this directly.
20293          * @method _remove
20294          * @private
20295          * @static
20296          */
20297         _remove: function(oDD) {
20298             for (var g in oDD.groups) {
20299                 if (g && this.ids[g][oDD.id]) {
20300                     delete this.ids[g][oDD.id];
20301                 }
20302             }
20303             delete this.handleIds[oDD.id];
20304         },
20305
20306         /**
20307          * Each DragDrop handle element must be registered.  This is done
20308          * automatically when executing DragDrop.setHandleElId()
20309          * @method regHandle
20310          * @param {String} sDDId the DragDrop id this element is a handle for
20311          * @param {String} sHandleId the id of the element that is the drag
20312          * handle
20313          * @static
20314          */
20315         regHandle: function(sDDId, sHandleId) {
20316             if (!this.handleIds[sDDId]) {
20317                 this.handleIds[sDDId] = {};
20318             }
20319             this.handleIds[sDDId][sHandleId] = sHandleId;
20320         },
20321
20322         /**
20323          * Utility function to determine if a given element has been
20324          * registered as a drag drop item.
20325          * @method isDragDrop
20326          * @param {String} id the element id to check
20327          * @return {boolean} true if this element is a DragDrop item,
20328          * false otherwise
20329          * @static
20330          */
20331         isDragDrop: function(id) {
20332             return ( this.getDDById(id) ) ? true : false;
20333         },
20334
20335         /**
20336          * Returns the drag and drop instances that are in all groups the
20337          * passed in instance belongs to.
20338          * @method getRelated
20339          * @param {DragDrop} p_oDD the obj to get related data for
20340          * @param {boolean} bTargetsOnly if true, only return targetable objs
20341          * @return {DragDrop[]} the related instances
20342          * @static
20343          */
20344         getRelated: function(p_oDD, bTargetsOnly) {
20345             var oDDs = [];
20346             for (var i in p_oDD.groups) {
20347                 for (j in this.ids[i]) {
20348                     var dd = this.ids[i][j];
20349                     if (! this.isTypeOfDD(dd)) {
20350                         continue;
20351                     }
20352                     if (!bTargetsOnly || dd.isTarget) {
20353                         oDDs[oDDs.length] = dd;
20354                     }
20355                 }
20356             }
20357
20358             return oDDs;
20359         },
20360
20361         /**
20362          * Returns true if the specified dd target is a legal target for
20363          * the specifice drag obj
20364          * @method isLegalTarget
20365          * @param {DragDrop} the drag obj
20366          * @param {DragDrop} the target
20367          * @return {boolean} true if the target is a legal target for the
20368          * dd obj
20369          * @static
20370          */
20371         isLegalTarget: function (oDD, oTargetDD) {
20372             var targets = this.getRelated(oDD, true);
20373             for (var i=0, len=targets.length;i<len;++i) {
20374                 if (targets[i].id == oTargetDD.id) {
20375                     return true;
20376                 }
20377             }
20378
20379             return false;
20380         },
20381
20382         /**
20383          * My goal is to be able to transparently determine if an object is
20384          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
20385          * returns "object", oDD.constructor.toString() always returns
20386          * "DragDrop" and not the name of the subclass.  So for now it just
20387          * evaluates a well-known variable in DragDrop.
20388          * @method isTypeOfDD
20389          * @param {Object} the object to evaluate
20390          * @return {boolean} true if typeof oDD = DragDrop
20391          * @static
20392          */
20393         isTypeOfDD: function (oDD) {
20394             return (oDD && oDD.__ygDragDrop);
20395         },
20396
20397         /**
20398          * Utility function to determine if a given element has been
20399          * registered as a drag drop handle for the given Drag Drop object.
20400          * @method isHandle
20401          * @param {String} id the element id to check
20402          * @return {boolean} true if this element is a DragDrop handle, false
20403          * otherwise
20404          * @static
20405          */
20406         isHandle: function(sDDId, sHandleId) {
20407             return ( this.handleIds[sDDId] &&
20408                             this.handleIds[sDDId][sHandleId] );
20409         },
20410
20411         /**
20412          * Returns the DragDrop instance for a given id
20413          * @method getDDById
20414          * @param {String} id the id of the DragDrop object
20415          * @return {DragDrop} the drag drop object, null if it is not found
20416          * @static
20417          */
20418         getDDById: function(id) {
20419             for (var i in this.ids) {
20420                 if (this.ids[i][id]) {
20421                     return this.ids[i][id];
20422                 }
20423             }
20424             return null;
20425         },
20426
20427         /**
20428          * Fired after a registered DragDrop object gets the mousedown event.
20429          * Sets up the events required to track the object being dragged
20430          * @method handleMouseDown
20431          * @param {Event} e the event
20432          * @param oDD the DragDrop object being dragged
20433          * @private
20434          * @static
20435          */
20436         handleMouseDown: function(e, oDD) {
20437             if(Roo.QuickTips){
20438                 Roo.QuickTips.disable();
20439             }
20440             this.currentTarget = e.getTarget();
20441
20442             this.dragCurrent = oDD;
20443
20444             var el = oDD.getEl();
20445
20446             // track start position
20447             this.startX = e.getPageX();
20448             this.startY = e.getPageY();
20449
20450             this.deltaX = this.startX - el.offsetLeft;
20451             this.deltaY = this.startY - el.offsetTop;
20452
20453             this.dragThreshMet = false;
20454
20455             this.clickTimeout = setTimeout(
20456                     function() {
20457                         var DDM = Roo.dd.DDM;
20458                         DDM.startDrag(DDM.startX, DDM.startY);
20459                     },
20460                     this.clickTimeThresh );
20461         },
20462
20463         /**
20464          * Fired when either the drag pixel threshol or the mousedown hold
20465          * time threshold has been met.
20466          * @method startDrag
20467          * @param x {int} the X position of the original mousedown
20468          * @param y {int} the Y position of the original mousedown
20469          * @static
20470          */
20471         startDrag: function(x, y) {
20472             clearTimeout(this.clickTimeout);
20473             if (this.dragCurrent) {
20474                 this.dragCurrent.b4StartDrag(x, y);
20475                 this.dragCurrent.startDrag(x, y);
20476             }
20477             this.dragThreshMet = true;
20478         },
20479
20480         /**
20481          * Internal function to handle the mouseup event.  Will be invoked
20482          * from the context of the document.
20483          * @method handleMouseUp
20484          * @param {Event} e the event
20485          * @private
20486          * @static
20487          */
20488         handleMouseUp: function(e) {
20489
20490             if(Roo.QuickTips){
20491                 Roo.QuickTips.enable();
20492             }
20493             if (! this.dragCurrent) {
20494                 return;
20495             }
20496
20497             clearTimeout(this.clickTimeout);
20498
20499             if (this.dragThreshMet) {
20500                 this.fireEvents(e, true);
20501             } else {
20502             }
20503
20504             this.stopDrag(e);
20505
20506             this.stopEvent(e);
20507         },
20508
20509         /**
20510          * Utility to stop event propagation and event default, if these
20511          * features are turned on.
20512          * @method stopEvent
20513          * @param {Event} e the event as returned by this.getEvent()
20514          * @static
20515          */
20516         stopEvent: function(e){
20517             if(this.stopPropagation) {
20518                 e.stopPropagation();
20519             }
20520
20521             if (this.preventDefault) {
20522                 e.preventDefault();
20523             }
20524         },
20525
20526         /**
20527          * Internal function to clean up event handlers after the drag
20528          * operation is complete
20529          * @method stopDrag
20530          * @param {Event} e the event
20531          * @private
20532          * @static
20533          */
20534         stopDrag: function(e) {
20535             // Fire the drag end event for the item that was dragged
20536             if (this.dragCurrent) {
20537                 if (this.dragThreshMet) {
20538                     this.dragCurrent.b4EndDrag(e);
20539                     this.dragCurrent.endDrag(e);
20540                 }
20541
20542                 this.dragCurrent.onMouseUp(e);
20543             }
20544
20545             this.dragCurrent = null;
20546             this.dragOvers = {};
20547         },
20548
20549         /**
20550          * Internal function to handle the mousemove event.  Will be invoked
20551          * from the context of the html element.
20552          *
20553          * @TODO figure out what we can do about mouse events lost when the
20554          * user drags objects beyond the window boundary.  Currently we can
20555          * detect this in internet explorer by verifying that the mouse is
20556          * down during the mousemove event.  Firefox doesn't give us the
20557          * button state on the mousemove event.
20558          * @method handleMouseMove
20559          * @param {Event} e the event
20560          * @private
20561          * @static
20562          */
20563         handleMouseMove: function(e) {
20564             if (! this.dragCurrent) {
20565                 return true;
20566             }
20567
20568             // var button = e.which || e.button;
20569
20570             // check for IE mouseup outside of page boundary
20571             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
20572                 this.stopEvent(e);
20573                 return this.handleMouseUp(e);
20574             }
20575
20576             if (!this.dragThreshMet) {
20577                 var diffX = Math.abs(this.startX - e.getPageX());
20578                 var diffY = Math.abs(this.startY - e.getPageY());
20579                 if (diffX > this.clickPixelThresh ||
20580                             diffY > this.clickPixelThresh) {
20581                     this.startDrag(this.startX, this.startY);
20582                 }
20583             }
20584
20585             if (this.dragThreshMet) {
20586                 this.dragCurrent.b4Drag(e);
20587                 this.dragCurrent.onDrag(e);
20588                 if(!this.dragCurrent.moveOnly){
20589                     this.fireEvents(e, false);
20590                 }
20591             }
20592
20593             this.stopEvent(e);
20594
20595             return true;
20596         },
20597
20598         /**
20599          * Iterates over all of the DragDrop elements to find ones we are
20600          * hovering over or dropping on
20601          * @method fireEvents
20602          * @param {Event} e the event
20603          * @param {boolean} isDrop is this a drop op or a mouseover op?
20604          * @private
20605          * @static
20606          */
20607         fireEvents: function(e, isDrop) {
20608             var dc = this.dragCurrent;
20609
20610             // If the user did the mouse up outside of the window, we could
20611             // get here even though we have ended the drag.
20612             if (!dc || dc.isLocked()) {
20613                 return;
20614             }
20615
20616             var pt = e.getPoint();
20617
20618             // cache the previous dragOver array
20619             var oldOvers = [];
20620
20621             var outEvts   = [];
20622             var overEvts  = [];
20623             var dropEvts  = [];
20624             var enterEvts = [];
20625
20626             // Check to see if the object(s) we were hovering over is no longer
20627             // being hovered over so we can fire the onDragOut event
20628             for (var i in this.dragOvers) {
20629
20630                 var ddo = this.dragOvers[i];
20631
20632                 if (! this.isTypeOfDD(ddo)) {
20633                     continue;
20634                 }
20635
20636                 if (! this.isOverTarget(pt, ddo, this.mode)) {
20637                     outEvts.push( ddo );
20638                 }
20639
20640                 oldOvers[i] = true;
20641                 delete this.dragOvers[i];
20642             }
20643
20644             for (var sGroup in dc.groups) {
20645
20646                 if ("string" != typeof sGroup) {
20647                     continue;
20648                 }
20649
20650                 for (i in this.ids[sGroup]) {
20651                     var oDD = this.ids[sGroup][i];
20652                     if (! this.isTypeOfDD(oDD)) {
20653                         continue;
20654                     }
20655
20656                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
20657                         if (this.isOverTarget(pt, oDD, this.mode)) {
20658                             // look for drop interactions
20659                             if (isDrop) {
20660                                 dropEvts.push( oDD );
20661                             // look for drag enter and drag over interactions
20662                             } else {
20663
20664                                 // initial drag over: dragEnter fires
20665                                 if (!oldOvers[oDD.id]) {
20666                                     enterEvts.push( oDD );
20667                                 // subsequent drag overs: dragOver fires
20668                                 } else {
20669                                     overEvts.push( oDD );
20670                                 }
20671
20672                                 this.dragOvers[oDD.id] = oDD;
20673                             }
20674                         }
20675                     }
20676                 }
20677             }
20678
20679             if (this.mode) {
20680                 if (outEvts.length) {
20681                     dc.b4DragOut(e, outEvts);
20682                     dc.onDragOut(e, outEvts);
20683                 }
20684
20685                 if (enterEvts.length) {
20686                     dc.onDragEnter(e, enterEvts);
20687                 }
20688
20689                 if (overEvts.length) {
20690                     dc.b4DragOver(e, overEvts);
20691                     dc.onDragOver(e, overEvts);
20692                 }
20693
20694                 if (dropEvts.length) {
20695                     dc.b4DragDrop(e, dropEvts);
20696                     dc.onDragDrop(e, dropEvts);
20697                 }
20698
20699             } else {
20700                 // fire dragout events
20701                 var len = 0;
20702                 for (i=0, len=outEvts.length; i<len; ++i) {
20703                     dc.b4DragOut(e, outEvts[i].id);
20704                     dc.onDragOut(e, outEvts[i].id);
20705                 }
20706
20707                 // fire enter events
20708                 for (i=0,len=enterEvts.length; i<len; ++i) {
20709                     // dc.b4DragEnter(e, oDD.id);
20710                     dc.onDragEnter(e, enterEvts[i].id);
20711                 }
20712
20713                 // fire over events
20714                 for (i=0,len=overEvts.length; i<len; ++i) {
20715                     dc.b4DragOver(e, overEvts[i].id);
20716                     dc.onDragOver(e, overEvts[i].id);
20717                 }
20718
20719                 // fire drop events
20720                 for (i=0, len=dropEvts.length; i<len; ++i) {
20721                     dc.b4DragDrop(e, dropEvts[i].id);
20722                     dc.onDragDrop(e, dropEvts[i].id);
20723                 }
20724
20725             }
20726
20727             // notify about a drop that did not find a target
20728             if (isDrop && !dropEvts.length) {
20729                 dc.onInvalidDrop(e);
20730             }
20731
20732         },
20733
20734         /**
20735          * Helper function for getting the best match from the list of drag
20736          * and drop objects returned by the drag and drop events when we are
20737          * in INTERSECT mode.  It returns either the first object that the
20738          * cursor is over, or the object that has the greatest overlap with
20739          * the dragged element.
20740          * @method getBestMatch
20741          * @param  {DragDrop[]} dds The array of drag and drop objects
20742          * targeted
20743          * @return {DragDrop}       The best single match
20744          * @static
20745          */
20746         getBestMatch: function(dds) {
20747             var winner = null;
20748             // Return null if the input is not what we expect
20749             //if (!dds || !dds.length || dds.length == 0) {
20750                // winner = null;
20751             // If there is only one item, it wins
20752             //} else if (dds.length == 1) {
20753
20754             var len = dds.length;
20755
20756             if (len == 1) {
20757                 winner = dds[0];
20758             } else {
20759                 // Loop through the targeted items
20760                 for (var i=0; i<len; ++i) {
20761                     var dd = dds[i];
20762                     // If the cursor is over the object, it wins.  If the
20763                     // cursor is over multiple matches, the first one we come
20764                     // to wins.
20765                     if (dd.cursorIsOver) {
20766                         winner = dd;
20767                         break;
20768                     // Otherwise the object with the most overlap wins
20769                     } else {
20770                         if (!winner ||
20771                             winner.overlap.getArea() < dd.overlap.getArea()) {
20772                             winner = dd;
20773                         }
20774                     }
20775                 }
20776             }
20777
20778             return winner;
20779         },
20780
20781         /**
20782          * Refreshes the cache of the top-left and bottom-right points of the
20783          * drag and drop objects in the specified group(s).  This is in the
20784          * format that is stored in the drag and drop instance, so typical
20785          * usage is:
20786          * <code>
20787          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20788          * </code>
20789          * Alternatively:
20790          * <code>
20791          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20792          * </code>
20793          * @TODO this really should be an indexed array.  Alternatively this
20794          * method could accept both.
20795          * @method refreshCache
20796          * @param {Object} groups an associative array of groups to refresh
20797          * @static
20798          */
20799         refreshCache: function(groups) {
20800             for (var sGroup in groups) {
20801                 if ("string" != typeof sGroup) {
20802                     continue;
20803                 }
20804                 for (var i in this.ids[sGroup]) {
20805                     var oDD = this.ids[sGroup][i];
20806
20807                     if (this.isTypeOfDD(oDD)) {
20808                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20809                         var loc = this.getLocation(oDD);
20810                         if (loc) {
20811                             this.locationCache[oDD.id] = loc;
20812                         } else {
20813                             delete this.locationCache[oDD.id];
20814                             // this will unregister the drag and drop object if
20815                             // the element is not in a usable state
20816                             // oDD.unreg();
20817                         }
20818                     }
20819                 }
20820             }
20821         },
20822
20823         /**
20824          * This checks to make sure an element exists and is in the DOM.  The
20825          * main purpose is to handle cases where innerHTML is used to remove
20826          * drag and drop objects from the DOM.  IE provides an 'unspecified
20827          * error' when trying to access the offsetParent of such an element
20828          * @method verifyEl
20829          * @param {HTMLElement} el the element to check
20830          * @return {boolean} true if the element looks usable
20831          * @static
20832          */
20833         verifyEl: function(el) {
20834             if (el) {
20835                 var parent;
20836                 if(Roo.isIE){
20837                     try{
20838                         parent = el.offsetParent;
20839                     }catch(e){}
20840                 }else{
20841                     parent = el.offsetParent;
20842                 }
20843                 if (parent) {
20844                     return true;
20845                 }
20846             }
20847
20848             return false;
20849         },
20850
20851         /**
20852          * Returns a Region object containing the drag and drop element's position
20853          * and size, including the padding configured for it
20854          * @method getLocation
20855          * @param {DragDrop} oDD the drag and drop object to get the
20856          *                       location for
20857          * @return {Roo.lib.Region} a Region object representing the total area
20858          *                             the element occupies, including any padding
20859          *                             the instance is configured for.
20860          * @static
20861          */
20862         getLocation: function(oDD) {
20863             if (! this.isTypeOfDD(oDD)) {
20864                 return null;
20865             }
20866
20867             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20868
20869             try {
20870                 pos= Roo.lib.Dom.getXY(el);
20871             } catch (e) { }
20872
20873             if (!pos) {
20874                 return null;
20875             }
20876
20877             x1 = pos[0];
20878             x2 = x1 + el.offsetWidth;
20879             y1 = pos[1];
20880             y2 = y1 + el.offsetHeight;
20881
20882             t = y1 - oDD.padding[0];
20883             r = x2 + oDD.padding[1];
20884             b = y2 + oDD.padding[2];
20885             l = x1 - oDD.padding[3];
20886
20887             return new Roo.lib.Region( t, r, b, l );
20888         },
20889
20890         /**
20891          * Checks the cursor location to see if it over the target
20892          * @method isOverTarget
20893          * @param {Roo.lib.Point} pt The point to evaluate
20894          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20895          * @return {boolean} true if the mouse is over the target
20896          * @private
20897          * @static
20898          */
20899         isOverTarget: function(pt, oTarget, intersect) {
20900             // use cache if available
20901             var loc = this.locationCache[oTarget.id];
20902             if (!loc || !this.useCache) {
20903                 loc = this.getLocation(oTarget);
20904                 this.locationCache[oTarget.id] = loc;
20905
20906             }
20907
20908             if (!loc) {
20909                 return false;
20910             }
20911
20912             oTarget.cursorIsOver = loc.contains( pt );
20913
20914             // DragDrop is using this as a sanity check for the initial mousedown
20915             // in this case we are done.  In POINT mode, if the drag obj has no
20916             // contraints, we are also done. Otherwise we need to evaluate the
20917             // location of the target as related to the actual location of the
20918             // dragged element.
20919             var dc = this.dragCurrent;
20920             if (!dc || !dc.getTargetCoord ||
20921                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20922                 return oTarget.cursorIsOver;
20923             }
20924
20925             oTarget.overlap = null;
20926
20927             // Get the current location of the drag element, this is the
20928             // location of the mouse event less the delta that represents
20929             // where the original mousedown happened on the element.  We
20930             // need to consider constraints and ticks as well.
20931             var pos = dc.getTargetCoord(pt.x, pt.y);
20932
20933             var el = dc.getDragEl();
20934             var curRegion = new Roo.lib.Region( pos.y,
20935                                                    pos.x + el.offsetWidth,
20936                                                    pos.y + el.offsetHeight,
20937                                                    pos.x );
20938
20939             var overlap = curRegion.intersect(loc);
20940
20941             if (overlap) {
20942                 oTarget.overlap = overlap;
20943                 return (intersect) ? true : oTarget.cursorIsOver;
20944             } else {
20945                 return false;
20946             }
20947         },
20948
20949         /**
20950          * unload event handler
20951          * @method _onUnload
20952          * @private
20953          * @static
20954          */
20955         _onUnload: function(e, me) {
20956             Roo.dd.DragDropMgr.unregAll();
20957         },
20958
20959         /**
20960          * Cleans up the drag and drop events and objects.
20961          * @method unregAll
20962          * @private
20963          * @static
20964          */
20965         unregAll: function() {
20966
20967             if (this.dragCurrent) {
20968                 this.stopDrag();
20969                 this.dragCurrent = null;
20970             }
20971
20972             this._execOnAll("unreg", []);
20973
20974             for (i in this.elementCache) {
20975                 delete this.elementCache[i];
20976             }
20977
20978             this.elementCache = {};
20979             this.ids = {};
20980         },
20981
20982         /**
20983          * A cache of DOM elements
20984          * @property elementCache
20985          * @private
20986          * @static
20987          */
20988         elementCache: {},
20989
20990         /**
20991          * Get the wrapper for the DOM element specified
20992          * @method getElWrapper
20993          * @param {String} id the id of the element to get
20994          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20995          * @private
20996          * @deprecated This wrapper isn't that useful
20997          * @static
20998          */
20999         getElWrapper: function(id) {
21000             var oWrapper = this.elementCache[id];
21001             if (!oWrapper || !oWrapper.el) {
21002                 oWrapper = this.elementCache[id] =
21003                     new this.ElementWrapper(Roo.getDom(id));
21004             }
21005             return oWrapper;
21006         },
21007
21008         /**
21009          * Returns the actual DOM element
21010          * @method getElement
21011          * @param {String} id the id of the elment to get
21012          * @return {Object} The element
21013          * @deprecated use Roo.getDom instead
21014          * @static
21015          */
21016         getElement: function(id) {
21017             return Roo.getDom(id);
21018         },
21019
21020         /**
21021          * Returns the style property for the DOM element (i.e.,
21022          * document.getElById(id).style)
21023          * @method getCss
21024          * @param {String} id the id of the elment to get
21025          * @return {Object} The style property of the element
21026          * @deprecated use Roo.getDom instead
21027          * @static
21028          */
21029         getCss: function(id) {
21030             var el = Roo.getDom(id);
21031             return (el) ? el.style : null;
21032         },
21033
21034         /**
21035          * Inner class for cached elements
21036          * @class DragDropMgr.ElementWrapper
21037          * @for DragDropMgr
21038          * @private
21039          * @deprecated
21040          */
21041         ElementWrapper: function(el) {
21042                 /**
21043                  * The element
21044                  * @property el
21045                  */
21046                 this.el = el || null;
21047                 /**
21048                  * The element id
21049                  * @property id
21050                  */
21051                 this.id = this.el && el.id;
21052                 /**
21053                  * A reference to the style property
21054                  * @property css
21055                  */
21056                 this.css = this.el && el.style;
21057             },
21058
21059         /**
21060          * Returns the X position of an html element
21061          * @method getPosX
21062          * @param el the element for which to get the position
21063          * @return {int} the X coordinate
21064          * @for DragDropMgr
21065          * @deprecated use Roo.lib.Dom.getX instead
21066          * @static
21067          */
21068         getPosX: function(el) {
21069             return Roo.lib.Dom.getX(el);
21070         },
21071
21072         /**
21073          * Returns the Y position of an html element
21074          * @method getPosY
21075          * @param el the element for which to get the position
21076          * @return {int} the Y coordinate
21077          * @deprecated use Roo.lib.Dom.getY instead
21078          * @static
21079          */
21080         getPosY: function(el) {
21081             return Roo.lib.Dom.getY(el);
21082         },
21083
21084         /**
21085          * Swap two nodes.  In IE, we use the native method, for others we
21086          * emulate the IE behavior
21087          * @method swapNode
21088          * @param n1 the first node to swap
21089          * @param n2 the other node to swap
21090          * @static
21091          */
21092         swapNode: function(n1, n2) {
21093             if (n1.swapNode) {
21094                 n1.swapNode(n2);
21095             } else {
21096                 var p = n2.parentNode;
21097                 var s = n2.nextSibling;
21098
21099                 if (s == n1) {
21100                     p.insertBefore(n1, n2);
21101                 } else if (n2 == n1.nextSibling) {
21102                     p.insertBefore(n2, n1);
21103                 } else {
21104                     n1.parentNode.replaceChild(n2, n1);
21105                     p.insertBefore(n1, s);
21106                 }
21107             }
21108         },
21109
21110         /**
21111          * Returns the current scroll position
21112          * @method getScroll
21113          * @private
21114          * @static
21115          */
21116         getScroll: function () {
21117             var t, l, dde=document.documentElement, db=document.body;
21118             if (dde && (dde.scrollTop || dde.scrollLeft)) {
21119                 t = dde.scrollTop;
21120                 l = dde.scrollLeft;
21121             } else if (db) {
21122                 t = db.scrollTop;
21123                 l = db.scrollLeft;
21124             } else {
21125
21126             }
21127             return { top: t, left: l };
21128         },
21129
21130         /**
21131          * Returns the specified element style property
21132          * @method getStyle
21133          * @param {HTMLElement} el          the element
21134          * @param {string}      styleProp   the style property
21135          * @return {string} The value of the style property
21136          * @deprecated use Roo.lib.Dom.getStyle
21137          * @static
21138          */
21139         getStyle: function(el, styleProp) {
21140             return Roo.fly(el).getStyle(styleProp);
21141         },
21142
21143         /**
21144          * Gets the scrollTop
21145          * @method getScrollTop
21146          * @return {int} the document's scrollTop
21147          * @static
21148          */
21149         getScrollTop: function () { return this.getScroll().top; },
21150
21151         /**
21152          * Gets the scrollLeft
21153          * @method getScrollLeft
21154          * @return {int} the document's scrollTop
21155          * @static
21156          */
21157         getScrollLeft: function () { return this.getScroll().left; },
21158
21159         /**
21160          * Sets the x/y position of an element to the location of the
21161          * target element.
21162          * @method moveToEl
21163          * @param {HTMLElement} moveEl      The element to move
21164          * @param {HTMLElement} targetEl    The position reference element
21165          * @static
21166          */
21167         moveToEl: function (moveEl, targetEl) {
21168             var aCoord = Roo.lib.Dom.getXY(targetEl);
21169             Roo.lib.Dom.setXY(moveEl, aCoord);
21170         },
21171
21172         /**
21173          * Numeric array sort function
21174          * @method numericSort
21175          * @static
21176          */
21177         numericSort: function(a, b) { return (a - b); },
21178
21179         /**
21180          * Internal counter
21181          * @property _timeoutCount
21182          * @private
21183          * @static
21184          */
21185         _timeoutCount: 0,
21186
21187         /**
21188          * Trying to make the load order less important.  Without this we get
21189          * an error if this file is loaded before the Event Utility.
21190          * @method _addListeners
21191          * @private
21192          * @static
21193          */
21194         _addListeners: function() {
21195             var DDM = Roo.dd.DDM;
21196             if ( Roo.lib.Event && document ) {
21197                 DDM._onLoad();
21198             } else {
21199                 if (DDM._timeoutCount > 2000) {
21200                 } else {
21201                     setTimeout(DDM._addListeners, 10);
21202                     if (document && document.body) {
21203                         DDM._timeoutCount += 1;
21204                     }
21205                 }
21206             }
21207         },
21208
21209         /**
21210          * Recursively searches the immediate parent and all child nodes for
21211          * the handle element in order to determine wheter or not it was
21212          * clicked.
21213          * @method handleWasClicked
21214          * @param node the html element to inspect
21215          * @static
21216          */
21217         handleWasClicked: function(node, id) {
21218             if (this.isHandle(id, node.id)) {
21219                 return true;
21220             } else {
21221                 // check to see if this is a text node child of the one we want
21222                 var p = node.parentNode;
21223
21224                 while (p) {
21225                     if (this.isHandle(id, p.id)) {
21226                         return true;
21227                     } else {
21228                         p = p.parentNode;
21229                     }
21230                 }
21231             }
21232
21233             return false;
21234         }
21235
21236     };
21237
21238 }();
21239
21240 // shorter alias, save a few bytes
21241 Roo.dd.DDM = Roo.dd.DragDropMgr;
21242 Roo.dd.DDM._addListeners();
21243
21244 }/*
21245  * Based on:
21246  * Ext JS Library 1.1.1
21247  * Copyright(c) 2006-2007, Ext JS, LLC.
21248  *
21249  * Originally Released Under LGPL - original licence link has changed is not relivant.
21250  *
21251  * Fork - LGPL
21252  * <script type="text/javascript">
21253  */
21254
21255 /**
21256  * @class Roo.dd.DD
21257  * A DragDrop implementation where the linked element follows the
21258  * mouse cursor during a drag.
21259  * @extends Roo.dd.DragDrop
21260  * @constructor
21261  * @param {String} id the id of the linked element
21262  * @param {String} sGroup the group of related DragDrop items
21263  * @param {object} config an object containing configurable attributes
21264  *                Valid properties for DD:
21265  *                    scroll
21266  */
21267 Roo.dd.DD = function(id, sGroup, config) {
21268     if (id) {
21269         this.init(id, sGroup, config);
21270     }
21271 };
21272
21273 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
21274
21275     /**
21276      * When set to true, the utility automatically tries to scroll the browser
21277      * window wehn a drag and drop element is dragged near the viewport boundary.
21278      * Defaults to true.
21279      * @property scroll
21280      * @type boolean
21281      */
21282     scroll: true,
21283
21284     /**
21285      * Sets the pointer offset to the distance between the linked element's top
21286      * left corner and the location the element was clicked
21287      * @method autoOffset
21288      * @param {int} iPageX the X coordinate of the click
21289      * @param {int} iPageY the Y coordinate of the click
21290      */
21291     autoOffset: function(iPageX, iPageY) {
21292         var x = iPageX - this.startPageX;
21293         var y = iPageY - this.startPageY;
21294         this.setDelta(x, y);
21295     },
21296
21297     /**
21298      * Sets the pointer offset.  You can call this directly to force the
21299      * offset to be in a particular location (e.g., pass in 0,0 to set it
21300      * to the center of the object)
21301      * @method setDelta
21302      * @param {int} iDeltaX the distance from the left
21303      * @param {int} iDeltaY the distance from the top
21304      */
21305     setDelta: function(iDeltaX, iDeltaY) {
21306         this.deltaX = iDeltaX;
21307         this.deltaY = iDeltaY;
21308     },
21309
21310     /**
21311      * Sets the drag element to the location of the mousedown or click event,
21312      * maintaining the cursor location relative to the location on the element
21313      * that was clicked.  Override this if you want to place the element in a
21314      * location other than where the cursor is.
21315      * @method setDragElPos
21316      * @param {int} iPageX the X coordinate of the mousedown or drag event
21317      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21318      */
21319     setDragElPos: function(iPageX, iPageY) {
21320         // the first time we do this, we are going to check to make sure
21321         // the element has css positioning
21322
21323         var el = this.getDragEl();
21324         this.alignElWithMouse(el, iPageX, iPageY);
21325     },
21326
21327     /**
21328      * Sets the element to the location of the mousedown or click event,
21329      * maintaining the cursor location relative to the location on the element
21330      * that was clicked.  Override this if you want to place the element in a
21331      * location other than where the cursor is.
21332      * @method alignElWithMouse
21333      * @param {HTMLElement} el the element to move
21334      * @param {int} iPageX the X coordinate of the mousedown or drag event
21335      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21336      */
21337     alignElWithMouse: function(el, iPageX, iPageY) {
21338         var oCoord = this.getTargetCoord(iPageX, iPageY);
21339         var fly = el.dom ? el : Roo.fly(el);
21340         if (!this.deltaSetXY) {
21341             var aCoord = [oCoord.x, oCoord.y];
21342             fly.setXY(aCoord);
21343             var newLeft = fly.getLeft(true);
21344             var newTop  = fly.getTop(true);
21345             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
21346         } else {
21347             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
21348         }
21349
21350         this.cachePosition(oCoord.x, oCoord.y);
21351         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
21352         return oCoord;
21353     },
21354
21355     /**
21356      * Saves the most recent position so that we can reset the constraints and
21357      * tick marks on-demand.  We need to know this so that we can calculate the
21358      * number of pixels the element is offset from its original position.
21359      * @method cachePosition
21360      * @param iPageX the current x position (optional, this just makes it so we
21361      * don't have to look it up again)
21362      * @param iPageY the current y position (optional, this just makes it so we
21363      * don't have to look it up again)
21364      */
21365     cachePosition: function(iPageX, iPageY) {
21366         if (iPageX) {
21367             this.lastPageX = iPageX;
21368             this.lastPageY = iPageY;
21369         } else {
21370             var aCoord = Roo.lib.Dom.getXY(this.getEl());
21371             this.lastPageX = aCoord[0];
21372             this.lastPageY = aCoord[1];
21373         }
21374     },
21375
21376     /**
21377      * Auto-scroll the window if the dragged object has been moved beyond the
21378      * visible window boundary.
21379      * @method autoScroll
21380      * @param {int} x the drag element's x position
21381      * @param {int} y the drag element's y position
21382      * @param {int} h the height of the drag element
21383      * @param {int} w the width of the drag element
21384      * @private
21385      */
21386     autoScroll: function(x, y, h, w) {
21387
21388         if (this.scroll) {
21389             // The client height
21390             var clientH = Roo.lib.Dom.getViewWidth();
21391
21392             // The client width
21393             var clientW = Roo.lib.Dom.getViewHeight();
21394
21395             // The amt scrolled down
21396             var st = this.DDM.getScrollTop();
21397
21398             // The amt scrolled right
21399             var sl = this.DDM.getScrollLeft();
21400
21401             // Location of the bottom of the element
21402             var bot = h + y;
21403
21404             // Location of the right of the element
21405             var right = w + x;
21406
21407             // The distance from the cursor to the bottom of the visible area,
21408             // adjusted so that we don't scroll if the cursor is beyond the
21409             // element drag constraints
21410             var toBot = (clientH + st - y - this.deltaY);
21411
21412             // The distance from the cursor to the right of the visible area
21413             var toRight = (clientW + sl - x - this.deltaX);
21414
21415
21416             // How close to the edge the cursor must be before we scroll
21417             // var thresh = (document.all) ? 100 : 40;
21418             var thresh = 40;
21419
21420             // How many pixels to scroll per autoscroll op.  This helps to reduce
21421             // clunky scrolling. IE is more sensitive about this ... it needs this
21422             // value to be higher.
21423             var scrAmt = (document.all) ? 80 : 30;
21424
21425             // Scroll down if we are near the bottom of the visible page and the
21426             // obj extends below the crease
21427             if ( bot > clientH && toBot < thresh ) {
21428                 window.scrollTo(sl, st + scrAmt);
21429             }
21430
21431             // Scroll up if the window is scrolled down and the top of the object
21432             // goes above the top border
21433             if ( y < st && st > 0 && y - st < thresh ) {
21434                 window.scrollTo(sl, st - scrAmt);
21435             }
21436
21437             // Scroll right if the obj is beyond the right border and the cursor is
21438             // near the border.
21439             if ( right > clientW && toRight < thresh ) {
21440                 window.scrollTo(sl + scrAmt, st);
21441             }
21442
21443             // Scroll left if the window has been scrolled to the right and the obj
21444             // extends past the left border
21445             if ( x < sl && sl > 0 && x - sl < thresh ) {
21446                 window.scrollTo(sl - scrAmt, st);
21447             }
21448         }
21449     },
21450
21451     /**
21452      * Finds the location the element should be placed if we want to move
21453      * it to where the mouse location less the click offset would place us.
21454      * @method getTargetCoord
21455      * @param {int} iPageX the X coordinate of the click
21456      * @param {int} iPageY the Y coordinate of the click
21457      * @return an object that contains the coordinates (Object.x and Object.y)
21458      * @private
21459      */
21460     getTargetCoord: function(iPageX, iPageY) {
21461
21462
21463         var x = iPageX - this.deltaX;
21464         var y = iPageY - this.deltaY;
21465
21466         if (this.constrainX) {
21467             if (x < this.minX) { x = this.minX; }
21468             if (x > this.maxX) { x = this.maxX; }
21469         }
21470
21471         if (this.constrainY) {
21472             if (y < this.minY) { y = this.minY; }
21473             if (y > this.maxY) { y = this.maxY; }
21474         }
21475
21476         x = this.getTick(x, this.xTicks);
21477         y = this.getTick(y, this.yTicks);
21478
21479
21480         return {x:x, y:y};
21481     },
21482
21483     /*
21484      * Sets up config options specific to this class. Overrides
21485      * Roo.dd.DragDrop, but all versions of this method through the
21486      * inheritance chain are called
21487      */
21488     applyConfig: function() {
21489         Roo.dd.DD.superclass.applyConfig.call(this);
21490         this.scroll = (this.config.scroll !== false);
21491     },
21492
21493     /*
21494      * Event that fires prior to the onMouseDown event.  Overrides
21495      * Roo.dd.DragDrop.
21496      */
21497     b4MouseDown: function(e) {
21498         // this.resetConstraints();
21499         this.autoOffset(e.getPageX(),
21500                             e.getPageY());
21501     },
21502
21503     /*
21504      * Event that fires prior to the onDrag event.  Overrides
21505      * Roo.dd.DragDrop.
21506      */
21507     b4Drag: function(e) {
21508         this.setDragElPos(e.getPageX(),
21509                             e.getPageY());
21510     },
21511
21512     toString: function() {
21513         return ("DD " + this.id);
21514     }
21515
21516     //////////////////////////////////////////////////////////////////////////
21517     // Debugging ygDragDrop events that can be overridden
21518     //////////////////////////////////////////////////////////////////////////
21519     /*
21520     startDrag: function(x, y) {
21521     },
21522
21523     onDrag: function(e) {
21524     },
21525
21526     onDragEnter: function(e, id) {
21527     },
21528
21529     onDragOver: function(e, id) {
21530     },
21531
21532     onDragOut: function(e, id) {
21533     },
21534
21535     onDragDrop: function(e, id) {
21536     },
21537
21538     endDrag: function(e) {
21539     }
21540
21541     */
21542
21543 });/*
21544  * Based on:
21545  * Ext JS Library 1.1.1
21546  * Copyright(c) 2006-2007, Ext JS, LLC.
21547  *
21548  * Originally Released Under LGPL - original licence link has changed is not relivant.
21549  *
21550  * Fork - LGPL
21551  * <script type="text/javascript">
21552  */
21553
21554 /**
21555  * @class Roo.dd.DDProxy
21556  * A DragDrop implementation that inserts an empty, bordered div into
21557  * the document that follows the cursor during drag operations.  At the time of
21558  * the click, the frame div is resized to the dimensions of the linked html
21559  * element, and moved to the exact location of the linked element.
21560  *
21561  * References to the "frame" element refer to the single proxy element that
21562  * was created to be dragged in place of all DDProxy elements on the
21563  * page.
21564  *
21565  * @extends Roo.dd.DD
21566  * @constructor
21567  * @param {String} id the id of the linked html element
21568  * @param {String} sGroup the group of related DragDrop objects
21569  * @param {object} config an object containing configurable attributes
21570  *                Valid properties for DDProxy in addition to those in DragDrop:
21571  *                   resizeFrame, centerFrame, dragElId
21572  */
21573 Roo.dd.DDProxy = function(id, sGroup, config) {
21574     if (id) {
21575         this.init(id, sGroup, config);
21576         this.initFrame();
21577     }
21578 };
21579
21580 /**
21581  * The default drag frame div id
21582  * @property Roo.dd.DDProxy.dragElId
21583  * @type String
21584  * @static
21585  */
21586 Roo.dd.DDProxy.dragElId = "ygddfdiv";
21587
21588 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
21589
21590     /**
21591      * By default we resize the drag frame to be the same size as the element
21592      * we want to drag (this is to get the frame effect).  We can turn it off
21593      * if we want a different behavior.
21594      * @property resizeFrame
21595      * @type boolean
21596      */
21597     resizeFrame: true,
21598
21599     /**
21600      * By default the frame is positioned exactly where the drag element is, so
21601      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
21602      * you do not have constraints on the obj is to have the drag frame centered
21603      * around the cursor.  Set centerFrame to true for this effect.
21604      * @property centerFrame
21605      * @type boolean
21606      */
21607     centerFrame: false,
21608
21609     /**
21610      * Creates the proxy element if it does not yet exist
21611      * @method createFrame
21612      */
21613     createFrame: function() {
21614         var self = this;
21615         var body = document.body;
21616
21617         if (!body || !body.firstChild) {
21618             setTimeout( function() { self.createFrame(); }, 50 );
21619             return;
21620         }
21621
21622         var div = this.getDragEl();
21623
21624         if (!div) {
21625             div    = document.createElement("div");
21626             div.id = this.dragElId;
21627             var s  = div.style;
21628
21629             s.position   = "absolute";
21630             s.visibility = "hidden";
21631             s.cursor     = "move";
21632             s.border     = "2px solid #aaa";
21633             s.zIndex     = 999;
21634
21635             // appendChild can blow up IE if invoked prior to the window load event
21636             // while rendering a table.  It is possible there are other scenarios
21637             // that would cause this to happen as well.
21638             body.insertBefore(div, body.firstChild);
21639         }
21640     },
21641
21642     /**
21643      * Initialization for the drag frame element.  Must be called in the
21644      * constructor of all subclasses
21645      * @method initFrame
21646      */
21647     initFrame: function() {
21648         this.createFrame();
21649     },
21650
21651     applyConfig: function() {
21652         Roo.dd.DDProxy.superclass.applyConfig.call(this);
21653
21654         this.resizeFrame = (this.config.resizeFrame !== false);
21655         this.centerFrame = (this.config.centerFrame);
21656         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
21657     },
21658
21659     /**
21660      * Resizes the drag frame to the dimensions of the clicked object, positions
21661      * it over the object, and finally displays it
21662      * @method showFrame
21663      * @param {int} iPageX X click position
21664      * @param {int} iPageY Y click position
21665      * @private
21666      */
21667     showFrame: function(iPageX, iPageY) {
21668         var el = this.getEl();
21669         var dragEl = this.getDragEl();
21670         var s = dragEl.style;
21671
21672         this._resizeProxy();
21673
21674         if (this.centerFrame) {
21675             this.setDelta( Math.round(parseInt(s.width,  10)/2),
21676                            Math.round(parseInt(s.height, 10)/2) );
21677         }
21678
21679         this.setDragElPos(iPageX, iPageY);
21680
21681         Roo.fly(dragEl).show();
21682     },
21683
21684     /**
21685      * The proxy is automatically resized to the dimensions of the linked
21686      * element when a drag is initiated, unless resizeFrame is set to false
21687      * @method _resizeProxy
21688      * @private
21689      */
21690     _resizeProxy: function() {
21691         if (this.resizeFrame) {
21692             var el = this.getEl();
21693             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
21694         }
21695     },
21696
21697     // overrides Roo.dd.DragDrop
21698     b4MouseDown: function(e) {
21699         var x = e.getPageX();
21700         var y = e.getPageY();
21701         this.autoOffset(x, y);
21702         this.setDragElPos(x, y);
21703     },
21704
21705     // overrides Roo.dd.DragDrop
21706     b4StartDrag: function(x, y) {
21707         // show the drag frame
21708         this.showFrame(x, y);
21709     },
21710
21711     // overrides Roo.dd.DragDrop
21712     b4EndDrag: function(e) {
21713         Roo.fly(this.getDragEl()).hide();
21714     },
21715
21716     // overrides Roo.dd.DragDrop
21717     // By default we try to move the element to the last location of the frame.
21718     // This is so that the default behavior mirrors that of Roo.dd.DD.
21719     endDrag: function(e) {
21720
21721         var lel = this.getEl();
21722         var del = this.getDragEl();
21723
21724         // Show the drag frame briefly so we can get its position
21725         del.style.visibility = "";
21726
21727         this.beforeMove();
21728         // Hide the linked element before the move to get around a Safari
21729         // rendering bug.
21730         lel.style.visibility = "hidden";
21731         Roo.dd.DDM.moveToEl(lel, del);
21732         del.style.visibility = "hidden";
21733         lel.style.visibility = "";
21734
21735         this.afterDrag();
21736     },
21737
21738     beforeMove : function(){
21739
21740     },
21741
21742     afterDrag : function(){
21743
21744     },
21745
21746     toString: function() {
21747         return ("DDProxy " + this.id);
21748     }
21749
21750 });
21751 /*
21752  * Based on:
21753  * Ext JS Library 1.1.1
21754  * Copyright(c) 2006-2007, Ext JS, LLC.
21755  *
21756  * Originally Released Under LGPL - original licence link has changed is not relivant.
21757  *
21758  * Fork - LGPL
21759  * <script type="text/javascript">
21760  */
21761
21762  /**
21763  * @class Roo.dd.DDTarget
21764  * A DragDrop implementation that does not move, but can be a drop
21765  * target.  You would get the same result by simply omitting implementation
21766  * for the event callbacks, but this way we reduce the processing cost of the
21767  * event listener and the callbacks.
21768  * @extends Roo.dd.DragDrop
21769  * @constructor
21770  * @param {String} id the id of the element that is a drop target
21771  * @param {String} sGroup the group of related DragDrop objects
21772  * @param {object} config an object containing configurable attributes
21773  *                 Valid properties for DDTarget in addition to those in
21774  *                 DragDrop:
21775  *                    none
21776  */
21777 Roo.dd.DDTarget = function(id, sGroup, config) {
21778     if (id) {
21779         this.initTarget(id, sGroup, config);
21780     }
21781     if (config && (config.listeners || config.events)) { 
21782         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21783             listeners : config.listeners || {}, 
21784             events : config.events || {} 
21785         });    
21786     }
21787 };
21788
21789 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21790 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21791     toString: function() {
21792         return ("DDTarget " + this.id);
21793     }
21794 });
21795 /*
21796  * Based on:
21797  * Ext JS Library 1.1.1
21798  * Copyright(c) 2006-2007, Ext JS, LLC.
21799  *
21800  * Originally Released Under LGPL - original licence link has changed is not relivant.
21801  *
21802  * Fork - LGPL
21803  * <script type="text/javascript">
21804  */
21805  
21806
21807 /**
21808  * @class Roo.dd.ScrollManager
21809  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21810  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21811  * @singleton
21812  */
21813 Roo.dd.ScrollManager = function(){
21814     var ddm = Roo.dd.DragDropMgr;
21815     var els = {};
21816     var dragEl = null;
21817     var proc = {};
21818     
21819     
21820     
21821     var onStop = function(e){
21822         dragEl = null;
21823         clearProc();
21824     };
21825     
21826     var triggerRefresh = function(){
21827         if(ddm.dragCurrent){
21828              ddm.refreshCache(ddm.dragCurrent.groups);
21829         }
21830     };
21831     
21832     var doScroll = function(){
21833         if(ddm.dragCurrent){
21834             var dds = Roo.dd.ScrollManager;
21835             if(!dds.animate){
21836                 if(proc.el.scroll(proc.dir, dds.increment)){
21837                     triggerRefresh();
21838                 }
21839             }else{
21840                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21841             }
21842         }
21843     };
21844     
21845     var clearProc = function(){
21846         if(proc.id){
21847             clearInterval(proc.id);
21848         }
21849         proc.id = 0;
21850         proc.el = null;
21851         proc.dir = "";
21852     };
21853     
21854     var startProc = function(el, dir){
21855          Roo.log('scroll startproc');
21856         clearProc();
21857         proc.el = el;
21858         proc.dir = dir;
21859         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21860     };
21861     
21862     var onFire = function(e, isDrop){
21863        
21864         if(isDrop || !ddm.dragCurrent){ return; }
21865         var dds = Roo.dd.ScrollManager;
21866         if(!dragEl || dragEl != ddm.dragCurrent){
21867             dragEl = ddm.dragCurrent;
21868             // refresh regions on drag start
21869             dds.refreshCache();
21870         }
21871         
21872         var xy = Roo.lib.Event.getXY(e);
21873         var pt = new Roo.lib.Point(xy[0], xy[1]);
21874         for(var id in els){
21875             var el = els[id], r = el._region;
21876             if(r && r.contains(pt) && el.isScrollable()){
21877                 if(r.bottom - pt.y <= dds.thresh){
21878                     if(proc.el != el){
21879                         startProc(el, "down");
21880                     }
21881                     return;
21882                 }else if(r.right - pt.x <= dds.thresh){
21883                     if(proc.el != el){
21884                         startProc(el, "left");
21885                     }
21886                     return;
21887                 }else if(pt.y - r.top <= dds.thresh){
21888                     if(proc.el != el){
21889                         startProc(el, "up");
21890                     }
21891                     return;
21892                 }else if(pt.x - r.left <= dds.thresh){
21893                     if(proc.el != el){
21894                         startProc(el, "right");
21895                     }
21896                     return;
21897                 }
21898             }
21899         }
21900         clearProc();
21901     };
21902     
21903     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21904     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21905     
21906     return {
21907         /**
21908          * Registers new overflow element(s) to auto scroll
21909          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21910          */
21911         register : function(el){
21912             if(el instanceof Array){
21913                 for(var i = 0, len = el.length; i < len; i++) {
21914                         this.register(el[i]);
21915                 }
21916             }else{
21917                 el = Roo.get(el);
21918                 els[el.id] = el;
21919             }
21920             Roo.dd.ScrollManager.els = els;
21921         },
21922         
21923         /**
21924          * Unregisters overflow element(s) so they are no longer scrolled
21925          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21926          */
21927         unregister : function(el){
21928             if(el instanceof Array){
21929                 for(var i = 0, len = el.length; i < len; i++) {
21930                         this.unregister(el[i]);
21931                 }
21932             }else{
21933                 el = Roo.get(el);
21934                 delete els[el.id];
21935             }
21936         },
21937         
21938         /**
21939          * The number of pixels from the edge of a container the pointer needs to be to 
21940          * trigger scrolling (defaults to 25)
21941          * @type Number
21942          */
21943         thresh : 25,
21944         
21945         /**
21946          * The number of pixels to scroll in each scroll increment (defaults to 50)
21947          * @type Number
21948          */
21949         increment : 100,
21950         
21951         /**
21952          * The frequency of scrolls in milliseconds (defaults to 500)
21953          * @type Number
21954          */
21955         frequency : 500,
21956         
21957         /**
21958          * True to animate the scroll (defaults to true)
21959          * @type Boolean
21960          */
21961         animate: true,
21962         
21963         /**
21964          * The animation duration in seconds - 
21965          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21966          * @type Number
21967          */
21968         animDuration: .4,
21969         
21970         /**
21971          * Manually trigger a cache refresh.
21972          */
21973         refreshCache : function(){
21974             for(var id in els){
21975                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21976                     els[id]._region = els[id].getRegion();
21977                 }
21978             }
21979         }
21980     };
21981 }();/*
21982  * Based on:
21983  * Ext JS Library 1.1.1
21984  * Copyright(c) 2006-2007, Ext JS, LLC.
21985  *
21986  * Originally Released Under LGPL - original licence link has changed is not relivant.
21987  *
21988  * Fork - LGPL
21989  * <script type="text/javascript">
21990  */
21991  
21992
21993 /**
21994  * @class Roo.dd.Registry
21995  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21996  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21997  * @singleton
21998  */
21999 Roo.dd.Registry = function(){
22000     var elements = {}; 
22001     var handles = {}; 
22002     var autoIdSeed = 0;
22003
22004     var getId = function(el, autogen){
22005         if(typeof el == "string"){
22006             return el;
22007         }
22008         var id = el.id;
22009         if(!id && autogen !== false){
22010             id = "roodd-" + (++autoIdSeed);
22011             el.id = id;
22012         }
22013         return id;
22014     };
22015     
22016     return {
22017     /**
22018      * Register a drag drop element
22019      * @param {String|HTMLElement} element The id or DOM node to register
22020      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
22021      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
22022      * knows how to interpret, plus there are some specific properties known to the Registry that should be
22023      * populated in the data object (if applicable):
22024      * <pre>
22025 Value      Description<br />
22026 ---------  ------------------------------------------<br />
22027 handles    Array of DOM nodes that trigger dragging<br />
22028            for the element being registered<br />
22029 isHandle   True if the element passed in triggers<br />
22030            dragging itself, else false
22031 </pre>
22032      */
22033         register : function(el, data){
22034             data = data || {};
22035             if(typeof el == "string"){
22036                 el = document.getElementById(el);
22037             }
22038             data.ddel = el;
22039             elements[getId(el)] = data;
22040             if(data.isHandle !== false){
22041                 handles[data.ddel.id] = data;
22042             }
22043             if(data.handles){
22044                 var hs = data.handles;
22045                 for(var i = 0, len = hs.length; i < len; i++){
22046                         handles[getId(hs[i])] = data;
22047                 }
22048             }
22049         },
22050
22051     /**
22052      * Unregister a drag drop element
22053      * @param {String|HTMLElement}  element The id or DOM node to unregister
22054      */
22055         unregister : function(el){
22056             var id = getId(el, false);
22057             var data = elements[id];
22058             if(data){
22059                 delete elements[id];
22060                 if(data.handles){
22061                     var hs = data.handles;
22062                     for(var i = 0, len = hs.length; i < len; i++){
22063                         delete handles[getId(hs[i], false)];
22064                     }
22065                 }
22066             }
22067         },
22068
22069     /**
22070      * Returns the handle registered for a DOM Node by id
22071      * @param {String|HTMLElement} id The DOM node or id to look up
22072      * @return {Object} handle The custom handle data
22073      */
22074         getHandle : function(id){
22075             if(typeof id != "string"){ // must be element?
22076                 id = id.id;
22077             }
22078             return handles[id];
22079         },
22080
22081     /**
22082      * Returns the handle that is registered for the DOM node that is the target of the event
22083      * @param {Event} e The event
22084      * @return {Object} handle The custom handle data
22085      */
22086         getHandleFromEvent : function(e){
22087             var t = Roo.lib.Event.getTarget(e);
22088             return t ? handles[t.id] : null;
22089         },
22090
22091     /**
22092      * Returns a custom data object that is registered for a DOM node by id
22093      * @param {String|HTMLElement} id The DOM node or id to look up
22094      * @return {Object} data The custom data
22095      */
22096         getTarget : function(id){
22097             if(typeof id != "string"){ // must be element?
22098                 id = id.id;
22099             }
22100             return elements[id];
22101         },
22102
22103     /**
22104      * Returns a custom data object that is registered for the DOM node that is the target of the event
22105      * @param {Event} e The event
22106      * @return {Object} data The custom data
22107      */
22108         getTargetFromEvent : function(e){
22109             var t = Roo.lib.Event.getTarget(e);
22110             return t ? elements[t.id] || handles[t.id] : null;
22111         }
22112     };
22113 }();/*
22114  * Based on:
22115  * Ext JS Library 1.1.1
22116  * Copyright(c) 2006-2007, Ext JS, LLC.
22117  *
22118  * Originally Released Under LGPL - original licence link has changed is not relivant.
22119  *
22120  * Fork - LGPL
22121  * <script type="text/javascript">
22122  */
22123  
22124
22125 /**
22126  * @class Roo.dd.StatusProxy
22127  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
22128  * default drag proxy used by all Roo.dd components.
22129  * @constructor
22130  * @param {Object} config
22131  */
22132 Roo.dd.StatusProxy = function(config){
22133     Roo.apply(this, config);
22134     this.id = this.id || Roo.id();
22135     this.el = new Roo.Layer({
22136         dh: {
22137             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
22138                 {tag: "div", cls: "x-dd-drop-icon"},
22139                 {tag: "div", cls: "x-dd-drag-ghost"}
22140             ]
22141         }, 
22142         shadow: !config || config.shadow !== false
22143     });
22144     this.ghost = Roo.get(this.el.dom.childNodes[1]);
22145     this.dropStatus = this.dropNotAllowed;
22146 };
22147
22148 Roo.dd.StatusProxy.prototype = {
22149     /**
22150      * @cfg {String} dropAllowed
22151      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
22152      */
22153     dropAllowed : "x-dd-drop-ok",
22154     /**
22155      * @cfg {String} dropNotAllowed
22156      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
22157      */
22158     dropNotAllowed : "x-dd-drop-nodrop",
22159
22160     /**
22161      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
22162      * over the current target element.
22163      * @param {String} cssClass The css class for the new drop status indicator image
22164      */
22165     setStatus : function(cssClass){
22166         cssClass = cssClass || this.dropNotAllowed;
22167         if(this.dropStatus != cssClass){
22168             this.el.replaceClass(this.dropStatus, cssClass);
22169             this.dropStatus = cssClass;
22170         }
22171     },
22172
22173     /**
22174      * Resets the status indicator to the default dropNotAllowed value
22175      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
22176      */
22177     reset : function(clearGhost){
22178         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
22179         this.dropStatus = this.dropNotAllowed;
22180         if(clearGhost){
22181             this.ghost.update("");
22182         }
22183     },
22184
22185     /**
22186      * Updates the contents of the ghost element
22187      * @param {String} html The html that will replace the current innerHTML of the ghost element
22188      */
22189     update : function(html){
22190         if(typeof html == "string"){
22191             this.ghost.update(html);
22192         }else{
22193             this.ghost.update("");
22194             html.style.margin = "0";
22195             this.ghost.dom.appendChild(html);
22196         }
22197         // ensure float = none set?? cant remember why though.
22198         var el = this.ghost.dom.firstChild;
22199                 if(el){
22200                         Roo.fly(el).setStyle('float', 'none');
22201                 }
22202     },
22203     
22204     /**
22205      * Returns the underlying proxy {@link Roo.Layer}
22206      * @return {Roo.Layer} el
22207     */
22208     getEl : function(){
22209         return this.el;
22210     },
22211
22212     /**
22213      * Returns the ghost element
22214      * @return {Roo.Element} el
22215      */
22216     getGhost : function(){
22217         return this.ghost;
22218     },
22219
22220     /**
22221      * Hides the proxy
22222      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
22223      */
22224     hide : function(clear){
22225         this.el.hide();
22226         if(clear){
22227             this.reset(true);
22228         }
22229     },
22230
22231     /**
22232      * Stops the repair animation if it's currently running
22233      */
22234     stop : function(){
22235         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
22236             this.anim.stop();
22237         }
22238     },
22239
22240     /**
22241      * Displays this proxy
22242      */
22243     show : function(){
22244         this.el.show();
22245     },
22246
22247     /**
22248      * Force the Layer to sync its shadow and shim positions to the element
22249      */
22250     sync : function(){
22251         this.el.sync();
22252     },
22253
22254     /**
22255      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
22256      * invalid drop operation by the item being dragged.
22257      * @param {Array} xy The XY position of the element ([x, y])
22258      * @param {Function} callback The function to call after the repair is complete
22259      * @param {Object} scope The scope in which to execute the callback
22260      */
22261     repair : function(xy, callback, scope){
22262         this.callback = callback;
22263         this.scope = scope;
22264         if(xy && this.animRepair !== false){
22265             this.el.addClass("x-dd-drag-repair");
22266             this.el.hideUnders(true);
22267             this.anim = this.el.shift({
22268                 duration: this.repairDuration || .5,
22269                 easing: 'easeOut',
22270                 xy: xy,
22271                 stopFx: true,
22272                 callback: this.afterRepair,
22273                 scope: this
22274             });
22275         }else{
22276             this.afterRepair();
22277         }
22278     },
22279
22280     // private
22281     afterRepair : function(){
22282         this.hide(true);
22283         if(typeof this.callback == "function"){
22284             this.callback.call(this.scope || this);
22285         }
22286         this.callback = null;
22287         this.scope = null;
22288     }
22289 };/*
22290  * Based on:
22291  * Ext JS Library 1.1.1
22292  * Copyright(c) 2006-2007, Ext JS, LLC.
22293  *
22294  * Originally Released Under LGPL - original licence link has changed is not relivant.
22295  *
22296  * Fork - LGPL
22297  * <script type="text/javascript">
22298  */
22299
22300 /**
22301  * @class Roo.dd.DragSource
22302  * @extends Roo.dd.DDProxy
22303  * A simple class that provides the basic implementation needed to make any element draggable.
22304  * @constructor
22305  * @param {String/HTMLElement/Element} el The container element
22306  * @param {Object} config
22307  */
22308 Roo.dd.DragSource = function(el, config){
22309     this.el = Roo.get(el);
22310     this.dragData = {};
22311     
22312     Roo.apply(this, config);
22313     
22314     if(!this.proxy){
22315         this.proxy = new Roo.dd.StatusProxy();
22316     }
22317
22318     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
22319           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
22320     
22321     this.dragging = false;
22322 };
22323
22324 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
22325     /**
22326      * @cfg {String} dropAllowed
22327      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22328      */
22329     dropAllowed : "x-dd-drop-ok",
22330     /**
22331      * @cfg {String} dropNotAllowed
22332      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22333      */
22334     dropNotAllowed : "x-dd-drop-nodrop",
22335
22336     /**
22337      * Returns the data object associated with this drag source
22338      * @return {Object} data An object containing arbitrary data
22339      */
22340     getDragData : function(e){
22341         return this.dragData;
22342     },
22343
22344     // private
22345     onDragEnter : function(e, id){
22346         var target = Roo.dd.DragDropMgr.getDDById(id);
22347         this.cachedTarget = target;
22348         if(this.beforeDragEnter(target, e, id) !== false){
22349             if(target.isNotifyTarget){
22350                 var status = target.notifyEnter(this, e, this.dragData);
22351                 this.proxy.setStatus(status);
22352             }else{
22353                 this.proxy.setStatus(this.dropAllowed);
22354             }
22355             
22356             if(this.afterDragEnter){
22357                 /**
22358                  * An empty function by default, but provided so that you can perform a custom action
22359                  * when the dragged item enters the drop target by providing an implementation.
22360                  * @param {Roo.dd.DragDrop} target The drop target
22361                  * @param {Event} e The event object
22362                  * @param {String} id The id of the dragged element
22363                  * @method afterDragEnter
22364                  */
22365                 this.afterDragEnter(target, e, id);
22366             }
22367         }
22368     },
22369
22370     /**
22371      * An empty function by default, but provided so that you can perform a custom action
22372      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
22373      * @param {Roo.dd.DragDrop} target The drop target
22374      * @param {Event} e The event object
22375      * @param {String} id The id of the dragged element
22376      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22377      */
22378     beforeDragEnter : function(target, e, id){
22379         return true;
22380     },
22381
22382     // private
22383     alignElWithMouse: function() {
22384         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
22385         this.proxy.sync();
22386     },
22387
22388     // private
22389     onDragOver : function(e, id){
22390         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22391         if(this.beforeDragOver(target, e, id) !== false){
22392             if(target.isNotifyTarget){
22393                 var status = target.notifyOver(this, e, this.dragData);
22394                 this.proxy.setStatus(status);
22395             }
22396
22397             if(this.afterDragOver){
22398                 /**
22399                  * An empty function by default, but provided so that you can perform a custom action
22400                  * while the dragged item is over the drop target by providing an implementation.
22401                  * @param {Roo.dd.DragDrop} target The drop target
22402                  * @param {Event} e The event object
22403                  * @param {String} id The id of the dragged element
22404                  * @method afterDragOver
22405                  */
22406                 this.afterDragOver(target, e, id);
22407             }
22408         }
22409     },
22410
22411     /**
22412      * An empty function by default, but provided so that you can perform a custom action
22413      * while the dragged item is over the drop target and optionally cancel the onDragOver.
22414      * @param {Roo.dd.DragDrop} target The drop target
22415      * @param {Event} e The event object
22416      * @param {String} id The id of the dragged element
22417      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22418      */
22419     beforeDragOver : function(target, e, id){
22420         return true;
22421     },
22422
22423     // private
22424     onDragOut : function(e, id){
22425         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22426         if(this.beforeDragOut(target, e, id) !== false){
22427             if(target.isNotifyTarget){
22428                 target.notifyOut(this, e, this.dragData);
22429             }
22430             this.proxy.reset();
22431             if(this.afterDragOut){
22432                 /**
22433                  * An empty function by default, but provided so that you can perform a custom action
22434                  * after the dragged item is dragged out of the target without dropping.
22435                  * @param {Roo.dd.DragDrop} target The drop target
22436                  * @param {Event} e The event object
22437                  * @param {String} id The id of the dragged element
22438                  * @method afterDragOut
22439                  */
22440                 this.afterDragOut(target, e, id);
22441             }
22442         }
22443         this.cachedTarget = null;
22444     },
22445
22446     /**
22447      * An empty function by default, but provided so that you can perform a custom action before the dragged
22448      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
22449      * @param {Roo.dd.DragDrop} target The drop target
22450      * @param {Event} e The event object
22451      * @param {String} id The id of the dragged element
22452      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22453      */
22454     beforeDragOut : function(target, e, id){
22455         return true;
22456     },
22457     
22458     // private
22459     onDragDrop : function(e, id){
22460         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22461         if(this.beforeDragDrop(target, e, id) !== false){
22462             if(target.isNotifyTarget){
22463                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
22464                     this.onValidDrop(target, e, id);
22465                 }else{
22466                     this.onInvalidDrop(target, e, id);
22467                 }
22468             }else{
22469                 this.onValidDrop(target, e, id);
22470             }
22471             
22472             if(this.afterDragDrop){
22473                 /**
22474                  * An empty function by default, but provided so that you can perform a custom action
22475                  * after a valid drag drop has occurred by providing an implementation.
22476                  * @param {Roo.dd.DragDrop} target The drop target
22477                  * @param {Event} e The event object
22478                  * @param {String} id The id of the dropped element
22479                  * @method afterDragDrop
22480                  */
22481                 this.afterDragDrop(target, e, id);
22482             }
22483         }
22484         delete this.cachedTarget;
22485     },
22486
22487     /**
22488      * An empty function by default, but provided so that you can perform a custom action before the dragged
22489      * item is dropped onto the target and optionally cancel the onDragDrop.
22490      * @param {Roo.dd.DragDrop} target The drop target
22491      * @param {Event} e The event object
22492      * @param {String} id The id of the dragged element
22493      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
22494      */
22495     beforeDragDrop : function(target, e, id){
22496         return true;
22497     },
22498
22499     // private
22500     onValidDrop : function(target, e, id){
22501         this.hideProxy();
22502         if(this.afterValidDrop){
22503             /**
22504              * An empty function by default, but provided so that you can perform a custom action
22505              * after a valid drop has occurred by providing an implementation.
22506              * @param {Object} target The target DD 
22507              * @param {Event} e The event object
22508              * @param {String} id The id of the dropped element
22509              * @method afterInvalidDrop
22510              */
22511             this.afterValidDrop(target, e, id);
22512         }
22513     },
22514
22515     // private
22516     getRepairXY : function(e, data){
22517         return this.el.getXY();  
22518     },
22519
22520     // private
22521     onInvalidDrop : function(target, e, id){
22522         this.beforeInvalidDrop(target, e, id);
22523         if(this.cachedTarget){
22524             if(this.cachedTarget.isNotifyTarget){
22525                 this.cachedTarget.notifyOut(this, e, this.dragData);
22526             }
22527             this.cacheTarget = null;
22528         }
22529         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
22530
22531         if(this.afterInvalidDrop){
22532             /**
22533              * An empty function by default, but provided so that you can perform a custom action
22534              * after an invalid drop has occurred by providing an implementation.
22535              * @param {Event} e The event object
22536              * @param {String} id The id of the dropped element
22537              * @method afterInvalidDrop
22538              */
22539             this.afterInvalidDrop(e, id);
22540         }
22541     },
22542
22543     // private
22544     afterRepair : function(){
22545         if(Roo.enableFx){
22546             this.el.highlight(this.hlColor || "c3daf9");
22547         }
22548         this.dragging = false;
22549     },
22550
22551     /**
22552      * An empty function by default, but provided so that you can perform a custom action after an invalid
22553      * drop has occurred.
22554      * @param {Roo.dd.DragDrop} target The drop target
22555      * @param {Event} e The event object
22556      * @param {String} id The id of the dragged element
22557      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
22558      */
22559     beforeInvalidDrop : function(target, e, id){
22560         return true;
22561     },
22562
22563     // private
22564     handleMouseDown : function(e){
22565         if(this.dragging) {
22566             return;
22567         }
22568         var data = this.getDragData(e);
22569         if(data && this.onBeforeDrag(data, e) !== false){
22570             this.dragData = data;
22571             this.proxy.stop();
22572             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
22573         } 
22574     },
22575
22576     /**
22577      * An empty function by default, but provided so that you can perform a custom action before the initial
22578      * drag event begins and optionally cancel it.
22579      * @param {Object} data An object containing arbitrary data to be shared with drop targets
22580      * @param {Event} e The event object
22581      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22582      */
22583     onBeforeDrag : function(data, e){
22584         return true;
22585     },
22586
22587     /**
22588      * An empty function by default, but provided so that you can perform a custom action once the initial
22589      * drag event has begun.  The drag cannot be canceled from this function.
22590      * @param {Number} x The x position of the click on the dragged object
22591      * @param {Number} y The y position of the click on the dragged object
22592      */
22593     onStartDrag : Roo.emptyFn,
22594
22595     // private - YUI override
22596     startDrag : function(x, y){
22597         this.proxy.reset();
22598         this.dragging = true;
22599         this.proxy.update("");
22600         this.onInitDrag(x, y);
22601         this.proxy.show();
22602     },
22603
22604     // private
22605     onInitDrag : function(x, y){
22606         var clone = this.el.dom.cloneNode(true);
22607         clone.id = Roo.id(); // prevent duplicate ids
22608         this.proxy.update(clone);
22609         this.onStartDrag(x, y);
22610         return true;
22611     },
22612
22613     /**
22614      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
22615      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
22616      */
22617     getProxy : function(){
22618         return this.proxy;  
22619     },
22620
22621     /**
22622      * Hides the drag source's {@link Roo.dd.StatusProxy}
22623      */
22624     hideProxy : function(){
22625         this.proxy.hide();  
22626         this.proxy.reset(true);
22627         this.dragging = false;
22628     },
22629
22630     // private
22631     triggerCacheRefresh : function(){
22632         Roo.dd.DDM.refreshCache(this.groups);
22633     },
22634
22635     // private - override to prevent hiding
22636     b4EndDrag: function(e) {
22637     },
22638
22639     // private - override to prevent moving
22640     endDrag : function(e){
22641         this.onEndDrag(this.dragData, e);
22642     },
22643
22644     // private
22645     onEndDrag : function(data, e){
22646     },
22647     
22648     // private - pin to cursor
22649     autoOffset : function(x, y) {
22650         this.setDelta(-12, -20);
22651     }    
22652 });/*
22653  * Based on:
22654  * Ext JS Library 1.1.1
22655  * Copyright(c) 2006-2007, Ext JS, LLC.
22656  *
22657  * Originally Released Under LGPL - original licence link has changed is not relivant.
22658  *
22659  * Fork - LGPL
22660  * <script type="text/javascript">
22661  */
22662
22663
22664 /**
22665  * @class Roo.dd.DropTarget
22666  * @extends Roo.dd.DDTarget
22667  * A simple class that provides the basic implementation needed to make any element a drop target that can have
22668  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
22669  * @constructor
22670  * @param {String/HTMLElement/Element} el The container element
22671  * @param {Object} config
22672  */
22673 Roo.dd.DropTarget = function(el, config){
22674     this.el = Roo.get(el);
22675     
22676     var listeners = false; ;
22677     if (config && config.listeners) {
22678         listeners= config.listeners;
22679         delete config.listeners;
22680     }
22681     Roo.apply(this, config);
22682     
22683     if(this.containerScroll){
22684         Roo.dd.ScrollManager.register(this.el);
22685     }
22686     this.addEvents( {
22687          /**
22688          * @scope Roo.dd.DropTarget
22689          */
22690          
22691          /**
22692          * @event enter
22693          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
22694          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
22695          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
22696          * 
22697          * IMPORTANT : it should set  this.valid to true|false
22698          * 
22699          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22700          * @param {Event} e The event
22701          * @param {Object} data An object containing arbitrary data supplied by the drag source
22702          */
22703         "enter" : true,
22704         
22705          /**
22706          * @event over
22707          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
22708          * This method will be called on every mouse movement while the drag source is over the drop target.
22709          * This default implementation simply returns the dropAllowed config value.
22710          * 
22711          * IMPORTANT : it should set  this.valid to true|false
22712          * 
22713          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22714          * @param {Event} e The event
22715          * @param {Object} data An object containing arbitrary data supplied by the drag source
22716          
22717          */
22718         "over" : true,
22719         /**
22720          * @event out
22721          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22722          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22723          * overClass (if any) from the drop element.
22724          * 
22725          * 
22726          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22727          * @param {Event} e The event
22728          * @param {Object} data An object containing arbitrary data supplied by the drag source
22729          */
22730          "out" : true,
22731          
22732         /**
22733          * @event drop
22734          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22735          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22736          * implementation that does something to process the drop event and returns true so that the drag source's
22737          * repair action does not run.
22738          * 
22739          * IMPORTANT : it should set this.success
22740          * 
22741          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22742          * @param {Event} e The event
22743          * @param {Object} data An object containing arbitrary data supplied by the drag source
22744         */
22745          "drop" : true
22746     });
22747             
22748      
22749     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22750         this.el.dom, 
22751         this.ddGroup || this.group,
22752         {
22753             isTarget: true,
22754             listeners : listeners || {} 
22755            
22756         
22757         }
22758     );
22759
22760 };
22761
22762 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22763     /**
22764      * @cfg {String} overClass
22765      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22766      */
22767      /**
22768      * @cfg {String} ddGroup
22769      * The drag drop group to handle drop events for
22770      */
22771      
22772     /**
22773      * @cfg {String} dropAllowed
22774      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22775      */
22776     dropAllowed : "x-dd-drop-ok",
22777     /**
22778      * @cfg {String} dropNotAllowed
22779      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22780      */
22781     dropNotAllowed : "x-dd-drop-nodrop",
22782     /**
22783      * @cfg {boolean} success
22784      * set this after drop listener.. 
22785      */
22786     success : false,
22787     /**
22788      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22789      * if the drop point is valid for over/enter..
22790      */
22791     valid : false,
22792     // private
22793     isTarget : true,
22794
22795     // private
22796     isNotifyTarget : true,
22797     
22798     /**
22799      * @hide
22800      */
22801     notifyEnter : function(dd, e, data)
22802     {
22803         this.valid = true;
22804         this.fireEvent('enter', dd, e, data);
22805         if(this.overClass){
22806             this.el.addClass(this.overClass);
22807         }
22808         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22809             this.valid ? this.dropAllowed : this.dropNotAllowed
22810         );
22811     },
22812
22813     /**
22814      * @hide
22815      */
22816     notifyOver : function(dd, e, data)
22817     {
22818         this.valid = true;
22819         this.fireEvent('over', dd, e, data);
22820         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22821             this.valid ? this.dropAllowed : this.dropNotAllowed
22822         );
22823     },
22824
22825     /**
22826      * @hide
22827      */
22828     notifyOut : function(dd, e, data)
22829     {
22830         this.fireEvent('out', dd, e, data);
22831         if(this.overClass){
22832             this.el.removeClass(this.overClass);
22833         }
22834     },
22835
22836     /**
22837      * @hide
22838      */
22839     notifyDrop : function(dd, e, data)
22840     {
22841         this.success = false;
22842         this.fireEvent('drop', dd, e, data);
22843         return this.success;
22844     }
22845 });/*
22846  * Based on:
22847  * Ext JS Library 1.1.1
22848  * Copyright(c) 2006-2007, Ext JS, LLC.
22849  *
22850  * Originally Released Under LGPL - original licence link has changed is not relivant.
22851  *
22852  * Fork - LGPL
22853  * <script type="text/javascript">
22854  */
22855
22856
22857 /**
22858  * @class Roo.dd.DragZone
22859  * @extends Roo.dd.DragSource
22860  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22861  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22862  * @constructor
22863  * @param {String/HTMLElement/Element} el The container element
22864  * @param {Object} config
22865  */
22866 Roo.dd.DragZone = function(el, config){
22867     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22868     if(this.containerScroll){
22869         Roo.dd.ScrollManager.register(this.el);
22870     }
22871 };
22872
22873 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22874     /**
22875      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22876      * for auto scrolling during drag operations.
22877      */
22878     /**
22879      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22880      * method after a failed drop (defaults to "c3daf9" - light blue)
22881      */
22882
22883     /**
22884      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22885      * for a valid target to drag based on the mouse down. Override this method
22886      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22887      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22888      * @param {EventObject} e The mouse down event
22889      * @return {Object} The dragData
22890      */
22891     getDragData : function(e){
22892         return Roo.dd.Registry.getHandleFromEvent(e);
22893     },
22894     
22895     /**
22896      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22897      * this.dragData.ddel
22898      * @param {Number} x The x position of the click on the dragged object
22899      * @param {Number} y The y position of the click on the dragged object
22900      * @return {Boolean} true to continue the drag, false to cancel
22901      */
22902     onInitDrag : function(x, y){
22903         this.proxy.update(this.dragData.ddel.cloneNode(true));
22904         this.onStartDrag(x, y);
22905         return true;
22906     },
22907     
22908     /**
22909      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22910      */
22911     afterRepair : function(){
22912         if(Roo.enableFx){
22913             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22914         }
22915         this.dragging = false;
22916     },
22917
22918     /**
22919      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22920      * the XY of this.dragData.ddel
22921      * @param {EventObject} e The mouse up event
22922      * @return {Array} The xy location (e.g. [100, 200])
22923      */
22924     getRepairXY : function(e){
22925         return Roo.Element.fly(this.dragData.ddel).getXY();  
22926     }
22927 });/*
22928  * Based on:
22929  * Ext JS Library 1.1.1
22930  * Copyright(c) 2006-2007, Ext JS, LLC.
22931  *
22932  * Originally Released Under LGPL - original licence link has changed is not relivant.
22933  *
22934  * Fork - LGPL
22935  * <script type="text/javascript">
22936  */
22937 /**
22938  * @class Roo.dd.DropZone
22939  * @extends Roo.dd.DropTarget
22940  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22941  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22942  * @constructor
22943  * @param {String/HTMLElement/Element} el The container element
22944  * @param {Object} config
22945  */
22946 Roo.dd.DropZone = function(el, config){
22947     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22948 };
22949
22950 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22951     /**
22952      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22953      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22954      * provide your own custom lookup.
22955      * @param {Event} e The event
22956      * @return {Object} data The custom data
22957      */
22958     getTargetFromEvent : function(e){
22959         return Roo.dd.Registry.getTargetFromEvent(e);
22960     },
22961
22962     /**
22963      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22964      * that it has registered.  This method has no default implementation and should be overridden to provide
22965      * node-specific processing if necessary.
22966      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22967      * {@link #getTargetFromEvent} for this node)
22968      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22969      * @param {Event} e The event
22970      * @param {Object} data An object containing arbitrary data supplied by the drag source
22971      */
22972     onNodeEnter : function(n, dd, e, data){
22973         
22974     },
22975
22976     /**
22977      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22978      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22979      * overridden to provide the proper feedback.
22980      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22981      * {@link #getTargetFromEvent} for this node)
22982      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22983      * @param {Event} e The event
22984      * @param {Object} data An object containing arbitrary data supplied by the drag source
22985      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22986      * underlying {@link Roo.dd.StatusProxy} can be updated
22987      */
22988     onNodeOver : function(n, dd, e, data){
22989         return this.dropAllowed;
22990     },
22991
22992     /**
22993      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22994      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22995      * node-specific processing if necessary.
22996      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22997      * {@link #getTargetFromEvent} for this node)
22998      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22999      * @param {Event} e The event
23000      * @param {Object} data An object containing arbitrary data supplied by the drag source
23001      */
23002     onNodeOut : function(n, dd, e, data){
23003         
23004     },
23005
23006     /**
23007      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
23008      * the drop node.  The default implementation returns false, so it should be overridden to provide the
23009      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
23010      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23011      * {@link #getTargetFromEvent} for this node)
23012      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23013      * @param {Event} e The event
23014      * @param {Object} data An object containing arbitrary data supplied by the drag source
23015      * @return {Boolean} True if the drop was valid, else false
23016      */
23017     onNodeDrop : function(n, dd, e, data){
23018         return false;
23019     },
23020
23021     /**
23022      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
23023      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
23024      * it should be overridden to provide the proper feedback if necessary.
23025      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23026      * @param {Event} e The event
23027      * @param {Object} data An object containing arbitrary data supplied by the drag source
23028      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23029      * underlying {@link Roo.dd.StatusProxy} can be updated
23030      */
23031     onContainerOver : function(dd, e, data){
23032         return this.dropNotAllowed;
23033     },
23034
23035     /**
23036      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
23037      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
23038      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
23039      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
23040      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23041      * @param {Event} e The event
23042      * @param {Object} data An object containing arbitrary data supplied by the drag source
23043      * @return {Boolean} True if the drop was valid, else false
23044      */
23045     onContainerDrop : function(dd, e, data){
23046         return false;
23047     },
23048
23049     /**
23050      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
23051      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
23052      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
23053      * you should override this method and provide a custom implementation.
23054      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23055      * @param {Event} e The event
23056      * @param {Object} data An object containing arbitrary data supplied by the drag source
23057      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23058      * underlying {@link Roo.dd.StatusProxy} can be updated
23059      */
23060     notifyEnter : function(dd, e, data){
23061         return this.dropNotAllowed;
23062     },
23063
23064     /**
23065      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
23066      * This method will be called on every mouse movement while the drag source is over the drop zone.
23067      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
23068      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
23069      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
23070      * registered node, it will call {@link #onContainerOver}.
23071      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23072      * @param {Event} e The event
23073      * @param {Object} data An object containing arbitrary data supplied by the drag source
23074      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23075      * underlying {@link Roo.dd.StatusProxy} can be updated
23076      */
23077     notifyOver : function(dd, e, data){
23078         var n = this.getTargetFromEvent(e);
23079         if(!n){ // not over valid drop target
23080             if(this.lastOverNode){
23081                 this.onNodeOut(this.lastOverNode, dd, e, data);
23082                 this.lastOverNode = null;
23083             }
23084             return this.onContainerOver(dd, e, data);
23085         }
23086         if(this.lastOverNode != n){
23087             if(this.lastOverNode){
23088                 this.onNodeOut(this.lastOverNode, dd, e, data);
23089             }
23090             this.onNodeEnter(n, dd, e, data);
23091             this.lastOverNode = n;
23092         }
23093         return this.onNodeOver(n, dd, e, data);
23094     },
23095
23096     /**
23097      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
23098      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
23099      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
23100      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23101      * @param {Event} e The event
23102      * @param {Object} data An object containing arbitrary data supplied by the drag zone
23103      */
23104     notifyOut : function(dd, e, data){
23105         if(this.lastOverNode){
23106             this.onNodeOut(this.lastOverNode, dd, e, data);
23107             this.lastOverNode = null;
23108         }
23109     },
23110
23111     /**
23112      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
23113      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
23114      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
23115      * otherwise it will call {@link #onContainerDrop}.
23116      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23117      * @param {Event} e The event
23118      * @param {Object} data An object containing arbitrary data supplied by the drag source
23119      * @return {Boolean} True if the drop was valid, else false
23120      */
23121     notifyDrop : function(dd, e, data){
23122         if(this.lastOverNode){
23123             this.onNodeOut(this.lastOverNode, dd, e, data);
23124             this.lastOverNode = null;
23125         }
23126         var n = this.getTargetFromEvent(e);
23127         return n ?
23128             this.onNodeDrop(n, dd, e, data) :
23129             this.onContainerDrop(dd, e, data);
23130     },
23131
23132     // private
23133     triggerCacheRefresh : function(){
23134         Roo.dd.DDM.refreshCache(this.groups);
23135     }  
23136 });/*
23137  * Based on:
23138  * Ext JS Library 1.1.1
23139  * Copyright(c) 2006-2007, Ext JS, LLC.
23140  *
23141  * Originally Released Under LGPL - original licence link has changed is not relivant.
23142  *
23143  * Fork - LGPL
23144  * <script type="text/javascript">
23145  */
23146
23147
23148 /**
23149  * @class Roo.data.SortTypes
23150  * @singleton
23151  * Defines the default sorting (casting?) comparison functions used when sorting data.
23152  */
23153 Roo.data.SortTypes = {
23154     /**
23155      * Default sort that does nothing
23156      * @param {Mixed} s The value being converted
23157      * @return {Mixed} The comparison value
23158      */
23159     none : function(s){
23160         return s;
23161     },
23162     
23163     /**
23164      * The regular expression used to strip tags
23165      * @type {RegExp}
23166      * @property
23167      */
23168     stripTagsRE : /<\/?[^>]+>/gi,
23169     
23170     /**
23171      * Strips all HTML tags to sort on text only
23172      * @param {Mixed} s The value being converted
23173      * @return {String} The comparison value
23174      */
23175     asText : function(s){
23176         return String(s).replace(this.stripTagsRE, "");
23177     },
23178     
23179     /**
23180      * Strips all HTML tags to sort on text only - Case insensitive
23181      * @param {Mixed} s The value being converted
23182      * @return {String} The comparison value
23183      */
23184     asUCText : function(s){
23185         return String(s).toUpperCase().replace(this.stripTagsRE, "");
23186     },
23187     
23188     /**
23189      * Case insensitive string
23190      * @param {Mixed} s The value being converted
23191      * @return {String} The comparison value
23192      */
23193     asUCString : function(s) {
23194         return String(s).toUpperCase();
23195     },
23196     
23197     /**
23198      * Date sorting
23199      * @param {Mixed} s The value being converted
23200      * @return {Number} The comparison value
23201      */
23202     asDate : function(s) {
23203         if(!s){
23204             return 0;
23205         }
23206         if(s instanceof Date){
23207             return s.getTime();
23208         }
23209         return Date.parse(String(s));
23210     },
23211     
23212     /**
23213      * Float sorting
23214      * @param {Mixed} s The value being converted
23215      * @return {Float} The comparison value
23216      */
23217     asFloat : function(s) {
23218         var val = parseFloat(String(s).replace(/,/g, ""));
23219         if(isNaN(val)) {
23220             val = 0;
23221         }
23222         return val;
23223     },
23224     
23225     /**
23226      * Integer sorting
23227      * @param {Mixed} s The value being converted
23228      * @return {Number} The comparison value
23229      */
23230     asInt : function(s) {
23231         var val = parseInt(String(s).replace(/,/g, ""));
23232         if(isNaN(val)) {
23233             val = 0;
23234         }
23235         return val;
23236     }
23237 };/*
23238  * Based on:
23239  * Ext JS Library 1.1.1
23240  * Copyright(c) 2006-2007, Ext JS, LLC.
23241  *
23242  * Originally Released Under LGPL - original licence link has changed is not relivant.
23243  *
23244  * Fork - LGPL
23245  * <script type="text/javascript">
23246  */
23247
23248 /**
23249 * @class Roo.data.Record
23250  * Instances of this class encapsulate both record <em>definition</em> information, and record
23251  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
23252  * to access Records cached in an {@link Roo.data.Store} object.<br>
23253  * <p>
23254  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
23255  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
23256  * objects.<br>
23257  * <p>
23258  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
23259  * @constructor
23260  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
23261  * {@link #create}. The parameters are the same.
23262  * @param {Array} data An associative Array of data values keyed by the field name.
23263  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
23264  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
23265  * not specified an integer id is generated.
23266  */
23267 Roo.data.Record = function(data, id){
23268     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
23269     this.data = data;
23270 };
23271
23272 /**
23273  * Generate a constructor for a specific record layout.
23274  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
23275  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
23276  * Each field definition object may contain the following properties: <ul>
23277  * <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,
23278  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
23279  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
23280  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
23281  * is being used, then this is a string containing the javascript expression to reference the data relative to 
23282  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
23283  * to the data item relative to the record element. If the mapping expression is the same as the field name,
23284  * this may be omitted.</p></li>
23285  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
23286  * <ul><li>auto (Default, implies no conversion)</li>
23287  * <li>string</li>
23288  * <li>int</li>
23289  * <li>float</li>
23290  * <li>boolean</li>
23291  * <li>date</li></ul></p></li>
23292  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
23293  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
23294  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
23295  * by the Reader into an object that will be stored in the Record. It is passed the
23296  * following parameters:<ul>
23297  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
23298  * </ul></p></li>
23299  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
23300  * </ul>
23301  * <br>usage:<br><pre><code>
23302 var TopicRecord = Roo.data.Record.create(
23303     {name: 'title', mapping: 'topic_title'},
23304     {name: 'author', mapping: 'username'},
23305     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
23306     {name: 'lastPost', mapping: 'post_time', type: 'date'},
23307     {name: 'lastPoster', mapping: 'user2'},
23308     {name: 'excerpt', mapping: 'post_text'}
23309 );
23310
23311 var myNewRecord = new TopicRecord({
23312     title: 'Do my job please',
23313     author: 'noobie',
23314     totalPosts: 1,
23315     lastPost: new Date(),
23316     lastPoster: 'Animal',
23317     excerpt: 'No way dude!'
23318 });
23319 myStore.add(myNewRecord);
23320 </code></pre>
23321  * @method create
23322  * @static
23323  */
23324 Roo.data.Record.create = function(o){
23325     var f = function(){
23326         f.superclass.constructor.apply(this, arguments);
23327     };
23328     Roo.extend(f, Roo.data.Record);
23329     var p = f.prototype;
23330     p.fields = new Roo.util.MixedCollection(false, function(field){
23331         return field.name;
23332     });
23333     for(var i = 0, len = o.length; i < len; i++){
23334         p.fields.add(new Roo.data.Field(o[i]));
23335     }
23336     f.getField = function(name){
23337         return p.fields.get(name);  
23338     };
23339     return f;
23340 };
23341
23342 Roo.data.Record.AUTO_ID = 1000;
23343 Roo.data.Record.EDIT = 'edit';
23344 Roo.data.Record.REJECT = 'reject';
23345 Roo.data.Record.COMMIT = 'commit';
23346
23347 Roo.data.Record.prototype = {
23348     /**
23349      * Readonly flag - true if this record has been modified.
23350      * @type Boolean
23351      */
23352     dirty : false,
23353     editing : false,
23354     error: null,
23355     modified: null,
23356
23357     // private
23358     join : function(store){
23359         this.store = store;
23360     },
23361
23362     /**
23363      * Set the named field to the specified value.
23364      * @param {String} name The name of the field to set.
23365      * @param {Object} value The value to set the field to.
23366      */
23367     set : function(name, value){
23368         if(this.data[name] == value){
23369             return;
23370         }
23371         this.dirty = true;
23372         if(!this.modified){
23373             this.modified = {};
23374         }
23375         if(typeof this.modified[name] == 'undefined'){
23376             this.modified[name] = this.data[name];
23377         }
23378         this.data[name] = value;
23379         if(!this.editing && this.store){
23380             this.store.afterEdit(this);
23381         }       
23382     },
23383
23384     /**
23385      * Get the value of the named field.
23386      * @param {String} name The name of the field to get the value of.
23387      * @return {Object} The value of the field.
23388      */
23389     get : function(name){
23390         return this.data[name]; 
23391     },
23392
23393     // private
23394     beginEdit : function(){
23395         this.editing = true;
23396         this.modified = {}; 
23397     },
23398
23399     // private
23400     cancelEdit : function(){
23401         this.editing = false;
23402         delete this.modified;
23403     },
23404
23405     // private
23406     endEdit : function(){
23407         this.editing = false;
23408         if(this.dirty && this.store){
23409             this.store.afterEdit(this);
23410         }
23411     },
23412
23413     /**
23414      * Usually called by the {@link Roo.data.Store} which owns the Record.
23415      * Rejects all changes made to the Record since either creation, or the last commit operation.
23416      * Modified fields are reverted to their original values.
23417      * <p>
23418      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23419      * of reject operations.
23420      */
23421     reject : function(){
23422         var m = this.modified;
23423         for(var n in m){
23424             if(typeof m[n] != "function"){
23425                 this.data[n] = m[n];
23426             }
23427         }
23428         this.dirty = false;
23429         delete this.modified;
23430         this.editing = false;
23431         if(this.store){
23432             this.store.afterReject(this);
23433         }
23434     },
23435
23436     /**
23437      * Usually called by the {@link Roo.data.Store} which owns the Record.
23438      * Commits all changes made to the Record since either creation, or the last commit operation.
23439      * <p>
23440      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23441      * of commit operations.
23442      */
23443     commit : function(){
23444         this.dirty = false;
23445         delete this.modified;
23446         this.editing = false;
23447         if(this.store){
23448             this.store.afterCommit(this);
23449         }
23450     },
23451
23452     // private
23453     hasError : function(){
23454         return this.error != null;
23455     },
23456
23457     // private
23458     clearError : function(){
23459         this.error = null;
23460     },
23461
23462     /**
23463      * Creates a copy of this record.
23464      * @param {String} id (optional) A new record id if you don't want to use this record's id
23465      * @return {Record}
23466      */
23467     copy : function(newId) {
23468         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
23469     }
23470 };/*
23471  * Based on:
23472  * Ext JS Library 1.1.1
23473  * Copyright(c) 2006-2007, Ext JS, LLC.
23474  *
23475  * Originally Released Under LGPL - original licence link has changed is not relivant.
23476  *
23477  * Fork - LGPL
23478  * <script type="text/javascript">
23479  */
23480
23481
23482
23483 /**
23484  * @class Roo.data.Store
23485  * @extends Roo.util.Observable
23486  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
23487  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
23488  * <p>
23489  * 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
23490  * has no knowledge of the format of the data returned by the Proxy.<br>
23491  * <p>
23492  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
23493  * instances from the data object. These records are cached and made available through accessor functions.
23494  * @constructor
23495  * Creates a new Store.
23496  * @param {Object} config A config object containing the objects needed for the Store to access data,
23497  * and read the data into Records.
23498  */
23499 Roo.data.Store = function(config){
23500     this.data = new Roo.util.MixedCollection(false);
23501     this.data.getKey = function(o){
23502         return o.id;
23503     };
23504     this.baseParams = {};
23505     // private
23506     this.paramNames = {
23507         "start" : "start",
23508         "limit" : "limit",
23509         "sort" : "sort",
23510         "dir" : "dir",
23511         "multisort" : "_multisort"
23512     };
23513
23514     if(config && config.data){
23515         this.inlineData = config.data;
23516         delete config.data;
23517     }
23518
23519     Roo.apply(this, config);
23520     
23521     if(this.reader){ // reader passed
23522         this.reader = Roo.factory(this.reader, Roo.data);
23523         this.reader.xmodule = this.xmodule || false;
23524         if(!this.recordType){
23525             this.recordType = this.reader.recordType;
23526         }
23527         if(this.reader.onMetaChange){
23528             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
23529         }
23530     }
23531
23532     if(this.recordType){
23533         this.fields = this.recordType.prototype.fields;
23534     }
23535     this.modified = [];
23536
23537     this.addEvents({
23538         /**
23539          * @event datachanged
23540          * Fires when the data cache has changed, and a widget which is using this Store
23541          * as a Record cache should refresh its view.
23542          * @param {Store} this
23543          */
23544         datachanged : true,
23545         /**
23546          * @event metachange
23547          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
23548          * @param {Store} this
23549          * @param {Object} meta The JSON metadata
23550          */
23551         metachange : true,
23552         /**
23553          * @event add
23554          * Fires when Records have been added to the Store
23555          * @param {Store} this
23556          * @param {Roo.data.Record[]} records The array of Records added
23557          * @param {Number} index The index at which the record(s) were added
23558          */
23559         add : true,
23560         /**
23561          * @event remove
23562          * Fires when a Record has been removed from the Store
23563          * @param {Store} this
23564          * @param {Roo.data.Record} record The Record that was removed
23565          * @param {Number} index The index at which the record was removed
23566          */
23567         remove : true,
23568         /**
23569          * @event update
23570          * Fires when a Record has been updated
23571          * @param {Store} this
23572          * @param {Roo.data.Record} record The Record that was updated
23573          * @param {String} operation The update operation being performed.  Value may be one of:
23574          * <pre><code>
23575  Roo.data.Record.EDIT
23576  Roo.data.Record.REJECT
23577  Roo.data.Record.COMMIT
23578          * </code></pre>
23579          */
23580         update : true,
23581         /**
23582          * @event clear
23583          * Fires when the data cache has been cleared.
23584          * @param {Store} this
23585          */
23586         clear : true,
23587         /**
23588          * @event beforeload
23589          * Fires before a request is made for a new data object.  If the beforeload handler returns false
23590          * the load action will be canceled.
23591          * @param {Store} this
23592          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23593          */
23594         beforeload : true,
23595         /**
23596          * @event beforeloadadd
23597          * Fires after a new set of Records has been loaded.
23598          * @param {Store} this
23599          * @param {Roo.data.Record[]} records The Records that were loaded
23600          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23601          */
23602         beforeloadadd : true,
23603         /**
23604          * @event load
23605          * Fires after a new set of Records has been loaded, before they are added to the store.
23606          * @param {Store} this
23607          * @param {Roo.data.Record[]} records The Records that were loaded
23608          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23609          * @params {Object} return from reader
23610          */
23611         load : true,
23612         /**
23613          * @event loadexception
23614          * Fires if an exception occurs in the Proxy during loading.
23615          * Called with the signature of the Proxy's "loadexception" event.
23616          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
23617          * 
23618          * @param {Proxy} 
23619          * @param {Object} return from JsonData.reader() - success, totalRecords, records
23620          * @param {Object} load options 
23621          * @param {Object} jsonData from your request (normally this contains the Exception)
23622          */
23623         loadexception : true
23624     });
23625     
23626     if(this.proxy){
23627         this.proxy = Roo.factory(this.proxy, Roo.data);
23628         this.proxy.xmodule = this.xmodule || false;
23629         this.relayEvents(this.proxy,  ["loadexception"]);
23630     }
23631     this.sortToggle = {};
23632     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
23633
23634     Roo.data.Store.superclass.constructor.call(this);
23635
23636     if(this.inlineData){
23637         this.loadData(this.inlineData);
23638         delete this.inlineData;
23639     }
23640 };
23641
23642 Roo.extend(Roo.data.Store, Roo.util.Observable, {
23643      /**
23644     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
23645     * without a remote query - used by combo/forms at present.
23646     */
23647     
23648     /**
23649     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
23650     */
23651     /**
23652     * @cfg {Array} data Inline data to be loaded when the store is initialized.
23653     */
23654     /**
23655     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
23656     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
23657     */
23658     /**
23659     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
23660     * on any HTTP request
23661     */
23662     /**
23663     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
23664     */
23665     /**
23666     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
23667     */
23668     multiSort: false,
23669     /**
23670     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
23671     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
23672     */
23673     remoteSort : false,
23674
23675     /**
23676     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
23677      * loaded or when a record is removed. (defaults to false).
23678     */
23679     pruneModifiedRecords : false,
23680
23681     // private
23682     lastOptions : null,
23683
23684     /**
23685      * Add Records to the Store and fires the add event.
23686      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23687      */
23688     add : function(records){
23689         records = [].concat(records);
23690         for(var i = 0, len = records.length; i < len; i++){
23691             records[i].join(this);
23692         }
23693         var index = this.data.length;
23694         this.data.addAll(records);
23695         this.fireEvent("add", this, records, index);
23696     },
23697
23698     /**
23699      * Remove a Record from the Store and fires the remove event.
23700      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
23701      */
23702     remove : function(record){
23703         var index = this.data.indexOf(record);
23704         this.data.removeAt(index);
23705  
23706         if(this.pruneModifiedRecords){
23707             this.modified.remove(record);
23708         }
23709         this.fireEvent("remove", this, record, index);
23710     },
23711
23712     /**
23713      * Remove all Records from the Store and fires the clear event.
23714      */
23715     removeAll : function(){
23716         this.data.clear();
23717         if(this.pruneModifiedRecords){
23718             this.modified = [];
23719         }
23720         this.fireEvent("clear", this);
23721     },
23722
23723     /**
23724      * Inserts Records to the Store at the given index and fires the add event.
23725      * @param {Number} index The start index at which to insert the passed Records.
23726      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23727      */
23728     insert : function(index, records){
23729         records = [].concat(records);
23730         for(var i = 0, len = records.length; i < len; i++){
23731             this.data.insert(index, records[i]);
23732             records[i].join(this);
23733         }
23734         this.fireEvent("add", this, records, index);
23735     },
23736
23737     /**
23738      * Get the index within the cache of the passed Record.
23739      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23740      * @return {Number} The index of the passed Record. Returns -1 if not found.
23741      */
23742     indexOf : function(record){
23743         return this.data.indexOf(record);
23744     },
23745
23746     /**
23747      * Get the index within the cache of the Record with the passed id.
23748      * @param {String} id The id of the Record to find.
23749      * @return {Number} The index of the Record. Returns -1 if not found.
23750      */
23751     indexOfId : function(id){
23752         return this.data.indexOfKey(id);
23753     },
23754
23755     /**
23756      * Get the Record with the specified id.
23757      * @param {String} id The id of the Record to find.
23758      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23759      */
23760     getById : function(id){
23761         return this.data.key(id);
23762     },
23763
23764     /**
23765      * Get the Record at the specified index.
23766      * @param {Number} index The index of the Record to find.
23767      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23768      */
23769     getAt : function(index){
23770         return this.data.itemAt(index);
23771     },
23772
23773     /**
23774      * Returns a range of Records between specified indices.
23775      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23776      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23777      * @return {Roo.data.Record[]} An array of Records
23778      */
23779     getRange : function(start, end){
23780         return this.data.getRange(start, end);
23781     },
23782
23783     // private
23784     storeOptions : function(o){
23785         o = Roo.apply({}, o);
23786         delete o.callback;
23787         delete o.scope;
23788         this.lastOptions = o;
23789     },
23790
23791     /**
23792      * Loads the Record cache from the configured Proxy using the configured Reader.
23793      * <p>
23794      * If using remote paging, then the first load call must specify the <em>start</em>
23795      * and <em>limit</em> properties in the options.params property to establish the initial
23796      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23797      * <p>
23798      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23799      * and this call will return before the new data has been loaded. Perform any post-processing
23800      * in a callback function, or in a "load" event handler.</strong>
23801      * <p>
23802      * @param {Object} options An object containing properties which control loading options:<ul>
23803      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23804      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23805      * passed the following arguments:<ul>
23806      * <li>r : Roo.data.Record[]</li>
23807      * <li>options: Options object from the load call</li>
23808      * <li>success: Boolean success indicator</li></ul></li>
23809      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23810      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23811      * </ul>
23812      */
23813     load : function(options){
23814         options = options || {};
23815         if(this.fireEvent("beforeload", this, options) !== false){
23816             this.storeOptions(options);
23817             var p = Roo.apply(options.params || {}, this.baseParams);
23818             // if meta was not loaded from remote source.. try requesting it.
23819             if (!this.reader.metaFromRemote) {
23820                 p._requestMeta = 1;
23821             }
23822             if(this.sortInfo && this.remoteSort){
23823                 var pn = this.paramNames;
23824                 p[pn["sort"]] = this.sortInfo.field;
23825                 p[pn["dir"]] = this.sortInfo.direction;
23826             }
23827             if (this.multiSort) {
23828                 var pn = this.paramNames;
23829                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23830             }
23831             
23832             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23833         }
23834     },
23835
23836     /**
23837      * Reloads the Record cache from the configured Proxy using the configured Reader and
23838      * the options from the last load operation performed.
23839      * @param {Object} options (optional) An object containing properties which may override the options
23840      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23841      * the most recently used options are reused).
23842      */
23843     reload : function(options){
23844         this.load(Roo.applyIf(options||{}, this.lastOptions));
23845     },
23846
23847     // private
23848     // Called as a callback by the Reader during a load operation.
23849     loadRecords : function(o, options, success){
23850         if(!o || success === false){
23851             if(success !== false){
23852                 this.fireEvent("load", this, [], options, o);
23853             }
23854             if(options.callback){
23855                 options.callback.call(options.scope || this, [], options, false);
23856             }
23857             return;
23858         }
23859         // if data returned failure - throw an exception.
23860         if (o.success === false) {
23861             // show a message if no listener is registered.
23862             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23863                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23864             }
23865             // loadmask wil be hooked into this..
23866             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23867             return;
23868         }
23869         var r = o.records, t = o.totalRecords || r.length;
23870         
23871         this.fireEvent("beforeloadadd", this, r, options, o);
23872         
23873         if(!options || options.add !== true){
23874             if(this.pruneModifiedRecords){
23875                 this.modified = [];
23876             }
23877             for(var i = 0, len = r.length; i < len; i++){
23878                 r[i].join(this);
23879             }
23880             if(this.snapshot){
23881                 this.data = this.snapshot;
23882                 delete this.snapshot;
23883             }
23884             this.data.clear();
23885             this.data.addAll(r);
23886             this.totalLength = t;
23887             this.applySort();
23888             this.fireEvent("datachanged", this);
23889         }else{
23890             this.totalLength = Math.max(t, this.data.length+r.length);
23891             this.add(r);
23892         }
23893         
23894         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23895                 
23896             var e = new Roo.data.Record({});
23897
23898             e.set(this.parent.displayField, this.parent.emptyTitle);
23899             e.set(this.parent.valueField, '');
23900
23901             this.insert(0, e);
23902         }
23903             
23904         this.fireEvent("load", this, r, options, o);
23905         if(options.callback){
23906             options.callback.call(options.scope || this, r, options, true);
23907         }
23908     },
23909
23910
23911     /**
23912      * Loads data from a passed data block. A Reader which understands the format of the data
23913      * must have been configured in the constructor.
23914      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23915      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23916      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23917      */
23918     loadData : function(o, append){
23919         var r = this.reader.readRecords(o);
23920         this.loadRecords(r, {add: append}, true);
23921     },
23922     
23923      /**
23924      * using 'cn' the nested child reader read the child array into it's child stores.
23925      * @param {Object} rec The record with a 'children array
23926      */
23927     loadDataFromChildren : function(rec)
23928     {
23929         this.loadData(this.reader.toLoadData(rec));
23930     },
23931     
23932
23933     /**
23934      * Gets the number of cached records.
23935      * <p>
23936      * <em>If using paging, this may not be the total size of the dataset. If the data object
23937      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23938      * the data set size</em>
23939      */
23940     getCount : function(){
23941         return this.data.length || 0;
23942     },
23943
23944     /**
23945      * Gets the total number of records in the dataset as returned by the server.
23946      * <p>
23947      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23948      * the dataset size</em>
23949      */
23950     getTotalCount : function(){
23951         return this.totalLength || 0;
23952     },
23953
23954     /**
23955      * Returns the sort state of the Store as an object with two properties:
23956      * <pre><code>
23957  field {String} The name of the field by which the Records are sorted
23958  direction {String} The sort order, "ASC" or "DESC"
23959      * </code></pre>
23960      */
23961     getSortState : function(){
23962         return this.sortInfo;
23963     },
23964
23965     // private
23966     applySort : function(){
23967         if(this.sortInfo && !this.remoteSort){
23968             var s = this.sortInfo, f = s.field;
23969             var st = this.fields.get(f).sortType;
23970             var fn = function(r1, r2){
23971                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23972                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23973             };
23974             this.data.sort(s.direction, fn);
23975             if(this.snapshot && this.snapshot != this.data){
23976                 this.snapshot.sort(s.direction, fn);
23977             }
23978         }
23979     },
23980
23981     /**
23982      * Sets the default sort column and order to be used by the next load operation.
23983      * @param {String} fieldName The name of the field to sort by.
23984      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23985      */
23986     setDefaultSort : function(field, dir){
23987         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23988     },
23989
23990     /**
23991      * Sort the Records.
23992      * If remote sorting is used, the sort is performed on the server, and the cache is
23993      * reloaded. If local sorting is used, the cache is sorted internally.
23994      * @param {String} fieldName The name of the field to sort by.
23995      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23996      */
23997     sort : function(fieldName, dir){
23998         var f = this.fields.get(fieldName);
23999         if(!dir){
24000             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
24001             
24002             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
24003                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
24004             }else{
24005                 dir = f.sortDir;
24006             }
24007         }
24008         this.sortToggle[f.name] = dir;
24009         this.sortInfo = {field: f.name, direction: dir};
24010         if(!this.remoteSort){
24011             this.applySort();
24012             this.fireEvent("datachanged", this);
24013         }else{
24014             this.load(this.lastOptions);
24015         }
24016     },
24017
24018     /**
24019      * Calls the specified function for each of the Records in the cache.
24020      * @param {Function} fn The function to call. The Record is passed as the first parameter.
24021      * Returning <em>false</em> aborts and exits the iteration.
24022      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
24023      */
24024     each : function(fn, scope){
24025         this.data.each(fn, scope);
24026     },
24027
24028     /**
24029      * Gets all records modified since the last commit.  Modified records are persisted across load operations
24030      * (e.g., during paging).
24031      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
24032      */
24033     getModifiedRecords : function(){
24034         return this.modified;
24035     },
24036
24037     // private
24038     createFilterFn : function(property, value, anyMatch){
24039         if(!value.exec){ // not a regex
24040             value = String(value);
24041             if(value.length == 0){
24042                 return false;
24043             }
24044             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
24045         }
24046         return function(r){
24047             return value.test(r.data[property]);
24048         };
24049     },
24050
24051     /**
24052      * Sums the value of <i>property</i> for each record between start and end and returns the result.
24053      * @param {String} property A field on your records
24054      * @param {Number} start The record index to start at (defaults to 0)
24055      * @param {Number} end The last record index to include (defaults to length - 1)
24056      * @return {Number} The sum
24057      */
24058     sum : function(property, start, end){
24059         var rs = this.data.items, v = 0;
24060         start = start || 0;
24061         end = (end || end === 0) ? end : rs.length-1;
24062
24063         for(var i = start; i <= end; i++){
24064             v += (rs[i].data[property] || 0);
24065         }
24066         return v;
24067     },
24068
24069     /**
24070      * Filter the records by a specified property.
24071      * @param {String} field A field on your records
24072      * @param {String/RegExp} value Either a string that the field
24073      * should start with or a RegExp to test against the field
24074      * @param {Boolean} anyMatch True to match any part not just the beginning
24075      */
24076     filter : function(property, value, anyMatch){
24077         var fn = this.createFilterFn(property, value, anyMatch);
24078         return fn ? this.filterBy(fn) : this.clearFilter();
24079     },
24080
24081     /**
24082      * Filter by a function. The specified function will be called with each
24083      * record in this data source. If the function returns true the record is included,
24084      * otherwise it is filtered.
24085      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24086      * @param {Object} scope (optional) The scope of the function (defaults to this)
24087      */
24088     filterBy : function(fn, scope){
24089         this.snapshot = this.snapshot || this.data;
24090         this.data = this.queryBy(fn, scope||this);
24091         this.fireEvent("datachanged", this);
24092     },
24093
24094     /**
24095      * Query the records by a specified property.
24096      * @param {String} field A field on your records
24097      * @param {String/RegExp} value Either a string that the field
24098      * should start with or a RegExp to test against the field
24099      * @param {Boolean} anyMatch True to match any part not just the beginning
24100      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24101      */
24102     query : function(property, value, anyMatch){
24103         var fn = this.createFilterFn(property, value, anyMatch);
24104         return fn ? this.queryBy(fn) : this.data.clone();
24105     },
24106
24107     /**
24108      * Query by a function. The specified function will be called with each
24109      * record in this data source. If the function returns true the record is included
24110      * in the results.
24111      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24112      * @param {Object} scope (optional) The scope of the function (defaults to this)
24113       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24114      **/
24115     queryBy : function(fn, scope){
24116         var data = this.snapshot || this.data;
24117         return data.filterBy(fn, scope||this);
24118     },
24119
24120     /**
24121      * Collects unique values for a particular dataIndex from this store.
24122      * @param {String} dataIndex The property to collect
24123      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
24124      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
24125      * @return {Array} An array of the unique values
24126      **/
24127     collect : function(dataIndex, allowNull, bypassFilter){
24128         var d = (bypassFilter === true && this.snapshot) ?
24129                 this.snapshot.items : this.data.items;
24130         var v, sv, r = [], l = {};
24131         for(var i = 0, len = d.length; i < len; i++){
24132             v = d[i].data[dataIndex];
24133             sv = String(v);
24134             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
24135                 l[sv] = true;
24136                 r[r.length] = v;
24137             }
24138         }
24139         return r;
24140     },
24141
24142     /**
24143      * Revert to a view of the Record cache with no filtering applied.
24144      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
24145      */
24146     clearFilter : function(suppressEvent){
24147         if(this.snapshot && this.snapshot != this.data){
24148             this.data = this.snapshot;
24149             delete this.snapshot;
24150             if(suppressEvent !== true){
24151                 this.fireEvent("datachanged", this);
24152             }
24153         }
24154     },
24155
24156     // private
24157     afterEdit : function(record){
24158         if(this.modified.indexOf(record) == -1){
24159             this.modified.push(record);
24160         }
24161         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
24162     },
24163     
24164     // private
24165     afterReject : function(record){
24166         this.modified.remove(record);
24167         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
24168     },
24169
24170     // private
24171     afterCommit : function(record){
24172         this.modified.remove(record);
24173         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
24174     },
24175
24176     /**
24177      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
24178      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
24179      */
24180     commitChanges : function(){
24181         var m = this.modified.slice(0);
24182         this.modified = [];
24183         for(var i = 0, len = m.length; i < len; i++){
24184             m[i].commit();
24185         }
24186     },
24187
24188     /**
24189      * Cancel outstanding changes on all changed records.
24190      */
24191     rejectChanges : function(){
24192         var m = this.modified.slice(0);
24193         this.modified = [];
24194         for(var i = 0, len = m.length; i < len; i++){
24195             m[i].reject();
24196         }
24197     },
24198
24199     onMetaChange : function(meta, rtype, o){
24200         this.recordType = rtype;
24201         this.fields = rtype.prototype.fields;
24202         delete this.snapshot;
24203         this.sortInfo = meta.sortInfo || this.sortInfo;
24204         this.modified = [];
24205         this.fireEvent('metachange', this, this.reader.meta);
24206     },
24207     
24208     moveIndex : function(data, type)
24209     {
24210         var index = this.indexOf(data);
24211         
24212         var newIndex = index + type;
24213         
24214         this.remove(data);
24215         
24216         this.insert(newIndex, data);
24217         
24218     }
24219 });/*
24220  * Based on:
24221  * Ext JS Library 1.1.1
24222  * Copyright(c) 2006-2007, Ext JS, LLC.
24223  *
24224  * Originally Released Under LGPL - original licence link has changed is not relivant.
24225  *
24226  * Fork - LGPL
24227  * <script type="text/javascript">
24228  */
24229
24230 /**
24231  * @class Roo.data.SimpleStore
24232  * @extends Roo.data.Store
24233  * Small helper class to make creating Stores from Array data easier.
24234  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
24235  * @cfg {Array} fields An array of field definition objects, or field name strings.
24236  * @cfg {Object} an existing reader (eg. copied from another store)
24237  * @cfg {Array} data The multi-dimensional array of data
24238  * @constructor
24239  * @param {Object} config
24240  */
24241 Roo.data.SimpleStore = function(config)
24242 {
24243     Roo.data.SimpleStore.superclass.constructor.call(this, {
24244         isLocal : true,
24245         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
24246                 id: config.id
24247             },
24248             Roo.data.Record.create(config.fields)
24249         ),
24250         proxy : new Roo.data.MemoryProxy(config.data)
24251     });
24252     this.load();
24253 };
24254 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
24255  * Based on:
24256  * Ext JS Library 1.1.1
24257  * Copyright(c) 2006-2007, Ext JS, LLC.
24258  *
24259  * Originally Released Under LGPL - original licence link has changed is not relivant.
24260  *
24261  * Fork - LGPL
24262  * <script type="text/javascript">
24263  */
24264
24265 /**
24266 /**
24267  * @extends Roo.data.Store
24268  * @class Roo.data.JsonStore
24269  * Small helper class to make creating Stores for JSON data easier. <br/>
24270 <pre><code>
24271 var store = new Roo.data.JsonStore({
24272     url: 'get-images.php',
24273     root: 'images',
24274     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
24275 });
24276 </code></pre>
24277  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
24278  * JsonReader and HttpProxy (unless inline data is provided).</b>
24279  * @cfg {Array} fields An array of field definition objects, or field name strings.
24280  * @constructor
24281  * @param {Object} config
24282  */
24283 Roo.data.JsonStore = function(c){
24284     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
24285         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
24286         reader: new Roo.data.JsonReader(c, c.fields)
24287     }));
24288 };
24289 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
24290  * Based on:
24291  * Ext JS Library 1.1.1
24292  * Copyright(c) 2006-2007, Ext JS, LLC.
24293  *
24294  * Originally Released Under LGPL - original licence link has changed is not relivant.
24295  *
24296  * Fork - LGPL
24297  * <script type="text/javascript">
24298  */
24299
24300  
24301 Roo.data.Field = function(config){
24302     if(typeof config == "string"){
24303         config = {name: config};
24304     }
24305     Roo.apply(this, config);
24306     
24307     if(!this.type){
24308         this.type = "auto";
24309     }
24310     
24311     var st = Roo.data.SortTypes;
24312     // named sortTypes are supported, here we look them up
24313     if(typeof this.sortType == "string"){
24314         this.sortType = st[this.sortType];
24315     }
24316     
24317     // set default sortType for strings and dates
24318     if(!this.sortType){
24319         switch(this.type){
24320             case "string":
24321                 this.sortType = st.asUCString;
24322                 break;
24323             case "date":
24324                 this.sortType = st.asDate;
24325                 break;
24326             default:
24327                 this.sortType = st.none;
24328         }
24329     }
24330
24331     // define once
24332     var stripRe = /[\$,%]/g;
24333
24334     // prebuilt conversion function for this field, instead of
24335     // switching every time we're reading a value
24336     if(!this.convert){
24337         var cv, dateFormat = this.dateFormat;
24338         switch(this.type){
24339             case "":
24340             case "auto":
24341             case undefined:
24342                 cv = function(v){ return v; };
24343                 break;
24344             case "string":
24345                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
24346                 break;
24347             case "int":
24348                 cv = function(v){
24349                     return v !== undefined && v !== null && v !== '' ?
24350                            parseInt(String(v).replace(stripRe, ""), 10) : '';
24351                     };
24352                 break;
24353             case "float":
24354                 cv = function(v){
24355                     return v !== undefined && v !== null && v !== '' ?
24356                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
24357                     };
24358                 break;
24359             case "bool":
24360             case "boolean":
24361                 cv = function(v){ return v === true || v === "true" || v == 1; };
24362                 break;
24363             case "date":
24364                 cv = function(v){
24365                     if(!v){
24366                         return '';
24367                     }
24368                     if(v instanceof Date){
24369                         return v;
24370                     }
24371                     if(dateFormat){
24372                         if(dateFormat == "timestamp"){
24373                             return new Date(v*1000);
24374                         }
24375                         return Date.parseDate(v, dateFormat);
24376                     }
24377                     var parsed = Date.parse(v);
24378                     return parsed ? new Date(parsed) : null;
24379                 };
24380              break;
24381             
24382         }
24383         this.convert = cv;
24384     }
24385 };
24386
24387 Roo.data.Field.prototype = {
24388     dateFormat: null,
24389     defaultValue: "",
24390     mapping: null,
24391     sortType : null,
24392     sortDir : "ASC"
24393 };/*
24394  * Based on:
24395  * Ext JS Library 1.1.1
24396  * Copyright(c) 2006-2007, Ext JS, LLC.
24397  *
24398  * Originally Released Under LGPL - original licence link has changed is not relivant.
24399  *
24400  * Fork - LGPL
24401  * <script type="text/javascript">
24402  */
24403  
24404 // Base class for reading structured data from a data source.  This class is intended to be
24405 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
24406
24407 /**
24408  * @class Roo.data.DataReader
24409  * Base class for reading structured data from a data source.  This class is intended to be
24410  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
24411  */
24412
24413 Roo.data.DataReader = function(meta, recordType){
24414     
24415     this.meta = meta;
24416     
24417     this.recordType = recordType instanceof Array ? 
24418         Roo.data.Record.create(recordType) : recordType;
24419 };
24420
24421 Roo.data.DataReader.prototype = {
24422     
24423     
24424     readerType : 'Data',
24425      /**
24426      * Create an empty record
24427      * @param {Object} data (optional) - overlay some values
24428      * @return {Roo.data.Record} record created.
24429      */
24430     newRow :  function(d) {
24431         var da =  {};
24432         this.recordType.prototype.fields.each(function(c) {
24433             switch( c.type) {
24434                 case 'int' : da[c.name] = 0; break;
24435                 case 'date' : da[c.name] = new Date(); break;
24436                 case 'float' : da[c.name] = 0.0; break;
24437                 case 'boolean' : da[c.name] = false; break;
24438                 default : da[c.name] = ""; break;
24439             }
24440             
24441         });
24442         return new this.recordType(Roo.apply(da, d));
24443     }
24444     
24445     
24446 };/*
24447  * Based on:
24448  * Ext JS Library 1.1.1
24449  * Copyright(c) 2006-2007, Ext JS, LLC.
24450  *
24451  * Originally Released Under LGPL - original licence link has changed is not relivant.
24452  *
24453  * Fork - LGPL
24454  * <script type="text/javascript">
24455  */
24456
24457 /**
24458  * @class Roo.data.DataProxy
24459  * @extends Roo.data.Observable
24460  * This class is an abstract base class for implementations which provide retrieval of
24461  * unformatted data objects.<br>
24462  * <p>
24463  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
24464  * (of the appropriate type which knows how to parse the data object) to provide a block of
24465  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
24466  * <p>
24467  * Custom implementations must implement the load method as described in
24468  * {@link Roo.data.HttpProxy#load}.
24469  */
24470 Roo.data.DataProxy = function(){
24471     this.addEvents({
24472         /**
24473          * @event beforeload
24474          * Fires before a network request is made to retrieve a data object.
24475          * @param {Object} This DataProxy object.
24476          * @param {Object} params The params parameter to the load function.
24477          */
24478         beforeload : true,
24479         /**
24480          * @event load
24481          * Fires before the load method's callback is called.
24482          * @param {Object} This DataProxy object.
24483          * @param {Object} o The data object.
24484          * @param {Object} arg The callback argument object passed to the load function.
24485          */
24486         load : true,
24487         /**
24488          * @event loadexception
24489          * Fires if an Exception occurs during data retrieval.
24490          * @param {Object} This DataProxy object.
24491          * @param {Object} o The data object.
24492          * @param {Object} arg The callback argument object passed to the load function.
24493          * @param {Object} e The Exception.
24494          */
24495         loadexception : true
24496     });
24497     Roo.data.DataProxy.superclass.constructor.call(this);
24498 };
24499
24500 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
24501
24502     /**
24503      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
24504      */
24505 /*
24506  * Based on:
24507  * Ext JS Library 1.1.1
24508  * Copyright(c) 2006-2007, Ext JS, LLC.
24509  *
24510  * Originally Released Under LGPL - original licence link has changed is not relivant.
24511  *
24512  * Fork - LGPL
24513  * <script type="text/javascript">
24514  */
24515 /**
24516  * @class Roo.data.MemoryProxy
24517  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
24518  * to the Reader when its load method is called.
24519  * @constructor
24520  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
24521  */
24522 Roo.data.MemoryProxy = function(data){
24523     if (data.data) {
24524         data = data.data;
24525     }
24526     Roo.data.MemoryProxy.superclass.constructor.call(this);
24527     this.data = data;
24528 };
24529
24530 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
24531     
24532     /**
24533      * Load data from the requested source (in this case an in-memory
24534      * data object passed to the constructor), read the data object into
24535      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24536      * process that block using the passed callback.
24537      * @param {Object} params This parameter is not used by the MemoryProxy class.
24538      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24539      * object into a block of Roo.data.Records.
24540      * @param {Function} callback The function into which to pass the block of Roo.data.records.
24541      * The function must be passed <ul>
24542      * <li>The Record block object</li>
24543      * <li>The "arg" argument from the load function</li>
24544      * <li>A boolean success indicator</li>
24545      * </ul>
24546      * @param {Object} scope The scope in which to call the callback
24547      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24548      */
24549     load : function(params, reader, callback, scope, arg){
24550         params = params || {};
24551         var result;
24552         try {
24553             result = reader.readRecords(params.data ? params.data :this.data);
24554         }catch(e){
24555             this.fireEvent("loadexception", this, arg, null, e);
24556             callback.call(scope, null, arg, false);
24557             return;
24558         }
24559         callback.call(scope, result, arg, true);
24560     },
24561     
24562     // private
24563     update : function(params, records){
24564         
24565     }
24566 });/*
24567  * Based on:
24568  * Ext JS Library 1.1.1
24569  * Copyright(c) 2006-2007, Ext JS, LLC.
24570  *
24571  * Originally Released Under LGPL - original licence link has changed is not relivant.
24572  *
24573  * Fork - LGPL
24574  * <script type="text/javascript">
24575  */
24576 /**
24577  * @class Roo.data.HttpProxy
24578  * @extends Roo.data.DataProxy
24579  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
24580  * configured to reference a certain URL.<br><br>
24581  * <p>
24582  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
24583  * from which the running page was served.<br><br>
24584  * <p>
24585  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
24586  * <p>
24587  * Be aware that to enable the browser to parse an XML document, the server must set
24588  * the Content-Type header in the HTTP response to "text/xml".
24589  * @constructor
24590  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
24591  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
24592  * will be used to make the request.
24593  */
24594 Roo.data.HttpProxy = function(conn){
24595     Roo.data.HttpProxy.superclass.constructor.call(this);
24596     // is conn a conn config or a real conn?
24597     this.conn = conn;
24598     this.useAjax = !conn || !conn.events;
24599   
24600 };
24601
24602 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
24603     // thse are take from connection...
24604     
24605     /**
24606      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
24607      */
24608     /**
24609      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
24610      * extra parameters to each request made by this object. (defaults to undefined)
24611      */
24612     /**
24613      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
24614      *  to each request made by this object. (defaults to undefined)
24615      */
24616     /**
24617      * @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)
24618      */
24619     /**
24620      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
24621      */
24622      /**
24623      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
24624      * @type Boolean
24625      */
24626   
24627
24628     /**
24629      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
24630      * @type Boolean
24631      */
24632     /**
24633      * Return the {@link Roo.data.Connection} object being used by this Proxy.
24634      * @return {Connection} The Connection object. This object may be used to subscribe to events on
24635      * a finer-grained basis than the DataProxy events.
24636      */
24637     getConnection : function(){
24638         return this.useAjax ? Roo.Ajax : this.conn;
24639     },
24640
24641     /**
24642      * Load data from the configured {@link Roo.data.Connection}, read the data object into
24643      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
24644      * process that block using the passed callback.
24645      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24646      * for the request to the remote server.
24647      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24648      * object into a block of Roo.data.Records.
24649      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24650      * The function must be passed <ul>
24651      * <li>The Record block object</li>
24652      * <li>The "arg" argument from the load function</li>
24653      * <li>A boolean success indicator</li>
24654      * </ul>
24655      * @param {Object} scope The scope in which to call the callback
24656      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24657      */
24658     load : function(params, reader, callback, scope, arg){
24659         if(this.fireEvent("beforeload", this, params) !== false){
24660             var  o = {
24661                 params : params || {},
24662                 request: {
24663                     callback : callback,
24664                     scope : scope,
24665                     arg : arg
24666                 },
24667                 reader: reader,
24668                 callback : this.loadResponse,
24669                 scope: this
24670             };
24671             if(this.useAjax){
24672                 Roo.applyIf(o, this.conn);
24673                 if(this.activeRequest){
24674                     Roo.Ajax.abort(this.activeRequest);
24675                 }
24676                 this.activeRequest = Roo.Ajax.request(o);
24677             }else{
24678                 this.conn.request(o);
24679             }
24680         }else{
24681             callback.call(scope||this, null, arg, false);
24682         }
24683     },
24684
24685     // private
24686     loadResponse : function(o, success, response){
24687         delete this.activeRequest;
24688         if(!success){
24689             this.fireEvent("loadexception", this, o, response);
24690             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24691             return;
24692         }
24693         var result;
24694         try {
24695             result = o.reader.read(response);
24696         }catch(e){
24697             this.fireEvent("loadexception", this, o, response, e);
24698             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24699             return;
24700         }
24701         
24702         this.fireEvent("load", this, o, o.request.arg);
24703         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24704     },
24705
24706     // private
24707     update : function(dataSet){
24708
24709     },
24710
24711     // private
24712     updateResponse : function(dataSet){
24713
24714     }
24715 });/*
24716  * Based on:
24717  * Ext JS Library 1.1.1
24718  * Copyright(c) 2006-2007, Ext JS, LLC.
24719  *
24720  * Originally Released Under LGPL - original licence link has changed is not relivant.
24721  *
24722  * Fork - LGPL
24723  * <script type="text/javascript">
24724  */
24725
24726 /**
24727  * @class Roo.data.ScriptTagProxy
24728  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24729  * other than the originating domain of the running page.<br><br>
24730  * <p>
24731  * <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
24732  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24733  * <p>
24734  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24735  * source code that is used as the source inside a &lt;script> tag.<br><br>
24736  * <p>
24737  * In order for the browser to process the returned data, the server must wrap the data object
24738  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24739  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24740  * depending on whether the callback name was passed:
24741  * <p>
24742  * <pre><code>
24743 boolean scriptTag = false;
24744 String cb = request.getParameter("callback");
24745 if (cb != null) {
24746     scriptTag = true;
24747     response.setContentType("text/javascript");
24748 } else {
24749     response.setContentType("application/x-json");
24750 }
24751 Writer out = response.getWriter();
24752 if (scriptTag) {
24753     out.write(cb + "(");
24754 }
24755 out.print(dataBlock.toJsonString());
24756 if (scriptTag) {
24757     out.write(");");
24758 }
24759 </pre></code>
24760  *
24761  * @constructor
24762  * @param {Object} config A configuration object.
24763  */
24764 Roo.data.ScriptTagProxy = function(config){
24765     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24766     Roo.apply(this, config);
24767     this.head = document.getElementsByTagName("head")[0];
24768 };
24769
24770 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24771
24772 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24773     /**
24774      * @cfg {String} url The URL from which to request the data object.
24775      */
24776     /**
24777      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24778      */
24779     timeout : 30000,
24780     /**
24781      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24782      * the server the name of the callback function set up by the load call to process the returned data object.
24783      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24784      * javascript output which calls this named function passing the data object as its only parameter.
24785      */
24786     callbackParam : "callback",
24787     /**
24788      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24789      * name to the request.
24790      */
24791     nocache : true,
24792
24793     /**
24794      * Load data from the configured URL, read the data object into
24795      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24796      * process that block using the passed callback.
24797      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24798      * for the request to the remote server.
24799      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24800      * object into a block of Roo.data.Records.
24801      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24802      * The function must be passed <ul>
24803      * <li>The Record block object</li>
24804      * <li>The "arg" argument from the load function</li>
24805      * <li>A boolean success indicator</li>
24806      * </ul>
24807      * @param {Object} scope The scope in which to call the callback
24808      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24809      */
24810     load : function(params, reader, callback, scope, arg){
24811         if(this.fireEvent("beforeload", this, params) !== false){
24812
24813             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24814
24815             var url = this.url;
24816             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24817             if(this.nocache){
24818                 url += "&_dc=" + (new Date().getTime());
24819             }
24820             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24821             var trans = {
24822                 id : transId,
24823                 cb : "stcCallback"+transId,
24824                 scriptId : "stcScript"+transId,
24825                 params : params,
24826                 arg : arg,
24827                 url : url,
24828                 callback : callback,
24829                 scope : scope,
24830                 reader : reader
24831             };
24832             var conn = this;
24833
24834             window[trans.cb] = function(o){
24835                 conn.handleResponse(o, trans);
24836             };
24837
24838             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24839
24840             if(this.autoAbort !== false){
24841                 this.abort();
24842             }
24843
24844             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24845
24846             var script = document.createElement("script");
24847             script.setAttribute("src", url);
24848             script.setAttribute("type", "text/javascript");
24849             script.setAttribute("id", trans.scriptId);
24850             this.head.appendChild(script);
24851
24852             this.trans = trans;
24853         }else{
24854             callback.call(scope||this, null, arg, false);
24855         }
24856     },
24857
24858     // private
24859     isLoading : function(){
24860         return this.trans ? true : false;
24861     },
24862
24863     /**
24864      * Abort the current server request.
24865      */
24866     abort : function(){
24867         if(this.isLoading()){
24868             this.destroyTrans(this.trans);
24869         }
24870     },
24871
24872     // private
24873     destroyTrans : function(trans, isLoaded){
24874         this.head.removeChild(document.getElementById(trans.scriptId));
24875         clearTimeout(trans.timeoutId);
24876         if(isLoaded){
24877             window[trans.cb] = undefined;
24878             try{
24879                 delete window[trans.cb];
24880             }catch(e){}
24881         }else{
24882             // if hasn't been loaded, wait for load to remove it to prevent script error
24883             window[trans.cb] = function(){
24884                 window[trans.cb] = undefined;
24885                 try{
24886                     delete window[trans.cb];
24887                 }catch(e){}
24888             };
24889         }
24890     },
24891
24892     // private
24893     handleResponse : function(o, trans){
24894         this.trans = false;
24895         this.destroyTrans(trans, true);
24896         var result;
24897         try {
24898             result = trans.reader.readRecords(o);
24899         }catch(e){
24900             this.fireEvent("loadexception", this, o, trans.arg, e);
24901             trans.callback.call(trans.scope||window, null, trans.arg, false);
24902             return;
24903         }
24904         this.fireEvent("load", this, o, trans.arg);
24905         trans.callback.call(trans.scope||window, result, trans.arg, true);
24906     },
24907
24908     // private
24909     handleFailure : function(trans){
24910         this.trans = false;
24911         this.destroyTrans(trans, false);
24912         this.fireEvent("loadexception", this, null, trans.arg);
24913         trans.callback.call(trans.scope||window, null, trans.arg, false);
24914     }
24915 });/*
24916  * Based on:
24917  * Ext JS Library 1.1.1
24918  * Copyright(c) 2006-2007, Ext JS, LLC.
24919  *
24920  * Originally Released Under LGPL - original licence link has changed is not relivant.
24921  *
24922  * Fork - LGPL
24923  * <script type="text/javascript">
24924  */
24925
24926 /**
24927  * @class Roo.data.JsonReader
24928  * @extends Roo.data.DataReader
24929  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24930  * based on mappings in a provided Roo.data.Record constructor.
24931  * 
24932  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24933  * in the reply previously. 
24934  * 
24935  * <p>
24936  * Example code:
24937  * <pre><code>
24938 var RecordDef = Roo.data.Record.create([
24939     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24940     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24941 ]);
24942 var myReader = new Roo.data.JsonReader({
24943     totalProperty: "results",    // The property which contains the total dataset size (optional)
24944     root: "rows",                // The property which contains an Array of row objects
24945     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24946 }, RecordDef);
24947 </code></pre>
24948  * <p>
24949  * This would consume a JSON file like this:
24950  * <pre><code>
24951 { 'results': 2, 'rows': [
24952     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24953     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24954 }
24955 </code></pre>
24956  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24957  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24958  * paged from the remote server.
24959  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24960  * @cfg {String} root name of the property which contains the Array of row objects.
24961  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24962  * @cfg {Array} fields Array of field definition objects
24963  * @constructor
24964  * Create a new JsonReader
24965  * @param {Object} meta Metadata configuration options
24966  * @param {Object} recordType Either an Array of field definition objects,
24967  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24968  */
24969 Roo.data.JsonReader = function(meta, recordType){
24970     
24971     meta = meta || {};
24972     // set some defaults:
24973     Roo.applyIf(meta, {
24974         totalProperty: 'total',
24975         successProperty : 'success',
24976         root : 'data',
24977         id : 'id'
24978     });
24979     
24980     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24981 };
24982 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24983     
24984     readerType : 'Json',
24985     
24986     /**
24987      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24988      * Used by Store query builder to append _requestMeta to params.
24989      * 
24990      */
24991     metaFromRemote : false,
24992     /**
24993      * This method is only used by a DataProxy which has retrieved data from a remote server.
24994      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24995      * @return {Object} data A data block which is used by an Roo.data.Store object as
24996      * a cache of Roo.data.Records.
24997      */
24998     read : function(response){
24999         var json = response.responseText;
25000        
25001         var o = /* eval:var:o */ eval("("+json+")");
25002         if(!o) {
25003             throw {message: "JsonReader.read: Json object not found"};
25004         }
25005         
25006         if(o.metaData){
25007             
25008             delete this.ef;
25009             this.metaFromRemote = true;
25010             this.meta = o.metaData;
25011             this.recordType = Roo.data.Record.create(o.metaData.fields);
25012             this.onMetaChange(this.meta, this.recordType, o);
25013         }
25014         return this.readRecords(o);
25015     },
25016
25017     // private function a store will implement
25018     onMetaChange : function(meta, recordType, o){
25019
25020     },
25021
25022     /**
25023          * @ignore
25024          */
25025     simpleAccess: function(obj, subsc) {
25026         return obj[subsc];
25027     },
25028
25029         /**
25030          * @ignore
25031          */
25032     getJsonAccessor: function(){
25033         var re = /[\[\.]/;
25034         return function(expr) {
25035             try {
25036                 return(re.test(expr))
25037                     ? new Function("obj", "return obj." + expr)
25038                     : function(obj){
25039                         return obj[expr];
25040                     };
25041             } catch(e){}
25042             return Roo.emptyFn;
25043         };
25044     }(),
25045
25046     /**
25047      * Create a data block containing Roo.data.Records from an XML document.
25048      * @param {Object} o An object which contains an Array of row objects in the property specified
25049      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
25050      * which contains the total size of the dataset.
25051      * @return {Object} data A data block which is used by an Roo.data.Store object as
25052      * a cache of Roo.data.Records.
25053      */
25054     readRecords : function(o){
25055         /**
25056          * After any data loads, the raw JSON data is available for further custom processing.
25057          * @type Object
25058          */
25059         this.o = o;
25060         var s = this.meta, Record = this.recordType,
25061             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
25062
25063 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
25064         if (!this.ef) {
25065             if(s.totalProperty) {
25066                     this.getTotal = this.getJsonAccessor(s.totalProperty);
25067                 }
25068                 if(s.successProperty) {
25069                     this.getSuccess = this.getJsonAccessor(s.successProperty);
25070                 }
25071                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
25072                 if (s.id) {
25073                         var g = this.getJsonAccessor(s.id);
25074                         this.getId = function(rec) {
25075                                 var r = g(rec);  
25076                                 return (r === undefined || r === "") ? null : r;
25077                         };
25078                 } else {
25079                         this.getId = function(){return null;};
25080                 }
25081             this.ef = [];
25082             for(var jj = 0; jj < fl; jj++){
25083                 f = fi[jj];
25084                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
25085                 this.ef[jj] = this.getJsonAccessor(map);
25086             }
25087         }
25088
25089         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
25090         if(s.totalProperty){
25091             var vt = parseInt(this.getTotal(o), 10);
25092             if(!isNaN(vt)){
25093                 totalRecords = vt;
25094             }
25095         }
25096         if(s.successProperty){
25097             var vs = this.getSuccess(o);
25098             if(vs === false || vs === 'false'){
25099                 success = false;
25100             }
25101         }
25102         var records = [];
25103         for(var i = 0; i < c; i++){
25104                 var n = root[i];
25105             var values = {};
25106             var id = this.getId(n);
25107             for(var j = 0; j < fl; j++){
25108                 f = fi[j];
25109             var v = this.ef[j](n);
25110             if (!f.convert) {
25111                 Roo.log('missing convert for ' + f.name);
25112                 Roo.log(f);
25113                 continue;
25114             }
25115             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
25116             }
25117             var record = new Record(values, id);
25118             record.json = n;
25119             records[i] = record;
25120         }
25121         return {
25122             raw : o,
25123             success : success,
25124             records : records,
25125             totalRecords : totalRecords
25126         };
25127     },
25128     // used when loading children.. @see loadDataFromChildren
25129     toLoadData: function(rec)
25130     {
25131         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25132         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25133         return { data : data, total : data.length };
25134         
25135     }
25136 });/*
25137  * Based on:
25138  * Ext JS Library 1.1.1
25139  * Copyright(c) 2006-2007, Ext JS, LLC.
25140  *
25141  * Originally Released Under LGPL - original licence link has changed is not relivant.
25142  *
25143  * Fork - LGPL
25144  * <script type="text/javascript">
25145  */
25146
25147 /**
25148  * @class Roo.data.XmlReader
25149  * @extends Roo.data.DataReader
25150  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
25151  * based on mappings in a provided Roo.data.Record constructor.<br><br>
25152  * <p>
25153  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
25154  * header in the HTTP response must be set to "text/xml".</em>
25155  * <p>
25156  * Example code:
25157  * <pre><code>
25158 var RecordDef = Roo.data.Record.create([
25159    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25160    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25161 ]);
25162 var myReader = new Roo.data.XmlReader({
25163    totalRecords: "results", // The element which contains the total dataset size (optional)
25164    record: "row",           // The repeated element which contains row information
25165    id: "id"                 // The element within the row that provides an ID for the record (optional)
25166 }, RecordDef);
25167 </code></pre>
25168  * <p>
25169  * This would consume an XML file like this:
25170  * <pre><code>
25171 &lt;?xml?>
25172 &lt;dataset>
25173  &lt;results>2&lt;/results>
25174  &lt;row>
25175    &lt;id>1&lt;/id>
25176    &lt;name>Bill&lt;/name>
25177    &lt;occupation>Gardener&lt;/occupation>
25178  &lt;/row>
25179  &lt;row>
25180    &lt;id>2&lt;/id>
25181    &lt;name>Ben&lt;/name>
25182    &lt;occupation>Horticulturalist&lt;/occupation>
25183  &lt;/row>
25184 &lt;/dataset>
25185 </code></pre>
25186  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
25187  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25188  * paged from the remote server.
25189  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
25190  * @cfg {String} success The DomQuery path to the success attribute used by forms.
25191  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
25192  * a record identifier value.
25193  * @constructor
25194  * Create a new XmlReader
25195  * @param {Object} meta Metadata configuration options
25196  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
25197  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
25198  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
25199  */
25200 Roo.data.XmlReader = function(meta, recordType){
25201     meta = meta || {};
25202     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25203 };
25204 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
25205     
25206     readerType : 'Xml',
25207     
25208     /**
25209      * This method is only used by a DataProxy which has retrieved data from a remote server.
25210          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
25211          * to contain a method called 'responseXML' that returns an XML document object.
25212      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25213      * a cache of Roo.data.Records.
25214      */
25215     read : function(response){
25216         var doc = response.responseXML;
25217         if(!doc) {
25218             throw {message: "XmlReader.read: XML Document not available"};
25219         }
25220         return this.readRecords(doc);
25221     },
25222
25223     /**
25224      * Create a data block containing Roo.data.Records from an XML document.
25225          * @param {Object} doc A parsed XML document.
25226      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25227      * a cache of Roo.data.Records.
25228      */
25229     readRecords : function(doc){
25230         /**
25231          * After any data loads/reads, the raw XML Document is available for further custom processing.
25232          * @type XMLDocument
25233          */
25234         this.xmlData = doc;
25235         var root = doc.documentElement || doc;
25236         var q = Roo.DomQuery;
25237         var recordType = this.recordType, fields = recordType.prototype.fields;
25238         var sid = this.meta.id;
25239         var totalRecords = 0, success = true;
25240         if(this.meta.totalRecords){
25241             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
25242         }
25243         
25244         if(this.meta.success){
25245             var sv = q.selectValue(this.meta.success, root, true);
25246             success = sv !== false && sv !== 'false';
25247         }
25248         var records = [];
25249         var ns = q.select(this.meta.record, root);
25250         for(var i = 0, len = ns.length; i < len; i++) {
25251                 var n = ns[i];
25252                 var values = {};
25253                 var id = sid ? q.selectValue(sid, n) : undefined;
25254                 for(var j = 0, jlen = fields.length; j < jlen; j++){
25255                     var f = fields.items[j];
25256                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
25257                     v = f.convert(v);
25258                     values[f.name] = v;
25259                 }
25260                 var record = new recordType(values, id);
25261                 record.node = n;
25262                 records[records.length] = record;
25263             }
25264
25265             return {
25266                 success : success,
25267                 records : records,
25268                 totalRecords : totalRecords || records.length
25269             };
25270     }
25271 });/*
25272  * Based on:
25273  * Ext JS Library 1.1.1
25274  * Copyright(c) 2006-2007, Ext JS, LLC.
25275  *
25276  * Originally Released Under LGPL - original licence link has changed is not relivant.
25277  *
25278  * Fork - LGPL
25279  * <script type="text/javascript">
25280  */
25281
25282 /**
25283  * @class Roo.data.ArrayReader
25284  * @extends Roo.data.DataReader
25285  * Data reader class to create an Array of Roo.data.Record objects from an Array.
25286  * Each element of that Array represents a row of data fields. The
25287  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
25288  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
25289  * <p>
25290  * Example code:.
25291  * <pre><code>
25292 var RecordDef = Roo.data.Record.create([
25293     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
25294     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
25295 ]);
25296 var myReader = new Roo.data.ArrayReader({
25297     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
25298 }, RecordDef);
25299 </code></pre>
25300  * <p>
25301  * This would consume an Array like this:
25302  * <pre><code>
25303 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
25304   </code></pre>
25305  
25306  * @constructor
25307  * Create a new JsonReader
25308  * @param {Object} meta Metadata configuration options.
25309  * @param {Object|Array} recordType Either an Array of field definition objects
25310  * 
25311  * @cfg {Array} fields Array of field definition objects
25312  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25313  * as specified to {@link Roo.data.Record#create},
25314  * or an {@link Roo.data.Record} object
25315  *
25316  * 
25317  * created using {@link Roo.data.Record#create}.
25318  */
25319 Roo.data.ArrayReader = function(meta, recordType)
25320 {    
25321     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25322 };
25323
25324 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
25325     
25326       /**
25327      * Create a data block containing Roo.data.Records from an XML document.
25328      * @param {Object} o An Array of row objects which represents the dataset.
25329      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
25330      * a cache of Roo.data.Records.
25331      */
25332     readRecords : function(o)
25333     {
25334         var sid = this.meta ? this.meta.id : null;
25335         var recordType = this.recordType, fields = recordType.prototype.fields;
25336         var records = [];
25337         var root = o;
25338         for(var i = 0; i < root.length; i++){
25339             var n = root[i];
25340             var values = {};
25341             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
25342             for(var j = 0, jlen = fields.length; j < jlen; j++){
25343                 var f = fields.items[j];
25344                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
25345                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
25346                 v = f.convert(v);
25347                 values[f.name] = v;
25348             }
25349             var record = new recordType(values, id);
25350             record.json = n;
25351             records[records.length] = record;
25352         }
25353         return {
25354             records : records,
25355             totalRecords : records.length
25356         };
25357     },
25358     // used when loading children.. @see loadDataFromChildren
25359     toLoadData: function(rec)
25360     {
25361         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25362         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25363         
25364     }
25365     
25366     
25367 });/*
25368  * Based on:
25369  * Ext JS Library 1.1.1
25370  * Copyright(c) 2006-2007, Ext JS, LLC.
25371  *
25372  * Originally Released Under LGPL - original licence link has changed is not relivant.
25373  *
25374  * Fork - LGPL
25375  * <script type="text/javascript">
25376  */
25377
25378
25379 /**
25380  * @class Roo.data.Tree
25381  * @extends Roo.util.Observable
25382  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
25383  * in the tree have most standard DOM functionality.
25384  * @constructor
25385  * @param {Node} root (optional) The root node
25386  */
25387 Roo.data.Tree = function(root){
25388    this.nodeHash = {};
25389    /**
25390     * The root node for this tree
25391     * @type Node
25392     */
25393    this.root = null;
25394    if(root){
25395        this.setRootNode(root);
25396    }
25397    this.addEvents({
25398        /**
25399         * @event append
25400         * Fires when a new child node is appended to a node in this tree.
25401         * @param {Tree} tree The owner tree
25402         * @param {Node} parent The parent node
25403         * @param {Node} node The newly appended node
25404         * @param {Number} index The index of the newly appended node
25405         */
25406        "append" : true,
25407        /**
25408         * @event remove
25409         * Fires when a child node is removed from a node in this tree.
25410         * @param {Tree} tree The owner tree
25411         * @param {Node} parent The parent node
25412         * @param {Node} node The child node removed
25413         */
25414        "remove" : true,
25415        /**
25416         * @event move
25417         * Fires when a node is moved to a new location in the tree
25418         * @param {Tree} tree The owner tree
25419         * @param {Node} node The node moved
25420         * @param {Node} oldParent The old parent of this node
25421         * @param {Node} newParent The new parent of this node
25422         * @param {Number} index The index it was moved to
25423         */
25424        "move" : true,
25425        /**
25426         * @event insert
25427         * Fires when a new child node is inserted in a node in this tree.
25428         * @param {Tree} tree The owner tree
25429         * @param {Node} parent The parent node
25430         * @param {Node} node The child node inserted
25431         * @param {Node} refNode The child node the node was inserted before
25432         */
25433        "insert" : true,
25434        /**
25435         * @event beforeappend
25436         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
25437         * @param {Tree} tree The owner tree
25438         * @param {Node} parent The parent node
25439         * @param {Node} node The child node to be appended
25440         */
25441        "beforeappend" : true,
25442        /**
25443         * @event beforeremove
25444         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
25445         * @param {Tree} tree The owner tree
25446         * @param {Node} parent The parent node
25447         * @param {Node} node The child node to be removed
25448         */
25449        "beforeremove" : true,
25450        /**
25451         * @event beforemove
25452         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
25453         * @param {Tree} tree The owner tree
25454         * @param {Node} node The node being moved
25455         * @param {Node} oldParent The parent of the node
25456         * @param {Node} newParent The new parent the node is moving to
25457         * @param {Number} index The index it is being moved to
25458         */
25459        "beforemove" : true,
25460        /**
25461         * @event beforeinsert
25462         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
25463         * @param {Tree} tree The owner tree
25464         * @param {Node} parent The parent node
25465         * @param {Node} node The child node to be inserted
25466         * @param {Node} refNode The child node the node is being inserted before
25467         */
25468        "beforeinsert" : true
25469    });
25470
25471     Roo.data.Tree.superclass.constructor.call(this);
25472 };
25473
25474 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
25475     pathSeparator: "/",
25476
25477     proxyNodeEvent : function(){
25478         return this.fireEvent.apply(this, arguments);
25479     },
25480
25481     /**
25482      * Returns the root node for this tree.
25483      * @return {Node}
25484      */
25485     getRootNode : function(){
25486         return this.root;
25487     },
25488
25489     /**
25490      * Sets the root node for this tree.
25491      * @param {Node} node
25492      * @return {Node}
25493      */
25494     setRootNode : function(node){
25495         this.root = node;
25496         node.ownerTree = this;
25497         node.isRoot = true;
25498         this.registerNode(node);
25499         return node;
25500     },
25501
25502     /**
25503      * Gets a node in this tree by its id.
25504      * @param {String} id
25505      * @return {Node}
25506      */
25507     getNodeById : function(id){
25508         return this.nodeHash[id];
25509     },
25510
25511     registerNode : function(node){
25512         this.nodeHash[node.id] = node;
25513     },
25514
25515     unregisterNode : function(node){
25516         delete this.nodeHash[node.id];
25517     },
25518
25519     toString : function(){
25520         return "[Tree"+(this.id?" "+this.id:"")+"]";
25521     }
25522 });
25523
25524 /**
25525  * @class Roo.data.Node
25526  * @extends Roo.util.Observable
25527  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
25528  * @cfg {String} id The id for this node. If one is not specified, one is generated.
25529  * @constructor
25530  * @param {Object} attributes The attributes/config for the node
25531  */
25532 Roo.data.Node = function(attributes){
25533     /**
25534      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
25535      * @type {Object}
25536      */
25537     this.attributes = attributes || {};
25538     this.leaf = this.attributes.leaf;
25539     /**
25540      * The node id. @type String
25541      */
25542     this.id = this.attributes.id;
25543     if(!this.id){
25544         this.id = Roo.id(null, "ynode-");
25545         this.attributes.id = this.id;
25546     }
25547      
25548     
25549     /**
25550      * All child nodes of this node. @type Array
25551      */
25552     this.childNodes = [];
25553     if(!this.childNodes.indexOf){ // indexOf is a must
25554         this.childNodes.indexOf = function(o){
25555             for(var i = 0, len = this.length; i < len; i++){
25556                 if(this[i] == o) {
25557                     return i;
25558                 }
25559             }
25560             return -1;
25561         };
25562     }
25563     /**
25564      * The parent node for this node. @type Node
25565      */
25566     this.parentNode = null;
25567     /**
25568      * The first direct child node of this node, or null if this node has no child nodes. @type Node
25569      */
25570     this.firstChild = null;
25571     /**
25572      * The last direct child node of this node, or null if this node has no child nodes. @type Node
25573      */
25574     this.lastChild = null;
25575     /**
25576      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
25577      */
25578     this.previousSibling = null;
25579     /**
25580      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
25581      */
25582     this.nextSibling = null;
25583
25584     this.addEvents({
25585        /**
25586         * @event append
25587         * Fires when a new child node is appended
25588         * @param {Tree} tree The owner tree
25589         * @param {Node} this This node
25590         * @param {Node} node The newly appended node
25591         * @param {Number} index The index of the newly appended node
25592         */
25593        "append" : true,
25594        /**
25595         * @event remove
25596         * Fires when a child node is removed
25597         * @param {Tree} tree The owner tree
25598         * @param {Node} this This node
25599         * @param {Node} node The removed node
25600         */
25601        "remove" : true,
25602        /**
25603         * @event move
25604         * Fires when this node is moved to a new location in the tree
25605         * @param {Tree} tree The owner tree
25606         * @param {Node} this This node
25607         * @param {Node} oldParent The old parent of this node
25608         * @param {Node} newParent The new parent of this node
25609         * @param {Number} index The index it was moved to
25610         */
25611        "move" : true,
25612        /**
25613         * @event insert
25614         * Fires when a new child node is inserted.
25615         * @param {Tree} tree The owner tree
25616         * @param {Node} this This node
25617         * @param {Node} node The child node inserted
25618         * @param {Node} refNode The child node the node was inserted before
25619         */
25620        "insert" : true,
25621        /**
25622         * @event beforeappend
25623         * Fires before a new child is appended, return false to cancel the append.
25624         * @param {Tree} tree The owner tree
25625         * @param {Node} this This node
25626         * @param {Node} node The child node to be appended
25627         */
25628        "beforeappend" : true,
25629        /**
25630         * @event beforeremove
25631         * Fires before a child is removed, return false to cancel the remove.
25632         * @param {Tree} tree The owner tree
25633         * @param {Node} this This node
25634         * @param {Node} node The child node to be removed
25635         */
25636        "beforeremove" : true,
25637        /**
25638         * @event beforemove
25639         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
25640         * @param {Tree} tree The owner tree
25641         * @param {Node} this This node
25642         * @param {Node} oldParent The parent of this node
25643         * @param {Node} newParent The new parent this node is moving to
25644         * @param {Number} index The index it is being moved to
25645         */
25646        "beforemove" : true,
25647        /**
25648         * @event beforeinsert
25649         * Fires before a new child is inserted, return false to cancel the insert.
25650         * @param {Tree} tree The owner tree
25651         * @param {Node} this This node
25652         * @param {Node} node The child node to be inserted
25653         * @param {Node} refNode The child node the node is being inserted before
25654         */
25655        "beforeinsert" : true
25656    });
25657     this.listeners = this.attributes.listeners;
25658     Roo.data.Node.superclass.constructor.call(this);
25659 };
25660
25661 Roo.extend(Roo.data.Node, Roo.util.Observable, {
25662     fireEvent : function(evtName){
25663         // first do standard event for this node
25664         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
25665             return false;
25666         }
25667         // then bubble it up to the tree if the event wasn't cancelled
25668         var ot = this.getOwnerTree();
25669         if(ot){
25670             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
25671                 return false;
25672             }
25673         }
25674         return true;
25675     },
25676
25677     /**
25678      * Returns true if this node is a leaf
25679      * @return {Boolean}
25680      */
25681     isLeaf : function(){
25682         return this.leaf === true;
25683     },
25684
25685     // private
25686     setFirstChild : function(node){
25687         this.firstChild = node;
25688     },
25689
25690     //private
25691     setLastChild : function(node){
25692         this.lastChild = node;
25693     },
25694
25695
25696     /**
25697      * Returns true if this node is the last child of its parent
25698      * @return {Boolean}
25699      */
25700     isLast : function(){
25701        return (!this.parentNode ? true : this.parentNode.lastChild == this);
25702     },
25703
25704     /**
25705      * Returns true if this node is the first child of its parent
25706      * @return {Boolean}
25707      */
25708     isFirst : function(){
25709        return (!this.parentNode ? true : this.parentNode.firstChild == this);
25710     },
25711
25712     hasChildNodes : function(){
25713         return !this.isLeaf() && this.childNodes.length > 0;
25714     },
25715
25716     /**
25717      * Insert node(s) as the last child node of this node.
25718      * @param {Node/Array} node The node or Array of nodes to append
25719      * @return {Node} The appended node if single append, or null if an array was passed
25720      */
25721     appendChild : function(node){
25722         var multi = false;
25723         if(node instanceof Array){
25724             multi = node;
25725         }else if(arguments.length > 1){
25726             multi = arguments;
25727         }
25728         
25729         // if passed an array or multiple args do them one by one
25730         if(multi){
25731             for(var i = 0, len = multi.length; i < len; i++) {
25732                 this.appendChild(multi[i]);
25733             }
25734         }else{
25735             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25736                 return false;
25737             }
25738             var index = this.childNodes.length;
25739             var oldParent = node.parentNode;
25740             // it's a move, make sure we move it cleanly
25741             if(oldParent){
25742                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25743                     return false;
25744                 }
25745                 oldParent.removeChild(node);
25746             }
25747             
25748             index = this.childNodes.length;
25749             if(index == 0){
25750                 this.setFirstChild(node);
25751             }
25752             this.childNodes.push(node);
25753             node.parentNode = this;
25754             var ps = this.childNodes[index-1];
25755             if(ps){
25756                 node.previousSibling = ps;
25757                 ps.nextSibling = node;
25758             }else{
25759                 node.previousSibling = null;
25760             }
25761             node.nextSibling = null;
25762             this.setLastChild(node);
25763             node.setOwnerTree(this.getOwnerTree());
25764             this.fireEvent("append", this.ownerTree, this, node, index);
25765             if(this.ownerTree) {
25766                 this.ownerTree.fireEvent("appendnode", this, node, index);
25767             }
25768             if(oldParent){
25769                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25770             }
25771             return node;
25772         }
25773     },
25774
25775     /**
25776      * Removes a child node from this node.
25777      * @param {Node} node The node to remove
25778      * @return {Node} The removed node
25779      */
25780     removeChild : function(node){
25781         var index = this.childNodes.indexOf(node);
25782         if(index == -1){
25783             return false;
25784         }
25785         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25786             return false;
25787         }
25788
25789         // remove it from childNodes collection
25790         this.childNodes.splice(index, 1);
25791
25792         // update siblings
25793         if(node.previousSibling){
25794             node.previousSibling.nextSibling = node.nextSibling;
25795         }
25796         if(node.nextSibling){
25797             node.nextSibling.previousSibling = node.previousSibling;
25798         }
25799
25800         // update child refs
25801         if(this.firstChild == node){
25802             this.setFirstChild(node.nextSibling);
25803         }
25804         if(this.lastChild == node){
25805             this.setLastChild(node.previousSibling);
25806         }
25807
25808         node.setOwnerTree(null);
25809         // clear any references from the node
25810         node.parentNode = null;
25811         node.previousSibling = null;
25812         node.nextSibling = null;
25813         this.fireEvent("remove", this.ownerTree, this, node);
25814         return node;
25815     },
25816
25817     /**
25818      * Inserts the first node before the second node in this nodes childNodes collection.
25819      * @param {Node} node The node to insert
25820      * @param {Node} refNode The node to insert before (if null the node is appended)
25821      * @return {Node} The inserted node
25822      */
25823     insertBefore : function(node, refNode){
25824         if(!refNode){ // like standard Dom, refNode can be null for append
25825             return this.appendChild(node);
25826         }
25827         // nothing to do
25828         if(node == refNode){
25829             return false;
25830         }
25831
25832         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25833             return false;
25834         }
25835         var index = this.childNodes.indexOf(refNode);
25836         var oldParent = node.parentNode;
25837         var refIndex = index;
25838
25839         // when moving internally, indexes will change after remove
25840         if(oldParent == this && this.childNodes.indexOf(node) < index){
25841             refIndex--;
25842         }
25843
25844         // it's a move, make sure we move it cleanly
25845         if(oldParent){
25846             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25847                 return false;
25848             }
25849             oldParent.removeChild(node);
25850         }
25851         if(refIndex == 0){
25852             this.setFirstChild(node);
25853         }
25854         this.childNodes.splice(refIndex, 0, node);
25855         node.parentNode = this;
25856         var ps = this.childNodes[refIndex-1];
25857         if(ps){
25858             node.previousSibling = ps;
25859             ps.nextSibling = node;
25860         }else{
25861             node.previousSibling = null;
25862         }
25863         node.nextSibling = refNode;
25864         refNode.previousSibling = node;
25865         node.setOwnerTree(this.getOwnerTree());
25866         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25867         if(oldParent){
25868             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25869         }
25870         return node;
25871     },
25872
25873     /**
25874      * Returns the child node at the specified index.
25875      * @param {Number} index
25876      * @return {Node}
25877      */
25878     item : function(index){
25879         return this.childNodes[index];
25880     },
25881
25882     /**
25883      * Replaces one child node in this node with another.
25884      * @param {Node} newChild The replacement node
25885      * @param {Node} oldChild The node to replace
25886      * @return {Node} The replaced node
25887      */
25888     replaceChild : function(newChild, oldChild){
25889         this.insertBefore(newChild, oldChild);
25890         this.removeChild(oldChild);
25891         return oldChild;
25892     },
25893
25894     /**
25895      * Returns the index of a child node
25896      * @param {Node} node
25897      * @return {Number} The index of the node or -1 if it was not found
25898      */
25899     indexOf : function(child){
25900         return this.childNodes.indexOf(child);
25901     },
25902
25903     /**
25904      * Returns the tree this node is in.
25905      * @return {Tree}
25906      */
25907     getOwnerTree : function(){
25908         // if it doesn't have one, look for one
25909         if(!this.ownerTree){
25910             var p = this;
25911             while(p){
25912                 if(p.ownerTree){
25913                     this.ownerTree = p.ownerTree;
25914                     break;
25915                 }
25916                 p = p.parentNode;
25917             }
25918         }
25919         return this.ownerTree;
25920     },
25921
25922     /**
25923      * Returns depth of this node (the root node has a depth of 0)
25924      * @return {Number}
25925      */
25926     getDepth : function(){
25927         var depth = 0;
25928         var p = this;
25929         while(p.parentNode){
25930             ++depth;
25931             p = p.parentNode;
25932         }
25933         return depth;
25934     },
25935
25936     // private
25937     setOwnerTree : function(tree){
25938         // if it's move, we need to update everyone
25939         if(tree != this.ownerTree){
25940             if(this.ownerTree){
25941                 this.ownerTree.unregisterNode(this);
25942             }
25943             this.ownerTree = tree;
25944             var cs = this.childNodes;
25945             for(var i = 0, len = cs.length; i < len; i++) {
25946                 cs[i].setOwnerTree(tree);
25947             }
25948             if(tree){
25949                 tree.registerNode(this);
25950             }
25951         }
25952     },
25953
25954     /**
25955      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25956      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25957      * @return {String} The path
25958      */
25959     getPath : function(attr){
25960         attr = attr || "id";
25961         var p = this.parentNode;
25962         var b = [this.attributes[attr]];
25963         while(p){
25964             b.unshift(p.attributes[attr]);
25965             p = p.parentNode;
25966         }
25967         var sep = this.getOwnerTree().pathSeparator;
25968         return sep + b.join(sep);
25969     },
25970
25971     /**
25972      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25973      * function call will be the scope provided or the current node. The arguments to the function
25974      * will be the args provided or the current node. If the function returns false at any point,
25975      * the bubble is stopped.
25976      * @param {Function} fn The function to call
25977      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25978      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25979      */
25980     bubble : function(fn, scope, args){
25981         var p = this;
25982         while(p){
25983             if(fn.call(scope || p, args || p) === false){
25984                 break;
25985             }
25986             p = p.parentNode;
25987         }
25988     },
25989
25990     /**
25991      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25992      * function call will be the scope provided or the current node. The arguments to the function
25993      * will be the args provided or the current node. If the function returns false at any point,
25994      * the cascade is stopped on that branch.
25995      * @param {Function} fn The function to call
25996      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25997      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25998      */
25999     cascade : function(fn, scope, args){
26000         if(fn.call(scope || this, args || this) !== false){
26001             var cs = this.childNodes;
26002             for(var i = 0, len = cs.length; i < len; i++) {
26003                 cs[i].cascade(fn, scope, args);
26004             }
26005         }
26006     },
26007
26008     /**
26009      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
26010      * function call will be the scope provided or the current node. The arguments to the function
26011      * will be the args provided or the current node. If the function returns false at any point,
26012      * the iteration stops.
26013      * @param {Function} fn The function to call
26014      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26015      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26016      */
26017     eachChild : function(fn, scope, args){
26018         var cs = this.childNodes;
26019         for(var i = 0, len = cs.length; i < len; i++) {
26020                 if(fn.call(scope || this, args || cs[i]) === false){
26021                     break;
26022                 }
26023         }
26024     },
26025
26026     /**
26027      * Finds the first child that has the attribute with the specified value.
26028      * @param {String} attribute The attribute name
26029      * @param {Mixed} value The value to search for
26030      * @return {Node} The found child or null if none was found
26031      */
26032     findChild : function(attribute, value){
26033         var cs = this.childNodes;
26034         for(var i = 0, len = cs.length; i < len; i++) {
26035                 if(cs[i].attributes[attribute] == value){
26036                     return cs[i];
26037                 }
26038         }
26039         return null;
26040     },
26041
26042     /**
26043      * Finds the first child by a custom function. The child matches if the function passed
26044      * returns true.
26045      * @param {Function} fn
26046      * @param {Object} scope (optional)
26047      * @return {Node} The found child or null if none was found
26048      */
26049     findChildBy : function(fn, scope){
26050         var cs = this.childNodes;
26051         for(var i = 0, len = cs.length; i < len; i++) {
26052                 if(fn.call(scope||cs[i], cs[i]) === true){
26053                     return cs[i];
26054                 }
26055         }
26056         return null;
26057     },
26058
26059     /**
26060      * Sorts this nodes children using the supplied sort function
26061      * @param {Function} fn
26062      * @param {Object} scope (optional)
26063      */
26064     sort : function(fn, scope){
26065         var cs = this.childNodes;
26066         var len = cs.length;
26067         if(len > 0){
26068             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
26069             cs.sort(sortFn);
26070             for(var i = 0; i < len; i++){
26071                 var n = cs[i];
26072                 n.previousSibling = cs[i-1];
26073                 n.nextSibling = cs[i+1];
26074                 if(i == 0){
26075                     this.setFirstChild(n);
26076                 }
26077                 if(i == len-1){
26078                     this.setLastChild(n);
26079                 }
26080             }
26081         }
26082     },
26083
26084     /**
26085      * Returns true if this node is an ancestor (at any point) of the passed node.
26086      * @param {Node} node
26087      * @return {Boolean}
26088      */
26089     contains : function(node){
26090         return node.isAncestor(this);
26091     },
26092
26093     /**
26094      * Returns true if the passed node is an ancestor (at any point) of this node.
26095      * @param {Node} node
26096      * @return {Boolean}
26097      */
26098     isAncestor : function(node){
26099         var p = this.parentNode;
26100         while(p){
26101             if(p == node){
26102                 return true;
26103             }
26104             p = p.parentNode;
26105         }
26106         return false;
26107     },
26108
26109     toString : function(){
26110         return "[Node"+(this.id?" "+this.id:"")+"]";
26111     }
26112 });/*
26113  * Based on:
26114  * Ext JS Library 1.1.1
26115  * Copyright(c) 2006-2007, Ext JS, LLC.
26116  *
26117  * Originally Released Under LGPL - original licence link has changed is not relivant.
26118  *
26119  * Fork - LGPL
26120  * <script type="text/javascript">
26121  */
26122
26123
26124 /**
26125  * @class Roo.Shadow
26126  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
26127  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
26128  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
26129  * @constructor
26130  * Create a new Shadow
26131  * @param {Object} config The config object
26132  */
26133 Roo.Shadow = function(config){
26134     Roo.apply(this, config);
26135     if(typeof this.mode != "string"){
26136         this.mode = this.defaultMode;
26137     }
26138     var o = this.offset, a = {h: 0};
26139     var rad = Math.floor(this.offset/2);
26140     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
26141         case "drop":
26142             a.w = 0;
26143             a.l = a.t = o;
26144             a.t -= 1;
26145             if(Roo.isIE){
26146                 a.l -= this.offset + rad;
26147                 a.t -= this.offset + rad;
26148                 a.w -= rad;
26149                 a.h -= rad;
26150                 a.t += 1;
26151             }
26152         break;
26153         case "sides":
26154             a.w = (o*2);
26155             a.l = -o;
26156             a.t = o-1;
26157             if(Roo.isIE){
26158                 a.l -= (this.offset - rad);
26159                 a.t -= this.offset + rad;
26160                 a.l += 1;
26161                 a.w -= (this.offset - rad)*2;
26162                 a.w -= rad + 1;
26163                 a.h -= 1;
26164             }
26165         break;
26166         case "frame":
26167             a.w = a.h = (o*2);
26168             a.l = a.t = -o;
26169             a.t += 1;
26170             a.h -= 2;
26171             if(Roo.isIE){
26172                 a.l -= (this.offset - rad);
26173                 a.t -= (this.offset - rad);
26174                 a.l += 1;
26175                 a.w -= (this.offset + rad + 1);
26176                 a.h -= (this.offset + rad);
26177                 a.h += 1;
26178             }
26179         break;
26180     };
26181
26182     this.adjusts = a;
26183 };
26184
26185 Roo.Shadow.prototype = {
26186     /**
26187      * @cfg {String} mode
26188      * The shadow display mode.  Supports the following options:<br />
26189      * sides: Shadow displays on both sides and bottom only<br />
26190      * frame: Shadow displays equally on all four sides<br />
26191      * drop: Traditional bottom-right drop shadow (default)
26192      */
26193     /**
26194      * @cfg {String} offset
26195      * The number of pixels to offset the shadow from the element (defaults to 4)
26196      */
26197     offset: 4,
26198
26199     // private
26200     defaultMode: "drop",
26201
26202     /**
26203      * Displays the shadow under the target element
26204      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
26205      */
26206     show : function(target){
26207         target = Roo.get(target);
26208         if(!this.el){
26209             this.el = Roo.Shadow.Pool.pull();
26210             if(this.el.dom.nextSibling != target.dom){
26211                 this.el.insertBefore(target);
26212             }
26213         }
26214         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
26215         if(Roo.isIE){
26216             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
26217         }
26218         this.realign(
26219             target.getLeft(true),
26220             target.getTop(true),
26221             target.getWidth(),
26222             target.getHeight()
26223         );
26224         this.el.dom.style.display = "block";
26225     },
26226
26227     /**
26228      * Returns true if the shadow is visible, else false
26229      */
26230     isVisible : function(){
26231         return this.el ? true : false;  
26232     },
26233
26234     /**
26235      * Direct alignment when values are already available. Show must be called at least once before
26236      * calling this method to ensure it is initialized.
26237      * @param {Number} left The target element left position
26238      * @param {Number} top The target element top position
26239      * @param {Number} width The target element width
26240      * @param {Number} height The target element height
26241      */
26242     realign : function(l, t, w, h){
26243         if(!this.el){
26244             return;
26245         }
26246         var a = this.adjusts, d = this.el.dom, s = d.style;
26247         var iea = 0;
26248         s.left = (l+a.l)+"px";
26249         s.top = (t+a.t)+"px";
26250         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
26251  
26252         if(s.width != sws || s.height != shs){
26253             s.width = sws;
26254             s.height = shs;
26255             if(!Roo.isIE){
26256                 var cn = d.childNodes;
26257                 var sww = Math.max(0, (sw-12))+"px";
26258                 cn[0].childNodes[1].style.width = sww;
26259                 cn[1].childNodes[1].style.width = sww;
26260                 cn[2].childNodes[1].style.width = sww;
26261                 cn[1].style.height = Math.max(0, (sh-12))+"px";
26262             }
26263         }
26264     },
26265
26266     /**
26267      * Hides this shadow
26268      */
26269     hide : function(){
26270         if(this.el){
26271             this.el.dom.style.display = "none";
26272             Roo.Shadow.Pool.push(this.el);
26273             delete this.el;
26274         }
26275     },
26276
26277     /**
26278      * Adjust the z-index of this shadow
26279      * @param {Number} zindex The new z-index
26280      */
26281     setZIndex : function(z){
26282         this.zIndex = z;
26283         if(this.el){
26284             this.el.setStyle("z-index", z);
26285         }
26286     }
26287 };
26288
26289 // Private utility class that manages the internal Shadow cache
26290 Roo.Shadow.Pool = function(){
26291     var p = [];
26292     var markup = Roo.isIE ?
26293                  '<div class="x-ie-shadow"></div>' :
26294                  '<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>';
26295     return {
26296         pull : function(){
26297             var sh = p.shift();
26298             if(!sh){
26299                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
26300                 sh.autoBoxAdjust = false;
26301             }
26302             return sh;
26303         },
26304
26305         push : function(sh){
26306             p.push(sh);
26307         }
26308     };
26309 }();/*
26310  * Based on:
26311  * Ext JS Library 1.1.1
26312  * Copyright(c) 2006-2007, Ext JS, LLC.
26313  *
26314  * Originally Released Under LGPL - original licence link has changed is not relivant.
26315  *
26316  * Fork - LGPL
26317  * <script type="text/javascript">
26318  */
26319
26320
26321 /**
26322  * @class Roo.SplitBar
26323  * @extends Roo.util.Observable
26324  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26325  * <br><br>
26326  * Usage:
26327  * <pre><code>
26328 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26329                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26330 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26331 split.minSize = 100;
26332 split.maxSize = 600;
26333 split.animate = true;
26334 split.on('moved', splitterMoved);
26335 </code></pre>
26336  * @constructor
26337  * Create a new SplitBar
26338  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26339  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26340  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26341  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26342                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26343                         position of the SplitBar).
26344  */
26345 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26346     
26347     /** @private */
26348     this.el = Roo.get(dragElement, true);
26349     this.el.dom.unselectable = "on";
26350     /** @private */
26351     this.resizingEl = Roo.get(resizingElement, true);
26352
26353     /**
26354      * @private
26355      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26356      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26357      * @type Number
26358      */
26359     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26360     
26361     /**
26362      * The minimum size of the resizing element. (Defaults to 0)
26363      * @type Number
26364      */
26365     this.minSize = 0;
26366     
26367     /**
26368      * The maximum size of the resizing element. (Defaults to 2000)
26369      * @type Number
26370      */
26371     this.maxSize = 2000;
26372     
26373     /**
26374      * Whether to animate the transition to the new size
26375      * @type Boolean
26376      */
26377     this.animate = false;
26378     
26379     /**
26380      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26381      * @type Boolean
26382      */
26383     this.useShim = false;
26384     
26385     /** @private */
26386     this.shim = null;
26387     
26388     if(!existingProxy){
26389         /** @private */
26390         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26391     }else{
26392         this.proxy = Roo.get(existingProxy).dom;
26393     }
26394     /** @private */
26395     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26396     
26397     /** @private */
26398     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26399     
26400     /** @private */
26401     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26402     
26403     /** @private */
26404     this.dragSpecs = {};
26405     
26406     /**
26407      * @private The adapter to use to positon and resize elements
26408      */
26409     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26410     this.adapter.init(this);
26411     
26412     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26413         /** @private */
26414         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26415         this.el.addClass("x-splitbar-h");
26416     }else{
26417         /** @private */
26418         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26419         this.el.addClass("x-splitbar-v");
26420     }
26421     
26422     this.addEvents({
26423         /**
26424          * @event resize
26425          * Fires when the splitter is moved (alias for {@link #event-moved})
26426          * @param {Roo.SplitBar} this
26427          * @param {Number} newSize the new width or height
26428          */
26429         "resize" : true,
26430         /**
26431          * @event moved
26432          * Fires when the splitter is moved
26433          * @param {Roo.SplitBar} this
26434          * @param {Number} newSize the new width or height
26435          */
26436         "moved" : true,
26437         /**
26438          * @event beforeresize
26439          * Fires before the splitter is dragged
26440          * @param {Roo.SplitBar} this
26441          */
26442         "beforeresize" : true,
26443
26444         "beforeapply" : true
26445     });
26446
26447     Roo.util.Observable.call(this);
26448 };
26449
26450 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26451     onStartProxyDrag : function(x, y){
26452         this.fireEvent("beforeresize", this);
26453         if(!this.overlay){
26454             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26455             o.unselectable();
26456             o.enableDisplayMode("block");
26457             // all splitbars share the same overlay
26458             Roo.SplitBar.prototype.overlay = o;
26459         }
26460         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26461         this.overlay.show();
26462         Roo.get(this.proxy).setDisplayed("block");
26463         var size = this.adapter.getElementSize(this);
26464         this.activeMinSize = this.getMinimumSize();;
26465         this.activeMaxSize = this.getMaximumSize();;
26466         var c1 = size - this.activeMinSize;
26467         var c2 = Math.max(this.activeMaxSize - size, 0);
26468         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26469             this.dd.resetConstraints();
26470             this.dd.setXConstraint(
26471                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26472                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26473             );
26474             this.dd.setYConstraint(0, 0);
26475         }else{
26476             this.dd.resetConstraints();
26477             this.dd.setXConstraint(0, 0);
26478             this.dd.setYConstraint(
26479                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26480                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26481             );
26482          }
26483         this.dragSpecs.startSize = size;
26484         this.dragSpecs.startPoint = [x, y];
26485         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26486     },
26487     
26488     /** 
26489      * @private Called after the drag operation by the DDProxy
26490      */
26491     onEndProxyDrag : function(e){
26492         Roo.get(this.proxy).setDisplayed(false);
26493         var endPoint = Roo.lib.Event.getXY(e);
26494         if(this.overlay){
26495             this.overlay.hide();
26496         }
26497         var newSize;
26498         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26499             newSize = this.dragSpecs.startSize + 
26500                 (this.placement == Roo.SplitBar.LEFT ?
26501                     endPoint[0] - this.dragSpecs.startPoint[0] :
26502                     this.dragSpecs.startPoint[0] - endPoint[0]
26503                 );
26504         }else{
26505             newSize = this.dragSpecs.startSize + 
26506                 (this.placement == Roo.SplitBar.TOP ?
26507                     endPoint[1] - this.dragSpecs.startPoint[1] :
26508                     this.dragSpecs.startPoint[1] - endPoint[1]
26509                 );
26510         }
26511         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26512         if(newSize != this.dragSpecs.startSize){
26513             if(this.fireEvent('beforeapply', this, newSize) !== false){
26514                 this.adapter.setElementSize(this, newSize);
26515                 this.fireEvent("moved", this, newSize);
26516                 this.fireEvent("resize", this, newSize);
26517             }
26518         }
26519     },
26520     
26521     /**
26522      * Get the adapter this SplitBar uses
26523      * @return The adapter object
26524      */
26525     getAdapter : function(){
26526         return this.adapter;
26527     },
26528     
26529     /**
26530      * Set the adapter this SplitBar uses
26531      * @param {Object} adapter A SplitBar adapter object
26532      */
26533     setAdapter : function(adapter){
26534         this.adapter = adapter;
26535         this.adapter.init(this);
26536     },
26537     
26538     /**
26539      * Gets the minimum size for the resizing element
26540      * @return {Number} The minimum size
26541      */
26542     getMinimumSize : function(){
26543         return this.minSize;
26544     },
26545     
26546     /**
26547      * Sets the minimum size for the resizing element
26548      * @param {Number} minSize The minimum size
26549      */
26550     setMinimumSize : function(minSize){
26551         this.minSize = minSize;
26552     },
26553     
26554     /**
26555      * Gets the maximum size for the resizing element
26556      * @return {Number} The maximum size
26557      */
26558     getMaximumSize : function(){
26559         return this.maxSize;
26560     },
26561     
26562     /**
26563      * Sets the maximum size for the resizing element
26564      * @param {Number} maxSize The maximum size
26565      */
26566     setMaximumSize : function(maxSize){
26567         this.maxSize = maxSize;
26568     },
26569     
26570     /**
26571      * Sets the initialize size for the resizing element
26572      * @param {Number} size The initial size
26573      */
26574     setCurrentSize : function(size){
26575         var oldAnimate = this.animate;
26576         this.animate = false;
26577         this.adapter.setElementSize(this, size);
26578         this.animate = oldAnimate;
26579     },
26580     
26581     /**
26582      * Destroy this splitbar. 
26583      * @param {Boolean} removeEl True to remove the element
26584      */
26585     destroy : function(removeEl){
26586         if(this.shim){
26587             this.shim.remove();
26588         }
26589         this.dd.unreg();
26590         this.proxy.parentNode.removeChild(this.proxy);
26591         if(removeEl){
26592             this.el.remove();
26593         }
26594     }
26595 });
26596
26597 /**
26598  * @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.
26599  */
26600 Roo.SplitBar.createProxy = function(dir){
26601     var proxy = new Roo.Element(document.createElement("div"));
26602     proxy.unselectable();
26603     var cls = 'x-splitbar-proxy';
26604     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26605     document.body.appendChild(proxy.dom);
26606     return proxy.dom;
26607 };
26608
26609 /** 
26610  * @class Roo.SplitBar.BasicLayoutAdapter
26611  * Default Adapter. It assumes the splitter and resizing element are not positioned
26612  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26613  */
26614 Roo.SplitBar.BasicLayoutAdapter = function(){
26615 };
26616
26617 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26618     // do nothing for now
26619     init : function(s){
26620     
26621     },
26622     /**
26623      * Called before drag operations to get the current size of the resizing element. 
26624      * @param {Roo.SplitBar} s The SplitBar using this adapter
26625      */
26626      getElementSize : function(s){
26627         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26628             return s.resizingEl.getWidth();
26629         }else{
26630             return s.resizingEl.getHeight();
26631         }
26632     },
26633     
26634     /**
26635      * Called after drag operations to set the size of the resizing element.
26636      * @param {Roo.SplitBar} s The SplitBar using this adapter
26637      * @param {Number} newSize The new size to set
26638      * @param {Function} onComplete A function to be invoked when resizing is complete
26639      */
26640     setElementSize : function(s, newSize, onComplete){
26641         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26642             if(!s.animate){
26643                 s.resizingEl.setWidth(newSize);
26644                 if(onComplete){
26645                     onComplete(s, newSize);
26646                 }
26647             }else{
26648                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26649             }
26650         }else{
26651             
26652             if(!s.animate){
26653                 s.resizingEl.setHeight(newSize);
26654                 if(onComplete){
26655                     onComplete(s, newSize);
26656                 }
26657             }else{
26658                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26659             }
26660         }
26661     }
26662 };
26663
26664 /** 
26665  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26666  * @extends Roo.SplitBar.BasicLayoutAdapter
26667  * Adapter that  moves the splitter element to align with the resized sizing element. 
26668  * Used with an absolute positioned SplitBar.
26669  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26670  * document.body, make sure you assign an id to the body element.
26671  */
26672 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26673     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26674     this.container = Roo.get(container);
26675 };
26676
26677 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26678     init : function(s){
26679         this.basic.init(s);
26680     },
26681     
26682     getElementSize : function(s){
26683         return this.basic.getElementSize(s);
26684     },
26685     
26686     setElementSize : function(s, newSize, onComplete){
26687         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26688     },
26689     
26690     moveSplitter : function(s){
26691         var yes = Roo.SplitBar;
26692         switch(s.placement){
26693             case yes.LEFT:
26694                 s.el.setX(s.resizingEl.getRight());
26695                 break;
26696             case yes.RIGHT:
26697                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26698                 break;
26699             case yes.TOP:
26700                 s.el.setY(s.resizingEl.getBottom());
26701                 break;
26702             case yes.BOTTOM:
26703                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26704                 break;
26705         }
26706     }
26707 };
26708
26709 /**
26710  * Orientation constant - Create a vertical SplitBar
26711  * @static
26712  * @type Number
26713  */
26714 Roo.SplitBar.VERTICAL = 1;
26715
26716 /**
26717  * Orientation constant - Create a horizontal SplitBar
26718  * @static
26719  * @type Number
26720  */
26721 Roo.SplitBar.HORIZONTAL = 2;
26722
26723 /**
26724  * Placement constant - The resizing element is to the left of the splitter element
26725  * @static
26726  * @type Number
26727  */
26728 Roo.SplitBar.LEFT = 1;
26729
26730 /**
26731  * Placement constant - The resizing element is to the right of the splitter element
26732  * @static
26733  * @type Number
26734  */
26735 Roo.SplitBar.RIGHT = 2;
26736
26737 /**
26738  * Placement constant - The resizing element is positioned above the splitter element
26739  * @static
26740  * @type Number
26741  */
26742 Roo.SplitBar.TOP = 3;
26743
26744 /**
26745  * Placement constant - The resizing element is positioned under splitter element
26746  * @static
26747  * @type Number
26748  */
26749 Roo.SplitBar.BOTTOM = 4;
26750 /*
26751  * Based on:
26752  * Ext JS Library 1.1.1
26753  * Copyright(c) 2006-2007, Ext JS, LLC.
26754  *
26755  * Originally Released Under LGPL - original licence link has changed is not relivant.
26756  *
26757  * Fork - LGPL
26758  * <script type="text/javascript">
26759  */
26760
26761 /**
26762  * @class Roo.View
26763  * @extends Roo.util.Observable
26764  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26765  * This class also supports single and multi selection modes. <br>
26766  * Create a data model bound view:
26767  <pre><code>
26768  var store = new Roo.data.Store(...);
26769
26770  var view = new Roo.View({
26771     el : "my-element",
26772     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26773  
26774     singleSelect: true,
26775     selectedClass: "ydataview-selected",
26776     store: store
26777  });
26778
26779  // listen for node click?
26780  view.on("click", function(vw, index, node, e){
26781  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26782  });
26783
26784  // load XML data
26785  dataModel.load("foobar.xml");
26786  </code></pre>
26787  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26788  * <br><br>
26789  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26790  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26791  * 
26792  * Note: old style constructor is still suported (container, template, config)
26793  * 
26794  * @constructor
26795  * Create a new View
26796  * @param {Object} config The config object
26797  * 
26798  */
26799 Roo.View = function(config, depreciated_tpl, depreciated_config){
26800     
26801     this.parent = false;
26802     
26803     if (typeof(depreciated_tpl) == 'undefined') {
26804         // new way.. - universal constructor.
26805         Roo.apply(this, config);
26806         this.el  = Roo.get(this.el);
26807     } else {
26808         // old format..
26809         this.el  = Roo.get(config);
26810         this.tpl = depreciated_tpl;
26811         Roo.apply(this, depreciated_config);
26812     }
26813     this.wrapEl  = this.el.wrap().wrap();
26814     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26815     
26816     
26817     if(typeof(this.tpl) == "string"){
26818         this.tpl = new Roo.Template(this.tpl);
26819     } else {
26820         // support xtype ctors..
26821         this.tpl = new Roo.factory(this.tpl, Roo);
26822     }
26823     
26824     
26825     this.tpl.compile();
26826     
26827     /** @private */
26828     this.addEvents({
26829         /**
26830          * @event beforeclick
26831          * Fires before a click is processed. Returns false to cancel the default action.
26832          * @param {Roo.View} this
26833          * @param {Number} index The index of the target node
26834          * @param {HTMLElement} node The target node
26835          * @param {Roo.EventObject} e The raw event object
26836          */
26837             "beforeclick" : true,
26838         /**
26839          * @event click
26840          * Fires when a template node is clicked.
26841          * @param {Roo.View} this
26842          * @param {Number} index The index of the target node
26843          * @param {HTMLElement} node The target node
26844          * @param {Roo.EventObject} e The raw event object
26845          */
26846             "click" : true,
26847         /**
26848          * @event dblclick
26849          * Fires when a template node is double clicked.
26850          * @param {Roo.View} this
26851          * @param {Number} index The index of the target node
26852          * @param {HTMLElement} node The target node
26853          * @param {Roo.EventObject} e The raw event object
26854          */
26855             "dblclick" : true,
26856         /**
26857          * @event contextmenu
26858          * Fires when a template node is right clicked.
26859          * @param {Roo.View} this
26860          * @param {Number} index The index of the target node
26861          * @param {HTMLElement} node The target node
26862          * @param {Roo.EventObject} e The raw event object
26863          */
26864             "contextmenu" : true,
26865         /**
26866          * @event selectionchange
26867          * Fires when the selected nodes change.
26868          * @param {Roo.View} this
26869          * @param {Array} selections Array of the selected nodes
26870          */
26871             "selectionchange" : true,
26872     
26873         /**
26874          * @event beforeselect
26875          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26876          * @param {Roo.View} this
26877          * @param {HTMLElement} node The node to be selected
26878          * @param {Array} selections Array of currently selected nodes
26879          */
26880             "beforeselect" : true,
26881         /**
26882          * @event preparedata
26883          * Fires on every row to render, to allow you to change the data.
26884          * @param {Roo.View} this
26885          * @param {Object} data to be rendered (change this)
26886          */
26887           "preparedata" : true
26888           
26889           
26890         });
26891
26892
26893
26894     this.el.on({
26895         "click": this.onClick,
26896         "dblclick": this.onDblClick,
26897         "contextmenu": this.onContextMenu,
26898         scope:this
26899     });
26900
26901     this.selections = [];
26902     this.nodes = [];
26903     this.cmp = new Roo.CompositeElementLite([]);
26904     if(this.store){
26905         this.store = Roo.factory(this.store, Roo.data);
26906         this.setStore(this.store, true);
26907     }
26908     
26909     if ( this.footer && this.footer.xtype) {
26910            
26911          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26912         
26913         this.footer.dataSource = this.store;
26914         this.footer.container = fctr;
26915         this.footer = Roo.factory(this.footer, Roo);
26916         fctr.insertFirst(this.el);
26917         
26918         // this is a bit insane - as the paging toolbar seems to detach the el..
26919 //        dom.parentNode.parentNode.parentNode
26920          // they get detached?
26921     }
26922     
26923     
26924     Roo.View.superclass.constructor.call(this);
26925     
26926     
26927 };
26928
26929 Roo.extend(Roo.View, Roo.util.Observable, {
26930     
26931      /**
26932      * @cfg {Roo.data.Store} store Data store to load data from.
26933      */
26934     store : false,
26935     
26936     /**
26937      * @cfg {String|Roo.Element} el The container element.
26938      */
26939     el : '',
26940     
26941     /**
26942      * @cfg {String|Roo.Template} tpl The template used by this View 
26943      */
26944     tpl : false,
26945     /**
26946      * @cfg {String} dataName the named area of the template to use as the data area
26947      *                          Works with domtemplates roo-name="name"
26948      */
26949     dataName: false,
26950     /**
26951      * @cfg {String} selectedClass The css class to add to selected nodes
26952      */
26953     selectedClass : "x-view-selected",
26954      /**
26955      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26956      */
26957     emptyText : "",
26958     
26959     /**
26960      * @cfg {String} text to display on mask (default Loading)
26961      */
26962     mask : false,
26963     /**
26964      * @cfg {Boolean} multiSelect Allow multiple selection
26965      */
26966     multiSelect : false,
26967     /**
26968      * @cfg {Boolean} singleSelect Allow single selection
26969      */
26970     singleSelect:  false,
26971     
26972     /**
26973      * @cfg {Boolean} toggleSelect - selecting 
26974      */
26975     toggleSelect : false,
26976     
26977     /**
26978      * @cfg {Boolean} tickable - selecting 
26979      */
26980     tickable : false,
26981     
26982     /**
26983      * Returns the element this view is bound to.
26984      * @return {Roo.Element}
26985      */
26986     getEl : function(){
26987         return this.wrapEl;
26988     },
26989     
26990     
26991
26992     /**
26993      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26994      */
26995     refresh : function(){
26996         //Roo.log('refresh');
26997         var t = this.tpl;
26998         
26999         // if we are using something like 'domtemplate', then
27000         // the what gets used is:
27001         // t.applySubtemplate(NAME, data, wrapping data..)
27002         // the outer template then get' applied with
27003         //     the store 'extra data'
27004         // and the body get's added to the
27005         //      roo-name="data" node?
27006         //      <span class='roo-tpl-{name}'></span> ?????
27007         
27008         
27009         
27010         this.clearSelections();
27011         this.el.update("");
27012         var html = [];
27013         var records = this.store.getRange();
27014         if(records.length < 1) {
27015             
27016             // is this valid??  = should it render a template??
27017             
27018             this.el.update(this.emptyText);
27019             return;
27020         }
27021         var el = this.el;
27022         if (this.dataName) {
27023             this.el.update(t.apply(this.store.meta)); //????
27024             el = this.el.child('.roo-tpl-' + this.dataName);
27025         }
27026         
27027         for(var i = 0, len = records.length; i < len; i++){
27028             var data = this.prepareData(records[i].data, i, records[i]);
27029             this.fireEvent("preparedata", this, data, i, records[i]);
27030             
27031             var d = Roo.apply({}, data);
27032             
27033             if(this.tickable){
27034                 Roo.apply(d, {'roo-id' : Roo.id()});
27035                 
27036                 var _this = this;
27037             
27038                 Roo.each(this.parent.item, function(item){
27039                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
27040                         return;
27041                     }
27042                     Roo.apply(d, {'roo-data-checked' : 'checked'});
27043                 });
27044             }
27045             
27046             html[html.length] = Roo.util.Format.trim(
27047                 this.dataName ?
27048                     t.applySubtemplate(this.dataName, d, this.store.meta) :
27049                     t.apply(d)
27050             );
27051         }
27052         
27053         
27054         
27055         el.update(html.join(""));
27056         this.nodes = el.dom.childNodes;
27057         this.updateIndexes(0);
27058     },
27059     
27060
27061     /**
27062      * Function to override to reformat the data that is sent to
27063      * the template for each node.
27064      * DEPRICATED - use the preparedata event handler.
27065      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
27066      * a JSON object for an UpdateManager bound view).
27067      */
27068     prepareData : function(data, index, record)
27069     {
27070         this.fireEvent("preparedata", this, data, index, record);
27071         return data;
27072     },
27073
27074     onUpdate : function(ds, record){
27075         // Roo.log('on update');   
27076         this.clearSelections();
27077         var index = this.store.indexOf(record);
27078         var n = this.nodes[index];
27079         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
27080         n.parentNode.removeChild(n);
27081         this.updateIndexes(index, index);
27082     },
27083
27084     
27085     
27086 // --------- FIXME     
27087     onAdd : function(ds, records, index)
27088     {
27089         //Roo.log(['on Add', ds, records, index] );        
27090         this.clearSelections();
27091         if(this.nodes.length == 0){
27092             this.refresh();
27093             return;
27094         }
27095         var n = this.nodes[index];
27096         for(var i = 0, len = records.length; i < len; i++){
27097             var d = this.prepareData(records[i].data, i, records[i]);
27098             if(n){
27099                 this.tpl.insertBefore(n, d);
27100             }else{
27101                 
27102                 this.tpl.append(this.el, d);
27103             }
27104         }
27105         this.updateIndexes(index);
27106     },
27107
27108     onRemove : function(ds, record, index){
27109        // Roo.log('onRemove');
27110         this.clearSelections();
27111         var el = this.dataName  ?
27112             this.el.child('.roo-tpl-' + this.dataName) :
27113             this.el; 
27114         
27115         el.dom.removeChild(this.nodes[index]);
27116         this.updateIndexes(index);
27117     },
27118
27119     /**
27120      * Refresh an individual node.
27121      * @param {Number} index
27122      */
27123     refreshNode : function(index){
27124         this.onUpdate(this.store, this.store.getAt(index));
27125     },
27126
27127     updateIndexes : function(startIndex, endIndex){
27128         var ns = this.nodes;
27129         startIndex = startIndex || 0;
27130         endIndex = endIndex || ns.length - 1;
27131         for(var i = startIndex; i <= endIndex; i++){
27132             ns[i].nodeIndex = i;
27133         }
27134     },
27135
27136     /**
27137      * Changes the data store this view uses and refresh the view.
27138      * @param {Store} store
27139      */
27140     setStore : function(store, initial){
27141         if(!initial && this.store){
27142             this.store.un("datachanged", this.refresh);
27143             this.store.un("add", this.onAdd);
27144             this.store.un("remove", this.onRemove);
27145             this.store.un("update", this.onUpdate);
27146             this.store.un("clear", this.refresh);
27147             this.store.un("beforeload", this.onBeforeLoad);
27148             this.store.un("load", this.onLoad);
27149             this.store.un("loadexception", this.onLoad);
27150         }
27151         if(store){
27152           
27153             store.on("datachanged", this.refresh, this);
27154             store.on("add", this.onAdd, this);
27155             store.on("remove", this.onRemove, this);
27156             store.on("update", this.onUpdate, this);
27157             store.on("clear", this.refresh, this);
27158             store.on("beforeload", this.onBeforeLoad, this);
27159             store.on("load", this.onLoad, this);
27160             store.on("loadexception", this.onLoad, this);
27161         }
27162         
27163         if(store){
27164             this.refresh();
27165         }
27166     },
27167     /**
27168      * onbeforeLoad - masks the loading area.
27169      *
27170      */
27171     onBeforeLoad : function(store,opts)
27172     {
27173          //Roo.log('onBeforeLoad');   
27174         if (!opts.add) {
27175             this.el.update("");
27176         }
27177         this.el.mask(this.mask ? this.mask : "Loading" ); 
27178     },
27179     onLoad : function ()
27180     {
27181         this.el.unmask();
27182     },
27183     
27184
27185     /**
27186      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
27187      * @param {HTMLElement} node
27188      * @return {HTMLElement} The template node
27189      */
27190     findItemFromChild : function(node){
27191         var el = this.dataName  ?
27192             this.el.child('.roo-tpl-' + this.dataName,true) :
27193             this.el.dom; 
27194         
27195         if(!node || node.parentNode == el){
27196                     return node;
27197             }
27198             var p = node.parentNode;
27199             while(p && p != el){
27200             if(p.parentNode == el){
27201                 return p;
27202             }
27203             p = p.parentNode;
27204         }
27205             return null;
27206     },
27207
27208     /** @ignore */
27209     onClick : function(e){
27210         var item = this.findItemFromChild(e.getTarget());
27211         if(item){
27212             var index = this.indexOf(item);
27213             if(this.onItemClick(item, index, e) !== false){
27214                 this.fireEvent("click", this, index, item, e);
27215             }
27216         }else{
27217             this.clearSelections();
27218         }
27219     },
27220
27221     /** @ignore */
27222     onContextMenu : function(e){
27223         var item = this.findItemFromChild(e.getTarget());
27224         if(item){
27225             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
27226         }
27227     },
27228
27229     /** @ignore */
27230     onDblClick : function(e){
27231         var item = this.findItemFromChild(e.getTarget());
27232         if(item){
27233             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
27234         }
27235     },
27236
27237     onItemClick : function(item, index, e)
27238     {
27239         if(this.fireEvent("beforeclick", this, index, item, e) === false){
27240             return false;
27241         }
27242         if (this.toggleSelect) {
27243             var m = this.isSelected(item) ? 'unselect' : 'select';
27244             //Roo.log(m);
27245             var _t = this;
27246             _t[m](item, true, false);
27247             return true;
27248         }
27249         if(this.multiSelect || this.singleSelect){
27250             if(this.multiSelect && e.shiftKey && this.lastSelection){
27251                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
27252             }else{
27253                 this.select(item, this.multiSelect && e.ctrlKey);
27254                 this.lastSelection = item;
27255             }
27256             
27257             if(!this.tickable){
27258                 e.preventDefault();
27259             }
27260             
27261         }
27262         return true;
27263     },
27264
27265     /**
27266      * Get the number of selected nodes.
27267      * @return {Number}
27268      */
27269     getSelectionCount : function(){
27270         return this.selections.length;
27271     },
27272
27273     /**
27274      * Get the currently selected nodes.
27275      * @return {Array} An array of HTMLElements
27276      */
27277     getSelectedNodes : function(){
27278         return this.selections;
27279     },
27280
27281     /**
27282      * Get the indexes of the selected nodes.
27283      * @return {Array}
27284      */
27285     getSelectedIndexes : function(){
27286         var indexes = [], s = this.selections;
27287         for(var i = 0, len = s.length; i < len; i++){
27288             indexes.push(s[i].nodeIndex);
27289         }
27290         return indexes;
27291     },
27292
27293     /**
27294      * Clear all selections
27295      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
27296      */
27297     clearSelections : function(suppressEvent){
27298         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
27299             this.cmp.elements = this.selections;
27300             this.cmp.removeClass(this.selectedClass);
27301             this.selections = [];
27302             if(!suppressEvent){
27303                 this.fireEvent("selectionchange", this, this.selections);
27304             }
27305         }
27306     },
27307
27308     /**
27309      * Returns true if the passed node is selected
27310      * @param {HTMLElement/Number} node The node or node index
27311      * @return {Boolean}
27312      */
27313     isSelected : function(node){
27314         var s = this.selections;
27315         if(s.length < 1){
27316             return false;
27317         }
27318         node = this.getNode(node);
27319         return s.indexOf(node) !== -1;
27320     },
27321
27322     /**
27323      * Selects nodes.
27324      * @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
27325      * @param {Boolean} keepExisting (optional) true to keep existing selections
27326      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27327      */
27328     select : function(nodeInfo, keepExisting, suppressEvent){
27329         if(nodeInfo instanceof Array){
27330             if(!keepExisting){
27331                 this.clearSelections(true);
27332             }
27333             for(var i = 0, len = nodeInfo.length; i < len; i++){
27334                 this.select(nodeInfo[i], true, true);
27335             }
27336             return;
27337         } 
27338         var node = this.getNode(nodeInfo);
27339         if(!node || this.isSelected(node)){
27340             return; // already selected.
27341         }
27342         if(!keepExisting){
27343             this.clearSelections(true);
27344         }
27345         
27346         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27347             Roo.fly(node).addClass(this.selectedClass);
27348             this.selections.push(node);
27349             if(!suppressEvent){
27350                 this.fireEvent("selectionchange", this, this.selections);
27351             }
27352         }
27353         
27354         
27355     },
27356       /**
27357      * Unselects nodes.
27358      * @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
27359      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27360      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27361      */
27362     unselect : function(nodeInfo, keepExisting, suppressEvent)
27363     {
27364         if(nodeInfo instanceof Array){
27365             Roo.each(this.selections, function(s) {
27366                 this.unselect(s, nodeInfo);
27367             }, this);
27368             return;
27369         }
27370         var node = this.getNode(nodeInfo);
27371         if(!node || !this.isSelected(node)){
27372             //Roo.log("not selected");
27373             return; // not selected.
27374         }
27375         // fireevent???
27376         var ns = [];
27377         Roo.each(this.selections, function(s) {
27378             if (s == node ) {
27379                 Roo.fly(node).removeClass(this.selectedClass);
27380
27381                 return;
27382             }
27383             ns.push(s);
27384         },this);
27385         
27386         this.selections= ns;
27387         this.fireEvent("selectionchange", this, this.selections);
27388     },
27389
27390     /**
27391      * Gets a template node.
27392      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27393      * @return {HTMLElement} The node or null if it wasn't found
27394      */
27395     getNode : function(nodeInfo){
27396         if(typeof nodeInfo == "string"){
27397             return document.getElementById(nodeInfo);
27398         }else if(typeof nodeInfo == "number"){
27399             return this.nodes[nodeInfo];
27400         }
27401         return nodeInfo;
27402     },
27403
27404     /**
27405      * Gets a range template nodes.
27406      * @param {Number} startIndex
27407      * @param {Number} endIndex
27408      * @return {Array} An array of nodes
27409      */
27410     getNodes : function(start, end){
27411         var ns = this.nodes;
27412         start = start || 0;
27413         end = typeof end == "undefined" ? ns.length - 1 : end;
27414         var nodes = [];
27415         if(start <= end){
27416             for(var i = start; i <= end; i++){
27417                 nodes.push(ns[i]);
27418             }
27419         } else{
27420             for(var i = start; i >= end; i--){
27421                 nodes.push(ns[i]);
27422             }
27423         }
27424         return nodes;
27425     },
27426
27427     /**
27428      * Finds the index of the passed node
27429      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27430      * @return {Number} The index of the node or -1
27431      */
27432     indexOf : function(node){
27433         node = this.getNode(node);
27434         if(typeof node.nodeIndex == "number"){
27435             return node.nodeIndex;
27436         }
27437         var ns = this.nodes;
27438         for(var i = 0, len = ns.length; i < len; i++){
27439             if(ns[i] == node){
27440                 return i;
27441             }
27442         }
27443         return -1;
27444     }
27445 });
27446 /*
27447  * Based on:
27448  * Ext JS Library 1.1.1
27449  * Copyright(c) 2006-2007, Ext JS, LLC.
27450  *
27451  * Originally Released Under LGPL - original licence link has changed is not relivant.
27452  *
27453  * Fork - LGPL
27454  * <script type="text/javascript">
27455  */
27456
27457 /**
27458  * @class Roo.JsonView
27459  * @extends Roo.View
27460  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27461 <pre><code>
27462 var view = new Roo.JsonView({
27463     container: "my-element",
27464     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27465     multiSelect: true, 
27466     jsonRoot: "data" 
27467 });
27468
27469 // listen for node click?
27470 view.on("click", function(vw, index, node, e){
27471     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27472 });
27473
27474 // direct load of JSON data
27475 view.load("foobar.php");
27476
27477 // Example from my blog list
27478 var tpl = new Roo.Template(
27479     '&lt;div class="entry"&gt;' +
27480     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27481     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27482     "&lt;/div&gt;&lt;hr /&gt;"
27483 );
27484
27485 var moreView = new Roo.JsonView({
27486     container :  "entry-list", 
27487     template : tpl,
27488     jsonRoot: "posts"
27489 });
27490 moreView.on("beforerender", this.sortEntries, this);
27491 moreView.load({
27492     url: "/blog/get-posts.php",
27493     params: "allposts=true",
27494     text: "Loading Blog Entries..."
27495 });
27496 </code></pre>
27497
27498 * Note: old code is supported with arguments : (container, template, config)
27499
27500
27501  * @constructor
27502  * Create a new JsonView
27503  * 
27504  * @param {Object} config The config object
27505  * 
27506  */
27507 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27508     
27509     
27510     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27511
27512     var um = this.el.getUpdateManager();
27513     um.setRenderer(this);
27514     um.on("update", this.onLoad, this);
27515     um.on("failure", this.onLoadException, this);
27516
27517     /**
27518      * @event beforerender
27519      * Fires before rendering of the downloaded JSON data.
27520      * @param {Roo.JsonView} this
27521      * @param {Object} data The JSON data loaded
27522      */
27523     /**
27524      * @event load
27525      * Fires when data is loaded.
27526      * @param {Roo.JsonView} this
27527      * @param {Object} data The JSON data loaded
27528      * @param {Object} response The raw Connect response object
27529      */
27530     /**
27531      * @event loadexception
27532      * Fires when loading fails.
27533      * @param {Roo.JsonView} this
27534      * @param {Object} response The raw Connect response object
27535      */
27536     this.addEvents({
27537         'beforerender' : true,
27538         'load' : true,
27539         'loadexception' : true
27540     });
27541 };
27542 Roo.extend(Roo.JsonView, Roo.View, {
27543     /**
27544      * @type {String} The root property in the loaded JSON object that contains the data
27545      */
27546     jsonRoot : "",
27547
27548     /**
27549      * Refreshes the view.
27550      */
27551     refresh : function(){
27552         this.clearSelections();
27553         this.el.update("");
27554         var html = [];
27555         var o = this.jsonData;
27556         if(o && o.length > 0){
27557             for(var i = 0, len = o.length; i < len; i++){
27558                 var data = this.prepareData(o[i], i, o);
27559                 html[html.length] = this.tpl.apply(data);
27560             }
27561         }else{
27562             html.push(this.emptyText);
27563         }
27564         this.el.update(html.join(""));
27565         this.nodes = this.el.dom.childNodes;
27566         this.updateIndexes(0);
27567     },
27568
27569     /**
27570      * 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.
27571      * @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:
27572      <pre><code>
27573      view.load({
27574          url: "your-url.php",
27575          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27576          callback: yourFunction,
27577          scope: yourObject, //(optional scope)
27578          discardUrl: false,
27579          nocache: false,
27580          text: "Loading...",
27581          timeout: 30,
27582          scripts: false
27583      });
27584      </code></pre>
27585      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27586      * 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.
27587      * @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}
27588      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27589      * @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.
27590      */
27591     load : function(){
27592         var um = this.el.getUpdateManager();
27593         um.update.apply(um, arguments);
27594     },
27595
27596     // note - render is a standard framework call...
27597     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27598     render : function(el, response){
27599         
27600         this.clearSelections();
27601         this.el.update("");
27602         var o;
27603         try{
27604             if (response != '') {
27605                 o = Roo.util.JSON.decode(response.responseText);
27606                 if(this.jsonRoot){
27607                     
27608                     o = o[this.jsonRoot];
27609                 }
27610             }
27611         } catch(e){
27612         }
27613         /**
27614          * The current JSON data or null
27615          */
27616         this.jsonData = o;
27617         this.beforeRender();
27618         this.refresh();
27619     },
27620
27621 /**
27622  * Get the number of records in the current JSON dataset
27623  * @return {Number}
27624  */
27625     getCount : function(){
27626         return this.jsonData ? this.jsonData.length : 0;
27627     },
27628
27629 /**
27630  * Returns the JSON object for the specified node(s)
27631  * @param {HTMLElement/Array} node The node or an array of nodes
27632  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27633  * you get the JSON object for the node
27634  */
27635     getNodeData : function(node){
27636         if(node instanceof Array){
27637             var data = [];
27638             for(var i = 0, len = node.length; i < len; i++){
27639                 data.push(this.getNodeData(node[i]));
27640             }
27641             return data;
27642         }
27643         return this.jsonData[this.indexOf(node)] || null;
27644     },
27645
27646     beforeRender : function(){
27647         this.snapshot = this.jsonData;
27648         if(this.sortInfo){
27649             this.sort.apply(this, this.sortInfo);
27650         }
27651         this.fireEvent("beforerender", this, this.jsonData);
27652     },
27653
27654     onLoad : function(el, o){
27655         this.fireEvent("load", this, this.jsonData, o);
27656     },
27657
27658     onLoadException : function(el, o){
27659         this.fireEvent("loadexception", this, o);
27660     },
27661
27662 /**
27663  * Filter the data by a specific property.
27664  * @param {String} property A property on your JSON objects
27665  * @param {String/RegExp} value Either string that the property values
27666  * should start with, or a RegExp to test against the property
27667  */
27668     filter : function(property, value){
27669         if(this.jsonData){
27670             var data = [];
27671             var ss = this.snapshot;
27672             if(typeof value == "string"){
27673                 var vlen = value.length;
27674                 if(vlen == 0){
27675                     this.clearFilter();
27676                     return;
27677                 }
27678                 value = value.toLowerCase();
27679                 for(var i = 0, len = ss.length; i < len; i++){
27680                     var o = ss[i];
27681                     if(o[property].substr(0, vlen).toLowerCase() == value){
27682                         data.push(o);
27683                     }
27684                 }
27685             } else if(value.exec){ // regex?
27686                 for(var i = 0, len = ss.length; i < len; i++){
27687                     var o = ss[i];
27688                     if(value.test(o[property])){
27689                         data.push(o);
27690                     }
27691                 }
27692             } else{
27693                 return;
27694             }
27695             this.jsonData = data;
27696             this.refresh();
27697         }
27698     },
27699
27700 /**
27701  * Filter by a function. The passed function will be called with each
27702  * object in the current dataset. If the function returns true the value is kept,
27703  * otherwise it is filtered.
27704  * @param {Function} fn
27705  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27706  */
27707     filterBy : function(fn, scope){
27708         if(this.jsonData){
27709             var data = [];
27710             var ss = this.snapshot;
27711             for(var i = 0, len = ss.length; i < len; i++){
27712                 var o = ss[i];
27713                 if(fn.call(scope || this, o)){
27714                     data.push(o);
27715                 }
27716             }
27717             this.jsonData = data;
27718             this.refresh();
27719         }
27720     },
27721
27722 /**
27723  * Clears the current filter.
27724  */
27725     clearFilter : function(){
27726         if(this.snapshot && this.jsonData != this.snapshot){
27727             this.jsonData = this.snapshot;
27728             this.refresh();
27729         }
27730     },
27731
27732
27733 /**
27734  * Sorts the data for this view and refreshes it.
27735  * @param {String} property A property on your JSON objects to sort on
27736  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27737  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27738  */
27739     sort : function(property, dir, sortType){
27740         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27741         if(this.jsonData){
27742             var p = property;
27743             var dsc = dir && dir.toLowerCase() == "desc";
27744             var f = function(o1, o2){
27745                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27746                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27747                 ;
27748                 if(v1 < v2){
27749                     return dsc ? +1 : -1;
27750                 } else if(v1 > v2){
27751                     return dsc ? -1 : +1;
27752                 } else{
27753                     return 0;
27754                 }
27755             };
27756             this.jsonData.sort(f);
27757             this.refresh();
27758             if(this.jsonData != this.snapshot){
27759                 this.snapshot.sort(f);
27760             }
27761         }
27762     }
27763 });/*
27764  * Based on:
27765  * Ext JS Library 1.1.1
27766  * Copyright(c) 2006-2007, Ext JS, LLC.
27767  *
27768  * Originally Released Under LGPL - original licence link has changed is not relivant.
27769  *
27770  * Fork - LGPL
27771  * <script type="text/javascript">
27772  */
27773  
27774
27775 /**
27776  * @class Roo.ColorPalette
27777  * @extends Roo.Component
27778  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27779  * Here's an example of typical usage:
27780  * <pre><code>
27781 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27782 cp.render('my-div');
27783
27784 cp.on('select', function(palette, selColor){
27785     // do something with selColor
27786 });
27787 </code></pre>
27788  * @constructor
27789  * Create a new ColorPalette
27790  * @param {Object} config The config object
27791  */
27792 Roo.ColorPalette = function(config){
27793     Roo.ColorPalette.superclass.constructor.call(this, config);
27794     this.addEvents({
27795         /**
27796              * @event select
27797              * Fires when a color is selected
27798              * @param {ColorPalette} this
27799              * @param {String} color The 6-digit color hex code (without the # symbol)
27800              */
27801         select: true
27802     });
27803
27804     if(this.handler){
27805         this.on("select", this.handler, this.scope, true);
27806     }
27807 };
27808 Roo.extend(Roo.ColorPalette, Roo.Component, {
27809     /**
27810      * @cfg {String} itemCls
27811      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27812      */
27813     itemCls : "x-color-palette",
27814     /**
27815      * @cfg {String} value
27816      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27817      * the hex codes are case-sensitive.
27818      */
27819     value : null,
27820     clickEvent:'click',
27821     // private
27822     ctype: "Roo.ColorPalette",
27823
27824     /**
27825      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27826      */
27827     allowReselect : false,
27828
27829     /**
27830      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27831      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27832      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27833      * of colors with the width setting until the box is symmetrical.</p>
27834      * <p>You can override individual colors if needed:</p>
27835      * <pre><code>
27836 var cp = new Roo.ColorPalette();
27837 cp.colors[0] = "FF0000";  // change the first box to red
27838 </code></pre>
27839
27840 Or you can provide a custom array of your own for complete control:
27841 <pre><code>
27842 var cp = new Roo.ColorPalette();
27843 cp.colors = ["000000", "993300", "333300"];
27844 </code></pre>
27845      * @type Array
27846      */
27847     colors : [
27848         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27849         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27850         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27851         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27852         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27853     ],
27854
27855     // private
27856     onRender : function(container, position){
27857         var t = new Roo.MasterTemplate(
27858             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27859         );
27860         var c = this.colors;
27861         for(var i = 0, len = c.length; i < len; i++){
27862             t.add([c[i]]);
27863         }
27864         var el = document.createElement("div");
27865         el.className = this.itemCls;
27866         t.overwrite(el);
27867         container.dom.insertBefore(el, position);
27868         this.el = Roo.get(el);
27869         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27870         if(this.clickEvent != 'click'){
27871             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27872         }
27873     },
27874
27875     // private
27876     afterRender : function(){
27877         Roo.ColorPalette.superclass.afterRender.call(this);
27878         if(this.value){
27879             var s = this.value;
27880             this.value = null;
27881             this.select(s);
27882         }
27883     },
27884
27885     // private
27886     handleClick : function(e, t){
27887         e.preventDefault();
27888         if(!this.disabled){
27889             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27890             this.select(c.toUpperCase());
27891         }
27892     },
27893
27894     /**
27895      * Selects the specified color in the palette (fires the select event)
27896      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27897      */
27898     select : function(color){
27899         color = color.replace("#", "");
27900         if(color != this.value || this.allowReselect){
27901             var el = this.el;
27902             if(this.value){
27903                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27904             }
27905             el.child("a.color-"+color).addClass("x-color-palette-sel");
27906             this.value = color;
27907             this.fireEvent("select", this, color);
27908         }
27909     }
27910 });/*
27911  * Based on:
27912  * Ext JS Library 1.1.1
27913  * Copyright(c) 2006-2007, Ext JS, LLC.
27914  *
27915  * Originally Released Under LGPL - original licence link has changed is not relivant.
27916  *
27917  * Fork - LGPL
27918  * <script type="text/javascript">
27919  */
27920  
27921 /**
27922  * @class Roo.DatePicker
27923  * @extends Roo.Component
27924  * Simple date picker class.
27925  * @constructor
27926  * Create a new DatePicker
27927  * @param {Object} config The config object
27928  */
27929 Roo.DatePicker = function(config){
27930     Roo.DatePicker.superclass.constructor.call(this, config);
27931
27932     this.value = config && config.value ?
27933                  config.value.clearTime() : new Date().clearTime();
27934
27935     this.addEvents({
27936         /**
27937              * @event select
27938              * Fires when a date is selected
27939              * @param {DatePicker} this
27940              * @param {Date} date The selected date
27941              */
27942         'select': true,
27943         /**
27944              * @event monthchange
27945              * Fires when the displayed month changes 
27946              * @param {DatePicker} this
27947              * @param {Date} date The selected month
27948              */
27949         'monthchange': true
27950     });
27951
27952     if(this.handler){
27953         this.on("select", this.handler,  this.scope || this);
27954     }
27955     // build the disabledDatesRE
27956     if(!this.disabledDatesRE && this.disabledDates){
27957         var dd = this.disabledDates;
27958         var re = "(?:";
27959         for(var i = 0; i < dd.length; i++){
27960             re += dd[i];
27961             if(i != dd.length-1) {
27962                 re += "|";
27963             }
27964         }
27965         this.disabledDatesRE = new RegExp(re + ")");
27966     }
27967 };
27968
27969 Roo.extend(Roo.DatePicker, Roo.Component, {
27970     /**
27971      * @cfg {String} todayText
27972      * The text to display on the button that selects the current date (defaults to "Today")
27973      */
27974     todayText : "Today",
27975     /**
27976      * @cfg {String} okText
27977      * The text to display on the ok button
27978      */
27979     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27980     /**
27981      * @cfg {String} cancelText
27982      * The text to display on the cancel button
27983      */
27984     cancelText : "Cancel",
27985     /**
27986      * @cfg {String} todayTip
27987      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27988      */
27989     todayTip : "{0} (Spacebar)",
27990     /**
27991      * @cfg {Date} minDate
27992      * Minimum allowable date (JavaScript date object, defaults to null)
27993      */
27994     minDate : null,
27995     /**
27996      * @cfg {Date} maxDate
27997      * Maximum allowable date (JavaScript date object, defaults to null)
27998      */
27999     maxDate : null,
28000     /**
28001      * @cfg {String} minText
28002      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
28003      */
28004     minText : "This date is before the minimum date",
28005     /**
28006      * @cfg {String} maxText
28007      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
28008      */
28009     maxText : "This date is after the maximum date",
28010     /**
28011      * @cfg {String} format
28012      * The default date format string which can be overriden for localization support.  The format must be
28013      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
28014      */
28015     format : "m/d/y",
28016     /**
28017      * @cfg {Array} disabledDays
28018      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
28019      */
28020     disabledDays : null,
28021     /**
28022      * @cfg {String} disabledDaysText
28023      * The tooltip to display when the date falls on a disabled day (defaults to "")
28024      */
28025     disabledDaysText : "",
28026     /**
28027      * @cfg {RegExp} disabledDatesRE
28028      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
28029      */
28030     disabledDatesRE : null,
28031     /**
28032      * @cfg {String} disabledDatesText
28033      * The tooltip text to display when the date falls on a disabled date (defaults to "")
28034      */
28035     disabledDatesText : "",
28036     /**
28037      * @cfg {Boolean} constrainToViewport
28038      * True to constrain the date picker to the viewport (defaults to true)
28039      */
28040     constrainToViewport : true,
28041     /**
28042      * @cfg {Array} monthNames
28043      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
28044      */
28045     monthNames : Date.monthNames,
28046     /**
28047      * @cfg {Array} dayNames
28048      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
28049      */
28050     dayNames : Date.dayNames,
28051     /**
28052      * @cfg {String} nextText
28053      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
28054      */
28055     nextText: 'Next Month (Control+Right)',
28056     /**
28057      * @cfg {String} prevText
28058      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
28059      */
28060     prevText: 'Previous Month (Control+Left)',
28061     /**
28062      * @cfg {String} monthYearText
28063      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
28064      */
28065     monthYearText: 'Choose a month (Control+Up/Down to move years)',
28066     /**
28067      * @cfg {Number} startDay
28068      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
28069      */
28070     startDay : 0,
28071     /**
28072      * @cfg {Bool} showClear
28073      * Show a clear button (usefull for date form elements that can be blank.)
28074      */
28075     
28076     showClear: false,
28077     
28078     /**
28079      * Sets the value of the date field
28080      * @param {Date} value The date to set
28081      */
28082     setValue : function(value){
28083         var old = this.value;
28084         
28085         if (typeof(value) == 'string') {
28086          
28087             value = Date.parseDate(value, this.format);
28088         }
28089         if (!value) {
28090             value = new Date();
28091         }
28092         
28093         this.value = value.clearTime(true);
28094         if(this.el){
28095             this.update(this.value);
28096         }
28097     },
28098
28099     /**
28100      * Gets the current selected value of the date field
28101      * @return {Date} The selected date
28102      */
28103     getValue : function(){
28104         return this.value;
28105     },
28106
28107     // private
28108     focus : function(){
28109         if(this.el){
28110             this.update(this.activeDate);
28111         }
28112     },
28113
28114     // privateval
28115     onRender : function(container, position){
28116         
28117         var m = [
28118              '<table cellspacing="0">',
28119                 '<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>',
28120                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
28121         var dn = this.dayNames;
28122         for(var i = 0; i < 7; i++){
28123             var d = this.startDay+i;
28124             if(d > 6){
28125                 d = d-7;
28126             }
28127             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
28128         }
28129         m[m.length] = "</tr></thead><tbody><tr>";
28130         for(var i = 0; i < 42; i++) {
28131             if(i % 7 == 0 && i != 0){
28132                 m[m.length] = "</tr><tr>";
28133             }
28134             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
28135         }
28136         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
28137             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
28138
28139         var el = document.createElement("div");
28140         el.className = "x-date-picker";
28141         el.innerHTML = m.join("");
28142
28143         container.dom.insertBefore(el, position);
28144
28145         this.el = Roo.get(el);
28146         this.eventEl = Roo.get(el.firstChild);
28147
28148         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
28149             handler: this.showPrevMonth,
28150             scope: this,
28151             preventDefault:true,
28152             stopDefault:true
28153         });
28154
28155         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
28156             handler: this.showNextMonth,
28157             scope: this,
28158             preventDefault:true,
28159             stopDefault:true
28160         });
28161
28162         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
28163
28164         this.monthPicker = this.el.down('div.x-date-mp');
28165         this.monthPicker.enableDisplayMode('block');
28166         
28167         var kn = new Roo.KeyNav(this.eventEl, {
28168             "left" : function(e){
28169                 e.ctrlKey ?
28170                     this.showPrevMonth() :
28171                     this.update(this.activeDate.add("d", -1));
28172             },
28173
28174             "right" : function(e){
28175                 e.ctrlKey ?
28176                     this.showNextMonth() :
28177                     this.update(this.activeDate.add("d", 1));
28178             },
28179
28180             "up" : function(e){
28181                 e.ctrlKey ?
28182                     this.showNextYear() :
28183                     this.update(this.activeDate.add("d", -7));
28184             },
28185
28186             "down" : function(e){
28187                 e.ctrlKey ?
28188                     this.showPrevYear() :
28189                     this.update(this.activeDate.add("d", 7));
28190             },
28191
28192             "pageUp" : function(e){
28193                 this.showNextMonth();
28194             },
28195
28196             "pageDown" : function(e){
28197                 this.showPrevMonth();
28198             },
28199
28200             "enter" : function(e){
28201                 e.stopPropagation();
28202                 return true;
28203             },
28204
28205             scope : this
28206         });
28207
28208         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
28209
28210         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
28211
28212         this.el.unselectable();
28213         
28214         this.cells = this.el.select("table.x-date-inner tbody td");
28215         this.textNodes = this.el.query("table.x-date-inner tbody span");
28216
28217         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
28218             text: "&#160;",
28219             tooltip: this.monthYearText
28220         });
28221
28222         this.mbtn.on('click', this.showMonthPicker, this);
28223         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
28224
28225
28226         var today = (new Date()).dateFormat(this.format);
28227         
28228         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
28229         if (this.showClear) {
28230             baseTb.add( new Roo.Toolbar.Fill());
28231         }
28232         baseTb.add({
28233             text: String.format(this.todayText, today),
28234             tooltip: String.format(this.todayTip, today),
28235             handler: this.selectToday,
28236             scope: this
28237         });
28238         
28239         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
28240             
28241         //});
28242         if (this.showClear) {
28243             
28244             baseTb.add( new Roo.Toolbar.Fill());
28245             baseTb.add({
28246                 text: '&#160;',
28247                 cls: 'x-btn-icon x-btn-clear',
28248                 handler: function() {
28249                     //this.value = '';
28250                     this.fireEvent("select", this, '');
28251                 },
28252                 scope: this
28253             });
28254         }
28255         
28256         
28257         if(Roo.isIE){
28258             this.el.repaint();
28259         }
28260         this.update(this.value);
28261     },
28262
28263     createMonthPicker : function(){
28264         if(!this.monthPicker.dom.firstChild){
28265             var buf = ['<table border="0" cellspacing="0">'];
28266             for(var i = 0; i < 6; i++){
28267                 buf.push(
28268                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
28269                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
28270                     i == 0 ?
28271                     '<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>' :
28272                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
28273                 );
28274             }
28275             buf.push(
28276                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
28277                     this.okText,
28278                     '</button><button type="button" class="x-date-mp-cancel">',
28279                     this.cancelText,
28280                     '</button></td></tr>',
28281                 '</table>'
28282             );
28283             this.monthPicker.update(buf.join(''));
28284             this.monthPicker.on('click', this.onMonthClick, this);
28285             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
28286
28287             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28288             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28289
28290             this.mpMonths.each(function(m, a, i){
28291                 i += 1;
28292                 if((i%2) == 0){
28293                     m.dom.xmonth = 5 + Math.round(i * .5);
28294                 }else{
28295                     m.dom.xmonth = Math.round((i-1) * .5);
28296                 }
28297             });
28298         }
28299     },
28300
28301     showMonthPicker : function(){
28302         this.createMonthPicker();
28303         var size = this.el.getSize();
28304         this.monthPicker.setSize(size);
28305         this.monthPicker.child('table').setSize(size);
28306
28307         this.mpSelMonth = (this.activeDate || this.value).getMonth();
28308         this.updateMPMonth(this.mpSelMonth);
28309         this.mpSelYear = (this.activeDate || this.value).getFullYear();
28310         this.updateMPYear(this.mpSelYear);
28311
28312         this.monthPicker.slideIn('t', {duration:.2});
28313     },
28314
28315     updateMPYear : function(y){
28316         this.mpyear = y;
28317         var ys = this.mpYears.elements;
28318         for(var i = 1; i <= 10; i++){
28319             var td = ys[i-1], y2;
28320             if((i%2) == 0){
28321                 y2 = y + Math.round(i * .5);
28322                 td.firstChild.innerHTML = y2;
28323                 td.xyear = y2;
28324             }else{
28325                 y2 = y - (5-Math.round(i * .5));
28326                 td.firstChild.innerHTML = y2;
28327                 td.xyear = y2;
28328             }
28329             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28330         }
28331     },
28332
28333     updateMPMonth : function(sm){
28334         this.mpMonths.each(function(m, a, i){
28335             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28336         });
28337     },
28338
28339     selectMPMonth: function(m){
28340         
28341     },
28342
28343     onMonthClick : function(e, t){
28344         e.stopEvent();
28345         var el = new Roo.Element(t), pn;
28346         if(el.is('button.x-date-mp-cancel')){
28347             this.hideMonthPicker();
28348         }
28349         else if(el.is('button.x-date-mp-ok')){
28350             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28351             this.hideMonthPicker();
28352         }
28353         else if(pn = el.up('td.x-date-mp-month', 2)){
28354             this.mpMonths.removeClass('x-date-mp-sel');
28355             pn.addClass('x-date-mp-sel');
28356             this.mpSelMonth = pn.dom.xmonth;
28357         }
28358         else if(pn = el.up('td.x-date-mp-year', 2)){
28359             this.mpYears.removeClass('x-date-mp-sel');
28360             pn.addClass('x-date-mp-sel');
28361             this.mpSelYear = pn.dom.xyear;
28362         }
28363         else if(el.is('a.x-date-mp-prev')){
28364             this.updateMPYear(this.mpyear-10);
28365         }
28366         else if(el.is('a.x-date-mp-next')){
28367             this.updateMPYear(this.mpyear+10);
28368         }
28369     },
28370
28371     onMonthDblClick : function(e, t){
28372         e.stopEvent();
28373         var el = new Roo.Element(t), pn;
28374         if(pn = el.up('td.x-date-mp-month', 2)){
28375             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28376             this.hideMonthPicker();
28377         }
28378         else if(pn = el.up('td.x-date-mp-year', 2)){
28379             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28380             this.hideMonthPicker();
28381         }
28382     },
28383
28384     hideMonthPicker : function(disableAnim){
28385         if(this.monthPicker){
28386             if(disableAnim === true){
28387                 this.monthPicker.hide();
28388             }else{
28389                 this.monthPicker.slideOut('t', {duration:.2});
28390             }
28391         }
28392     },
28393
28394     // private
28395     showPrevMonth : function(e){
28396         this.update(this.activeDate.add("mo", -1));
28397     },
28398
28399     // private
28400     showNextMonth : function(e){
28401         this.update(this.activeDate.add("mo", 1));
28402     },
28403
28404     // private
28405     showPrevYear : function(){
28406         this.update(this.activeDate.add("y", -1));
28407     },
28408
28409     // private
28410     showNextYear : function(){
28411         this.update(this.activeDate.add("y", 1));
28412     },
28413
28414     // private
28415     handleMouseWheel : function(e){
28416         var delta = e.getWheelDelta();
28417         if(delta > 0){
28418             this.showPrevMonth();
28419             e.stopEvent();
28420         } else if(delta < 0){
28421             this.showNextMonth();
28422             e.stopEvent();
28423         }
28424     },
28425
28426     // private
28427     handleDateClick : function(e, t){
28428         e.stopEvent();
28429         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28430             this.setValue(new Date(t.dateValue));
28431             this.fireEvent("select", this, this.value);
28432         }
28433     },
28434
28435     // private
28436     selectToday : function(){
28437         this.setValue(new Date().clearTime());
28438         this.fireEvent("select", this, this.value);
28439     },
28440
28441     // private
28442     update : function(date)
28443     {
28444         var vd = this.activeDate;
28445         this.activeDate = date;
28446         if(vd && this.el){
28447             var t = date.getTime();
28448             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28449                 this.cells.removeClass("x-date-selected");
28450                 this.cells.each(function(c){
28451                    if(c.dom.firstChild.dateValue == t){
28452                        c.addClass("x-date-selected");
28453                        setTimeout(function(){
28454                             try{c.dom.firstChild.focus();}catch(e){}
28455                        }, 50);
28456                        return false;
28457                    }
28458                 });
28459                 return;
28460             }
28461         }
28462         
28463         var days = date.getDaysInMonth();
28464         var firstOfMonth = date.getFirstDateOfMonth();
28465         var startingPos = firstOfMonth.getDay()-this.startDay;
28466
28467         if(startingPos <= this.startDay){
28468             startingPos += 7;
28469         }
28470
28471         var pm = date.add("mo", -1);
28472         var prevStart = pm.getDaysInMonth()-startingPos;
28473
28474         var cells = this.cells.elements;
28475         var textEls = this.textNodes;
28476         days += startingPos;
28477
28478         // convert everything to numbers so it's fast
28479         var day = 86400000;
28480         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28481         var today = new Date().clearTime().getTime();
28482         var sel = date.clearTime().getTime();
28483         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28484         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28485         var ddMatch = this.disabledDatesRE;
28486         var ddText = this.disabledDatesText;
28487         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28488         var ddaysText = this.disabledDaysText;
28489         var format = this.format;
28490
28491         var setCellClass = function(cal, cell){
28492             cell.title = "";
28493             var t = d.getTime();
28494             cell.firstChild.dateValue = t;
28495             if(t == today){
28496                 cell.className += " x-date-today";
28497                 cell.title = cal.todayText;
28498             }
28499             if(t == sel){
28500                 cell.className += " x-date-selected";
28501                 setTimeout(function(){
28502                     try{cell.firstChild.focus();}catch(e){}
28503                 }, 50);
28504             }
28505             // disabling
28506             if(t < min) {
28507                 cell.className = " x-date-disabled";
28508                 cell.title = cal.minText;
28509                 return;
28510             }
28511             if(t > max) {
28512                 cell.className = " x-date-disabled";
28513                 cell.title = cal.maxText;
28514                 return;
28515             }
28516             if(ddays){
28517                 if(ddays.indexOf(d.getDay()) != -1){
28518                     cell.title = ddaysText;
28519                     cell.className = " x-date-disabled";
28520                 }
28521             }
28522             if(ddMatch && format){
28523                 var fvalue = d.dateFormat(format);
28524                 if(ddMatch.test(fvalue)){
28525                     cell.title = ddText.replace("%0", fvalue);
28526                     cell.className = " x-date-disabled";
28527                 }
28528             }
28529         };
28530
28531         var i = 0;
28532         for(; i < startingPos; i++) {
28533             textEls[i].innerHTML = (++prevStart);
28534             d.setDate(d.getDate()+1);
28535             cells[i].className = "x-date-prevday";
28536             setCellClass(this, cells[i]);
28537         }
28538         for(; i < days; i++){
28539             intDay = i - startingPos + 1;
28540             textEls[i].innerHTML = (intDay);
28541             d.setDate(d.getDate()+1);
28542             cells[i].className = "x-date-active";
28543             setCellClass(this, cells[i]);
28544         }
28545         var extraDays = 0;
28546         for(; i < 42; i++) {
28547              textEls[i].innerHTML = (++extraDays);
28548              d.setDate(d.getDate()+1);
28549              cells[i].className = "x-date-nextday";
28550              setCellClass(this, cells[i]);
28551         }
28552
28553         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28554         this.fireEvent('monthchange', this, date);
28555         
28556         if(!this.internalRender){
28557             var main = this.el.dom.firstChild;
28558             var w = main.offsetWidth;
28559             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28560             Roo.fly(main).setWidth(w);
28561             this.internalRender = true;
28562             // opera does not respect the auto grow header center column
28563             // then, after it gets a width opera refuses to recalculate
28564             // without a second pass
28565             if(Roo.isOpera && !this.secondPass){
28566                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28567                 this.secondPass = true;
28568                 this.update.defer(10, this, [date]);
28569             }
28570         }
28571         
28572         
28573     }
28574 });        /*
28575  * Based on:
28576  * Ext JS Library 1.1.1
28577  * Copyright(c) 2006-2007, Ext JS, LLC.
28578  *
28579  * Originally Released Under LGPL - original licence link has changed is not relivant.
28580  *
28581  * Fork - LGPL
28582  * <script type="text/javascript">
28583  */
28584 /**
28585  * @class Roo.TabPanel
28586  * @extends Roo.util.Observable
28587  * A lightweight tab container.
28588  * <br><br>
28589  * Usage:
28590  * <pre><code>
28591 // basic tabs 1, built from existing content
28592 var tabs = new Roo.TabPanel("tabs1");
28593 tabs.addTab("script", "View Script");
28594 tabs.addTab("markup", "View Markup");
28595 tabs.activate("script");
28596
28597 // more advanced tabs, built from javascript
28598 var jtabs = new Roo.TabPanel("jtabs");
28599 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28600
28601 // set up the UpdateManager
28602 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28603 var updater = tab2.getUpdateManager();
28604 updater.setDefaultUrl("ajax1.htm");
28605 tab2.on('activate', updater.refresh, updater, true);
28606
28607 // Use setUrl for Ajax loading
28608 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28609 tab3.setUrl("ajax2.htm", null, true);
28610
28611 // Disabled tab
28612 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28613 tab4.disable();
28614
28615 jtabs.activate("jtabs-1");
28616  * </code></pre>
28617  * @constructor
28618  * Create a new TabPanel.
28619  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28620  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28621  */
28622 Roo.TabPanel = function(container, config){
28623     /**
28624     * The container element for this TabPanel.
28625     * @type Roo.Element
28626     */
28627     this.el = Roo.get(container, true);
28628     if(config){
28629         if(typeof config == "boolean"){
28630             this.tabPosition = config ? "bottom" : "top";
28631         }else{
28632             Roo.apply(this, config);
28633         }
28634     }
28635     if(this.tabPosition == "bottom"){
28636         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28637         this.el.addClass("x-tabs-bottom");
28638     }
28639     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28640     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28641     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28642     if(Roo.isIE){
28643         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28644     }
28645     if(this.tabPosition != "bottom"){
28646         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28647          * @type Roo.Element
28648          */
28649         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28650         this.el.addClass("x-tabs-top");
28651     }
28652     this.items = [];
28653
28654     this.bodyEl.setStyle("position", "relative");
28655
28656     this.active = null;
28657     this.activateDelegate = this.activate.createDelegate(this);
28658
28659     this.addEvents({
28660         /**
28661          * @event tabchange
28662          * Fires when the active tab changes
28663          * @param {Roo.TabPanel} this
28664          * @param {Roo.TabPanelItem} activePanel The new active tab
28665          */
28666         "tabchange": true,
28667         /**
28668          * @event beforetabchange
28669          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28670          * @param {Roo.TabPanel} this
28671          * @param {Object} e Set cancel to true on this object to cancel the tab change
28672          * @param {Roo.TabPanelItem} tab The tab being changed to
28673          */
28674         "beforetabchange" : true
28675     });
28676
28677     Roo.EventManager.onWindowResize(this.onResize, this);
28678     this.cpad = this.el.getPadding("lr");
28679     this.hiddenCount = 0;
28680
28681
28682     // toolbar on the tabbar support...
28683     if (this.toolbar) {
28684         var tcfg = this.toolbar;
28685         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28686         this.toolbar = new Roo.Toolbar(tcfg);
28687         if (Roo.isSafari) {
28688             var tbl = tcfg.container.child('table', true);
28689             tbl.setAttribute('width', '100%');
28690         }
28691         
28692     }
28693    
28694
28695
28696     Roo.TabPanel.superclass.constructor.call(this);
28697 };
28698
28699 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28700     /*
28701      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28702      */
28703     tabPosition : "top",
28704     /*
28705      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28706      */
28707     currentTabWidth : 0,
28708     /*
28709      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28710      */
28711     minTabWidth : 40,
28712     /*
28713      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28714      */
28715     maxTabWidth : 250,
28716     /*
28717      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28718      */
28719     preferredTabWidth : 175,
28720     /*
28721      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28722      */
28723     resizeTabs : false,
28724     /*
28725      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28726      */
28727     monitorResize : true,
28728     /*
28729      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28730      */
28731     toolbar : false,
28732
28733     /**
28734      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28735      * @param {String} id The id of the div to use <b>or create</b>
28736      * @param {String} text The text for the tab
28737      * @param {String} content (optional) Content to put in the TabPanelItem body
28738      * @param {Boolean} closable (optional) True to create a close icon on the tab
28739      * @return {Roo.TabPanelItem} The created TabPanelItem
28740      */
28741     addTab : function(id, text, content, closable){
28742         var item = new Roo.TabPanelItem(this, id, text, closable);
28743         this.addTabItem(item);
28744         if(content){
28745             item.setContent(content);
28746         }
28747         return item;
28748     },
28749
28750     /**
28751      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28752      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28753      * @return {Roo.TabPanelItem}
28754      */
28755     getTab : function(id){
28756         return this.items[id];
28757     },
28758
28759     /**
28760      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28761      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28762      */
28763     hideTab : function(id){
28764         var t = this.items[id];
28765         if(!t.isHidden()){
28766            t.setHidden(true);
28767            this.hiddenCount++;
28768            this.autoSizeTabs();
28769         }
28770     },
28771
28772     /**
28773      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28774      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28775      */
28776     unhideTab : function(id){
28777         var t = this.items[id];
28778         if(t.isHidden()){
28779            t.setHidden(false);
28780            this.hiddenCount--;
28781            this.autoSizeTabs();
28782         }
28783     },
28784
28785     /**
28786      * Adds an existing {@link Roo.TabPanelItem}.
28787      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28788      */
28789     addTabItem : function(item){
28790         this.items[item.id] = item;
28791         this.items.push(item);
28792         if(this.resizeTabs){
28793            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28794            this.autoSizeTabs();
28795         }else{
28796             item.autoSize();
28797         }
28798     },
28799
28800     /**
28801      * Removes a {@link Roo.TabPanelItem}.
28802      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28803      */
28804     removeTab : function(id){
28805         var items = this.items;
28806         var tab = items[id];
28807         if(!tab) { return; }
28808         var index = items.indexOf(tab);
28809         if(this.active == tab && items.length > 1){
28810             var newTab = this.getNextAvailable(index);
28811             if(newTab) {
28812                 newTab.activate();
28813             }
28814         }
28815         this.stripEl.dom.removeChild(tab.pnode.dom);
28816         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28817             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28818         }
28819         items.splice(index, 1);
28820         delete this.items[tab.id];
28821         tab.fireEvent("close", tab);
28822         tab.purgeListeners();
28823         this.autoSizeTabs();
28824     },
28825
28826     getNextAvailable : function(start){
28827         var items = this.items;
28828         var index = start;
28829         // look for a next tab that will slide over to
28830         // replace the one being removed
28831         while(index < items.length){
28832             var item = items[++index];
28833             if(item && !item.isHidden()){
28834                 return item;
28835             }
28836         }
28837         // if one isn't found select the previous tab (on the left)
28838         index = start;
28839         while(index >= 0){
28840             var item = items[--index];
28841             if(item && !item.isHidden()){
28842                 return item;
28843             }
28844         }
28845         return null;
28846     },
28847
28848     /**
28849      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28850      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28851      */
28852     disableTab : function(id){
28853         var tab = this.items[id];
28854         if(tab && this.active != tab){
28855             tab.disable();
28856         }
28857     },
28858
28859     /**
28860      * Enables a {@link Roo.TabPanelItem} that is disabled.
28861      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28862      */
28863     enableTab : function(id){
28864         var tab = this.items[id];
28865         tab.enable();
28866     },
28867
28868     /**
28869      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28870      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28871      * @return {Roo.TabPanelItem} The TabPanelItem.
28872      */
28873     activate : function(id){
28874         var tab = this.items[id];
28875         if(!tab){
28876             return null;
28877         }
28878         if(tab == this.active || tab.disabled){
28879             return tab;
28880         }
28881         var e = {};
28882         this.fireEvent("beforetabchange", this, e, tab);
28883         if(e.cancel !== true && !tab.disabled){
28884             if(this.active){
28885                 this.active.hide();
28886             }
28887             this.active = this.items[id];
28888             this.active.show();
28889             this.fireEvent("tabchange", this, this.active);
28890         }
28891         return tab;
28892     },
28893
28894     /**
28895      * Gets the active {@link Roo.TabPanelItem}.
28896      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28897      */
28898     getActiveTab : function(){
28899         return this.active;
28900     },
28901
28902     /**
28903      * Updates the tab body element to fit the height of the container element
28904      * for overflow scrolling
28905      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28906      */
28907     syncHeight : function(targetHeight){
28908         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28909         var bm = this.bodyEl.getMargins();
28910         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28911         this.bodyEl.setHeight(newHeight);
28912         return newHeight;
28913     },
28914
28915     onResize : function(){
28916         if(this.monitorResize){
28917             this.autoSizeTabs();
28918         }
28919     },
28920
28921     /**
28922      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28923      */
28924     beginUpdate : function(){
28925         this.updating = true;
28926     },
28927
28928     /**
28929      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28930      */
28931     endUpdate : function(){
28932         this.updating = false;
28933         this.autoSizeTabs();
28934     },
28935
28936     /**
28937      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28938      */
28939     autoSizeTabs : function(){
28940         var count = this.items.length;
28941         var vcount = count - this.hiddenCount;
28942         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28943             return;
28944         }
28945         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28946         var availWidth = Math.floor(w / vcount);
28947         var b = this.stripBody;
28948         if(b.getWidth() > w){
28949             var tabs = this.items;
28950             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28951             if(availWidth < this.minTabWidth){
28952                 /*if(!this.sleft){    // incomplete scrolling code
28953                     this.createScrollButtons();
28954                 }
28955                 this.showScroll();
28956                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28957             }
28958         }else{
28959             if(this.currentTabWidth < this.preferredTabWidth){
28960                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28961             }
28962         }
28963     },
28964
28965     /**
28966      * Returns the number of tabs in this TabPanel.
28967      * @return {Number}
28968      */
28969      getCount : function(){
28970          return this.items.length;
28971      },
28972
28973     /**
28974      * Resizes all the tabs to the passed width
28975      * @param {Number} The new width
28976      */
28977     setTabWidth : function(width){
28978         this.currentTabWidth = width;
28979         for(var i = 0, len = this.items.length; i < len; i++) {
28980                 if(!this.items[i].isHidden()) {
28981                 this.items[i].setWidth(width);
28982             }
28983         }
28984     },
28985
28986     /**
28987      * Destroys this TabPanel
28988      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28989      */
28990     destroy : function(removeEl){
28991         Roo.EventManager.removeResizeListener(this.onResize, this);
28992         for(var i = 0, len = this.items.length; i < len; i++){
28993             this.items[i].purgeListeners();
28994         }
28995         if(removeEl === true){
28996             this.el.update("");
28997             this.el.remove();
28998         }
28999     }
29000 });
29001
29002 /**
29003  * @class Roo.TabPanelItem
29004  * @extends Roo.util.Observable
29005  * Represents an individual item (tab plus body) in a TabPanel.
29006  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
29007  * @param {String} id The id of this TabPanelItem
29008  * @param {String} text The text for the tab of this TabPanelItem
29009  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
29010  */
29011 Roo.TabPanelItem = function(tabPanel, id, text, closable){
29012     /**
29013      * The {@link Roo.TabPanel} this TabPanelItem belongs to
29014      * @type Roo.TabPanel
29015      */
29016     this.tabPanel = tabPanel;
29017     /**
29018      * The id for this TabPanelItem
29019      * @type String
29020      */
29021     this.id = id;
29022     /** @private */
29023     this.disabled = false;
29024     /** @private */
29025     this.text = text;
29026     /** @private */
29027     this.loaded = false;
29028     this.closable = closable;
29029
29030     /**
29031      * The body element for this TabPanelItem.
29032      * @type Roo.Element
29033      */
29034     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
29035     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
29036     this.bodyEl.setStyle("display", "block");
29037     this.bodyEl.setStyle("zoom", "1");
29038     this.hideAction();
29039
29040     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
29041     /** @private */
29042     this.el = Roo.get(els.el, true);
29043     this.inner = Roo.get(els.inner, true);
29044     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
29045     this.pnode = Roo.get(els.el.parentNode, true);
29046     this.el.on("mousedown", this.onTabMouseDown, this);
29047     this.el.on("click", this.onTabClick, this);
29048     /** @private */
29049     if(closable){
29050         var c = Roo.get(els.close, true);
29051         c.dom.title = this.closeText;
29052         c.addClassOnOver("close-over");
29053         c.on("click", this.closeClick, this);
29054      }
29055
29056     this.addEvents({
29057          /**
29058          * @event activate
29059          * Fires when this tab becomes the active tab.
29060          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29061          * @param {Roo.TabPanelItem} this
29062          */
29063         "activate": true,
29064         /**
29065          * @event beforeclose
29066          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
29067          * @param {Roo.TabPanelItem} this
29068          * @param {Object} e Set cancel to true on this object to cancel the close.
29069          */
29070         "beforeclose": true,
29071         /**
29072          * @event close
29073          * Fires when this tab is closed.
29074          * @param {Roo.TabPanelItem} this
29075          */
29076          "close": true,
29077         /**
29078          * @event deactivate
29079          * Fires when this tab is no longer the active tab.
29080          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29081          * @param {Roo.TabPanelItem} this
29082          */
29083          "deactivate" : true
29084     });
29085     this.hidden = false;
29086
29087     Roo.TabPanelItem.superclass.constructor.call(this);
29088 };
29089
29090 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
29091     purgeListeners : function(){
29092        Roo.util.Observable.prototype.purgeListeners.call(this);
29093        this.el.removeAllListeners();
29094     },
29095     /**
29096      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
29097      */
29098     show : function(){
29099         this.pnode.addClass("on");
29100         this.showAction();
29101         if(Roo.isOpera){
29102             this.tabPanel.stripWrap.repaint();
29103         }
29104         this.fireEvent("activate", this.tabPanel, this);
29105     },
29106
29107     /**
29108      * Returns true if this tab is the active tab.
29109      * @return {Boolean}
29110      */
29111     isActive : function(){
29112         return this.tabPanel.getActiveTab() == this;
29113     },
29114
29115     /**
29116      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
29117      */
29118     hide : function(){
29119         this.pnode.removeClass("on");
29120         this.hideAction();
29121         this.fireEvent("deactivate", this.tabPanel, this);
29122     },
29123
29124     hideAction : function(){
29125         this.bodyEl.hide();
29126         this.bodyEl.setStyle("position", "absolute");
29127         this.bodyEl.setLeft("-20000px");
29128         this.bodyEl.setTop("-20000px");
29129     },
29130
29131     showAction : function(){
29132         this.bodyEl.setStyle("position", "relative");
29133         this.bodyEl.setTop("");
29134         this.bodyEl.setLeft("");
29135         this.bodyEl.show();
29136     },
29137
29138     /**
29139      * Set the tooltip for the tab.
29140      * @param {String} tooltip The tab's tooltip
29141      */
29142     setTooltip : function(text){
29143         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
29144             this.textEl.dom.qtip = text;
29145             this.textEl.dom.removeAttribute('title');
29146         }else{
29147             this.textEl.dom.title = text;
29148         }
29149     },
29150
29151     onTabClick : function(e){
29152         e.preventDefault();
29153         this.tabPanel.activate(this.id);
29154     },
29155
29156     onTabMouseDown : function(e){
29157         e.preventDefault();
29158         this.tabPanel.activate(this.id);
29159     },
29160
29161     getWidth : function(){
29162         return this.inner.getWidth();
29163     },
29164
29165     setWidth : function(width){
29166         var iwidth = width - this.pnode.getPadding("lr");
29167         this.inner.setWidth(iwidth);
29168         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
29169         this.pnode.setWidth(width);
29170     },
29171
29172     /**
29173      * Show or hide the tab
29174      * @param {Boolean} hidden True to hide or false to show.
29175      */
29176     setHidden : function(hidden){
29177         this.hidden = hidden;
29178         this.pnode.setStyle("display", hidden ? "none" : "");
29179     },
29180
29181     /**
29182      * Returns true if this tab is "hidden"
29183      * @return {Boolean}
29184      */
29185     isHidden : function(){
29186         return this.hidden;
29187     },
29188
29189     /**
29190      * Returns the text for this tab
29191      * @return {String}
29192      */
29193     getText : function(){
29194         return this.text;
29195     },
29196
29197     autoSize : function(){
29198         //this.el.beginMeasure();
29199         this.textEl.setWidth(1);
29200         /*
29201          *  #2804 [new] Tabs in Roojs
29202          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
29203          */
29204         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
29205         //this.el.endMeasure();
29206     },
29207
29208     /**
29209      * Sets the text for the tab (Note: this also sets the tooltip text)
29210      * @param {String} text The tab's text and tooltip
29211      */
29212     setText : function(text){
29213         this.text = text;
29214         this.textEl.update(text);
29215         this.setTooltip(text);
29216         if(!this.tabPanel.resizeTabs){
29217             this.autoSize();
29218         }
29219     },
29220     /**
29221      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
29222      */
29223     activate : function(){
29224         this.tabPanel.activate(this.id);
29225     },
29226
29227     /**
29228      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
29229      */
29230     disable : function(){
29231         if(this.tabPanel.active != this){
29232             this.disabled = true;
29233             this.pnode.addClass("disabled");
29234         }
29235     },
29236
29237     /**
29238      * Enables this TabPanelItem if it was previously disabled.
29239      */
29240     enable : function(){
29241         this.disabled = false;
29242         this.pnode.removeClass("disabled");
29243     },
29244
29245     /**
29246      * Sets the content for this TabPanelItem.
29247      * @param {String} content The content
29248      * @param {Boolean} loadScripts true to look for and load scripts
29249      */
29250     setContent : function(content, loadScripts){
29251         this.bodyEl.update(content, loadScripts);
29252     },
29253
29254     /**
29255      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
29256      * @return {Roo.UpdateManager} The UpdateManager
29257      */
29258     getUpdateManager : function(){
29259         return this.bodyEl.getUpdateManager();
29260     },
29261
29262     /**
29263      * Set a URL to be used to load the content for this TabPanelItem.
29264      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
29265      * @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)
29266      * @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)
29267      * @return {Roo.UpdateManager} The UpdateManager
29268      */
29269     setUrl : function(url, params, loadOnce){
29270         if(this.refreshDelegate){
29271             this.un('activate', this.refreshDelegate);
29272         }
29273         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
29274         this.on("activate", this.refreshDelegate);
29275         return this.bodyEl.getUpdateManager();
29276     },
29277
29278     /** @private */
29279     _handleRefresh : function(url, params, loadOnce){
29280         if(!loadOnce || !this.loaded){
29281             var updater = this.bodyEl.getUpdateManager();
29282             updater.update(url, params, this._setLoaded.createDelegate(this));
29283         }
29284     },
29285
29286     /**
29287      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
29288      *   Will fail silently if the setUrl method has not been called.
29289      *   This does not activate the panel, just updates its content.
29290      */
29291     refresh : function(){
29292         if(this.refreshDelegate){
29293            this.loaded = false;
29294            this.refreshDelegate();
29295         }
29296     },
29297
29298     /** @private */
29299     _setLoaded : function(){
29300         this.loaded = true;
29301     },
29302
29303     /** @private */
29304     closeClick : function(e){
29305         var o = {};
29306         e.stopEvent();
29307         this.fireEvent("beforeclose", this, o);
29308         if(o.cancel !== true){
29309             this.tabPanel.removeTab(this.id);
29310         }
29311     },
29312     /**
29313      * The text displayed in the tooltip for the close icon.
29314      * @type String
29315      */
29316     closeText : "Close this tab"
29317 });
29318
29319 /** @private */
29320 Roo.TabPanel.prototype.createStrip = function(container){
29321     var strip = document.createElement("div");
29322     strip.className = "x-tabs-wrap";
29323     container.appendChild(strip);
29324     return strip;
29325 };
29326 /** @private */
29327 Roo.TabPanel.prototype.createStripList = function(strip){
29328     // div wrapper for retard IE
29329     // returns the "tr" element.
29330     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29331         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29332         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29333     return strip.firstChild.firstChild.firstChild.firstChild;
29334 };
29335 /** @private */
29336 Roo.TabPanel.prototype.createBody = function(container){
29337     var body = document.createElement("div");
29338     Roo.id(body, "tab-body");
29339     Roo.fly(body).addClass("x-tabs-body");
29340     container.appendChild(body);
29341     return body;
29342 };
29343 /** @private */
29344 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29345     var body = Roo.getDom(id);
29346     if(!body){
29347         body = document.createElement("div");
29348         body.id = id;
29349     }
29350     Roo.fly(body).addClass("x-tabs-item-body");
29351     bodyEl.insertBefore(body, bodyEl.firstChild);
29352     return body;
29353 };
29354 /** @private */
29355 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29356     var td = document.createElement("td");
29357     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29358     //stripEl.appendChild(td);
29359     if(closable){
29360         td.className = "x-tabs-closable";
29361         if(!this.closeTpl){
29362             this.closeTpl = new Roo.Template(
29363                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29364                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29365                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29366             );
29367         }
29368         var el = this.closeTpl.overwrite(td, {"text": text});
29369         var close = el.getElementsByTagName("div")[0];
29370         var inner = el.getElementsByTagName("em")[0];
29371         return {"el": el, "close": close, "inner": inner};
29372     } else {
29373         if(!this.tabTpl){
29374             this.tabTpl = new Roo.Template(
29375                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29376                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29377             );
29378         }
29379         var el = this.tabTpl.overwrite(td, {"text": text});
29380         var inner = el.getElementsByTagName("em")[0];
29381         return {"el": el, "inner": inner};
29382     }
29383 };/*
29384  * Based on:
29385  * Ext JS Library 1.1.1
29386  * Copyright(c) 2006-2007, Ext JS, LLC.
29387  *
29388  * Originally Released Under LGPL - original licence link has changed is not relivant.
29389  *
29390  * Fork - LGPL
29391  * <script type="text/javascript">
29392  */
29393
29394 /**
29395  * @class Roo.Button
29396  * @extends Roo.util.Observable
29397  * Simple Button class
29398  * @cfg {String} text The button text
29399  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29400  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29401  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29402  * @cfg {Object} scope The scope of the handler
29403  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29404  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29405  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29406  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29407  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29408  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29409    applies if enableToggle = true)
29410  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29411  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29412   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29413  * @constructor
29414  * Create a new button
29415  * @param {Object} config The config object
29416  */
29417 Roo.Button = function(renderTo, config)
29418 {
29419     if (!config) {
29420         config = renderTo;
29421         renderTo = config.renderTo || false;
29422     }
29423     
29424     Roo.apply(this, config);
29425     this.addEvents({
29426         /**
29427              * @event click
29428              * Fires when this button is clicked
29429              * @param {Button} this
29430              * @param {EventObject} e The click event
29431              */
29432             "click" : true,
29433         /**
29434              * @event toggle
29435              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29436              * @param {Button} this
29437              * @param {Boolean} pressed
29438              */
29439             "toggle" : true,
29440         /**
29441              * @event mouseover
29442              * Fires when the mouse hovers over the button
29443              * @param {Button} this
29444              * @param {Event} e The event object
29445              */
29446         'mouseover' : true,
29447         /**
29448              * @event mouseout
29449              * Fires when the mouse exits the button
29450              * @param {Button} this
29451              * @param {Event} e The event object
29452              */
29453         'mouseout': true,
29454          /**
29455              * @event render
29456              * Fires when the button is rendered
29457              * @param {Button} this
29458              */
29459         'render': true
29460     });
29461     if(this.menu){
29462         this.menu = Roo.menu.MenuMgr.get(this.menu);
29463     }
29464     // register listeners first!!  - so render can be captured..
29465     Roo.util.Observable.call(this);
29466     if(renderTo){
29467         this.render(renderTo);
29468     }
29469     
29470   
29471 };
29472
29473 Roo.extend(Roo.Button, Roo.util.Observable, {
29474     /**
29475      * 
29476      */
29477     
29478     /**
29479      * Read-only. True if this button is hidden
29480      * @type Boolean
29481      */
29482     hidden : false,
29483     /**
29484      * Read-only. True if this button is disabled
29485      * @type Boolean
29486      */
29487     disabled : false,
29488     /**
29489      * Read-only. True if this button is pressed (only if enableToggle = true)
29490      * @type Boolean
29491      */
29492     pressed : false,
29493
29494     /**
29495      * @cfg {Number} tabIndex 
29496      * The DOM tabIndex for this button (defaults to undefined)
29497      */
29498     tabIndex : undefined,
29499
29500     /**
29501      * @cfg {Boolean} enableToggle
29502      * True to enable pressed/not pressed toggling (defaults to false)
29503      */
29504     enableToggle: false,
29505     /**
29506      * @cfg {Mixed} menu
29507      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29508      */
29509     menu : undefined,
29510     /**
29511      * @cfg {String} menuAlign
29512      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29513      */
29514     menuAlign : "tl-bl?",
29515
29516     /**
29517      * @cfg {String} iconCls
29518      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29519      */
29520     iconCls : undefined,
29521     /**
29522      * @cfg {String} type
29523      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29524      */
29525     type : 'button',
29526
29527     // private
29528     menuClassTarget: 'tr',
29529
29530     /**
29531      * @cfg {String} clickEvent
29532      * The type of event to map to the button's event handler (defaults to 'click')
29533      */
29534     clickEvent : 'click',
29535
29536     /**
29537      * @cfg {Boolean} handleMouseEvents
29538      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29539      */
29540     handleMouseEvents : true,
29541
29542     /**
29543      * @cfg {String} tooltipType
29544      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29545      */
29546     tooltipType : 'qtip',
29547
29548     /**
29549      * @cfg {String} cls
29550      * A CSS class to apply to the button's main element.
29551      */
29552     
29553     /**
29554      * @cfg {Roo.Template} template (Optional)
29555      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29556      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29557      * require code modifications if required elements (e.g. a button) aren't present.
29558      */
29559
29560     // private
29561     render : function(renderTo){
29562         var btn;
29563         if(this.hideParent){
29564             this.parentEl = Roo.get(renderTo);
29565         }
29566         if(!this.dhconfig){
29567             if(!this.template){
29568                 if(!Roo.Button.buttonTemplate){
29569                     // hideous table template
29570                     Roo.Button.buttonTemplate = new Roo.Template(
29571                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29572                         '<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>',
29573                         "</tr></tbody></table>");
29574                 }
29575                 this.template = Roo.Button.buttonTemplate;
29576             }
29577             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29578             var btnEl = btn.child("button:first");
29579             btnEl.on('focus', this.onFocus, this);
29580             btnEl.on('blur', this.onBlur, this);
29581             if(this.cls){
29582                 btn.addClass(this.cls);
29583             }
29584             if(this.icon){
29585                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29586             }
29587             if(this.iconCls){
29588                 btnEl.addClass(this.iconCls);
29589                 if(!this.cls){
29590                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29591                 }
29592             }
29593             if(this.tabIndex !== undefined){
29594                 btnEl.dom.tabIndex = this.tabIndex;
29595             }
29596             if(this.tooltip){
29597                 if(typeof this.tooltip == 'object'){
29598                     Roo.QuickTips.tips(Roo.apply({
29599                           target: btnEl.id
29600                     }, this.tooltip));
29601                 } else {
29602                     btnEl.dom[this.tooltipType] = this.tooltip;
29603                 }
29604             }
29605         }else{
29606             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29607         }
29608         this.el = btn;
29609         if(this.id){
29610             this.el.dom.id = this.el.id = this.id;
29611         }
29612         if(this.menu){
29613             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29614             this.menu.on("show", this.onMenuShow, this);
29615             this.menu.on("hide", this.onMenuHide, this);
29616         }
29617         btn.addClass("x-btn");
29618         if(Roo.isIE && !Roo.isIE7){
29619             this.autoWidth.defer(1, this);
29620         }else{
29621             this.autoWidth();
29622         }
29623         if(this.handleMouseEvents){
29624             btn.on("mouseover", this.onMouseOver, this);
29625             btn.on("mouseout", this.onMouseOut, this);
29626             btn.on("mousedown", this.onMouseDown, this);
29627         }
29628         btn.on(this.clickEvent, this.onClick, this);
29629         //btn.on("mouseup", this.onMouseUp, this);
29630         if(this.hidden){
29631             this.hide();
29632         }
29633         if(this.disabled){
29634             this.disable();
29635         }
29636         Roo.ButtonToggleMgr.register(this);
29637         if(this.pressed){
29638             this.el.addClass("x-btn-pressed");
29639         }
29640         if(this.repeat){
29641             var repeater = new Roo.util.ClickRepeater(btn,
29642                 typeof this.repeat == "object" ? this.repeat : {}
29643             );
29644             repeater.on("click", this.onClick,  this);
29645         }
29646         
29647         this.fireEvent('render', this);
29648         
29649     },
29650     /**
29651      * Returns the button's underlying element
29652      * @return {Roo.Element} The element
29653      */
29654     getEl : function(){
29655         return this.el;  
29656     },
29657     
29658     /**
29659      * Destroys this Button and removes any listeners.
29660      */
29661     destroy : function(){
29662         Roo.ButtonToggleMgr.unregister(this);
29663         this.el.removeAllListeners();
29664         this.purgeListeners();
29665         this.el.remove();
29666     },
29667
29668     // private
29669     autoWidth : function(){
29670         if(this.el){
29671             this.el.setWidth("auto");
29672             if(Roo.isIE7 && Roo.isStrict){
29673                 var ib = this.el.child('button');
29674                 if(ib && ib.getWidth() > 20){
29675                     ib.clip();
29676                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29677                 }
29678             }
29679             if(this.minWidth){
29680                 if(this.hidden){
29681                     this.el.beginMeasure();
29682                 }
29683                 if(this.el.getWidth() < this.minWidth){
29684                     this.el.setWidth(this.minWidth);
29685                 }
29686                 if(this.hidden){
29687                     this.el.endMeasure();
29688                 }
29689             }
29690         }
29691     },
29692
29693     /**
29694      * Assigns this button's click handler
29695      * @param {Function} handler The function to call when the button is clicked
29696      * @param {Object} scope (optional) Scope for the function passed in
29697      */
29698     setHandler : function(handler, scope){
29699         this.handler = handler;
29700         this.scope = scope;  
29701     },
29702     
29703     /**
29704      * Sets this button's text
29705      * @param {String} text The button text
29706      */
29707     setText : function(text){
29708         this.text = text;
29709         if(this.el){
29710             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29711         }
29712         this.autoWidth();
29713     },
29714     
29715     /**
29716      * Gets the text for this button
29717      * @return {String} The button text
29718      */
29719     getText : function(){
29720         return this.text;  
29721     },
29722     
29723     /**
29724      * Show this button
29725      */
29726     show: function(){
29727         this.hidden = false;
29728         if(this.el){
29729             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29730         }
29731     },
29732     
29733     /**
29734      * Hide this button
29735      */
29736     hide: function(){
29737         this.hidden = true;
29738         if(this.el){
29739             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29740         }
29741     },
29742     
29743     /**
29744      * Convenience function for boolean show/hide
29745      * @param {Boolean} visible True to show, false to hide
29746      */
29747     setVisible: function(visible){
29748         if(visible) {
29749             this.show();
29750         }else{
29751             this.hide();
29752         }
29753     },
29754     
29755     /**
29756      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29757      * @param {Boolean} state (optional) Force a particular state
29758      */
29759     toggle : function(state){
29760         state = state === undefined ? !this.pressed : state;
29761         if(state != this.pressed){
29762             if(state){
29763                 this.el.addClass("x-btn-pressed");
29764                 this.pressed = true;
29765                 this.fireEvent("toggle", this, true);
29766             }else{
29767                 this.el.removeClass("x-btn-pressed");
29768                 this.pressed = false;
29769                 this.fireEvent("toggle", this, false);
29770             }
29771             if(this.toggleHandler){
29772                 this.toggleHandler.call(this.scope || this, this, state);
29773             }
29774         }
29775     },
29776     
29777     /**
29778      * Focus the button
29779      */
29780     focus : function(){
29781         this.el.child('button:first').focus();
29782     },
29783     
29784     /**
29785      * Disable this button
29786      */
29787     disable : function(){
29788         if(this.el){
29789             this.el.addClass("x-btn-disabled");
29790         }
29791         this.disabled = true;
29792     },
29793     
29794     /**
29795      * Enable this button
29796      */
29797     enable : function(){
29798         if(this.el){
29799             this.el.removeClass("x-btn-disabled");
29800         }
29801         this.disabled = false;
29802     },
29803
29804     /**
29805      * Convenience function for boolean enable/disable
29806      * @param {Boolean} enabled True to enable, false to disable
29807      */
29808     setDisabled : function(v){
29809         this[v !== true ? "enable" : "disable"]();
29810     },
29811
29812     // private
29813     onClick : function(e)
29814     {
29815         if(e){
29816             e.preventDefault();
29817         }
29818         if(e.button != 0){
29819             return;
29820         }
29821         if(!this.disabled){
29822             if(this.enableToggle){
29823                 this.toggle();
29824             }
29825             if(this.menu && !this.menu.isVisible()){
29826                 this.menu.show(this.el, this.menuAlign);
29827             }
29828             this.fireEvent("click", this, e);
29829             if(this.handler){
29830                 this.el.removeClass("x-btn-over");
29831                 this.handler.call(this.scope || this, this, e);
29832             }
29833         }
29834     },
29835     // private
29836     onMouseOver : function(e){
29837         if(!this.disabled){
29838             this.el.addClass("x-btn-over");
29839             this.fireEvent('mouseover', this, e);
29840         }
29841     },
29842     // private
29843     onMouseOut : function(e){
29844         if(!e.within(this.el,  true)){
29845             this.el.removeClass("x-btn-over");
29846             this.fireEvent('mouseout', this, e);
29847         }
29848     },
29849     // private
29850     onFocus : function(e){
29851         if(!this.disabled){
29852             this.el.addClass("x-btn-focus");
29853         }
29854     },
29855     // private
29856     onBlur : function(e){
29857         this.el.removeClass("x-btn-focus");
29858     },
29859     // private
29860     onMouseDown : function(e){
29861         if(!this.disabled && e.button == 0){
29862             this.el.addClass("x-btn-click");
29863             Roo.get(document).on('mouseup', this.onMouseUp, this);
29864         }
29865     },
29866     // private
29867     onMouseUp : function(e){
29868         if(e.button == 0){
29869             this.el.removeClass("x-btn-click");
29870             Roo.get(document).un('mouseup', this.onMouseUp, this);
29871         }
29872     },
29873     // private
29874     onMenuShow : function(e){
29875         this.el.addClass("x-btn-menu-active");
29876     },
29877     // private
29878     onMenuHide : function(e){
29879         this.el.removeClass("x-btn-menu-active");
29880     }   
29881 });
29882
29883 // Private utility class used by Button
29884 Roo.ButtonToggleMgr = function(){
29885    var groups = {};
29886    
29887    function toggleGroup(btn, state){
29888        if(state){
29889            var g = groups[btn.toggleGroup];
29890            for(var i = 0, l = g.length; i < l; i++){
29891                if(g[i] != btn){
29892                    g[i].toggle(false);
29893                }
29894            }
29895        }
29896    }
29897    
29898    return {
29899        register : function(btn){
29900            if(!btn.toggleGroup){
29901                return;
29902            }
29903            var g = groups[btn.toggleGroup];
29904            if(!g){
29905                g = groups[btn.toggleGroup] = [];
29906            }
29907            g.push(btn);
29908            btn.on("toggle", toggleGroup);
29909        },
29910        
29911        unregister : function(btn){
29912            if(!btn.toggleGroup){
29913                return;
29914            }
29915            var g = groups[btn.toggleGroup];
29916            if(g){
29917                g.remove(btn);
29918                btn.un("toggle", toggleGroup);
29919            }
29920        }
29921    };
29922 }();/*
29923  * Based on:
29924  * Ext JS Library 1.1.1
29925  * Copyright(c) 2006-2007, Ext JS, LLC.
29926  *
29927  * Originally Released Under LGPL - original licence link has changed is not relivant.
29928  *
29929  * Fork - LGPL
29930  * <script type="text/javascript">
29931  */
29932  
29933 /**
29934  * @class Roo.SplitButton
29935  * @extends Roo.Button
29936  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29937  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29938  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29939  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29940  * @cfg {String} arrowTooltip The title attribute of the arrow
29941  * @constructor
29942  * Create a new menu button
29943  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29944  * @param {Object} config The config object
29945  */
29946 Roo.SplitButton = function(renderTo, config){
29947     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29948     /**
29949      * @event arrowclick
29950      * Fires when this button's arrow is clicked
29951      * @param {SplitButton} this
29952      * @param {EventObject} e The click event
29953      */
29954     this.addEvents({"arrowclick":true});
29955 };
29956
29957 Roo.extend(Roo.SplitButton, Roo.Button, {
29958     render : function(renderTo){
29959         // this is one sweet looking template!
29960         var tpl = new Roo.Template(
29961             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29962             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29963             '<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>',
29964             "</tbody></table></td><td>",
29965             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29966             '<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>',
29967             "</tbody></table></td></tr></table>"
29968         );
29969         var btn = tpl.append(renderTo, [this.text, this.type], true);
29970         var btnEl = btn.child("button");
29971         if(this.cls){
29972             btn.addClass(this.cls);
29973         }
29974         if(this.icon){
29975             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29976         }
29977         if(this.iconCls){
29978             btnEl.addClass(this.iconCls);
29979             if(!this.cls){
29980                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29981             }
29982         }
29983         this.el = btn;
29984         if(this.handleMouseEvents){
29985             btn.on("mouseover", this.onMouseOver, this);
29986             btn.on("mouseout", this.onMouseOut, this);
29987             btn.on("mousedown", this.onMouseDown, this);
29988             btn.on("mouseup", this.onMouseUp, this);
29989         }
29990         btn.on(this.clickEvent, this.onClick, this);
29991         if(this.tooltip){
29992             if(typeof this.tooltip == 'object'){
29993                 Roo.QuickTips.tips(Roo.apply({
29994                       target: btnEl.id
29995                 }, this.tooltip));
29996             } else {
29997                 btnEl.dom[this.tooltipType] = this.tooltip;
29998             }
29999         }
30000         if(this.arrowTooltip){
30001             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
30002         }
30003         if(this.hidden){
30004             this.hide();
30005         }
30006         if(this.disabled){
30007             this.disable();
30008         }
30009         if(this.pressed){
30010             this.el.addClass("x-btn-pressed");
30011         }
30012         if(Roo.isIE && !Roo.isIE7){
30013             this.autoWidth.defer(1, this);
30014         }else{
30015             this.autoWidth();
30016         }
30017         if(this.menu){
30018             this.menu.on("show", this.onMenuShow, this);
30019             this.menu.on("hide", this.onMenuHide, this);
30020         }
30021         this.fireEvent('render', this);
30022     },
30023
30024     // private
30025     autoWidth : function(){
30026         if(this.el){
30027             var tbl = this.el.child("table:first");
30028             var tbl2 = this.el.child("table:last");
30029             this.el.setWidth("auto");
30030             tbl.setWidth("auto");
30031             if(Roo.isIE7 && Roo.isStrict){
30032                 var ib = this.el.child('button:first');
30033                 if(ib && ib.getWidth() > 20){
30034                     ib.clip();
30035                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30036                 }
30037             }
30038             if(this.minWidth){
30039                 if(this.hidden){
30040                     this.el.beginMeasure();
30041                 }
30042                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
30043                     tbl.setWidth(this.minWidth-tbl2.getWidth());
30044                 }
30045                 if(this.hidden){
30046                     this.el.endMeasure();
30047                 }
30048             }
30049             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
30050         } 
30051     },
30052     /**
30053      * Sets this button's click handler
30054      * @param {Function} handler The function to call when the button is clicked
30055      * @param {Object} scope (optional) Scope for the function passed above
30056      */
30057     setHandler : function(handler, scope){
30058         this.handler = handler;
30059         this.scope = scope;  
30060     },
30061     
30062     /**
30063      * Sets this button's arrow click handler
30064      * @param {Function} handler The function to call when the arrow is clicked
30065      * @param {Object} scope (optional) Scope for the function passed above
30066      */
30067     setArrowHandler : function(handler, scope){
30068         this.arrowHandler = handler;
30069         this.scope = scope;  
30070     },
30071     
30072     /**
30073      * Focus the button
30074      */
30075     focus : function(){
30076         if(this.el){
30077             this.el.child("button:first").focus();
30078         }
30079     },
30080
30081     // private
30082     onClick : function(e){
30083         e.preventDefault();
30084         if(!this.disabled){
30085             if(e.getTarget(".x-btn-menu-arrow-wrap")){
30086                 if(this.menu && !this.menu.isVisible()){
30087                     this.menu.show(this.el, this.menuAlign);
30088                 }
30089                 this.fireEvent("arrowclick", this, e);
30090                 if(this.arrowHandler){
30091                     this.arrowHandler.call(this.scope || this, this, e);
30092                 }
30093             }else{
30094                 this.fireEvent("click", this, e);
30095                 if(this.handler){
30096                     this.handler.call(this.scope || this, this, e);
30097                 }
30098             }
30099         }
30100     },
30101     // private
30102     onMouseDown : function(e){
30103         if(!this.disabled){
30104             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
30105         }
30106     },
30107     // private
30108     onMouseUp : function(e){
30109         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
30110     }   
30111 });
30112
30113
30114 // backwards compat
30115 Roo.MenuButton = Roo.SplitButton;/*
30116  * Based on:
30117  * Ext JS Library 1.1.1
30118  * Copyright(c) 2006-2007, Ext JS, LLC.
30119  *
30120  * Originally Released Under LGPL - original licence link has changed is not relivant.
30121  *
30122  * Fork - LGPL
30123  * <script type="text/javascript">
30124  */
30125
30126 /**
30127  * @class Roo.Toolbar
30128  * Basic Toolbar class.
30129  * @constructor
30130  * Creates a new Toolbar
30131  * @param {Object} container The config object
30132  */ 
30133 Roo.Toolbar = function(container, buttons, config)
30134 {
30135     /// old consturctor format still supported..
30136     if(container instanceof Array){ // omit the container for later rendering
30137         buttons = container;
30138         config = buttons;
30139         container = null;
30140     }
30141     if (typeof(container) == 'object' && container.xtype) {
30142         config = container;
30143         container = config.container;
30144         buttons = config.buttons || []; // not really - use items!!
30145     }
30146     var xitems = [];
30147     if (config && config.items) {
30148         xitems = config.items;
30149         delete config.items;
30150     }
30151     Roo.apply(this, config);
30152     this.buttons = buttons;
30153     
30154     if(container){
30155         this.render(container);
30156     }
30157     this.xitems = xitems;
30158     Roo.each(xitems, function(b) {
30159         this.add(b);
30160     }, this);
30161     
30162 };
30163
30164 Roo.Toolbar.prototype = {
30165     /**
30166      * @cfg {Array} items
30167      * array of button configs or elements to add (will be converted to a MixedCollection)
30168      */
30169     
30170     /**
30171      * @cfg {String/HTMLElement/Element} container
30172      * The id or element that will contain the toolbar
30173      */
30174     // private
30175     render : function(ct){
30176         this.el = Roo.get(ct);
30177         if(this.cls){
30178             this.el.addClass(this.cls);
30179         }
30180         // using a table allows for vertical alignment
30181         // 100% width is needed by Safari...
30182         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
30183         this.tr = this.el.child("tr", true);
30184         var autoId = 0;
30185         this.items = new Roo.util.MixedCollection(false, function(o){
30186             return o.id || ("item" + (++autoId));
30187         });
30188         if(this.buttons){
30189             this.add.apply(this, this.buttons);
30190             delete this.buttons;
30191         }
30192     },
30193
30194     /**
30195      * Adds element(s) to the toolbar -- this function takes a variable number of 
30196      * arguments of mixed type and adds them to the toolbar.
30197      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
30198      * <ul>
30199      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
30200      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
30201      * <li>Field: Any form field (equivalent to {@link #addField})</li>
30202      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
30203      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
30204      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
30205      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
30206      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
30207      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
30208      * </ul>
30209      * @param {Mixed} arg2
30210      * @param {Mixed} etc.
30211      */
30212     add : function(){
30213         var a = arguments, l = a.length;
30214         for(var i = 0; i < l; i++){
30215             this._add(a[i]);
30216         }
30217     },
30218     // private..
30219     _add : function(el) {
30220         
30221         if (el.xtype) {
30222             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
30223         }
30224         
30225         if (el.applyTo){ // some kind of form field
30226             return this.addField(el);
30227         } 
30228         if (el.render){ // some kind of Toolbar.Item
30229             return this.addItem(el);
30230         }
30231         if (typeof el == "string"){ // string
30232             if(el == "separator" || el == "-"){
30233                 return this.addSeparator();
30234             }
30235             if (el == " "){
30236                 return this.addSpacer();
30237             }
30238             if(el == "->"){
30239                 return this.addFill();
30240             }
30241             return this.addText(el);
30242             
30243         }
30244         if(el.tagName){ // element
30245             return this.addElement(el);
30246         }
30247         if(typeof el == "object"){ // must be button config?
30248             return this.addButton(el);
30249         }
30250         // and now what?!?!
30251         return false;
30252         
30253     },
30254     
30255     /**
30256      * Add an Xtype element
30257      * @param {Object} xtype Xtype Object
30258      * @return {Object} created Object
30259      */
30260     addxtype : function(e){
30261         return this.add(e);  
30262     },
30263     
30264     /**
30265      * Returns the Element for this toolbar.
30266      * @return {Roo.Element}
30267      */
30268     getEl : function(){
30269         return this.el;  
30270     },
30271     
30272     /**
30273      * Adds a separator
30274      * @return {Roo.Toolbar.Item} The separator item
30275      */
30276     addSeparator : function(){
30277         return this.addItem(new Roo.Toolbar.Separator());
30278     },
30279
30280     /**
30281      * Adds a spacer element
30282      * @return {Roo.Toolbar.Spacer} The spacer item
30283      */
30284     addSpacer : function(){
30285         return this.addItem(new Roo.Toolbar.Spacer());
30286     },
30287
30288     /**
30289      * Adds a fill element that forces subsequent additions to the right side of the toolbar
30290      * @return {Roo.Toolbar.Fill} The fill item
30291      */
30292     addFill : function(){
30293         return this.addItem(new Roo.Toolbar.Fill());
30294     },
30295
30296     /**
30297      * Adds any standard HTML element to the toolbar
30298      * @param {String/HTMLElement/Element} el The element or id of the element to add
30299      * @return {Roo.Toolbar.Item} The element's item
30300      */
30301     addElement : function(el){
30302         return this.addItem(new Roo.Toolbar.Item(el));
30303     },
30304     /**
30305      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
30306      * @type Roo.util.MixedCollection  
30307      */
30308     items : false,
30309      
30310     /**
30311      * Adds any Toolbar.Item or subclass
30312      * @param {Roo.Toolbar.Item} item
30313      * @return {Roo.Toolbar.Item} The item
30314      */
30315     addItem : function(item){
30316         var td = this.nextBlock();
30317         item.render(td);
30318         this.items.add(item);
30319         return item;
30320     },
30321     
30322     /**
30323      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30324      * @param {Object/Array} config A button config or array of configs
30325      * @return {Roo.Toolbar.Button/Array}
30326      */
30327     addButton : function(config){
30328         if(config instanceof Array){
30329             var buttons = [];
30330             for(var i = 0, len = config.length; i < len; i++) {
30331                 buttons.push(this.addButton(config[i]));
30332             }
30333             return buttons;
30334         }
30335         var b = config;
30336         if(!(config instanceof Roo.Toolbar.Button)){
30337             b = config.split ?
30338                 new Roo.Toolbar.SplitButton(config) :
30339                 new Roo.Toolbar.Button(config);
30340         }
30341         var td = this.nextBlock();
30342         b.render(td);
30343         this.items.add(b);
30344         return b;
30345     },
30346     
30347     /**
30348      * Adds text to the toolbar
30349      * @param {String} text The text to add
30350      * @return {Roo.Toolbar.Item} The element's item
30351      */
30352     addText : function(text){
30353         return this.addItem(new Roo.Toolbar.TextItem(text));
30354     },
30355     
30356     /**
30357      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30358      * @param {Number} index The index where the item is to be inserted
30359      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30360      * @return {Roo.Toolbar.Button/Item}
30361      */
30362     insertButton : function(index, item){
30363         if(item instanceof Array){
30364             var buttons = [];
30365             for(var i = 0, len = item.length; i < len; i++) {
30366                buttons.push(this.insertButton(index + i, item[i]));
30367             }
30368             return buttons;
30369         }
30370         if (!(item instanceof Roo.Toolbar.Button)){
30371            item = new Roo.Toolbar.Button(item);
30372         }
30373         var td = document.createElement("td");
30374         this.tr.insertBefore(td, this.tr.childNodes[index]);
30375         item.render(td);
30376         this.items.insert(index, item);
30377         return item;
30378     },
30379     
30380     /**
30381      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30382      * @param {Object} config
30383      * @return {Roo.Toolbar.Item} The element's item
30384      */
30385     addDom : function(config, returnEl){
30386         var td = this.nextBlock();
30387         Roo.DomHelper.overwrite(td, config);
30388         var ti = new Roo.Toolbar.Item(td.firstChild);
30389         ti.render(td);
30390         this.items.add(ti);
30391         return ti;
30392     },
30393
30394     /**
30395      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30396      * @type Roo.util.MixedCollection  
30397      */
30398     fields : false,
30399     
30400     /**
30401      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30402      * Note: the field should not have been rendered yet. For a field that has already been
30403      * rendered, use {@link #addElement}.
30404      * @param {Roo.form.Field} field
30405      * @return {Roo.ToolbarItem}
30406      */
30407      
30408       
30409     addField : function(field) {
30410         if (!this.fields) {
30411             var autoId = 0;
30412             this.fields = new Roo.util.MixedCollection(false, function(o){
30413                 return o.id || ("item" + (++autoId));
30414             });
30415
30416         }
30417         
30418         var td = this.nextBlock();
30419         field.render(td);
30420         var ti = new Roo.Toolbar.Item(td.firstChild);
30421         ti.render(td);
30422         this.items.add(ti);
30423         this.fields.add(field);
30424         return ti;
30425     },
30426     /**
30427      * Hide the toolbar
30428      * @method hide
30429      */
30430      
30431       
30432     hide : function()
30433     {
30434         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30435         this.el.child('div').hide();
30436     },
30437     /**
30438      * Show the toolbar
30439      * @method show
30440      */
30441     show : function()
30442     {
30443         this.el.child('div').show();
30444     },
30445       
30446     // private
30447     nextBlock : function(){
30448         var td = document.createElement("td");
30449         this.tr.appendChild(td);
30450         return td;
30451     },
30452
30453     // private
30454     destroy : function(){
30455         if(this.items){ // rendered?
30456             Roo.destroy.apply(Roo, this.items.items);
30457         }
30458         if(this.fields){ // rendered?
30459             Roo.destroy.apply(Roo, this.fields.items);
30460         }
30461         Roo.Element.uncache(this.el, this.tr);
30462     }
30463 };
30464
30465 /**
30466  * @class Roo.Toolbar.Item
30467  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30468  * @constructor
30469  * Creates a new Item
30470  * @param {HTMLElement} el 
30471  */
30472 Roo.Toolbar.Item = function(el){
30473     var cfg = {};
30474     if (typeof (el.xtype) != 'undefined') {
30475         cfg = el;
30476         el = cfg.el;
30477     }
30478     
30479     this.el = Roo.getDom(el);
30480     this.id = Roo.id(this.el);
30481     this.hidden = false;
30482     
30483     this.addEvents({
30484          /**
30485              * @event render
30486              * Fires when the button is rendered
30487              * @param {Button} this
30488              */
30489         'render': true
30490     });
30491     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30492 };
30493 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30494 //Roo.Toolbar.Item.prototype = {
30495     
30496     /**
30497      * Get this item's HTML Element
30498      * @return {HTMLElement}
30499      */
30500     getEl : function(){
30501        return this.el;  
30502     },
30503
30504     // private
30505     render : function(td){
30506         
30507          this.td = td;
30508         td.appendChild(this.el);
30509         
30510         this.fireEvent('render', this);
30511     },
30512     
30513     /**
30514      * Removes and destroys this item.
30515      */
30516     destroy : function(){
30517         this.td.parentNode.removeChild(this.td);
30518     },
30519     
30520     /**
30521      * Shows this item.
30522      */
30523     show: function(){
30524         this.hidden = false;
30525         this.td.style.display = "";
30526     },
30527     
30528     /**
30529      * Hides this item.
30530      */
30531     hide: function(){
30532         this.hidden = true;
30533         this.td.style.display = "none";
30534     },
30535     
30536     /**
30537      * Convenience function for boolean show/hide.
30538      * @param {Boolean} visible true to show/false to hide
30539      */
30540     setVisible: function(visible){
30541         if(visible) {
30542             this.show();
30543         }else{
30544             this.hide();
30545         }
30546     },
30547     
30548     /**
30549      * Try to focus this item.
30550      */
30551     focus : function(){
30552         Roo.fly(this.el).focus();
30553     },
30554     
30555     /**
30556      * Disables this item.
30557      */
30558     disable : function(){
30559         Roo.fly(this.td).addClass("x-item-disabled");
30560         this.disabled = true;
30561         this.el.disabled = true;
30562     },
30563     
30564     /**
30565      * Enables this item.
30566      */
30567     enable : function(){
30568         Roo.fly(this.td).removeClass("x-item-disabled");
30569         this.disabled = false;
30570         this.el.disabled = false;
30571     }
30572 });
30573
30574
30575 /**
30576  * @class Roo.Toolbar.Separator
30577  * @extends Roo.Toolbar.Item
30578  * A simple toolbar separator class
30579  * @constructor
30580  * Creates a new Separator
30581  */
30582 Roo.Toolbar.Separator = function(cfg){
30583     
30584     var s = document.createElement("span");
30585     s.className = "ytb-sep";
30586     if (cfg) {
30587         cfg.el = s;
30588     }
30589     
30590     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30591 };
30592 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30593     enable:Roo.emptyFn,
30594     disable:Roo.emptyFn,
30595     focus:Roo.emptyFn
30596 });
30597
30598 /**
30599  * @class Roo.Toolbar.Spacer
30600  * @extends Roo.Toolbar.Item
30601  * A simple element that adds extra horizontal space to a toolbar.
30602  * @constructor
30603  * Creates a new Spacer
30604  */
30605 Roo.Toolbar.Spacer = function(cfg){
30606     var s = document.createElement("div");
30607     s.className = "ytb-spacer";
30608     if (cfg) {
30609         cfg.el = s;
30610     }
30611     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30612 };
30613 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30614     enable:Roo.emptyFn,
30615     disable:Roo.emptyFn,
30616     focus:Roo.emptyFn
30617 });
30618
30619 /**
30620  * @class Roo.Toolbar.Fill
30621  * @extends Roo.Toolbar.Spacer
30622  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30623  * @constructor
30624  * Creates a new Spacer
30625  */
30626 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30627     // private
30628     render : function(td){
30629         td.style.width = '100%';
30630         Roo.Toolbar.Fill.superclass.render.call(this, td);
30631     }
30632 });
30633
30634 /**
30635  * @class Roo.Toolbar.TextItem
30636  * @extends Roo.Toolbar.Item
30637  * A simple class that renders text directly into a toolbar.
30638  * @constructor
30639  * Creates a new TextItem
30640  * @cfg {string} text 
30641  */
30642 Roo.Toolbar.TextItem = function(cfg){
30643     var  text = cfg || "";
30644     if (typeof(cfg) == 'object') {
30645         text = cfg.text || "";
30646     }  else {
30647         cfg = null;
30648     }
30649     var s = document.createElement("span");
30650     s.className = "ytb-text";
30651     s.innerHTML = text;
30652     if (cfg) {
30653         cfg.el  = s;
30654     }
30655     
30656     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30657 };
30658 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30659     
30660      
30661     enable:Roo.emptyFn,
30662     disable:Roo.emptyFn,
30663     focus:Roo.emptyFn
30664 });
30665
30666 /**
30667  * @class Roo.Toolbar.Button
30668  * @extends Roo.Button
30669  * A button that renders into a toolbar.
30670  * @constructor
30671  * Creates a new Button
30672  * @param {Object} config A standard {@link Roo.Button} config object
30673  */
30674 Roo.Toolbar.Button = function(config){
30675     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30676 };
30677 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30678     render : function(td){
30679         this.td = td;
30680         Roo.Toolbar.Button.superclass.render.call(this, td);
30681     },
30682     
30683     /**
30684      * Removes and destroys this button
30685      */
30686     destroy : function(){
30687         Roo.Toolbar.Button.superclass.destroy.call(this);
30688         this.td.parentNode.removeChild(this.td);
30689     },
30690     
30691     /**
30692      * Shows this button
30693      */
30694     show: function(){
30695         this.hidden = false;
30696         this.td.style.display = "";
30697     },
30698     
30699     /**
30700      * Hides this button
30701      */
30702     hide: function(){
30703         this.hidden = true;
30704         this.td.style.display = "none";
30705     },
30706
30707     /**
30708      * Disables this item
30709      */
30710     disable : function(){
30711         Roo.fly(this.td).addClass("x-item-disabled");
30712         this.disabled = true;
30713     },
30714
30715     /**
30716      * Enables this item
30717      */
30718     enable : function(){
30719         Roo.fly(this.td).removeClass("x-item-disabled");
30720         this.disabled = false;
30721     }
30722 });
30723 // backwards compat
30724 Roo.ToolbarButton = Roo.Toolbar.Button;
30725
30726 /**
30727  * @class Roo.Toolbar.SplitButton
30728  * @extends Roo.SplitButton
30729  * A menu button that renders into a toolbar.
30730  * @constructor
30731  * Creates a new SplitButton
30732  * @param {Object} config A standard {@link Roo.SplitButton} config object
30733  */
30734 Roo.Toolbar.SplitButton = function(config){
30735     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30736 };
30737 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30738     render : function(td){
30739         this.td = td;
30740         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30741     },
30742     
30743     /**
30744      * Removes and destroys this button
30745      */
30746     destroy : function(){
30747         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30748         this.td.parentNode.removeChild(this.td);
30749     },
30750     
30751     /**
30752      * Shows this button
30753      */
30754     show: function(){
30755         this.hidden = false;
30756         this.td.style.display = "";
30757     },
30758     
30759     /**
30760      * Hides this button
30761      */
30762     hide: function(){
30763         this.hidden = true;
30764         this.td.style.display = "none";
30765     }
30766 });
30767
30768 // backwards compat
30769 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30770  * Based on:
30771  * Ext JS Library 1.1.1
30772  * Copyright(c) 2006-2007, Ext JS, LLC.
30773  *
30774  * Originally Released Under LGPL - original licence link has changed is not relivant.
30775  *
30776  * Fork - LGPL
30777  * <script type="text/javascript">
30778  */
30779  
30780 /**
30781  * @class Roo.PagingToolbar
30782  * @extends Roo.Toolbar
30783  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30784  * @constructor
30785  * Create a new PagingToolbar
30786  * @param {Object} config The config object
30787  */
30788 Roo.PagingToolbar = function(el, ds, config)
30789 {
30790     // old args format still supported... - xtype is prefered..
30791     if (typeof(el) == 'object' && el.xtype) {
30792         // created from xtype...
30793         config = el;
30794         ds = el.dataSource;
30795         el = config.container;
30796     }
30797     var items = [];
30798     if (config.items) {
30799         items = config.items;
30800         config.items = [];
30801     }
30802     
30803     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30804     this.ds = ds;
30805     this.cursor = 0;
30806     this.renderButtons(this.el);
30807     this.bind(ds);
30808     
30809     // supprot items array.
30810    
30811     Roo.each(items, function(e) {
30812         this.add(Roo.factory(e));
30813     },this);
30814     
30815 };
30816
30817 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30818     /**
30819      * @cfg {Roo.data.Store} dataSource
30820      * The underlying data store providing the paged data
30821      */
30822     /**
30823      * @cfg {String/HTMLElement/Element} container
30824      * container The id or element that will contain the toolbar
30825      */
30826     /**
30827      * @cfg {Boolean} displayInfo
30828      * True to display the displayMsg (defaults to false)
30829      */
30830     /**
30831      * @cfg {Number} pageSize
30832      * The number of records to display per page (defaults to 20)
30833      */
30834     pageSize: 20,
30835     /**
30836      * @cfg {String} displayMsg
30837      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30838      */
30839     displayMsg : 'Displaying {0} - {1} of {2}',
30840     /**
30841      * @cfg {String} emptyMsg
30842      * The message to display when no records are found (defaults to "No data to display")
30843      */
30844     emptyMsg : 'No data to display',
30845     /**
30846      * Customizable piece of the default paging text (defaults to "Page")
30847      * @type String
30848      */
30849     beforePageText : "Page",
30850     /**
30851      * Customizable piece of the default paging text (defaults to "of %0")
30852      * @type String
30853      */
30854     afterPageText : "of {0}",
30855     /**
30856      * Customizable piece of the default paging text (defaults to "First Page")
30857      * @type String
30858      */
30859     firstText : "First Page",
30860     /**
30861      * Customizable piece of the default paging text (defaults to "Previous Page")
30862      * @type String
30863      */
30864     prevText : "Previous Page",
30865     /**
30866      * Customizable piece of the default paging text (defaults to "Next Page")
30867      * @type String
30868      */
30869     nextText : "Next Page",
30870     /**
30871      * Customizable piece of the default paging text (defaults to "Last Page")
30872      * @type String
30873      */
30874     lastText : "Last Page",
30875     /**
30876      * Customizable piece of the default paging text (defaults to "Refresh")
30877      * @type String
30878      */
30879     refreshText : "Refresh",
30880
30881     // private
30882     renderButtons : function(el){
30883         Roo.PagingToolbar.superclass.render.call(this, el);
30884         this.first = this.addButton({
30885             tooltip: this.firstText,
30886             cls: "x-btn-icon x-grid-page-first",
30887             disabled: true,
30888             handler: this.onClick.createDelegate(this, ["first"])
30889         });
30890         this.prev = this.addButton({
30891             tooltip: this.prevText,
30892             cls: "x-btn-icon x-grid-page-prev",
30893             disabled: true,
30894             handler: this.onClick.createDelegate(this, ["prev"])
30895         });
30896         //this.addSeparator();
30897         this.add(this.beforePageText);
30898         this.field = Roo.get(this.addDom({
30899            tag: "input",
30900            type: "text",
30901            size: "3",
30902            value: "1",
30903            cls: "x-grid-page-number"
30904         }).el);
30905         this.field.on("keydown", this.onPagingKeydown, this);
30906         this.field.on("focus", function(){this.dom.select();});
30907         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30908         this.field.setHeight(18);
30909         //this.addSeparator();
30910         this.next = this.addButton({
30911             tooltip: this.nextText,
30912             cls: "x-btn-icon x-grid-page-next",
30913             disabled: true,
30914             handler: this.onClick.createDelegate(this, ["next"])
30915         });
30916         this.last = this.addButton({
30917             tooltip: this.lastText,
30918             cls: "x-btn-icon x-grid-page-last",
30919             disabled: true,
30920             handler: this.onClick.createDelegate(this, ["last"])
30921         });
30922         //this.addSeparator();
30923         this.loading = this.addButton({
30924             tooltip: this.refreshText,
30925             cls: "x-btn-icon x-grid-loading",
30926             handler: this.onClick.createDelegate(this, ["refresh"])
30927         });
30928
30929         if(this.displayInfo){
30930             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30931         }
30932     },
30933
30934     // private
30935     updateInfo : function(){
30936         if(this.displayEl){
30937             var count = this.ds.getCount();
30938             var msg = count == 0 ?
30939                 this.emptyMsg :
30940                 String.format(
30941                     this.displayMsg,
30942                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30943                 );
30944             this.displayEl.update(msg);
30945         }
30946     },
30947
30948     // private
30949     onLoad : function(ds, r, o){
30950        this.cursor = o.params ? o.params.start : 0;
30951        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30952
30953        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30954        this.field.dom.value = ap;
30955        this.first.setDisabled(ap == 1);
30956        this.prev.setDisabled(ap == 1);
30957        this.next.setDisabled(ap == ps);
30958        this.last.setDisabled(ap == ps);
30959        this.loading.enable();
30960        this.updateInfo();
30961     },
30962
30963     // private
30964     getPageData : function(){
30965         var total = this.ds.getTotalCount();
30966         return {
30967             total : total,
30968             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30969             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30970         };
30971     },
30972
30973     // private
30974     onLoadError : function(){
30975         this.loading.enable();
30976     },
30977
30978     // private
30979     onPagingKeydown : function(e){
30980         var k = e.getKey();
30981         var d = this.getPageData();
30982         if(k == e.RETURN){
30983             var v = this.field.dom.value, pageNum;
30984             if(!v || isNaN(pageNum = parseInt(v, 10))){
30985                 this.field.dom.value = d.activePage;
30986                 return;
30987             }
30988             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30989             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30990             e.stopEvent();
30991         }
30992         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))
30993         {
30994           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30995           this.field.dom.value = pageNum;
30996           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30997           e.stopEvent();
30998         }
30999         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31000         {
31001           var v = this.field.dom.value, pageNum; 
31002           var increment = (e.shiftKey) ? 10 : 1;
31003           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31004             increment *= -1;
31005           }
31006           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31007             this.field.dom.value = d.activePage;
31008             return;
31009           }
31010           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31011           {
31012             this.field.dom.value = parseInt(v, 10) + increment;
31013             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31014             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31015           }
31016           e.stopEvent();
31017         }
31018     },
31019
31020     // private
31021     beforeLoad : function(){
31022         if(this.loading){
31023             this.loading.disable();
31024         }
31025     },
31026
31027     // private
31028     onClick : function(which){
31029         var ds = this.ds;
31030         switch(which){
31031             case "first":
31032                 ds.load({params:{start: 0, limit: this.pageSize}});
31033             break;
31034             case "prev":
31035                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31036             break;
31037             case "next":
31038                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31039             break;
31040             case "last":
31041                 var total = ds.getTotalCount();
31042                 var extra = total % this.pageSize;
31043                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31044                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31045             break;
31046             case "refresh":
31047                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31048             break;
31049         }
31050     },
31051
31052     /**
31053      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31054      * @param {Roo.data.Store} store The data store to unbind
31055      */
31056     unbind : function(ds){
31057         ds.un("beforeload", this.beforeLoad, this);
31058         ds.un("load", this.onLoad, this);
31059         ds.un("loadexception", this.onLoadError, this);
31060         ds.un("remove", this.updateInfo, this);
31061         ds.un("add", this.updateInfo, this);
31062         this.ds = undefined;
31063     },
31064
31065     /**
31066      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31067      * @param {Roo.data.Store} store The data store to bind
31068      */
31069     bind : function(ds){
31070         ds.on("beforeload", this.beforeLoad, this);
31071         ds.on("load", this.onLoad, this);
31072         ds.on("loadexception", this.onLoadError, this);
31073         ds.on("remove", this.updateInfo, this);
31074         ds.on("add", this.updateInfo, this);
31075         this.ds = ds;
31076     }
31077 });/*
31078  * Based on:
31079  * Ext JS Library 1.1.1
31080  * Copyright(c) 2006-2007, Ext JS, LLC.
31081  *
31082  * Originally Released Under LGPL - original licence link has changed is not relivant.
31083  *
31084  * Fork - LGPL
31085  * <script type="text/javascript">
31086  */
31087
31088 /**
31089  * @class Roo.Resizable
31090  * @extends Roo.util.Observable
31091  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
31092  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
31093  * 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
31094  * the element will be wrapped for you automatically.</p>
31095  * <p>Here is the list of valid resize handles:</p>
31096  * <pre>
31097 Value   Description
31098 ------  -------------------
31099  'n'     north
31100  's'     south
31101  'e'     east
31102  'w'     west
31103  'nw'    northwest
31104  'sw'    southwest
31105  'se'    southeast
31106  'ne'    northeast
31107  'hd'    horizontal drag
31108  'all'   all
31109 </pre>
31110  * <p>Here's an example showing the creation of a typical Resizable:</p>
31111  * <pre><code>
31112 var resizer = new Roo.Resizable("element-id", {
31113     handles: 'all',
31114     minWidth: 200,
31115     minHeight: 100,
31116     maxWidth: 500,
31117     maxHeight: 400,
31118     pinned: true
31119 });
31120 resizer.on("resize", myHandler);
31121 </code></pre>
31122  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
31123  * resizer.east.setDisplayed(false);</p>
31124  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
31125  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
31126  * resize operation's new size (defaults to [0, 0])
31127  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
31128  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
31129  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
31130  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
31131  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
31132  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
31133  * @cfg {Number} width The width of the element in pixels (defaults to null)
31134  * @cfg {Number} height The height of the element in pixels (defaults to null)
31135  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
31136  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
31137  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
31138  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
31139  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
31140  * in favor of the handles config option (defaults to false)
31141  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
31142  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
31143  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
31144  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
31145  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
31146  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
31147  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
31148  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
31149  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
31150  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
31151  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
31152  * @constructor
31153  * Create a new resizable component
31154  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
31155  * @param {Object} config configuration options
31156   */
31157 Roo.Resizable = function(el, config)
31158 {
31159     this.el = Roo.get(el);
31160
31161     if(config && config.wrap){
31162         config.resizeChild = this.el;
31163         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
31164         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
31165         this.el.setStyle("overflow", "hidden");
31166         this.el.setPositioning(config.resizeChild.getPositioning());
31167         config.resizeChild.clearPositioning();
31168         if(!config.width || !config.height){
31169             var csize = config.resizeChild.getSize();
31170             this.el.setSize(csize.width, csize.height);
31171         }
31172         if(config.pinned && !config.adjustments){
31173             config.adjustments = "auto";
31174         }
31175     }
31176
31177     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
31178     this.proxy.unselectable();
31179     this.proxy.enableDisplayMode('block');
31180
31181     Roo.apply(this, config);
31182
31183     if(this.pinned){
31184         this.disableTrackOver = true;
31185         this.el.addClass("x-resizable-pinned");
31186     }
31187     // if the element isn't positioned, make it relative
31188     var position = this.el.getStyle("position");
31189     if(position != "absolute" && position != "fixed"){
31190         this.el.setStyle("position", "relative");
31191     }
31192     if(!this.handles){ // no handles passed, must be legacy style
31193         this.handles = 's,e,se';
31194         if(this.multiDirectional){
31195             this.handles += ',n,w';
31196         }
31197     }
31198     if(this.handles == "all"){
31199         this.handles = "n s e w ne nw se sw";
31200     }
31201     var hs = this.handles.split(/\s*?[,;]\s*?| /);
31202     var ps = Roo.Resizable.positions;
31203     for(var i = 0, len = hs.length; i < len; i++){
31204         if(hs[i] && ps[hs[i]]){
31205             var pos = ps[hs[i]];
31206             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
31207         }
31208     }
31209     // legacy
31210     this.corner = this.southeast;
31211     
31212     // updateBox = the box can move..
31213     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
31214         this.updateBox = true;
31215     }
31216
31217     this.activeHandle = null;
31218
31219     if(this.resizeChild){
31220         if(typeof this.resizeChild == "boolean"){
31221             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
31222         }else{
31223             this.resizeChild = Roo.get(this.resizeChild, true);
31224         }
31225     }
31226     
31227     if(this.adjustments == "auto"){
31228         var rc = this.resizeChild;
31229         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
31230         if(rc && (hw || hn)){
31231             rc.position("relative");
31232             rc.setLeft(hw ? hw.el.getWidth() : 0);
31233             rc.setTop(hn ? hn.el.getHeight() : 0);
31234         }
31235         this.adjustments = [
31236             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
31237             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
31238         ];
31239     }
31240
31241     if(this.draggable){
31242         this.dd = this.dynamic ?
31243             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
31244         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
31245     }
31246
31247     // public events
31248     this.addEvents({
31249         /**
31250          * @event beforeresize
31251          * Fired before resize is allowed. Set enabled to false to cancel resize.
31252          * @param {Roo.Resizable} this
31253          * @param {Roo.EventObject} e The mousedown event
31254          */
31255         "beforeresize" : true,
31256         /**
31257          * @event resizing
31258          * Fired a resizing.
31259          * @param {Roo.Resizable} this
31260          * @param {Number} x The new x position
31261          * @param {Number} y The new y position
31262          * @param {Number} w The new w width
31263          * @param {Number} h The new h hight
31264          * @param {Roo.EventObject} e The mouseup event
31265          */
31266         "resizing" : true,
31267         /**
31268          * @event resize
31269          * Fired after a resize.
31270          * @param {Roo.Resizable} this
31271          * @param {Number} width The new width
31272          * @param {Number} height The new height
31273          * @param {Roo.EventObject} e The mouseup event
31274          */
31275         "resize" : true
31276     });
31277
31278     if(this.width !== null && this.height !== null){
31279         this.resizeTo(this.width, this.height);
31280     }else{
31281         this.updateChildSize();
31282     }
31283     if(Roo.isIE){
31284         this.el.dom.style.zoom = 1;
31285     }
31286     Roo.Resizable.superclass.constructor.call(this);
31287 };
31288
31289 Roo.extend(Roo.Resizable, Roo.util.Observable, {
31290         resizeChild : false,
31291         adjustments : [0, 0],
31292         minWidth : 5,
31293         minHeight : 5,
31294         maxWidth : 10000,
31295         maxHeight : 10000,
31296         enabled : true,
31297         animate : false,
31298         duration : .35,
31299         dynamic : false,
31300         handles : false,
31301         multiDirectional : false,
31302         disableTrackOver : false,
31303         easing : 'easeOutStrong',
31304         widthIncrement : 0,
31305         heightIncrement : 0,
31306         pinned : false,
31307         width : null,
31308         height : null,
31309         preserveRatio : false,
31310         transparent: false,
31311         minX: 0,
31312         minY: 0,
31313         draggable: false,
31314
31315         /**
31316          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31317          */
31318         constrainTo: undefined,
31319         /**
31320          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31321          */
31322         resizeRegion: undefined,
31323
31324
31325     /**
31326      * Perform a manual resize
31327      * @param {Number} width
31328      * @param {Number} height
31329      */
31330     resizeTo : function(width, height){
31331         this.el.setSize(width, height);
31332         this.updateChildSize();
31333         this.fireEvent("resize", this, width, height, null);
31334     },
31335
31336     // private
31337     startSizing : function(e, handle){
31338         this.fireEvent("beforeresize", this, e);
31339         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31340
31341             if(!this.overlay){
31342                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31343                 this.overlay.unselectable();
31344                 this.overlay.enableDisplayMode("block");
31345                 this.overlay.on("mousemove", this.onMouseMove, this);
31346                 this.overlay.on("mouseup", this.onMouseUp, this);
31347             }
31348             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31349
31350             this.resizing = true;
31351             this.startBox = this.el.getBox();
31352             this.startPoint = e.getXY();
31353             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31354                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31355
31356             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31357             this.overlay.show();
31358
31359             if(this.constrainTo) {
31360                 var ct = Roo.get(this.constrainTo);
31361                 this.resizeRegion = ct.getRegion().adjust(
31362                     ct.getFrameWidth('t'),
31363                     ct.getFrameWidth('l'),
31364                     -ct.getFrameWidth('b'),
31365                     -ct.getFrameWidth('r')
31366                 );
31367             }
31368
31369             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31370             this.proxy.show();
31371             this.proxy.setBox(this.startBox);
31372             if(!this.dynamic){
31373                 this.proxy.setStyle('visibility', 'visible');
31374             }
31375         }
31376     },
31377
31378     // private
31379     onMouseDown : function(handle, e){
31380         if(this.enabled){
31381             e.stopEvent();
31382             this.activeHandle = handle;
31383             this.startSizing(e, handle);
31384         }
31385     },
31386
31387     // private
31388     onMouseUp : function(e){
31389         var size = this.resizeElement();
31390         this.resizing = false;
31391         this.handleOut();
31392         this.overlay.hide();
31393         this.proxy.hide();
31394         this.fireEvent("resize", this, size.width, size.height, e);
31395     },
31396
31397     // private
31398     updateChildSize : function(){
31399         
31400         if(this.resizeChild){
31401             var el = this.el;
31402             var child = this.resizeChild;
31403             var adj = this.adjustments;
31404             if(el.dom.offsetWidth){
31405                 var b = el.getSize(true);
31406                 child.setSize(b.width+adj[0], b.height+adj[1]);
31407             }
31408             // Second call here for IE
31409             // The first call enables instant resizing and
31410             // the second call corrects scroll bars if they
31411             // exist
31412             if(Roo.isIE){
31413                 setTimeout(function(){
31414                     if(el.dom.offsetWidth){
31415                         var b = el.getSize(true);
31416                         child.setSize(b.width+adj[0], b.height+adj[1]);
31417                     }
31418                 }, 10);
31419             }
31420         }
31421     },
31422
31423     // private
31424     snap : function(value, inc, min){
31425         if(!inc || !value) {
31426             return value;
31427         }
31428         var newValue = value;
31429         var m = value % inc;
31430         if(m > 0){
31431             if(m > (inc/2)){
31432                 newValue = value + (inc-m);
31433             }else{
31434                 newValue = value - m;
31435             }
31436         }
31437         return Math.max(min, newValue);
31438     },
31439
31440     // private
31441     resizeElement : function(){
31442         var box = this.proxy.getBox();
31443         if(this.updateBox){
31444             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31445         }else{
31446             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31447         }
31448         this.updateChildSize();
31449         if(!this.dynamic){
31450             this.proxy.hide();
31451         }
31452         return box;
31453     },
31454
31455     // private
31456     constrain : function(v, diff, m, mx){
31457         if(v - diff < m){
31458             diff = v - m;
31459         }else if(v - diff > mx){
31460             diff = mx - v;
31461         }
31462         return diff;
31463     },
31464
31465     // private
31466     onMouseMove : function(e){
31467         
31468         if(this.enabled){
31469             try{// try catch so if something goes wrong the user doesn't get hung
31470
31471             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31472                 return;
31473             }
31474
31475             //var curXY = this.startPoint;
31476             var curSize = this.curSize || this.startBox;
31477             var x = this.startBox.x, y = this.startBox.y;
31478             var ox = x, oy = y;
31479             var w = curSize.width, h = curSize.height;
31480             var ow = w, oh = h;
31481             var mw = this.minWidth, mh = this.minHeight;
31482             var mxw = this.maxWidth, mxh = this.maxHeight;
31483             var wi = this.widthIncrement;
31484             var hi = this.heightIncrement;
31485
31486             var eventXY = e.getXY();
31487             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31488             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31489
31490             var pos = this.activeHandle.position;
31491
31492             switch(pos){
31493                 case "east":
31494                     w += diffX;
31495                     w = Math.min(Math.max(mw, w), mxw);
31496                     break;
31497              
31498                 case "south":
31499                     h += diffY;
31500                     h = Math.min(Math.max(mh, h), mxh);
31501                     break;
31502                 case "southeast":
31503                     w += diffX;
31504                     h += diffY;
31505                     w = Math.min(Math.max(mw, w), mxw);
31506                     h = Math.min(Math.max(mh, h), mxh);
31507                     break;
31508                 case "north":
31509                     diffY = this.constrain(h, diffY, mh, mxh);
31510                     y += diffY;
31511                     h -= diffY;
31512                     break;
31513                 case "hdrag":
31514                     
31515                     if (wi) {
31516                         var adiffX = Math.abs(diffX);
31517                         var sub = (adiffX % wi); // how much 
31518                         if (sub > (wi/2)) { // far enough to snap
31519                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31520                         } else {
31521                             // remove difference.. 
31522                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31523                         }
31524                     }
31525                     x += diffX;
31526                     x = Math.max(this.minX, x);
31527                     break;
31528                 case "west":
31529                     diffX = this.constrain(w, diffX, mw, mxw);
31530                     x += diffX;
31531                     w -= diffX;
31532                     break;
31533                 case "northeast":
31534                     w += diffX;
31535                     w = Math.min(Math.max(mw, w), mxw);
31536                     diffY = this.constrain(h, diffY, mh, mxh);
31537                     y += diffY;
31538                     h -= diffY;
31539                     break;
31540                 case "northwest":
31541                     diffX = this.constrain(w, diffX, mw, mxw);
31542                     diffY = this.constrain(h, diffY, mh, mxh);
31543                     y += diffY;
31544                     h -= diffY;
31545                     x += diffX;
31546                     w -= diffX;
31547                     break;
31548                case "southwest":
31549                     diffX = this.constrain(w, diffX, mw, mxw);
31550                     h += diffY;
31551                     h = Math.min(Math.max(mh, h), mxh);
31552                     x += diffX;
31553                     w -= diffX;
31554                     break;
31555             }
31556
31557             var sw = this.snap(w, wi, mw);
31558             var sh = this.snap(h, hi, mh);
31559             if(sw != w || sh != h){
31560                 switch(pos){
31561                     case "northeast":
31562                         y -= sh - h;
31563                     break;
31564                     case "north":
31565                         y -= sh - h;
31566                         break;
31567                     case "southwest":
31568                         x -= sw - w;
31569                     break;
31570                     case "west":
31571                         x -= sw - w;
31572                         break;
31573                     case "northwest":
31574                         x -= sw - w;
31575                         y -= sh - h;
31576                     break;
31577                 }
31578                 w = sw;
31579                 h = sh;
31580             }
31581
31582             if(this.preserveRatio){
31583                 switch(pos){
31584                     case "southeast":
31585                     case "east":
31586                         h = oh * (w/ow);
31587                         h = Math.min(Math.max(mh, h), mxh);
31588                         w = ow * (h/oh);
31589                        break;
31590                     case "south":
31591                         w = ow * (h/oh);
31592                         w = Math.min(Math.max(mw, w), mxw);
31593                         h = oh * (w/ow);
31594                         break;
31595                     case "northeast":
31596                         w = ow * (h/oh);
31597                         w = Math.min(Math.max(mw, w), mxw);
31598                         h = oh * (w/ow);
31599                     break;
31600                     case "north":
31601                         var tw = w;
31602                         w = ow * (h/oh);
31603                         w = Math.min(Math.max(mw, w), mxw);
31604                         h = oh * (w/ow);
31605                         x += (tw - w) / 2;
31606                         break;
31607                     case "southwest":
31608                         h = oh * (w/ow);
31609                         h = Math.min(Math.max(mh, h), mxh);
31610                         var tw = w;
31611                         w = ow * (h/oh);
31612                         x += tw - w;
31613                         break;
31614                     case "west":
31615                         var th = h;
31616                         h = oh * (w/ow);
31617                         h = Math.min(Math.max(mh, h), mxh);
31618                         y += (th - h) / 2;
31619                         var tw = w;
31620                         w = ow * (h/oh);
31621                         x += tw - w;
31622                        break;
31623                     case "northwest":
31624                         var tw = w;
31625                         var th = h;
31626                         h = oh * (w/ow);
31627                         h = Math.min(Math.max(mh, h), mxh);
31628                         w = ow * (h/oh);
31629                         y += th - h;
31630                         x += tw - w;
31631                        break;
31632
31633                 }
31634             }
31635             if (pos == 'hdrag') {
31636                 w = ow;
31637             }
31638             this.proxy.setBounds(x, y, w, h);
31639             if(this.dynamic){
31640                 this.resizeElement();
31641             }
31642             }catch(e){}
31643         }
31644         this.fireEvent("resizing", this, x, y, w, h, e);
31645     },
31646
31647     // private
31648     handleOver : function(){
31649         if(this.enabled){
31650             this.el.addClass("x-resizable-over");
31651         }
31652     },
31653
31654     // private
31655     handleOut : function(){
31656         if(!this.resizing){
31657             this.el.removeClass("x-resizable-over");
31658         }
31659     },
31660
31661     /**
31662      * Returns the element this component is bound to.
31663      * @return {Roo.Element}
31664      */
31665     getEl : function(){
31666         return this.el;
31667     },
31668
31669     /**
31670      * Returns the resizeChild element (or null).
31671      * @return {Roo.Element}
31672      */
31673     getResizeChild : function(){
31674         return this.resizeChild;
31675     },
31676     groupHandler : function()
31677     {
31678         
31679     },
31680     /**
31681      * Destroys this resizable. If the element was wrapped and
31682      * removeEl is not true then the element remains.
31683      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31684      */
31685     destroy : function(removeEl){
31686         this.proxy.remove();
31687         if(this.overlay){
31688             this.overlay.removeAllListeners();
31689             this.overlay.remove();
31690         }
31691         var ps = Roo.Resizable.positions;
31692         for(var k in ps){
31693             if(typeof ps[k] != "function" && this[ps[k]]){
31694                 var h = this[ps[k]];
31695                 h.el.removeAllListeners();
31696                 h.el.remove();
31697             }
31698         }
31699         if(removeEl){
31700             this.el.update("");
31701             this.el.remove();
31702         }
31703     }
31704 });
31705
31706 // private
31707 // hash to map config positions to true positions
31708 Roo.Resizable.positions = {
31709     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31710     hd: "hdrag"
31711 };
31712
31713 // private
31714 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31715     if(!this.tpl){
31716         // only initialize the template if resizable is used
31717         var tpl = Roo.DomHelper.createTemplate(
31718             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31719         );
31720         tpl.compile();
31721         Roo.Resizable.Handle.prototype.tpl = tpl;
31722     }
31723     this.position = pos;
31724     this.rz = rz;
31725     // show north drag fro topdra
31726     var handlepos = pos == 'hdrag' ? 'north' : pos;
31727     
31728     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31729     if (pos == 'hdrag') {
31730         this.el.setStyle('cursor', 'pointer');
31731     }
31732     this.el.unselectable();
31733     if(transparent){
31734         this.el.setOpacity(0);
31735     }
31736     this.el.on("mousedown", this.onMouseDown, this);
31737     if(!disableTrackOver){
31738         this.el.on("mouseover", this.onMouseOver, this);
31739         this.el.on("mouseout", this.onMouseOut, this);
31740     }
31741 };
31742
31743 // private
31744 Roo.Resizable.Handle.prototype = {
31745     afterResize : function(rz){
31746         Roo.log('after?');
31747         // do nothing
31748     },
31749     // private
31750     onMouseDown : function(e){
31751         this.rz.onMouseDown(this, e);
31752     },
31753     // private
31754     onMouseOver : function(e){
31755         this.rz.handleOver(this, e);
31756     },
31757     // private
31758     onMouseOut : function(e){
31759         this.rz.handleOut(this, e);
31760     }
31761 };/*
31762  * Based on:
31763  * Ext JS Library 1.1.1
31764  * Copyright(c) 2006-2007, Ext JS, LLC.
31765  *
31766  * Originally Released Under LGPL - original licence link has changed is not relivant.
31767  *
31768  * Fork - LGPL
31769  * <script type="text/javascript">
31770  */
31771
31772 /**
31773  * @class Roo.Editor
31774  * @extends Roo.Component
31775  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31776  * @constructor
31777  * Create a new Editor
31778  * @param {Roo.form.Field} field The Field object (or descendant)
31779  * @param {Object} config The config object
31780  */
31781 Roo.Editor = function(field, config){
31782     Roo.Editor.superclass.constructor.call(this, config);
31783     this.field = field;
31784     this.addEvents({
31785         /**
31786              * @event beforestartedit
31787              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31788              * false from the handler of this event.
31789              * @param {Editor} this
31790              * @param {Roo.Element} boundEl The underlying element bound to this editor
31791              * @param {Mixed} value The field value being set
31792              */
31793         "beforestartedit" : true,
31794         /**
31795              * @event startedit
31796              * Fires when this editor is displayed
31797              * @param {Roo.Element} boundEl The underlying element bound to this editor
31798              * @param {Mixed} value The starting field value
31799              */
31800         "startedit" : true,
31801         /**
31802              * @event beforecomplete
31803              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31804              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31805              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31806              * event will not fire since no edit actually occurred.
31807              * @param {Editor} this
31808              * @param {Mixed} value The current field value
31809              * @param {Mixed} startValue The original field value
31810              */
31811         "beforecomplete" : true,
31812         /**
31813              * @event complete
31814              * Fires after editing is complete and any changed value has been written to the underlying field.
31815              * @param {Editor} this
31816              * @param {Mixed} value The current field value
31817              * @param {Mixed} startValue The original field value
31818              */
31819         "complete" : true,
31820         /**
31821          * @event specialkey
31822          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31823          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31824          * @param {Roo.form.Field} this
31825          * @param {Roo.EventObject} e The event object
31826          */
31827         "specialkey" : true
31828     });
31829 };
31830
31831 Roo.extend(Roo.Editor, Roo.Component, {
31832     /**
31833      * @cfg {Boolean/String} autosize
31834      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31835      * or "height" to adopt the height only (defaults to false)
31836      */
31837     /**
31838      * @cfg {Boolean} revertInvalid
31839      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31840      * validation fails (defaults to true)
31841      */
31842     /**
31843      * @cfg {Boolean} ignoreNoChange
31844      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31845      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31846      * will never be ignored.
31847      */
31848     /**
31849      * @cfg {Boolean} hideEl
31850      * False to keep the bound element visible while the editor is displayed (defaults to true)
31851      */
31852     /**
31853      * @cfg {Mixed} value
31854      * The data value of the underlying field (defaults to "")
31855      */
31856     value : "",
31857     /**
31858      * @cfg {String} alignment
31859      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31860      */
31861     alignment: "c-c?",
31862     /**
31863      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31864      * for bottom-right shadow (defaults to "frame")
31865      */
31866     shadow : "frame",
31867     /**
31868      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31869      */
31870     constrain : false,
31871     /**
31872      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31873      */
31874     completeOnEnter : false,
31875     /**
31876      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31877      */
31878     cancelOnEsc : false,
31879     /**
31880      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31881      */
31882     updateEl : false,
31883
31884     // private
31885     onRender : function(ct, position){
31886         this.el = new Roo.Layer({
31887             shadow: this.shadow,
31888             cls: "x-editor",
31889             parentEl : ct,
31890             shim : this.shim,
31891             shadowOffset:4,
31892             id: this.id,
31893             constrain: this.constrain
31894         });
31895         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31896         if(this.field.msgTarget != 'title'){
31897             this.field.msgTarget = 'qtip';
31898         }
31899         this.field.render(this.el);
31900         if(Roo.isGecko){
31901             this.field.el.dom.setAttribute('autocomplete', 'off');
31902         }
31903         this.field.on("specialkey", this.onSpecialKey, this);
31904         if(this.swallowKeys){
31905             this.field.el.swallowEvent(['keydown','keypress']);
31906         }
31907         this.field.show();
31908         this.field.on("blur", this.onBlur, this);
31909         if(this.field.grow){
31910             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31911         }
31912     },
31913
31914     onSpecialKey : function(field, e)
31915     {
31916         //Roo.log('editor onSpecialKey');
31917         if(this.completeOnEnter && e.getKey() == e.ENTER){
31918             e.stopEvent();
31919             this.completeEdit();
31920             return;
31921         }
31922         // do not fire special key otherwise it might hide close the editor...
31923         if(e.getKey() == e.ENTER){    
31924             return;
31925         }
31926         if(this.cancelOnEsc && e.getKey() == e.ESC){
31927             this.cancelEdit();
31928             return;
31929         } 
31930         this.fireEvent('specialkey', field, e);
31931     
31932     },
31933
31934     /**
31935      * Starts the editing process and shows the editor.
31936      * @param {String/HTMLElement/Element} el The element to edit
31937      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31938       * to the innerHTML of el.
31939      */
31940     startEdit : function(el, value){
31941         if(this.editing){
31942             this.completeEdit();
31943         }
31944         this.boundEl = Roo.get(el);
31945         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31946         if(!this.rendered){
31947             this.render(this.parentEl || document.body);
31948         }
31949         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31950             return;
31951         }
31952         this.startValue = v;
31953         this.field.setValue(v);
31954         if(this.autoSize){
31955             var sz = this.boundEl.getSize();
31956             switch(this.autoSize){
31957                 case "width":
31958                 this.setSize(sz.width,  "");
31959                 break;
31960                 case "height":
31961                 this.setSize("",  sz.height);
31962                 break;
31963                 default:
31964                 this.setSize(sz.width,  sz.height);
31965             }
31966         }
31967         this.el.alignTo(this.boundEl, this.alignment);
31968         this.editing = true;
31969         if(Roo.QuickTips){
31970             Roo.QuickTips.disable();
31971         }
31972         this.show();
31973     },
31974
31975     /**
31976      * Sets the height and width of this editor.
31977      * @param {Number} width The new width
31978      * @param {Number} height The new height
31979      */
31980     setSize : function(w, h){
31981         this.field.setSize(w, h);
31982         if(this.el){
31983             this.el.sync();
31984         }
31985     },
31986
31987     /**
31988      * Realigns the editor to the bound field based on the current alignment config value.
31989      */
31990     realign : function(){
31991         this.el.alignTo(this.boundEl, this.alignment);
31992     },
31993
31994     /**
31995      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31996      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31997      */
31998     completeEdit : function(remainVisible){
31999         if(!this.editing){
32000             return;
32001         }
32002         var v = this.getValue();
32003         if(this.revertInvalid !== false && !this.field.isValid()){
32004             v = this.startValue;
32005             this.cancelEdit(true);
32006         }
32007         if(String(v) === String(this.startValue) && this.ignoreNoChange){
32008             this.editing = false;
32009             this.hide();
32010             return;
32011         }
32012         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
32013             this.editing = false;
32014             if(this.updateEl && this.boundEl){
32015                 this.boundEl.update(v);
32016             }
32017             if(remainVisible !== true){
32018                 this.hide();
32019             }
32020             this.fireEvent("complete", this, v, this.startValue);
32021         }
32022     },
32023
32024     // private
32025     onShow : function(){
32026         this.el.show();
32027         if(this.hideEl !== false){
32028             this.boundEl.hide();
32029         }
32030         this.field.show();
32031         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
32032             this.fixIEFocus = true;
32033             this.deferredFocus.defer(50, this);
32034         }else{
32035             this.field.focus();
32036         }
32037         this.fireEvent("startedit", this.boundEl, this.startValue);
32038     },
32039
32040     deferredFocus : function(){
32041         if(this.editing){
32042             this.field.focus();
32043         }
32044     },
32045
32046     /**
32047      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
32048      * reverted to the original starting value.
32049      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
32050      * cancel (defaults to false)
32051      */
32052     cancelEdit : function(remainVisible){
32053         if(this.editing){
32054             this.setValue(this.startValue);
32055             if(remainVisible !== true){
32056                 this.hide();
32057             }
32058         }
32059     },
32060
32061     // private
32062     onBlur : function(){
32063         if(this.allowBlur !== true && this.editing){
32064             this.completeEdit();
32065         }
32066     },
32067
32068     // private
32069     onHide : function(){
32070         if(this.editing){
32071             this.completeEdit();
32072             return;
32073         }
32074         this.field.blur();
32075         if(this.field.collapse){
32076             this.field.collapse();
32077         }
32078         this.el.hide();
32079         if(this.hideEl !== false){
32080             this.boundEl.show();
32081         }
32082         if(Roo.QuickTips){
32083             Roo.QuickTips.enable();
32084         }
32085     },
32086
32087     /**
32088      * Sets the data value of the editor
32089      * @param {Mixed} value Any valid value supported by the underlying field
32090      */
32091     setValue : function(v){
32092         this.field.setValue(v);
32093     },
32094
32095     /**
32096      * Gets the data value of the editor
32097      * @return {Mixed} The data value
32098      */
32099     getValue : function(){
32100         return this.field.getValue();
32101     }
32102 });/*
32103  * Based on:
32104  * Ext JS Library 1.1.1
32105  * Copyright(c) 2006-2007, Ext JS, LLC.
32106  *
32107  * Originally Released Under LGPL - original licence link has changed is not relivant.
32108  *
32109  * Fork - LGPL
32110  * <script type="text/javascript">
32111  */
32112  
32113 /**
32114  * @class Roo.BasicDialog
32115  * @extends Roo.util.Observable
32116  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
32117  * <pre><code>
32118 var dlg = new Roo.BasicDialog("my-dlg", {
32119     height: 200,
32120     width: 300,
32121     minHeight: 100,
32122     minWidth: 150,
32123     modal: true,
32124     proxyDrag: true,
32125     shadow: true
32126 });
32127 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
32128 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
32129 dlg.addButton('Cancel', dlg.hide, dlg);
32130 dlg.show();
32131 </code></pre>
32132   <b>A Dialog should always be a direct child of the body element.</b>
32133  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
32134  * @cfg {String} title Default text to display in the title bar (defaults to null)
32135  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32136  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32137  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
32138  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
32139  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
32140  * (defaults to null with no animation)
32141  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
32142  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
32143  * property for valid values (defaults to 'all')
32144  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
32145  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
32146  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
32147  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
32148  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
32149  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
32150  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
32151  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
32152  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
32153  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
32154  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
32155  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
32156  * draggable = true (defaults to false)
32157  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
32158  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32159  * shadow (defaults to false)
32160  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
32161  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
32162  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
32163  * @cfg {Array} buttons Array of buttons
32164  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
32165  * @constructor
32166  * Create a new BasicDialog.
32167  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
32168  * @param {Object} config Configuration options
32169  */
32170 Roo.BasicDialog = function(el, config){
32171     this.el = Roo.get(el);
32172     var dh = Roo.DomHelper;
32173     if(!this.el && config && config.autoCreate){
32174         if(typeof config.autoCreate == "object"){
32175             if(!config.autoCreate.id){
32176                 config.autoCreate.id = el;
32177             }
32178             this.el = dh.append(document.body,
32179                         config.autoCreate, true);
32180         }else{
32181             this.el = dh.append(document.body,
32182                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
32183         }
32184     }
32185     el = this.el;
32186     el.setDisplayed(true);
32187     el.hide = this.hideAction;
32188     this.id = el.id;
32189     el.addClass("x-dlg");
32190
32191     Roo.apply(this, config);
32192
32193     this.proxy = el.createProxy("x-dlg-proxy");
32194     this.proxy.hide = this.hideAction;
32195     this.proxy.setOpacity(.5);
32196     this.proxy.hide();
32197
32198     if(config.width){
32199         el.setWidth(config.width);
32200     }
32201     if(config.height){
32202         el.setHeight(config.height);
32203     }
32204     this.size = el.getSize();
32205     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
32206         this.xy = [config.x,config.y];
32207     }else{
32208         this.xy = el.getCenterXY(true);
32209     }
32210     /** The header element @type Roo.Element */
32211     this.header = el.child("> .x-dlg-hd");
32212     /** The body element @type Roo.Element */
32213     this.body = el.child("> .x-dlg-bd");
32214     /** The footer element @type Roo.Element */
32215     this.footer = el.child("> .x-dlg-ft");
32216
32217     if(!this.header){
32218         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
32219     }
32220     if(!this.body){
32221         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
32222     }
32223
32224     this.header.unselectable();
32225     if(this.title){
32226         this.header.update(this.title);
32227     }
32228     // this element allows the dialog to be focused for keyboard event
32229     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
32230     this.focusEl.swallowEvent("click", true);
32231
32232     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
32233
32234     // wrap the body and footer for special rendering
32235     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
32236     if(this.footer){
32237         this.bwrap.dom.appendChild(this.footer.dom);
32238     }
32239
32240     this.bg = this.el.createChild({
32241         tag: "div", cls:"x-dlg-bg",
32242         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
32243     });
32244     this.centerBg = this.bg.child("div.x-dlg-bg-center");
32245
32246
32247     if(this.autoScroll !== false && !this.autoTabs){
32248         this.body.setStyle("overflow", "auto");
32249     }
32250
32251     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
32252
32253     if(this.closable !== false){
32254         this.el.addClass("x-dlg-closable");
32255         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
32256         this.close.on("click", this.closeClick, this);
32257         this.close.addClassOnOver("x-dlg-close-over");
32258     }
32259     if(this.collapsible !== false){
32260         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
32261         this.collapseBtn.on("click", this.collapseClick, this);
32262         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
32263         this.header.on("dblclick", this.collapseClick, this);
32264     }
32265     if(this.resizable !== false){
32266         this.el.addClass("x-dlg-resizable");
32267         this.resizer = new Roo.Resizable(el, {
32268             minWidth: this.minWidth || 80,
32269             minHeight:this.minHeight || 80,
32270             handles: this.resizeHandles || "all",
32271             pinned: true
32272         });
32273         this.resizer.on("beforeresize", this.beforeResize, this);
32274         this.resizer.on("resize", this.onResize, this);
32275     }
32276     if(this.draggable !== false){
32277         el.addClass("x-dlg-draggable");
32278         if (!this.proxyDrag) {
32279             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
32280         }
32281         else {
32282             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
32283         }
32284         dd.setHandleElId(this.header.id);
32285         dd.endDrag = this.endMove.createDelegate(this);
32286         dd.startDrag = this.startMove.createDelegate(this);
32287         dd.onDrag = this.onDrag.createDelegate(this);
32288         dd.scroll = false;
32289         this.dd = dd;
32290     }
32291     if(this.modal){
32292         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
32293         this.mask.enableDisplayMode("block");
32294         this.mask.hide();
32295         this.el.addClass("x-dlg-modal");
32296     }
32297     if(this.shadow){
32298         this.shadow = new Roo.Shadow({
32299             mode : typeof this.shadow == "string" ? this.shadow : "sides",
32300             offset : this.shadowOffset
32301         });
32302     }else{
32303         this.shadowOffset = 0;
32304     }
32305     if(Roo.useShims && this.shim !== false){
32306         this.shim = this.el.createShim();
32307         this.shim.hide = this.hideAction;
32308         this.shim.hide();
32309     }else{
32310         this.shim = false;
32311     }
32312     if(this.autoTabs){
32313         this.initTabs();
32314     }
32315     if (this.buttons) { 
32316         var bts= this.buttons;
32317         this.buttons = [];
32318         Roo.each(bts, function(b) {
32319             this.addButton(b);
32320         }, this);
32321     }
32322     
32323     
32324     this.addEvents({
32325         /**
32326          * @event keydown
32327          * Fires when a key is pressed
32328          * @param {Roo.BasicDialog} this
32329          * @param {Roo.EventObject} e
32330          */
32331         "keydown" : true,
32332         /**
32333          * @event move
32334          * Fires when this dialog is moved by the user.
32335          * @param {Roo.BasicDialog} this
32336          * @param {Number} x The new page X
32337          * @param {Number} y The new page Y
32338          */
32339         "move" : true,
32340         /**
32341          * @event resize
32342          * Fires when this dialog is resized by the user.
32343          * @param {Roo.BasicDialog} this
32344          * @param {Number} width The new width
32345          * @param {Number} height The new height
32346          */
32347         "resize" : true,
32348         /**
32349          * @event beforehide
32350          * Fires before this dialog is hidden.
32351          * @param {Roo.BasicDialog} this
32352          */
32353         "beforehide" : true,
32354         /**
32355          * @event hide
32356          * Fires when this dialog is hidden.
32357          * @param {Roo.BasicDialog} this
32358          */
32359         "hide" : true,
32360         /**
32361          * @event beforeshow
32362          * Fires before this dialog is shown.
32363          * @param {Roo.BasicDialog} this
32364          */
32365         "beforeshow" : true,
32366         /**
32367          * @event show
32368          * Fires when this dialog is shown.
32369          * @param {Roo.BasicDialog} this
32370          */
32371         "show" : true
32372     });
32373     el.on("keydown", this.onKeyDown, this);
32374     el.on("mousedown", this.toFront, this);
32375     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32376     this.el.hide();
32377     Roo.DialogManager.register(this);
32378     Roo.BasicDialog.superclass.constructor.call(this);
32379 };
32380
32381 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32382     shadowOffset: Roo.isIE ? 6 : 5,
32383     minHeight: 80,
32384     minWidth: 200,
32385     minButtonWidth: 75,
32386     defaultButton: null,
32387     buttonAlign: "right",
32388     tabTag: 'div',
32389     firstShow: true,
32390
32391     /**
32392      * Sets the dialog title text
32393      * @param {String} text The title text to display
32394      * @return {Roo.BasicDialog} this
32395      */
32396     setTitle : function(text){
32397         this.header.update(text);
32398         return this;
32399     },
32400
32401     // private
32402     closeClick : function(){
32403         this.hide();
32404     },
32405
32406     // private
32407     collapseClick : function(){
32408         this[this.collapsed ? "expand" : "collapse"]();
32409     },
32410
32411     /**
32412      * Collapses the dialog to its minimized state (only the title bar is visible).
32413      * Equivalent to the user clicking the collapse dialog button.
32414      */
32415     collapse : function(){
32416         if(!this.collapsed){
32417             this.collapsed = true;
32418             this.el.addClass("x-dlg-collapsed");
32419             this.restoreHeight = this.el.getHeight();
32420             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32421         }
32422     },
32423
32424     /**
32425      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32426      * clicking the expand dialog button.
32427      */
32428     expand : function(){
32429         if(this.collapsed){
32430             this.collapsed = false;
32431             this.el.removeClass("x-dlg-collapsed");
32432             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32433         }
32434     },
32435
32436     /**
32437      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32438      * @return {Roo.TabPanel} The tabs component
32439      */
32440     initTabs : function(){
32441         var tabs = this.getTabs();
32442         while(tabs.getTab(0)){
32443             tabs.removeTab(0);
32444         }
32445         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32446             var dom = el.dom;
32447             tabs.addTab(Roo.id(dom), dom.title);
32448             dom.title = "";
32449         });
32450         tabs.activate(0);
32451         return tabs;
32452     },
32453
32454     // private
32455     beforeResize : function(){
32456         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32457     },
32458
32459     // private
32460     onResize : function(){
32461         this.refreshSize();
32462         this.syncBodyHeight();
32463         this.adjustAssets();
32464         this.focus();
32465         this.fireEvent("resize", this, this.size.width, this.size.height);
32466     },
32467
32468     // private
32469     onKeyDown : function(e){
32470         if(this.isVisible()){
32471             this.fireEvent("keydown", this, e);
32472         }
32473     },
32474
32475     /**
32476      * Resizes the dialog.
32477      * @param {Number} width
32478      * @param {Number} height
32479      * @return {Roo.BasicDialog} this
32480      */
32481     resizeTo : function(width, height){
32482         this.el.setSize(width, height);
32483         this.size = {width: width, height: height};
32484         this.syncBodyHeight();
32485         if(this.fixedcenter){
32486             this.center();
32487         }
32488         if(this.isVisible()){
32489             this.constrainXY();
32490             this.adjustAssets();
32491         }
32492         this.fireEvent("resize", this, width, height);
32493         return this;
32494     },
32495
32496
32497     /**
32498      * Resizes the dialog to fit the specified content size.
32499      * @param {Number} width
32500      * @param {Number} height
32501      * @return {Roo.BasicDialog} this
32502      */
32503     setContentSize : function(w, h){
32504         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32505         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32506         //if(!this.el.isBorderBox()){
32507             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32508             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32509         //}
32510         if(this.tabs){
32511             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32512             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32513         }
32514         this.resizeTo(w, h);
32515         return this;
32516     },
32517
32518     /**
32519      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32520      * executed in response to a particular key being pressed while the dialog is active.
32521      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32522      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32523      * @param {Function} fn The function to call
32524      * @param {Object} scope (optional) The scope of the function
32525      * @return {Roo.BasicDialog} this
32526      */
32527     addKeyListener : function(key, fn, scope){
32528         var keyCode, shift, ctrl, alt;
32529         if(typeof key == "object" && !(key instanceof Array)){
32530             keyCode = key["key"];
32531             shift = key["shift"];
32532             ctrl = key["ctrl"];
32533             alt = key["alt"];
32534         }else{
32535             keyCode = key;
32536         }
32537         var handler = function(dlg, e){
32538             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32539                 var k = e.getKey();
32540                 if(keyCode instanceof Array){
32541                     for(var i = 0, len = keyCode.length; i < len; i++){
32542                         if(keyCode[i] == k){
32543                           fn.call(scope || window, dlg, k, e);
32544                           return;
32545                         }
32546                     }
32547                 }else{
32548                     if(k == keyCode){
32549                         fn.call(scope || window, dlg, k, e);
32550                     }
32551                 }
32552             }
32553         };
32554         this.on("keydown", handler);
32555         return this;
32556     },
32557
32558     /**
32559      * Returns the TabPanel component (creates it if it doesn't exist).
32560      * Note: If you wish to simply check for the existence of tabs without creating them,
32561      * check for a null 'tabs' property.
32562      * @return {Roo.TabPanel} The tabs component
32563      */
32564     getTabs : function(){
32565         if(!this.tabs){
32566             this.el.addClass("x-dlg-auto-tabs");
32567             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32568             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32569         }
32570         return this.tabs;
32571     },
32572
32573     /**
32574      * Adds a button to the footer section of the dialog.
32575      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32576      * object or a valid Roo.DomHelper element config
32577      * @param {Function} handler The function called when the button is clicked
32578      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32579      * @return {Roo.Button} The new button
32580      */
32581     addButton : function(config, handler, scope){
32582         var dh = Roo.DomHelper;
32583         if(!this.footer){
32584             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32585         }
32586         if(!this.btnContainer){
32587             var tb = this.footer.createChild({
32588
32589                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32590                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32591             }, null, true);
32592             this.btnContainer = tb.firstChild.firstChild.firstChild;
32593         }
32594         var bconfig = {
32595             handler: handler,
32596             scope: scope,
32597             minWidth: this.minButtonWidth,
32598             hideParent:true
32599         };
32600         if(typeof config == "string"){
32601             bconfig.text = config;
32602         }else{
32603             if(config.tag){
32604                 bconfig.dhconfig = config;
32605             }else{
32606                 Roo.apply(bconfig, config);
32607             }
32608         }
32609         var fc = false;
32610         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32611             bconfig.position = Math.max(0, bconfig.position);
32612             fc = this.btnContainer.childNodes[bconfig.position];
32613         }
32614          
32615         var btn = new Roo.Button(
32616             fc ? 
32617                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32618                 : this.btnContainer.appendChild(document.createElement("td")),
32619             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32620             bconfig
32621         );
32622         this.syncBodyHeight();
32623         if(!this.buttons){
32624             /**
32625              * Array of all the buttons that have been added to this dialog via addButton
32626              * @type Array
32627              */
32628             this.buttons = [];
32629         }
32630         this.buttons.push(btn);
32631         return btn;
32632     },
32633
32634     /**
32635      * Sets the default button to be focused when the dialog is displayed.
32636      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32637      * @return {Roo.BasicDialog} this
32638      */
32639     setDefaultButton : function(btn){
32640         this.defaultButton = btn;
32641         return this;
32642     },
32643
32644     // private
32645     getHeaderFooterHeight : function(safe){
32646         var height = 0;
32647         if(this.header){
32648            height += this.header.getHeight();
32649         }
32650         if(this.footer){
32651            var fm = this.footer.getMargins();
32652             height += (this.footer.getHeight()+fm.top+fm.bottom);
32653         }
32654         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32655         height += this.centerBg.getPadding("tb");
32656         return height;
32657     },
32658
32659     // private
32660     syncBodyHeight : function()
32661     {
32662         var bd = this.body, // the text
32663             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32664             bw = this.bwrap;
32665         var height = this.size.height - this.getHeaderFooterHeight(false);
32666         bd.setHeight(height-bd.getMargins("tb"));
32667         var hh = this.header.getHeight();
32668         var h = this.size.height-hh;
32669         cb.setHeight(h);
32670         
32671         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32672         bw.setHeight(h-cb.getPadding("tb"));
32673         
32674         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32675         bd.setWidth(bw.getWidth(true));
32676         if(this.tabs){
32677             this.tabs.syncHeight();
32678             if(Roo.isIE){
32679                 this.tabs.el.repaint();
32680             }
32681         }
32682     },
32683
32684     /**
32685      * Restores the previous state of the dialog if Roo.state is configured.
32686      * @return {Roo.BasicDialog} this
32687      */
32688     restoreState : function(){
32689         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32690         if(box && box.width){
32691             this.xy = [box.x, box.y];
32692             this.resizeTo(box.width, box.height);
32693         }
32694         return this;
32695     },
32696
32697     // private
32698     beforeShow : function(){
32699         this.expand();
32700         if(this.fixedcenter){
32701             this.xy = this.el.getCenterXY(true);
32702         }
32703         if(this.modal){
32704             Roo.get(document.body).addClass("x-body-masked");
32705             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32706             this.mask.show();
32707         }
32708         this.constrainXY();
32709     },
32710
32711     // private
32712     animShow : function(){
32713         var b = Roo.get(this.animateTarget).getBox();
32714         this.proxy.setSize(b.width, b.height);
32715         this.proxy.setLocation(b.x, b.y);
32716         this.proxy.show();
32717         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32718                     true, .35, this.showEl.createDelegate(this));
32719     },
32720
32721     /**
32722      * Shows the dialog.
32723      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32724      * @return {Roo.BasicDialog} this
32725      */
32726     show : function(animateTarget){
32727         if (this.fireEvent("beforeshow", this) === false){
32728             return;
32729         }
32730         if(this.syncHeightBeforeShow){
32731             this.syncBodyHeight();
32732         }else if(this.firstShow){
32733             this.firstShow = false;
32734             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32735         }
32736         this.animateTarget = animateTarget || this.animateTarget;
32737         if(!this.el.isVisible()){
32738             this.beforeShow();
32739             if(this.animateTarget && Roo.get(this.animateTarget)){
32740                 this.animShow();
32741             }else{
32742                 this.showEl();
32743             }
32744         }
32745         return this;
32746     },
32747
32748     // private
32749     showEl : function(){
32750         this.proxy.hide();
32751         this.el.setXY(this.xy);
32752         this.el.show();
32753         this.adjustAssets(true);
32754         this.toFront();
32755         this.focus();
32756         // IE peekaboo bug - fix found by Dave Fenwick
32757         if(Roo.isIE){
32758             this.el.repaint();
32759         }
32760         this.fireEvent("show", this);
32761     },
32762
32763     /**
32764      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32765      * dialog itself will receive focus.
32766      */
32767     focus : function(){
32768         if(this.defaultButton){
32769             this.defaultButton.focus();
32770         }else{
32771             this.focusEl.focus();
32772         }
32773     },
32774
32775     // private
32776     constrainXY : function(){
32777         if(this.constraintoviewport !== false){
32778             if(!this.viewSize){
32779                 if(this.container){
32780                     var s = this.container.getSize();
32781                     this.viewSize = [s.width, s.height];
32782                 }else{
32783                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32784                 }
32785             }
32786             var s = Roo.get(this.container||document).getScroll();
32787
32788             var x = this.xy[0], y = this.xy[1];
32789             var w = this.size.width, h = this.size.height;
32790             var vw = this.viewSize[0], vh = this.viewSize[1];
32791             // only move it if it needs it
32792             var moved = false;
32793             // first validate right/bottom
32794             if(x + w > vw+s.left){
32795                 x = vw - w;
32796                 moved = true;
32797             }
32798             if(y + h > vh+s.top){
32799                 y = vh - h;
32800                 moved = true;
32801             }
32802             // then make sure top/left isn't negative
32803             if(x < s.left){
32804                 x = s.left;
32805                 moved = true;
32806             }
32807             if(y < s.top){
32808                 y = s.top;
32809                 moved = true;
32810             }
32811             if(moved){
32812                 // cache xy
32813                 this.xy = [x, y];
32814                 if(this.isVisible()){
32815                     this.el.setLocation(x, y);
32816                     this.adjustAssets();
32817                 }
32818             }
32819         }
32820     },
32821
32822     // private
32823     onDrag : function(){
32824         if(!this.proxyDrag){
32825             this.xy = this.el.getXY();
32826             this.adjustAssets();
32827         }
32828     },
32829
32830     // private
32831     adjustAssets : function(doShow){
32832         var x = this.xy[0], y = this.xy[1];
32833         var w = this.size.width, h = this.size.height;
32834         if(doShow === true){
32835             if(this.shadow){
32836                 this.shadow.show(this.el);
32837             }
32838             if(this.shim){
32839                 this.shim.show();
32840             }
32841         }
32842         if(this.shadow && this.shadow.isVisible()){
32843             this.shadow.show(this.el);
32844         }
32845         if(this.shim && this.shim.isVisible()){
32846             this.shim.setBounds(x, y, w, h);
32847         }
32848     },
32849
32850     // private
32851     adjustViewport : function(w, h){
32852         if(!w || !h){
32853             w = Roo.lib.Dom.getViewWidth();
32854             h = Roo.lib.Dom.getViewHeight();
32855         }
32856         // cache the size
32857         this.viewSize = [w, h];
32858         if(this.modal && this.mask.isVisible()){
32859             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32860             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32861         }
32862         if(this.isVisible()){
32863             this.constrainXY();
32864         }
32865     },
32866
32867     /**
32868      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32869      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32870      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32871      */
32872     destroy : function(removeEl){
32873         if(this.isVisible()){
32874             this.animateTarget = null;
32875             this.hide();
32876         }
32877         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32878         if(this.tabs){
32879             this.tabs.destroy(removeEl);
32880         }
32881         Roo.destroy(
32882              this.shim,
32883              this.proxy,
32884              this.resizer,
32885              this.close,
32886              this.mask
32887         );
32888         if(this.dd){
32889             this.dd.unreg();
32890         }
32891         if(this.buttons){
32892            for(var i = 0, len = this.buttons.length; i < len; i++){
32893                this.buttons[i].destroy();
32894            }
32895         }
32896         this.el.removeAllListeners();
32897         if(removeEl === true){
32898             this.el.update("");
32899             this.el.remove();
32900         }
32901         Roo.DialogManager.unregister(this);
32902     },
32903
32904     // private
32905     startMove : function(){
32906         if(this.proxyDrag){
32907             this.proxy.show();
32908         }
32909         if(this.constraintoviewport !== false){
32910             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32911         }
32912     },
32913
32914     // private
32915     endMove : function(){
32916         if(!this.proxyDrag){
32917             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32918         }else{
32919             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32920             this.proxy.hide();
32921         }
32922         this.refreshSize();
32923         this.adjustAssets();
32924         this.focus();
32925         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32926     },
32927
32928     /**
32929      * Brings this dialog to the front of any other visible dialogs
32930      * @return {Roo.BasicDialog} this
32931      */
32932     toFront : function(){
32933         Roo.DialogManager.bringToFront(this);
32934         return this;
32935     },
32936
32937     /**
32938      * Sends this dialog to the back (under) of any other visible dialogs
32939      * @return {Roo.BasicDialog} this
32940      */
32941     toBack : function(){
32942         Roo.DialogManager.sendToBack(this);
32943         return this;
32944     },
32945
32946     /**
32947      * Centers this dialog in the viewport
32948      * @return {Roo.BasicDialog} this
32949      */
32950     center : function(){
32951         var xy = this.el.getCenterXY(true);
32952         this.moveTo(xy[0], xy[1]);
32953         return this;
32954     },
32955
32956     /**
32957      * Moves the dialog's top-left corner to the specified point
32958      * @param {Number} x
32959      * @param {Number} y
32960      * @return {Roo.BasicDialog} this
32961      */
32962     moveTo : function(x, y){
32963         this.xy = [x,y];
32964         if(this.isVisible()){
32965             this.el.setXY(this.xy);
32966             this.adjustAssets();
32967         }
32968         return this;
32969     },
32970
32971     /**
32972      * Aligns the dialog to the specified element
32973      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32974      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32975      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32976      * @return {Roo.BasicDialog} this
32977      */
32978     alignTo : function(element, position, offsets){
32979         this.xy = this.el.getAlignToXY(element, position, offsets);
32980         if(this.isVisible()){
32981             this.el.setXY(this.xy);
32982             this.adjustAssets();
32983         }
32984         return this;
32985     },
32986
32987     /**
32988      * Anchors an element to another element and realigns it when the window is resized.
32989      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32990      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32991      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32992      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32993      * is a number, it is used as the buffer delay (defaults to 50ms).
32994      * @return {Roo.BasicDialog} this
32995      */
32996     anchorTo : function(el, alignment, offsets, monitorScroll){
32997         var action = function(){
32998             this.alignTo(el, alignment, offsets);
32999         };
33000         Roo.EventManager.onWindowResize(action, this);
33001         var tm = typeof monitorScroll;
33002         if(tm != 'undefined'){
33003             Roo.EventManager.on(window, 'scroll', action, this,
33004                 {buffer: tm == 'number' ? monitorScroll : 50});
33005         }
33006         action.call(this);
33007         return this;
33008     },
33009
33010     /**
33011      * Returns true if the dialog is visible
33012      * @return {Boolean}
33013      */
33014     isVisible : function(){
33015         return this.el.isVisible();
33016     },
33017
33018     // private
33019     animHide : function(callback){
33020         var b = Roo.get(this.animateTarget).getBox();
33021         this.proxy.show();
33022         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
33023         this.el.hide();
33024         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
33025                     this.hideEl.createDelegate(this, [callback]));
33026     },
33027
33028     /**
33029      * Hides the dialog.
33030      * @param {Function} callback (optional) Function to call when the dialog is hidden
33031      * @return {Roo.BasicDialog} this
33032      */
33033     hide : function(callback){
33034         if (this.fireEvent("beforehide", this) === false){
33035             return;
33036         }
33037         if(this.shadow){
33038             this.shadow.hide();
33039         }
33040         if(this.shim) {
33041           this.shim.hide();
33042         }
33043         // sometimes animateTarget seems to get set.. causing problems...
33044         // this just double checks..
33045         if(this.animateTarget && Roo.get(this.animateTarget)) {
33046            this.animHide(callback);
33047         }else{
33048             this.el.hide();
33049             this.hideEl(callback);
33050         }
33051         return this;
33052     },
33053
33054     // private
33055     hideEl : function(callback){
33056         this.proxy.hide();
33057         if(this.modal){
33058             this.mask.hide();
33059             Roo.get(document.body).removeClass("x-body-masked");
33060         }
33061         this.fireEvent("hide", this);
33062         if(typeof callback == "function"){
33063             callback();
33064         }
33065     },
33066
33067     // private
33068     hideAction : function(){
33069         this.setLeft("-10000px");
33070         this.setTop("-10000px");
33071         this.setStyle("visibility", "hidden");
33072     },
33073
33074     // private
33075     refreshSize : function(){
33076         this.size = this.el.getSize();
33077         this.xy = this.el.getXY();
33078         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
33079     },
33080
33081     // private
33082     // z-index is managed by the DialogManager and may be overwritten at any time
33083     setZIndex : function(index){
33084         if(this.modal){
33085             this.mask.setStyle("z-index", index);
33086         }
33087         if(this.shim){
33088             this.shim.setStyle("z-index", ++index);
33089         }
33090         if(this.shadow){
33091             this.shadow.setZIndex(++index);
33092         }
33093         this.el.setStyle("z-index", ++index);
33094         if(this.proxy){
33095             this.proxy.setStyle("z-index", ++index);
33096         }
33097         if(this.resizer){
33098             this.resizer.proxy.setStyle("z-index", ++index);
33099         }
33100
33101         this.lastZIndex = index;
33102     },
33103
33104     /**
33105      * Returns the element for this dialog
33106      * @return {Roo.Element} The underlying dialog Element
33107      */
33108     getEl : function(){
33109         return this.el;
33110     }
33111 });
33112
33113 /**
33114  * @class Roo.DialogManager
33115  * Provides global access to BasicDialogs that have been created and
33116  * support for z-indexing (layering) multiple open dialogs.
33117  */
33118 Roo.DialogManager = function(){
33119     var list = {};
33120     var accessList = [];
33121     var front = null;
33122
33123     // private
33124     var sortDialogs = function(d1, d2){
33125         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
33126     };
33127
33128     // private
33129     var orderDialogs = function(){
33130         accessList.sort(sortDialogs);
33131         var seed = Roo.DialogManager.zseed;
33132         for(var i = 0, len = accessList.length; i < len; i++){
33133             var dlg = accessList[i];
33134             if(dlg){
33135                 dlg.setZIndex(seed + (i*10));
33136             }
33137         }
33138     };
33139
33140     return {
33141         /**
33142          * The starting z-index for BasicDialogs (defaults to 9000)
33143          * @type Number The z-index value
33144          */
33145         zseed : 9000,
33146
33147         // private
33148         register : function(dlg){
33149             list[dlg.id] = dlg;
33150             accessList.push(dlg);
33151         },
33152
33153         // private
33154         unregister : function(dlg){
33155             delete list[dlg.id];
33156             var i=0;
33157             var len=0;
33158             if(!accessList.indexOf){
33159                 for(  i = 0, len = accessList.length; i < len; i++){
33160                     if(accessList[i] == dlg){
33161                         accessList.splice(i, 1);
33162                         return;
33163                     }
33164                 }
33165             }else{
33166                  i = accessList.indexOf(dlg);
33167                 if(i != -1){
33168                     accessList.splice(i, 1);
33169                 }
33170             }
33171         },
33172
33173         /**
33174          * Gets a registered dialog by id
33175          * @param {String/Object} id The id of the dialog or a dialog
33176          * @return {Roo.BasicDialog} this
33177          */
33178         get : function(id){
33179             return typeof id == "object" ? id : list[id];
33180         },
33181
33182         /**
33183          * Brings the specified dialog to the front
33184          * @param {String/Object} dlg The id of the dialog or a dialog
33185          * @return {Roo.BasicDialog} this
33186          */
33187         bringToFront : function(dlg){
33188             dlg = this.get(dlg);
33189             if(dlg != front){
33190                 front = dlg;
33191                 dlg._lastAccess = new Date().getTime();
33192                 orderDialogs();
33193             }
33194             return dlg;
33195         },
33196
33197         /**
33198          * Sends the specified dialog to the back
33199          * @param {String/Object} dlg The id of the dialog or a dialog
33200          * @return {Roo.BasicDialog} this
33201          */
33202         sendToBack : function(dlg){
33203             dlg = this.get(dlg);
33204             dlg._lastAccess = -(new Date().getTime());
33205             orderDialogs();
33206             return dlg;
33207         },
33208
33209         /**
33210          * Hides all dialogs
33211          */
33212         hideAll : function(){
33213             for(var id in list){
33214                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
33215                     list[id].hide();
33216                 }
33217             }
33218         }
33219     };
33220 }();
33221
33222 /**
33223  * @class Roo.LayoutDialog
33224  * @extends Roo.BasicDialog
33225  * Dialog which provides adjustments for working with a layout in a Dialog.
33226  * Add your necessary layout config options to the dialog's config.<br>
33227  * Example usage (including a nested layout):
33228  * <pre><code>
33229 if(!dialog){
33230     dialog = new Roo.LayoutDialog("download-dlg", {
33231         modal: true,
33232         width:600,
33233         height:450,
33234         shadow:true,
33235         minWidth:500,
33236         minHeight:350,
33237         autoTabs:true,
33238         proxyDrag:true,
33239         // layout config merges with the dialog config
33240         center:{
33241             tabPosition: "top",
33242             alwaysShowTabs: true
33243         }
33244     });
33245     dialog.addKeyListener(27, dialog.hide, dialog);
33246     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
33247     dialog.addButton("Build It!", this.getDownload, this);
33248
33249     // we can even add nested layouts
33250     var innerLayout = new Roo.BorderLayout("dl-inner", {
33251         east: {
33252             initialSize: 200,
33253             autoScroll:true,
33254             split:true
33255         },
33256         center: {
33257             autoScroll:true
33258         }
33259     });
33260     innerLayout.beginUpdate();
33261     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
33262     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
33263     innerLayout.endUpdate(true);
33264
33265     var layout = dialog.getLayout();
33266     layout.beginUpdate();
33267     layout.add("center", new Roo.ContentPanel("standard-panel",
33268                         {title: "Download the Source", fitToFrame:true}));
33269     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
33270                {title: "Build your own roo.js"}));
33271     layout.getRegion("center").showPanel(sp);
33272     layout.endUpdate();
33273 }
33274 </code></pre>
33275     * @constructor
33276     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
33277     * @param {Object} config configuration options
33278   */
33279 Roo.LayoutDialog = function(el, cfg){
33280     
33281     var config=  cfg;
33282     if (typeof(cfg) == 'undefined') {
33283         config = Roo.apply({}, el);
33284         // not sure why we use documentElement here.. - it should always be body.
33285         // IE7 borks horribly if we use documentElement.
33286         // webkit also does not like documentElement - it creates a body element...
33287         el = Roo.get( document.body || document.documentElement ).createChild();
33288         //config.autoCreate = true;
33289     }
33290     
33291     
33292     config.autoTabs = false;
33293     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
33294     this.body.setStyle({overflow:"hidden", position:"relative"});
33295     this.layout = new Roo.BorderLayout(this.body.dom, config);
33296     this.layout.monitorWindowResize = false;
33297     this.el.addClass("x-dlg-auto-layout");
33298     // fix case when center region overwrites center function
33299     this.center = Roo.BasicDialog.prototype.center;
33300     this.on("show", this.layout.layout, this.layout, true);
33301     if (config.items) {
33302         var xitems = config.items;
33303         delete config.items;
33304         Roo.each(xitems, this.addxtype, this);
33305     }
33306     
33307     
33308 };
33309 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
33310     /**
33311      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33312      * @deprecated
33313      */
33314     endUpdate : function(){
33315         this.layout.endUpdate();
33316     },
33317
33318     /**
33319      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33320      *  @deprecated
33321      */
33322     beginUpdate : function(){
33323         this.layout.beginUpdate();
33324     },
33325
33326     /**
33327      * Get the BorderLayout for this dialog
33328      * @return {Roo.BorderLayout}
33329      */
33330     getLayout : function(){
33331         return this.layout;
33332     },
33333
33334     showEl : function(){
33335         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33336         if(Roo.isIE7){
33337             this.layout.layout();
33338         }
33339     },
33340
33341     // private
33342     // Use the syncHeightBeforeShow config option to control this automatically
33343     syncBodyHeight : function(){
33344         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33345         if(this.layout){this.layout.layout();}
33346     },
33347     
33348       /**
33349      * Add an xtype element (actually adds to the layout.)
33350      * @return {Object} xdata xtype object data.
33351      */
33352     
33353     addxtype : function(c) {
33354         return this.layout.addxtype(c);
33355     }
33356 });/*
33357  * Based on:
33358  * Ext JS Library 1.1.1
33359  * Copyright(c) 2006-2007, Ext JS, LLC.
33360  *
33361  * Originally Released Under LGPL - original licence link has changed is not relivant.
33362  *
33363  * Fork - LGPL
33364  * <script type="text/javascript">
33365  */
33366  
33367 /**
33368  * @class Roo.MessageBox
33369  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33370  * Example usage:
33371  *<pre><code>
33372 // Basic alert:
33373 Roo.Msg.alert('Status', 'Changes saved successfully.');
33374
33375 // Prompt for user data:
33376 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33377     if (btn == 'ok'){
33378         // process text value...
33379     }
33380 });
33381
33382 // Show a dialog using config options:
33383 Roo.Msg.show({
33384    title:'Save Changes?',
33385    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33386    buttons: Roo.Msg.YESNOCANCEL,
33387    fn: processResult,
33388    animEl: 'elId'
33389 });
33390 </code></pre>
33391  * @singleton
33392  */
33393 Roo.MessageBox = function(){
33394     var dlg, opt, mask, waitTimer;
33395     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33396     var buttons, activeTextEl, bwidth;
33397
33398     // private
33399     var handleButton = function(button){
33400         dlg.hide();
33401         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33402     };
33403
33404     // private
33405     var handleHide = function(){
33406         if(opt && opt.cls){
33407             dlg.el.removeClass(opt.cls);
33408         }
33409         if(waitTimer){
33410             Roo.TaskMgr.stop(waitTimer);
33411             waitTimer = null;
33412         }
33413     };
33414
33415     // private
33416     var updateButtons = function(b){
33417         var width = 0;
33418         if(!b){
33419             buttons["ok"].hide();
33420             buttons["cancel"].hide();
33421             buttons["yes"].hide();
33422             buttons["no"].hide();
33423             dlg.footer.dom.style.display = 'none';
33424             return width;
33425         }
33426         dlg.footer.dom.style.display = '';
33427         for(var k in buttons){
33428             if(typeof buttons[k] != "function"){
33429                 if(b[k]){
33430                     buttons[k].show();
33431                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33432                     width += buttons[k].el.getWidth()+15;
33433                 }else{
33434                     buttons[k].hide();
33435                 }
33436             }
33437         }
33438         return width;
33439     };
33440
33441     // private
33442     var handleEsc = function(d, k, e){
33443         if(opt && opt.closable !== false){
33444             dlg.hide();
33445         }
33446         if(e){
33447             e.stopEvent();
33448         }
33449     };
33450
33451     return {
33452         /**
33453          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33454          * @return {Roo.BasicDialog} The BasicDialog element
33455          */
33456         getDialog : function(){
33457            if(!dlg){
33458                 dlg = new Roo.BasicDialog("x-msg-box", {
33459                     autoCreate : true,
33460                     shadow: true,
33461                     draggable: true,
33462                     resizable:false,
33463                     constraintoviewport:false,
33464                     fixedcenter:true,
33465                     collapsible : false,
33466                     shim:true,
33467                     modal: true,
33468                     width:400, height:100,
33469                     buttonAlign:"center",
33470                     closeClick : function(){
33471                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33472                             handleButton("no");
33473                         }else{
33474                             handleButton("cancel");
33475                         }
33476                     }
33477                 });
33478                 dlg.on("hide", handleHide);
33479                 mask = dlg.mask;
33480                 dlg.addKeyListener(27, handleEsc);
33481                 buttons = {};
33482                 var bt = this.buttonText;
33483                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33484                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33485                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33486                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33487                 bodyEl = dlg.body.createChild({
33488
33489                     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>'
33490                 });
33491                 msgEl = bodyEl.dom.firstChild;
33492                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33493                 textboxEl.enableDisplayMode();
33494                 textboxEl.addKeyListener([10,13], function(){
33495                     if(dlg.isVisible() && opt && opt.buttons){
33496                         if(opt.buttons.ok){
33497                             handleButton("ok");
33498                         }else if(opt.buttons.yes){
33499                             handleButton("yes");
33500                         }
33501                     }
33502                 });
33503                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33504                 textareaEl.enableDisplayMode();
33505                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33506                 progressEl.enableDisplayMode();
33507                 var pf = progressEl.dom.firstChild;
33508                 if (pf) {
33509                     pp = Roo.get(pf.firstChild);
33510                     pp.setHeight(pf.offsetHeight);
33511                 }
33512                 
33513             }
33514             return dlg;
33515         },
33516
33517         /**
33518          * Updates the message box body text
33519          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33520          * the XHTML-compliant non-breaking space character '&amp;#160;')
33521          * @return {Roo.MessageBox} This message box
33522          */
33523         updateText : function(text){
33524             if(!dlg.isVisible() && !opt.width){
33525                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33526             }
33527             msgEl.innerHTML = text || '&#160;';
33528       
33529             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33530             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33531             var w = Math.max(
33532                     Math.min(opt.width || cw , this.maxWidth), 
33533                     Math.max(opt.minWidth || this.minWidth, bwidth)
33534             );
33535             if(opt.prompt){
33536                 activeTextEl.setWidth(w);
33537             }
33538             if(dlg.isVisible()){
33539                 dlg.fixedcenter = false;
33540             }
33541             // to big, make it scroll. = But as usual stupid IE does not support
33542             // !important..
33543             
33544             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33545                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33546                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33547             } else {
33548                 bodyEl.dom.style.height = '';
33549                 bodyEl.dom.style.overflowY = '';
33550             }
33551             if (cw > w) {
33552                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33553             } else {
33554                 bodyEl.dom.style.overflowX = '';
33555             }
33556             
33557             dlg.setContentSize(w, bodyEl.getHeight());
33558             if(dlg.isVisible()){
33559                 dlg.fixedcenter = true;
33560             }
33561             return this;
33562         },
33563
33564         /**
33565          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33566          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33567          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33568          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33569          * @return {Roo.MessageBox} This message box
33570          */
33571         updateProgress : function(value, text){
33572             if(text){
33573                 this.updateText(text);
33574             }
33575             if (pp) { // weird bug on my firefox - for some reason this is not defined
33576                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33577             }
33578             return this;
33579         },        
33580
33581         /**
33582          * Returns true if the message box is currently displayed
33583          * @return {Boolean} True if the message box is visible, else false
33584          */
33585         isVisible : function(){
33586             return dlg && dlg.isVisible();  
33587         },
33588
33589         /**
33590          * Hides the message box if it is displayed
33591          */
33592         hide : function(){
33593             if(this.isVisible()){
33594                 dlg.hide();
33595             }  
33596         },
33597
33598         /**
33599          * Displays a new message box, or reinitializes an existing message box, based on the config options
33600          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33601          * The following config object properties are supported:
33602          * <pre>
33603 Property    Type             Description
33604 ----------  ---------------  ------------------------------------------------------------------------------------
33605 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33606                                    closes (defaults to undefined)
33607 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33608                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33609 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33610                                    progress and wait dialogs will ignore this property and always hide the
33611                                    close button as they can only be closed programmatically.
33612 cls               String           A custom CSS class to apply to the message box element
33613 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33614                                    displayed (defaults to 75)
33615 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33616                                    function will be btn (the name of the button that was clicked, if applicable,
33617                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33618                                    Progress and wait dialogs will ignore this option since they do not respond to
33619                                    user actions and can only be closed programmatically, so any required function
33620                                    should be called by the same code after it closes the dialog.
33621 icon              String           A CSS class that provides a background image to be used as an icon for
33622                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33623 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33624 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33625 modal             Boolean          False to allow user interaction with the page while the message box is
33626                                    displayed (defaults to true)
33627 msg               String           A string that will replace the existing message box body text (defaults
33628                                    to the XHTML-compliant non-breaking space character '&#160;')
33629 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33630 progress          Boolean          True to display a progress bar (defaults to false)
33631 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33632 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33633 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33634 title             String           The title text
33635 value             String           The string value to set into the active textbox element if displayed
33636 wait              Boolean          True to display a progress bar (defaults to false)
33637 width             Number           The width of the dialog in pixels
33638 </pre>
33639          *
33640          * Example usage:
33641          * <pre><code>
33642 Roo.Msg.show({
33643    title: 'Address',
33644    msg: 'Please enter your address:',
33645    width: 300,
33646    buttons: Roo.MessageBox.OKCANCEL,
33647    multiline: true,
33648    fn: saveAddress,
33649    animEl: 'addAddressBtn'
33650 });
33651 </code></pre>
33652          * @param {Object} config Configuration options
33653          * @return {Roo.MessageBox} This message box
33654          */
33655         show : function(options)
33656         {
33657             
33658             // this causes nightmares if you show one dialog after another
33659             // especially on callbacks..
33660              
33661             if(this.isVisible()){
33662                 
33663                 this.hide();
33664                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33665                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33666                 Roo.log("New Dialog Message:" +  options.msg )
33667                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33668                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33669                 
33670             }
33671             var d = this.getDialog();
33672             opt = options;
33673             d.setTitle(opt.title || "&#160;");
33674             d.close.setDisplayed(opt.closable !== false);
33675             activeTextEl = textboxEl;
33676             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33677             if(opt.prompt){
33678                 if(opt.multiline){
33679                     textboxEl.hide();
33680                     textareaEl.show();
33681                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33682                         opt.multiline : this.defaultTextHeight);
33683                     activeTextEl = textareaEl;
33684                 }else{
33685                     textboxEl.show();
33686                     textareaEl.hide();
33687                 }
33688             }else{
33689                 textboxEl.hide();
33690                 textareaEl.hide();
33691             }
33692             progressEl.setDisplayed(opt.progress === true);
33693             this.updateProgress(0);
33694             activeTextEl.dom.value = opt.value || "";
33695             if(opt.prompt){
33696                 dlg.setDefaultButton(activeTextEl);
33697             }else{
33698                 var bs = opt.buttons;
33699                 var db = null;
33700                 if(bs && bs.ok){
33701                     db = buttons["ok"];
33702                 }else if(bs && bs.yes){
33703                     db = buttons["yes"];
33704                 }
33705                 dlg.setDefaultButton(db);
33706             }
33707             bwidth = updateButtons(opt.buttons);
33708             this.updateText(opt.msg);
33709             if(opt.cls){
33710                 d.el.addClass(opt.cls);
33711             }
33712             d.proxyDrag = opt.proxyDrag === true;
33713             d.modal = opt.modal !== false;
33714             d.mask = opt.modal !== false ? mask : false;
33715             if(!d.isVisible()){
33716                 // force it to the end of the z-index stack so it gets a cursor in FF
33717                 document.body.appendChild(dlg.el.dom);
33718                 d.animateTarget = null;
33719                 d.show(options.animEl);
33720             }
33721             return this;
33722         },
33723
33724         /**
33725          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33726          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33727          * and closing the message box when the process is complete.
33728          * @param {String} title The title bar text
33729          * @param {String} msg The message box body text
33730          * @return {Roo.MessageBox} This message box
33731          */
33732         progress : function(title, msg){
33733             this.show({
33734                 title : title,
33735                 msg : msg,
33736                 buttons: false,
33737                 progress:true,
33738                 closable:false,
33739                 minWidth: this.minProgressWidth,
33740                 modal : true
33741             });
33742             return this;
33743         },
33744
33745         /**
33746          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33747          * If a callback function is passed it will be called after the user clicks the button, and the
33748          * id of the button that was clicked will be passed as the only parameter to the callback
33749          * (could also be the top-right close button).
33750          * @param {String} title The title bar text
33751          * @param {String} msg The message box body text
33752          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33753          * @param {Object} scope (optional) The scope of the callback function
33754          * @return {Roo.MessageBox} This message box
33755          */
33756         alert : function(title, msg, fn, scope){
33757             this.show({
33758                 title : title,
33759                 msg : msg,
33760                 buttons: this.OK,
33761                 fn: fn,
33762                 scope : scope,
33763                 modal : true
33764             });
33765             return this;
33766         },
33767
33768         /**
33769          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33770          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33771          * You are responsible for closing the message box when the process is complete.
33772          * @param {String} msg The message box body text
33773          * @param {String} title (optional) The title bar text
33774          * @return {Roo.MessageBox} This message box
33775          */
33776         wait : function(msg, title){
33777             this.show({
33778                 title : title,
33779                 msg : msg,
33780                 buttons: false,
33781                 closable:false,
33782                 progress:true,
33783                 modal:true,
33784                 width:300,
33785                 wait:true
33786             });
33787             waitTimer = Roo.TaskMgr.start({
33788                 run: function(i){
33789                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33790                 },
33791                 interval: 1000
33792             });
33793             return this;
33794         },
33795
33796         /**
33797          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33798          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33799          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33800          * @param {String} title The title bar text
33801          * @param {String} msg The message box body text
33802          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33803          * @param {Object} scope (optional) The scope of the callback function
33804          * @return {Roo.MessageBox} This message box
33805          */
33806         confirm : function(title, msg, fn, scope){
33807             this.show({
33808                 title : title,
33809                 msg : msg,
33810                 buttons: this.YESNO,
33811                 fn: fn,
33812                 scope : scope,
33813                 modal : true
33814             });
33815             return this;
33816         },
33817
33818         /**
33819          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33820          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33821          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33822          * (could also be the top-right close button) and the text that was entered will be passed as the two
33823          * parameters to the callback.
33824          * @param {String} title The title bar text
33825          * @param {String} msg The message box body text
33826          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33827          * @param {Object} scope (optional) The scope of the callback function
33828          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33829          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33830          * @return {Roo.MessageBox} This message box
33831          */
33832         prompt : function(title, msg, fn, scope, multiline){
33833             this.show({
33834                 title : title,
33835                 msg : msg,
33836                 buttons: this.OKCANCEL,
33837                 fn: fn,
33838                 minWidth:250,
33839                 scope : scope,
33840                 prompt:true,
33841                 multiline: multiline,
33842                 modal : true
33843             });
33844             return this;
33845         },
33846
33847         /**
33848          * Button config that displays a single OK button
33849          * @type Object
33850          */
33851         OK : {ok:true},
33852         /**
33853          * Button config that displays Yes and No buttons
33854          * @type Object
33855          */
33856         YESNO : {yes:true, no:true},
33857         /**
33858          * Button config that displays OK and Cancel buttons
33859          * @type Object
33860          */
33861         OKCANCEL : {ok:true, cancel:true},
33862         /**
33863          * Button config that displays Yes, No and Cancel buttons
33864          * @type Object
33865          */
33866         YESNOCANCEL : {yes:true, no:true, cancel:true},
33867
33868         /**
33869          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33870          * @type Number
33871          */
33872         defaultTextHeight : 75,
33873         /**
33874          * The maximum width in pixels of the message box (defaults to 600)
33875          * @type Number
33876          */
33877         maxWidth : 600,
33878         /**
33879          * The minimum width in pixels of the message box (defaults to 100)
33880          * @type Number
33881          */
33882         minWidth : 100,
33883         /**
33884          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33885          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33886          * @type Number
33887          */
33888         minProgressWidth : 250,
33889         /**
33890          * An object containing the default button text strings that can be overriden for localized language support.
33891          * Supported properties are: ok, cancel, yes and no.
33892          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33893          * @type Object
33894          */
33895         buttonText : {
33896             ok : "OK",
33897             cancel : "Cancel",
33898             yes : "Yes",
33899             no : "No"
33900         }
33901     };
33902 }();
33903
33904 /**
33905  * Shorthand for {@link Roo.MessageBox}
33906  */
33907 Roo.Msg = Roo.MessageBox;/*
33908  * Based on:
33909  * Ext JS Library 1.1.1
33910  * Copyright(c) 2006-2007, Ext JS, LLC.
33911  *
33912  * Originally Released Under LGPL - original licence link has changed is not relivant.
33913  *
33914  * Fork - LGPL
33915  * <script type="text/javascript">
33916  */
33917 /**
33918  * @class Roo.QuickTips
33919  * Provides attractive and customizable tooltips for any element.
33920  * @singleton
33921  */
33922 Roo.QuickTips = function(){
33923     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33924     var ce, bd, xy, dd;
33925     var visible = false, disabled = true, inited = false;
33926     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33927     
33928     var onOver = function(e){
33929         if(disabled){
33930             return;
33931         }
33932         var t = e.getTarget();
33933         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33934             return;
33935         }
33936         if(ce && t == ce.el){
33937             clearTimeout(hideProc);
33938             return;
33939         }
33940         if(t && tagEls[t.id]){
33941             tagEls[t.id].el = t;
33942             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33943             return;
33944         }
33945         var ttp, et = Roo.fly(t);
33946         var ns = cfg.namespace;
33947         if(tm.interceptTitles && t.title){
33948             ttp = t.title;
33949             t.qtip = ttp;
33950             t.removeAttribute("title");
33951             e.preventDefault();
33952         }else{
33953             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33954         }
33955         if(ttp){
33956             showProc = show.defer(tm.showDelay, tm, [{
33957                 el: t, 
33958                 text: ttp.replace(/\\n/g,'<br/>'),
33959                 width: et.getAttributeNS(ns, cfg.width),
33960                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33961                 title: et.getAttributeNS(ns, cfg.title),
33962                     cls: et.getAttributeNS(ns, cfg.cls)
33963             }]);
33964         }
33965     };
33966     
33967     var onOut = function(e){
33968         clearTimeout(showProc);
33969         var t = e.getTarget();
33970         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33971             hideProc = setTimeout(hide, tm.hideDelay);
33972         }
33973     };
33974     
33975     var onMove = function(e){
33976         if(disabled){
33977             return;
33978         }
33979         xy = e.getXY();
33980         xy[1] += 18;
33981         if(tm.trackMouse && ce){
33982             el.setXY(xy);
33983         }
33984     };
33985     
33986     var onDown = function(e){
33987         clearTimeout(showProc);
33988         clearTimeout(hideProc);
33989         if(!e.within(el)){
33990             if(tm.hideOnClick){
33991                 hide();
33992                 tm.disable();
33993                 tm.enable.defer(100, tm);
33994             }
33995         }
33996     };
33997     
33998     var getPad = function(){
33999         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
34000     };
34001
34002     var show = function(o){
34003         if(disabled){
34004             return;
34005         }
34006         clearTimeout(dismissProc);
34007         ce = o;
34008         if(removeCls){ // in case manually hidden
34009             el.removeClass(removeCls);
34010             removeCls = null;
34011         }
34012         if(ce.cls){
34013             el.addClass(ce.cls);
34014             removeCls = ce.cls;
34015         }
34016         if(ce.title){
34017             tipTitle.update(ce.title);
34018             tipTitle.show();
34019         }else{
34020             tipTitle.update('');
34021             tipTitle.hide();
34022         }
34023         el.dom.style.width  = tm.maxWidth+'px';
34024         //tipBody.dom.style.width = '';
34025         tipBodyText.update(o.text);
34026         var p = getPad(), w = ce.width;
34027         if(!w){
34028             var td = tipBodyText.dom;
34029             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
34030             if(aw > tm.maxWidth){
34031                 w = tm.maxWidth;
34032             }else if(aw < tm.minWidth){
34033                 w = tm.minWidth;
34034             }else{
34035                 w = aw;
34036             }
34037         }
34038         //tipBody.setWidth(w);
34039         el.setWidth(parseInt(w, 10) + p);
34040         if(ce.autoHide === false){
34041             close.setDisplayed(true);
34042             if(dd){
34043                 dd.unlock();
34044             }
34045         }else{
34046             close.setDisplayed(false);
34047             if(dd){
34048                 dd.lock();
34049             }
34050         }
34051         if(xy){
34052             el.avoidY = xy[1]-18;
34053             el.setXY(xy);
34054         }
34055         if(tm.animate){
34056             el.setOpacity(.1);
34057             el.setStyle("visibility", "visible");
34058             el.fadeIn({callback: afterShow});
34059         }else{
34060             afterShow();
34061         }
34062     };
34063     
34064     var afterShow = function(){
34065         if(ce){
34066             el.show();
34067             esc.enable();
34068             if(tm.autoDismiss && ce.autoHide !== false){
34069                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
34070             }
34071         }
34072     };
34073     
34074     var hide = function(noanim){
34075         clearTimeout(dismissProc);
34076         clearTimeout(hideProc);
34077         ce = null;
34078         if(el.isVisible()){
34079             esc.disable();
34080             if(noanim !== true && tm.animate){
34081                 el.fadeOut({callback: afterHide});
34082             }else{
34083                 afterHide();
34084             } 
34085         }
34086     };
34087     
34088     var afterHide = function(){
34089         el.hide();
34090         if(removeCls){
34091             el.removeClass(removeCls);
34092             removeCls = null;
34093         }
34094     };
34095     
34096     return {
34097         /**
34098         * @cfg {Number} minWidth
34099         * The minimum width of the quick tip (defaults to 40)
34100         */
34101        minWidth : 40,
34102         /**
34103         * @cfg {Number} maxWidth
34104         * The maximum width of the quick tip (defaults to 300)
34105         */
34106        maxWidth : 300,
34107         /**
34108         * @cfg {Boolean} interceptTitles
34109         * True to automatically use the element's DOM title value if available (defaults to false)
34110         */
34111        interceptTitles : false,
34112         /**
34113         * @cfg {Boolean} trackMouse
34114         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
34115         */
34116        trackMouse : false,
34117         /**
34118         * @cfg {Boolean} hideOnClick
34119         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
34120         */
34121        hideOnClick : true,
34122         /**
34123         * @cfg {Number} showDelay
34124         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
34125         */
34126        showDelay : 500,
34127         /**
34128         * @cfg {Number} hideDelay
34129         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
34130         */
34131        hideDelay : 200,
34132         /**
34133         * @cfg {Boolean} autoHide
34134         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
34135         * Used in conjunction with hideDelay.
34136         */
34137        autoHide : true,
34138         /**
34139         * @cfg {Boolean}
34140         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
34141         * (defaults to true).  Used in conjunction with autoDismissDelay.
34142         */
34143        autoDismiss : true,
34144         /**
34145         * @cfg {Number}
34146         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
34147         */
34148        autoDismissDelay : 5000,
34149        /**
34150         * @cfg {Boolean} animate
34151         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
34152         */
34153        animate : false,
34154
34155        /**
34156         * @cfg {String} title
34157         * Title text to display (defaults to '').  This can be any valid HTML markup.
34158         */
34159         title: '',
34160        /**
34161         * @cfg {String} text
34162         * Body text to display (defaults to '').  This can be any valid HTML markup.
34163         */
34164         text : '',
34165        /**
34166         * @cfg {String} cls
34167         * A CSS class to apply to the base quick tip element (defaults to '').
34168         */
34169         cls : '',
34170        /**
34171         * @cfg {Number} width
34172         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
34173         * minWidth or maxWidth.
34174         */
34175         width : null,
34176
34177     /**
34178      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
34179      * or display QuickTips in a page.
34180      */
34181        init : function(){
34182           tm = Roo.QuickTips;
34183           cfg = tm.tagConfig;
34184           if(!inited){
34185               if(!Roo.isReady){ // allow calling of init() before onReady
34186                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
34187                   return;
34188               }
34189               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
34190               el.fxDefaults = {stopFx: true};
34191               // maximum custom styling
34192               //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>');
34193               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>');              
34194               tipTitle = el.child('h3');
34195               tipTitle.enableDisplayMode("block");
34196               tipBody = el.child('div.x-tip-bd');
34197               tipBodyText = el.child('div.x-tip-bd-inner');
34198               //bdLeft = el.child('div.x-tip-bd-left');
34199               //bdRight = el.child('div.x-tip-bd-right');
34200               close = el.child('div.x-tip-close');
34201               close.enableDisplayMode("block");
34202               close.on("click", hide);
34203               var d = Roo.get(document);
34204               d.on("mousedown", onDown);
34205               d.on("mouseover", onOver);
34206               d.on("mouseout", onOut);
34207               d.on("mousemove", onMove);
34208               esc = d.addKeyListener(27, hide);
34209               esc.disable();
34210               if(Roo.dd.DD){
34211                   dd = el.initDD("default", null, {
34212                       onDrag : function(){
34213                           el.sync();  
34214                       }
34215                   });
34216                   dd.setHandleElId(tipTitle.id);
34217                   dd.lock();
34218               }
34219               inited = true;
34220           }
34221           this.enable(); 
34222        },
34223
34224     /**
34225      * Configures a new quick tip instance and assigns it to a target element.  The following config options
34226      * are supported:
34227      * <pre>
34228 Property    Type                   Description
34229 ----------  ---------------------  ------------------------------------------------------------------------
34230 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
34231      * </ul>
34232      * @param {Object} config The config object
34233      */
34234        register : function(config){
34235            var cs = config instanceof Array ? config : arguments;
34236            for(var i = 0, len = cs.length; i < len; i++) {
34237                var c = cs[i];
34238                var target = c.target;
34239                if(target){
34240                    if(target instanceof Array){
34241                        for(var j = 0, jlen = target.length; j < jlen; j++){
34242                            tagEls[target[j]] = c;
34243                        }
34244                    }else{
34245                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
34246                    }
34247                }
34248            }
34249        },
34250
34251     /**
34252      * Removes this quick tip from its element and destroys it.
34253      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
34254      */
34255        unregister : function(el){
34256            delete tagEls[Roo.id(el)];
34257        },
34258
34259     /**
34260      * Enable this quick tip.
34261      */
34262        enable : function(){
34263            if(inited && disabled){
34264                locks.pop();
34265                if(locks.length < 1){
34266                    disabled = false;
34267                }
34268            }
34269        },
34270
34271     /**
34272      * Disable this quick tip.
34273      */
34274        disable : function(){
34275           disabled = true;
34276           clearTimeout(showProc);
34277           clearTimeout(hideProc);
34278           clearTimeout(dismissProc);
34279           if(ce){
34280               hide(true);
34281           }
34282           locks.push(1);
34283        },
34284
34285     /**
34286      * Returns true if the quick tip is enabled, else false.
34287      */
34288        isEnabled : function(){
34289             return !disabled;
34290        },
34291
34292         // private
34293        tagConfig : {
34294            namespace : "roo", // was ext?? this may break..
34295            alt_namespace : "ext",
34296            attribute : "qtip",
34297            width : "width",
34298            target : "target",
34299            title : "qtitle",
34300            hide : "hide",
34301            cls : "qclass"
34302        }
34303    };
34304 }();
34305
34306 // backwards compat
34307 Roo.QuickTips.tips = Roo.QuickTips.register;/*
34308  * Based on:
34309  * Ext JS Library 1.1.1
34310  * Copyright(c) 2006-2007, Ext JS, LLC.
34311  *
34312  * Originally Released Under LGPL - original licence link has changed is not relivant.
34313  *
34314  * Fork - LGPL
34315  * <script type="text/javascript">
34316  */
34317  
34318
34319 /**
34320  * @class Roo.tree.TreePanel
34321  * @extends Roo.data.Tree
34322
34323  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34324  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34325  * @cfg {Boolean} enableDD true to enable drag and drop
34326  * @cfg {Boolean} enableDrag true to enable just drag
34327  * @cfg {Boolean} enableDrop true to enable just drop
34328  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34329  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34330  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34331  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34332  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34333  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34334  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34335  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34336  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34337  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34338  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34339  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34340  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
34341  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34342  * @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>
34343  * @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>
34344  * 
34345  * @constructor
34346  * @param {String/HTMLElement/Element} el The container element
34347  * @param {Object} config
34348  */
34349 Roo.tree.TreePanel = function(el, config){
34350     var root = false;
34351     var loader = false;
34352     if (config.root) {
34353         root = config.root;
34354         delete config.root;
34355     }
34356     if (config.loader) {
34357         loader = config.loader;
34358         delete config.loader;
34359     }
34360     
34361     Roo.apply(this, config);
34362     Roo.tree.TreePanel.superclass.constructor.call(this);
34363     this.el = Roo.get(el);
34364     this.el.addClass('x-tree');
34365     //console.log(root);
34366     if (root) {
34367         this.setRootNode( Roo.factory(root, Roo.tree));
34368     }
34369     if (loader) {
34370         this.loader = Roo.factory(loader, Roo.tree);
34371     }
34372    /**
34373     * Read-only. The id of the container element becomes this TreePanel's id.
34374     */
34375     this.id = this.el.id;
34376     this.addEvents({
34377         /**
34378         * @event beforeload
34379         * Fires before a node is loaded, return false to cancel
34380         * @param {Node} node The node being loaded
34381         */
34382         "beforeload" : true,
34383         /**
34384         * @event load
34385         * Fires when a node is loaded
34386         * @param {Node} node The node that was loaded
34387         */
34388         "load" : true,
34389         /**
34390         * @event textchange
34391         * Fires when the text for a node is changed
34392         * @param {Node} node The node
34393         * @param {String} text The new text
34394         * @param {String} oldText The old text
34395         */
34396         "textchange" : true,
34397         /**
34398         * @event beforeexpand
34399         * Fires before a node is expanded, return false to cancel.
34400         * @param {Node} node The node
34401         * @param {Boolean} deep
34402         * @param {Boolean} anim
34403         */
34404         "beforeexpand" : true,
34405         /**
34406         * @event beforecollapse
34407         * Fires before a node is collapsed, return false to cancel.
34408         * @param {Node} node The node
34409         * @param {Boolean} deep
34410         * @param {Boolean} anim
34411         */
34412         "beforecollapse" : true,
34413         /**
34414         * @event expand
34415         * Fires when a node is expanded
34416         * @param {Node} node The node
34417         */
34418         "expand" : true,
34419         /**
34420         * @event disabledchange
34421         * Fires when the disabled status of a node changes
34422         * @param {Node} node The node
34423         * @param {Boolean} disabled
34424         */
34425         "disabledchange" : true,
34426         /**
34427         * @event collapse
34428         * Fires when a node is collapsed
34429         * @param {Node} node The node
34430         */
34431         "collapse" : true,
34432         /**
34433         * @event beforeclick
34434         * Fires before click processing on a node. Return false to cancel the default action.
34435         * @param {Node} node The node
34436         * @param {Roo.EventObject} e The event object
34437         */
34438         "beforeclick":true,
34439         /**
34440         * @event checkchange
34441         * Fires when a node with a checkbox's checked property changes
34442         * @param {Node} this This node
34443         * @param {Boolean} checked
34444         */
34445         "checkchange":true,
34446         /**
34447         * @event click
34448         * Fires when a node is clicked
34449         * @param {Node} node The node
34450         * @param {Roo.EventObject} e The event object
34451         */
34452         "click":true,
34453         /**
34454         * @event dblclick
34455         * Fires when a node is double clicked
34456         * @param {Node} node The node
34457         * @param {Roo.EventObject} e The event object
34458         */
34459         "dblclick":true,
34460         /**
34461         * @event contextmenu
34462         * Fires when a node is right clicked
34463         * @param {Node} node The node
34464         * @param {Roo.EventObject} e The event object
34465         */
34466         "contextmenu":true,
34467         /**
34468         * @event beforechildrenrendered
34469         * Fires right before the child nodes for a node are rendered
34470         * @param {Node} node The node
34471         */
34472         "beforechildrenrendered":true,
34473         /**
34474         * @event startdrag
34475         * Fires when a node starts being dragged
34476         * @param {Roo.tree.TreePanel} this
34477         * @param {Roo.tree.TreeNode} node
34478         * @param {event} e The raw browser event
34479         */ 
34480        "startdrag" : true,
34481        /**
34482         * @event enddrag
34483         * Fires when a drag operation is complete
34484         * @param {Roo.tree.TreePanel} this
34485         * @param {Roo.tree.TreeNode} node
34486         * @param {event} e The raw browser event
34487         */
34488        "enddrag" : true,
34489        /**
34490         * @event dragdrop
34491         * Fires when a dragged node is dropped on a valid DD target
34492         * @param {Roo.tree.TreePanel} this
34493         * @param {Roo.tree.TreeNode} node
34494         * @param {DD} dd The dd it was dropped on
34495         * @param {event} e The raw browser event
34496         */
34497        "dragdrop" : true,
34498        /**
34499         * @event beforenodedrop
34500         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34501         * passed to handlers has the following properties:<br />
34502         * <ul style="padding:5px;padding-left:16px;">
34503         * <li>tree - The TreePanel</li>
34504         * <li>target - The node being targeted for the drop</li>
34505         * <li>data - The drag data from the drag source</li>
34506         * <li>point - The point of the drop - append, above or below</li>
34507         * <li>source - The drag source</li>
34508         * <li>rawEvent - Raw mouse event</li>
34509         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34510         * to be inserted by setting them on this object.</li>
34511         * <li>cancel - Set this to true to cancel the drop.</li>
34512         * </ul>
34513         * @param {Object} dropEvent
34514         */
34515        "beforenodedrop" : true,
34516        /**
34517         * @event nodedrop
34518         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34519         * passed to handlers has the following properties:<br />
34520         * <ul style="padding:5px;padding-left:16px;">
34521         * <li>tree - The TreePanel</li>
34522         * <li>target - The node being targeted for the drop</li>
34523         * <li>data - The drag data from the drag source</li>
34524         * <li>point - The point of the drop - append, above or below</li>
34525         * <li>source - The drag source</li>
34526         * <li>rawEvent - Raw mouse event</li>
34527         * <li>dropNode - Dropped node(s).</li>
34528         * </ul>
34529         * @param {Object} dropEvent
34530         */
34531        "nodedrop" : true,
34532         /**
34533         * @event nodedragover
34534         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34535         * passed to handlers has the following properties:<br />
34536         * <ul style="padding:5px;padding-left:16px;">
34537         * <li>tree - The TreePanel</li>
34538         * <li>target - The node being targeted for the drop</li>
34539         * <li>data - The drag data from the drag source</li>
34540         * <li>point - The point of the drop - append, above or below</li>
34541         * <li>source - The drag source</li>
34542         * <li>rawEvent - Raw mouse event</li>
34543         * <li>dropNode - Drop node(s) provided by the source.</li>
34544         * <li>cancel - Set this to true to signal drop not allowed.</li>
34545         * </ul>
34546         * @param {Object} dragOverEvent
34547         */
34548        "nodedragover" : true,
34549        /**
34550         * @event appendnode
34551         * Fires when append node to the tree
34552         * @param {Roo.tree.TreePanel} this
34553         * @param {Roo.tree.TreeNode} node
34554         * @param {Number} index The index of the newly appended node
34555         */
34556        "appendnode" : true
34557         
34558     });
34559     if(this.singleExpand){
34560        this.on("beforeexpand", this.restrictExpand, this);
34561     }
34562     if (this.editor) {
34563         this.editor.tree = this;
34564         this.editor = Roo.factory(this.editor, Roo.tree);
34565     }
34566     
34567     if (this.selModel) {
34568         this.selModel = Roo.factory(this.selModel, Roo.tree);
34569     }
34570    
34571 };
34572 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34573     rootVisible : true,
34574     animate: Roo.enableFx,
34575     lines : true,
34576     enableDD : false,
34577     hlDrop : Roo.enableFx,
34578   
34579     renderer: false,
34580     
34581     rendererTip: false,
34582     // private
34583     restrictExpand : function(node){
34584         var p = node.parentNode;
34585         if(p){
34586             if(p.expandedChild && p.expandedChild.parentNode == p){
34587                 p.expandedChild.collapse();
34588             }
34589             p.expandedChild = node;
34590         }
34591     },
34592
34593     // private override
34594     setRootNode : function(node){
34595         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34596         if(!this.rootVisible){
34597             node.ui = new Roo.tree.RootTreeNodeUI(node);
34598         }
34599         return node;
34600     },
34601
34602     /**
34603      * Returns the container element for this TreePanel
34604      */
34605     getEl : function(){
34606         return this.el;
34607     },
34608
34609     /**
34610      * Returns the default TreeLoader for this TreePanel
34611      */
34612     getLoader : function(){
34613         return this.loader;
34614     },
34615
34616     /**
34617      * Expand all nodes
34618      */
34619     expandAll : function(){
34620         this.root.expand(true);
34621     },
34622
34623     /**
34624      * Collapse all nodes
34625      */
34626     collapseAll : function(){
34627         this.root.collapse(true);
34628     },
34629
34630     /**
34631      * Returns the selection model used by this TreePanel
34632      */
34633     getSelectionModel : function(){
34634         if(!this.selModel){
34635             this.selModel = new Roo.tree.DefaultSelectionModel();
34636         }
34637         return this.selModel;
34638     },
34639
34640     /**
34641      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34642      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34643      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34644      * @return {Array}
34645      */
34646     getChecked : function(a, startNode){
34647         startNode = startNode || this.root;
34648         var r = [];
34649         var f = function(){
34650             if(this.attributes.checked){
34651                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34652             }
34653         }
34654         startNode.cascade(f);
34655         return r;
34656     },
34657
34658     /**
34659      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34660      * @param {String} path
34661      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34662      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34663      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34664      */
34665     expandPath : function(path, attr, callback){
34666         attr = attr || "id";
34667         var keys = path.split(this.pathSeparator);
34668         var curNode = this.root;
34669         if(curNode.attributes[attr] != keys[1]){ // invalid root
34670             if(callback){
34671                 callback(false, null);
34672             }
34673             return;
34674         }
34675         var index = 1;
34676         var f = function(){
34677             if(++index == keys.length){
34678                 if(callback){
34679                     callback(true, curNode);
34680                 }
34681                 return;
34682             }
34683             var c = curNode.findChild(attr, keys[index]);
34684             if(!c){
34685                 if(callback){
34686                     callback(false, curNode);
34687                 }
34688                 return;
34689             }
34690             curNode = c;
34691             c.expand(false, false, f);
34692         };
34693         curNode.expand(false, false, f);
34694     },
34695
34696     /**
34697      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34698      * @param {String} path
34699      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34700      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34701      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34702      */
34703     selectPath : function(path, attr, callback){
34704         attr = attr || "id";
34705         var keys = path.split(this.pathSeparator);
34706         var v = keys.pop();
34707         if(keys.length > 0){
34708             var f = function(success, node){
34709                 if(success && node){
34710                     var n = node.findChild(attr, v);
34711                     if(n){
34712                         n.select();
34713                         if(callback){
34714                             callback(true, n);
34715                         }
34716                     }else if(callback){
34717                         callback(false, n);
34718                     }
34719                 }else{
34720                     if(callback){
34721                         callback(false, n);
34722                     }
34723                 }
34724             };
34725             this.expandPath(keys.join(this.pathSeparator), attr, f);
34726         }else{
34727             this.root.select();
34728             if(callback){
34729                 callback(true, this.root);
34730             }
34731         }
34732     },
34733
34734     getTreeEl : function(){
34735         return this.el;
34736     },
34737
34738     /**
34739      * Trigger rendering of this TreePanel
34740      */
34741     render : function(){
34742         if (this.innerCt) {
34743             return this; // stop it rendering more than once!!
34744         }
34745         
34746         this.innerCt = this.el.createChild({tag:"ul",
34747                cls:"x-tree-root-ct " +
34748                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34749
34750         if(this.containerScroll){
34751             Roo.dd.ScrollManager.register(this.el);
34752         }
34753         if((this.enableDD || this.enableDrop) && !this.dropZone){
34754            /**
34755             * The dropZone used by this tree if drop is enabled
34756             * @type Roo.tree.TreeDropZone
34757             */
34758              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34759                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34760            });
34761         }
34762         if((this.enableDD || this.enableDrag) && !this.dragZone){
34763            /**
34764             * The dragZone used by this tree if drag is enabled
34765             * @type Roo.tree.TreeDragZone
34766             */
34767             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34768                ddGroup: this.ddGroup || "TreeDD",
34769                scroll: this.ddScroll
34770            });
34771         }
34772         this.getSelectionModel().init(this);
34773         if (!this.root) {
34774             Roo.log("ROOT not set in tree");
34775             return this;
34776         }
34777         this.root.render();
34778         if(!this.rootVisible){
34779             this.root.renderChildren();
34780         }
34781         return this;
34782     }
34783 });/*
34784  * Based on:
34785  * Ext JS Library 1.1.1
34786  * Copyright(c) 2006-2007, Ext JS, LLC.
34787  *
34788  * Originally Released Under LGPL - original licence link has changed is not relivant.
34789  *
34790  * Fork - LGPL
34791  * <script type="text/javascript">
34792  */
34793  
34794
34795 /**
34796  * @class Roo.tree.DefaultSelectionModel
34797  * @extends Roo.util.Observable
34798  * The default single selection for a TreePanel.
34799  * @param {Object} cfg Configuration
34800  */
34801 Roo.tree.DefaultSelectionModel = function(cfg){
34802    this.selNode = null;
34803    
34804    
34805    
34806    this.addEvents({
34807        /**
34808         * @event selectionchange
34809         * Fires when the selected node changes
34810         * @param {DefaultSelectionModel} this
34811         * @param {TreeNode} node the new selection
34812         */
34813        "selectionchange" : true,
34814
34815        /**
34816         * @event beforeselect
34817         * Fires before the selected node changes, return false to cancel the change
34818         * @param {DefaultSelectionModel} this
34819         * @param {TreeNode} node the new selection
34820         * @param {TreeNode} node the old selection
34821         */
34822        "beforeselect" : true
34823    });
34824    
34825     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34826 };
34827
34828 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34829     init : function(tree){
34830         this.tree = tree;
34831         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34832         tree.on("click", this.onNodeClick, this);
34833     },
34834     
34835     onNodeClick : function(node, e){
34836         if (e.ctrlKey && this.selNode == node)  {
34837             this.unselect(node);
34838             return;
34839         }
34840         this.select(node);
34841     },
34842     
34843     /**
34844      * Select a node.
34845      * @param {TreeNode} node The node to select
34846      * @return {TreeNode} The selected node
34847      */
34848     select : function(node){
34849         var last = this.selNode;
34850         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34851             if(last){
34852                 last.ui.onSelectedChange(false);
34853             }
34854             this.selNode = node;
34855             node.ui.onSelectedChange(true);
34856             this.fireEvent("selectionchange", this, node, last);
34857         }
34858         return node;
34859     },
34860     
34861     /**
34862      * Deselect a node.
34863      * @param {TreeNode} node The node to unselect
34864      */
34865     unselect : function(node){
34866         if(this.selNode == node){
34867             this.clearSelections();
34868         }    
34869     },
34870     
34871     /**
34872      * Clear all selections
34873      */
34874     clearSelections : function(){
34875         var n = this.selNode;
34876         if(n){
34877             n.ui.onSelectedChange(false);
34878             this.selNode = null;
34879             this.fireEvent("selectionchange", this, null);
34880         }
34881         return n;
34882     },
34883     
34884     /**
34885      * Get the selected node
34886      * @return {TreeNode} The selected node
34887      */
34888     getSelectedNode : function(){
34889         return this.selNode;    
34890     },
34891     
34892     /**
34893      * Returns true if the node is selected
34894      * @param {TreeNode} node The node to check
34895      * @return {Boolean}
34896      */
34897     isSelected : function(node){
34898         return this.selNode == node;  
34899     },
34900
34901     /**
34902      * Selects the node above the selected node in the tree, intelligently walking the nodes
34903      * @return TreeNode The new selection
34904      */
34905     selectPrevious : function(){
34906         var s = this.selNode || this.lastSelNode;
34907         if(!s){
34908             return null;
34909         }
34910         var ps = s.previousSibling;
34911         if(ps){
34912             if(!ps.isExpanded() || ps.childNodes.length < 1){
34913                 return this.select(ps);
34914             } else{
34915                 var lc = ps.lastChild;
34916                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34917                     lc = lc.lastChild;
34918                 }
34919                 return this.select(lc);
34920             }
34921         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34922             return this.select(s.parentNode);
34923         }
34924         return null;
34925     },
34926
34927     /**
34928      * Selects the node above the selected node in the tree, intelligently walking the nodes
34929      * @return TreeNode The new selection
34930      */
34931     selectNext : function(){
34932         var s = this.selNode || this.lastSelNode;
34933         if(!s){
34934             return null;
34935         }
34936         if(s.firstChild && s.isExpanded()){
34937              return this.select(s.firstChild);
34938          }else if(s.nextSibling){
34939              return this.select(s.nextSibling);
34940          }else if(s.parentNode){
34941             var newS = null;
34942             s.parentNode.bubble(function(){
34943                 if(this.nextSibling){
34944                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34945                     return false;
34946                 }
34947             });
34948             return newS;
34949          }
34950         return null;
34951     },
34952
34953     onKeyDown : function(e){
34954         var s = this.selNode || this.lastSelNode;
34955         // undesirable, but required
34956         var sm = this;
34957         if(!s){
34958             return;
34959         }
34960         var k = e.getKey();
34961         switch(k){
34962              case e.DOWN:
34963                  e.stopEvent();
34964                  this.selectNext();
34965              break;
34966              case e.UP:
34967                  e.stopEvent();
34968                  this.selectPrevious();
34969              break;
34970              case e.RIGHT:
34971                  e.preventDefault();
34972                  if(s.hasChildNodes()){
34973                      if(!s.isExpanded()){
34974                          s.expand();
34975                      }else if(s.firstChild){
34976                          this.select(s.firstChild, e);
34977                      }
34978                  }
34979              break;
34980              case e.LEFT:
34981                  e.preventDefault();
34982                  if(s.hasChildNodes() && s.isExpanded()){
34983                      s.collapse();
34984                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34985                      this.select(s.parentNode, e);
34986                  }
34987              break;
34988         };
34989     }
34990 });
34991
34992 /**
34993  * @class Roo.tree.MultiSelectionModel
34994  * @extends Roo.util.Observable
34995  * Multi selection for a TreePanel.
34996  * @param {Object} cfg Configuration
34997  */
34998 Roo.tree.MultiSelectionModel = function(){
34999    this.selNodes = [];
35000    this.selMap = {};
35001    this.addEvents({
35002        /**
35003         * @event selectionchange
35004         * Fires when the selected nodes change
35005         * @param {MultiSelectionModel} this
35006         * @param {Array} nodes Array of the selected nodes
35007         */
35008        "selectionchange" : true
35009    });
35010    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
35011    
35012 };
35013
35014 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
35015     init : function(tree){
35016         this.tree = tree;
35017         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35018         tree.on("click", this.onNodeClick, this);
35019     },
35020     
35021     onNodeClick : function(node, e){
35022         this.select(node, e, e.ctrlKey);
35023     },
35024     
35025     /**
35026      * Select a node.
35027      * @param {TreeNode} node The node to select
35028      * @param {EventObject} e (optional) An event associated with the selection
35029      * @param {Boolean} keepExisting True to retain existing selections
35030      * @return {TreeNode} The selected node
35031      */
35032     select : function(node, e, keepExisting){
35033         if(keepExisting !== true){
35034             this.clearSelections(true);
35035         }
35036         if(this.isSelected(node)){
35037             this.lastSelNode = node;
35038             return node;
35039         }
35040         this.selNodes.push(node);
35041         this.selMap[node.id] = node;
35042         this.lastSelNode = node;
35043         node.ui.onSelectedChange(true);
35044         this.fireEvent("selectionchange", this, this.selNodes);
35045         return node;
35046     },
35047     
35048     /**
35049      * Deselect a node.
35050      * @param {TreeNode} node The node to unselect
35051      */
35052     unselect : function(node){
35053         if(this.selMap[node.id]){
35054             node.ui.onSelectedChange(false);
35055             var sn = this.selNodes;
35056             var index = -1;
35057             if(sn.indexOf){
35058                 index = sn.indexOf(node);
35059             }else{
35060                 for(var i = 0, len = sn.length; i < len; i++){
35061                     if(sn[i] == node){
35062                         index = i;
35063                         break;
35064                     }
35065                 }
35066             }
35067             if(index != -1){
35068                 this.selNodes.splice(index, 1);
35069             }
35070             delete this.selMap[node.id];
35071             this.fireEvent("selectionchange", this, this.selNodes);
35072         }
35073     },
35074     
35075     /**
35076      * Clear all selections
35077      */
35078     clearSelections : function(suppressEvent){
35079         var sn = this.selNodes;
35080         if(sn.length > 0){
35081             for(var i = 0, len = sn.length; i < len; i++){
35082                 sn[i].ui.onSelectedChange(false);
35083             }
35084             this.selNodes = [];
35085             this.selMap = {};
35086             if(suppressEvent !== true){
35087                 this.fireEvent("selectionchange", this, this.selNodes);
35088             }
35089         }
35090     },
35091     
35092     /**
35093      * Returns true if the node is selected
35094      * @param {TreeNode} node The node to check
35095      * @return {Boolean}
35096      */
35097     isSelected : function(node){
35098         return this.selMap[node.id] ? true : false;  
35099     },
35100     
35101     /**
35102      * Returns an array of the selected nodes
35103      * @return {Array}
35104      */
35105     getSelectedNodes : function(){
35106         return this.selNodes;    
35107     },
35108
35109     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
35110
35111     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
35112
35113     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
35114 });/*
35115  * Based on:
35116  * Ext JS Library 1.1.1
35117  * Copyright(c) 2006-2007, Ext JS, LLC.
35118  *
35119  * Originally Released Under LGPL - original licence link has changed is not relivant.
35120  *
35121  * Fork - LGPL
35122  * <script type="text/javascript">
35123  */
35124  
35125 /**
35126  * @class Roo.tree.TreeNode
35127  * @extends Roo.data.Node
35128  * @cfg {String} text The text for this node
35129  * @cfg {Boolean} expanded true to start the node expanded
35130  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
35131  * @cfg {Boolean} allowDrop false if this node cannot be drop on
35132  * @cfg {Boolean} disabled true to start the node disabled
35133  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
35134  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
35135  * @cfg {String} cls A css class to be added to the node
35136  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
35137  * @cfg {String} href URL of the link used for the node (defaults to #)
35138  * @cfg {String} hrefTarget target frame for the link
35139  * @cfg {String} qtip An Ext QuickTip for the node
35140  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
35141  * @cfg {Boolean} singleClickExpand True for single click expand on this node
35142  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
35143  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
35144  * (defaults to undefined with no checkbox rendered)
35145  * @constructor
35146  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
35147  */
35148 Roo.tree.TreeNode = function(attributes){
35149     attributes = attributes || {};
35150     if(typeof attributes == "string"){
35151         attributes = {text: attributes};
35152     }
35153     this.childrenRendered = false;
35154     this.rendered = false;
35155     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
35156     this.expanded = attributes.expanded === true;
35157     this.isTarget = attributes.isTarget !== false;
35158     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
35159     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
35160
35161     /**
35162      * Read-only. The text for this node. To change it use setText().
35163      * @type String
35164      */
35165     this.text = attributes.text;
35166     /**
35167      * True if this node is disabled.
35168      * @type Boolean
35169      */
35170     this.disabled = attributes.disabled === true;
35171
35172     this.addEvents({
35173         /**
35174         * @event textchange
35175         * Fires when the text for this node is changed
35176         * @param {Node} this This node
35177         * @param {String} text The new text
35178         * @param {String} oldText The old text
35179         */
35180         "textchange" : true,
35181         /**
35182         * @event beforeexpand
35183         * Fires before this node is expanded, return false to cancel.
35184         * @param {Node} this This node
35185         * @param {Boolean} deep
35186         * @param {Boolean} anim
35187         */
35188         "beforeexpand" : true,
35189         /**
35190         * @event beforecollapse
35191         * Fires before this node is collapsed, return false to cancel.
35192         * @param {Node} this This node
35193         * @param {Boolean} deep
35194         * @param {Boolean} anim
35195         */
35196         "beforecollapse" : true,
35197         /**
35198         * @event expand
35199         * Fires when this node is expanded
35200         * @param {Node} this This node
35201         */
35202         "expand" : true,
35203         /**
35204         * @event disabledchange
35205         * Fires when the disabled status of this node changes
35206         * @param {Node} this This node
35207         * @param {Boolean} disabled
35208         */
35209         "disabledchange" : true,
35210         /**
35211         * @event collapse
35212         * Fires when this node is collapsed
35213         * @param {Node} this This node
35214         */
35215         "collapse" : true,
35216         /**
35217         * @event beforeclick
35218         * Fires before click processing. Return false to cancel the default action.
35219         * @param {Node} this This node
35220         * @param {Roo.EventObject} e The event object
35221         */
35222         "beforeclick":true,
35223         /**
35224         * @event checkchange
35225         * Fires when a node with a checkbox's checked property changes
35226         * @param {Node} this This node
35227         * @param {Boolean} checked
35228         */
35229         "checkchange":true,
35230         /**
35231         * @event click
35232         * Fires when this node is clicked
35233         * @param {Node} this This node
35234         * @param {Roo.EventObject} e The event object
35235         */
35236         "click":true,
35237         /**
35238         * @event dblclick
35239         * Fires when this node is double clicked
35240         * @param {Node} this This node
35241         * @param {Roo.EventObject} e The event object
35242         */
35243         "dblclick":true,
35244         /**
35245         * @event contextmenu
35246         * Fires when this node is right clicked
35247         * @param {Node} this This node
35248         * @param {Roo.EventObject} e The event object
35249         */
35250         "contextmenu":true,
35251         /**
35252         * @event beforechildrenrendered
35253         * Fires right before the child nodes for this node are rendered
35254         * @param {Node} this This node
35255         */
35256         "beforechildrenrendered":true
35257     });
35258
35259     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
35260
35261     /**
35262      * Read-only. The UI for this node
35263      * @type TreeNodeUI
35264      */
35265     this.ui = new uiClass(this);
35266     
35267     // finally support items[]
35268     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
35269         return;
35270     }
35271     
35272     
35273     Roo.each(this.attributes.items, function(c) {
35274         this.appendChild(Roo.factory(c,Roo.Tree));
35275     }, this);
35276     delete this.attributes.items;
35277     
35278     
35279     
35280 };
35281 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
35282     preventHScroll: true,
35283     /**
35284      * Returns true if this node is expanded
35285      * @return {Boolean}
35286      */
35287     isExpanded : function(){
35288         return this.expanded;
35289     },
35290
35291     /**
35292      * Returns the UI object for this node
35293      * @return {TreeNodeUI}
35294      */
35295     getUI : function(){
35296         return this.ui;
35297     },
35298
35299     // private override
35300     setFirstChild : function(node){
35301         var of = this.firstChild;
35302         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
35303         if(this.childrenRendered && of && node != of){
35304             of.renderIndent(true, true);
35305         }
35306         if(this.rendered){
35307             this.renderIndent(true, true);
35308         }
35309     },
35310
35311     // private override
35312     setLastChild : function(node){
35313         var ol = this.lastChild;
35314         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
35315         if(this.childrenRendered && ol && node != ol){
35316             ol.renderIndent(true, true);
35317         }
35318         if(this.rendered){
35319             this.renderIndent(true, true);
35320         }
35321     },
35322
35323     // these methods are overridden to provide lazy rendering support
35324     // private override
35325     appendChild : function()
35326     {
35327         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35328         if(node && this.childrenRendered){
35329             node.render();
35330         }
35331         this.ui.updateExpandIcon();
35332         return node;
35333     },
35334
35335     // private override
35336     removeChild : function(node){
35337         this.ownerTree.getSelectionModel().unselect(node);
35338         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35339         // if it's been rendered remove dom node
35340         if(this.childrenRendered){
35341             node.ui.remove();
35342         }
35343         if(this.childNodes.length < 1){
35344             this.collapse(false, false);
35345         }else{
35346             this.ui.updateExpandIcon();
35347         }
35348         if(!this.firstChild) {
35349             this.childrenRendered = false;
35350         }
35351         return node;
35352     },
35353
35354     // private override
35355     insertBefore : function(node, refNode){
35356         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35357         if(newNode && refNode && this.childrenRendered){
35358             node.render();
35359         }
35360         this.ui.updateExpandIcon();
35361         return newNode;
35362     },
35363
35364     /**
35365      * Sets the text for this node
35366      * @param {String} text
35367      */
35368     setText : function(text){
35369         var oldText = this.text;
35370         this.text = text;
35371         this.attributes.text = text;
35372         if(this.rendered){ // event without subscribing
35373             this.ui.onTextChange(this, text, oldText);
35374         }
35375         this.fireEvent("textchange", this, text, oldText);
35376     },
35377
35378     /**
35379      * Triggers selection of this node
35380      */
35381     select : function(){
35382         this.getOwnerTree().getSelectionModel().select(this);
35383     },
35384
35385     /**
35386      * Triggers deselection of this node
35387      */
35388     unselect : function(){
35389         this.getOwnerTree().getSelectionModel().unselect(this);
35390     },
35391
35392     /**
35393      * Returns true if this node is selected
35394      * @return {Boolean}
35395      */
35396     isSelected : function(){
35397         return this.getOwnerTree().getSelectionModel().isSelected(this);
35398     },
35399
35400     /**
35401      * Expand this node.
35402      * @param {Boolean} deep (optional) True to expand all children as well
35403      * @param {Boolean} anim (optional) false to cancel the default animation
35404      * @param {Function} callback (optional) A callback to be called when
35405      * expanding this node completes (does not wait for deep expand to complete).
35406      * Called with 1 parameter, this node.
35407      */
35408     expand : function(deep, anim, callback){
35409         if(!this.expanded){
35410             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35411                 return;
35412             }
35413             if(!this.childrenRendered){
35414                 this.renderChildren();
35415             }
35416             this.expanded = true;
35417             
35418             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
35419                 this.ui.animExpand(function(){
35420                     this.fireEvent("expand", this);
35421                     if(typeof callback == "function"){
35422                         callback(this);
35423                     }
35424                     if(deep === true){
35425                         this.expandChildNodes(true);
35426                     }
35427                 }.createDelegate(this));
35428                 return;
35429             }else{
35430                 this.ui.expand();
35431                 this.fireEvent("expand", this);
35432                 if(typeof callback == "function"){
35433                     callback(this);
35434                 }
35435             }
35436         }else{
35437            if(typeof callback == "function"){
35438                callback(this);
35439            }
35440         }
35441         if(deep === true){
35442             this.expandChildNodes(true);
35443         }
35444     },
35445
35446     isHiddenRoot : function(){
35447         return this.isRoot && !this.getOwnerTree().rootVisible;
35448     },
35449
35450     /**
35451      * Collapse this node.
35452      * @param {Boolean} deep (optional) True to collapse all children as well
35453      * @param {Boolean} anim (optional) false to cancel the default animation
35454      */
35455     collapse : function(deep, anim){
35456         if(this.expanded && !this.isHiddenRoot()){
35457             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35458                 return;
35459             }
35460             this.expanded = false;
35461             if((this.getOwnerTree().animate && anim !== false) || anim){
35462                 this.ui.animCollapse(function(){
35463                     this.fireEvent("collapse", this);
35464                     if(deep === true){
35465                         this.collapseChildNodes(true);
35466                     }
35467                 }.createDelegate(this));
35468                 return;
35469             }else{
35470                 this.ui.collapse();
35471                 this.fireEvent("collapse", this);
35472             }
35473         }
35474         if(deep === true){
35475             var cs = this.childNodes;
35476             for(var i = 0, len = cs.length; i < len; i++) {
35477                 cs[i].collapse(true, false);
35478             }
35479         }
35480     },
35481
35482     // private
35483     delayedExpand : function(delay){
35484         if(!this.expandProcId){
35485             this.expandProcId = this.expand.defer(delay, this);
35486         }
35487     },
35488
35489     // private
35490     cancelExpand : function(){
35491         if(this.expandProcId){
35492             clearTimeout(this.expandProcId);
35493         }
35494         this.expandProcId = false;
35495     },
35496
35497     /**
35498      * Toggles expanded/collapsed state of the node
35499      */
35500     toggle : function(){
35501         if(this.expanded){
35502             this.collapse();
35503         }else{
35504             this.expand();
35505         }
35506     },
35507
35508     /**
35509      * Ensures all parent nodes are expanded
35510      */
35511     ensureVisible : function(callback){
35512         var tree = this.getOwnerTree();
35513         tree.expandPath(this.parentNode.getPath(), false, function(){
35514             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35515             Roo.callback(callback);
35516         }.createDelegate(this));
35517     },
35518
35519     /**
35520      * Expand all child nodes
35521      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35522      */
35523     expandChildNodes : function(deep){
35524         var cs = this.childNodes;
35525         for(var i = 0, len = cs.length; i < len; i++) {
35526                 cs[i].expand(deep);
35527         }
35528     },
35529
35530     /**
35531      * Collapse all child nodes
35532      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35533      */
35534     collapseChildNodes : function(deep){
35535         var cs = this.childNodes;
35536         for(var i = 0, len = cs.length; i < len; i++) {
35537                 cs[i].collapse(deep);
35538         }
35539     },
35540
35541     /**
35542      * Disables this node
35543      */
35544     disable : function(){
35545         this.disabled = true;
35546         this.unselect();
35547         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35548             this.ui.onDisableChange(this, true);
35549         }
35550         this.fireEvent("disabledchange", this, true);
35551     },
35552
35553     /**
35554      * Enables this node
35555      */
35556     enable : function(){
35557         this.disabled = false;
35558         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35559             this.ui.onDisableChange(this, false);
35560         }
35561         this.fireEvent("disabledchange", this, false);
35562     },
35563
35564     // private
35565     renderChildren : function(suppressEvent){
35566         if(suppressEvent !== false){
35567             this.fireEvent("beforechildrenrendered", this);
35568         }
35569         var cs = this.childNodes;
35570         for(var i = 0, len = cs.length; i < len; i++){
35571             cs[i].render(true);
35572         }
35573         this.childrenRendered = true;
35574     },
35575
35576     // private
35577     sort : function(fn, scope){
35578         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35579         if(this.childrenRendered){
35580             var cs = this.childNodes;
35581             for(var i = 0, len = cs.length; i < len; i++){
35582                 cs[i].render(true);
35583             }
35584         }
35585     },
35586
35587     // private
35588     render : function(bulkRender){
35589         this.ui.render(bulkRender);
35590         if(!this.rendered){
35591             this.rendered = true;
35592             if(this.expanded){
35593                 this.expanded = false;
35594                 this.expand(false, false);
35595             }
35596         }
35597     },
35598
35599     // private
35600     renderIndent : function(deep, refresh){
35601         if(refresh){
35602             this.ui.childIndent = null;
35603         }
35604         this.ui.renderIndent();
35605         if(deep === true && this.childrenRendered){
35606             var cs = this.childNodes;
35607             for(var i = 0, len = cs.length; i < len; i++){
35608                 cs[i].renderIndent(true, refresh);
35609             }
35610         }
35611     }
35612 });/*
35613  * Based on:
35614  * Ext JS Library 1.1.1
35615  * Copyright(c) 2006-2007, Ext JS, LLC.
35616  *
35617  * Originally Released Under LGPL - original licence link has changed is not relivant.
35618  *
35619  * Fork - LGPL
35620  * <script type="text/javascript">
35621  */
35622  
35623 /**
35624  * @class Roo.tree.AsyncTreeNode
35625  * @extends Roo.tree.TreeNode
35626  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35627  * @constructor
35628  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35629  */
35630  Roo.tree.AsyncTreeNode = function(config){
35631     this.loaded = false;
35632     this.loading = false;
35633     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35634     /**
35635     * @event beforeload
35636     * Fires before this node is loaded, return false to cancel
35637     * @param {Node} this This node
35638     */
35639     this.addEvents({'beforeload':true, 'load': true});
35640     /**
35641     * @event load
35642     * Fires when this node is loaded
35643     * @param {Node} this This node
35644     */
35645     /**
35646      * The loader used by this node (defaults to using the tree's defined loader)
35647      * @type TreeLoader
35648      * @property loader
35649      */
35650 };
35651 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35652     expand : function(deep, anim, callback){
35653         if(this.loading){ // if an async load is already running, waiting til it's done
35654             var timer;
35655             var f = function(){
35656                 if(!this.loading){ // done loading
35657                     clearInterval(timer);
35658                     this.expand(deep, anim, callback);
35659                 }
35660             }.createDelegate(this);
35661             timer = setInterval(f, 200);
35662             return;
35663         }
35664         if(!this.loaded){
35665             if(this.fireEvent("beforeload", this) === false){
35666                 return;
35667             }
35668             this.loading = true;
35669             this.ui.beforeLoad(this);
35670             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35671             if(loader){
35672                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35673                 return;
35674             }
35675         }
35676         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35677     },
35678     
35679     /**
35680      * Returns true if this node is currently loading
35681      * @return {Boolean}
35682      */
35683     isLoading : function(){
35684         return this.loading;  
35685     },
35686     
35687     loadComplete : function(deep, anim, callback){
35688         this.loading = false;
35689         this.loaded = true;
35690         this.ui.afterLoad(this);
35691         this.fireEvent("load", this);
35692         this.expand(deep, anim, callback);
35693     },
35694     
35695     /**
35696      * Returns true if this node has been loaded
35697      * @return {Boolean}
35698      */
35699     isLoaded : function(){
35700         return this.loaded;
35701     },
35702     
35703     hasChildNodes : function(){
35704         if(!this.isLeaf() && !this.loaded){
35705             return true;
35706         }else{
35707             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35708         }
35709     },
35710
35711     /**
35712      * Trigger a reload for this node
35713      * @param {Function} callback
35714      */
35715     reload : function(callback){
35716         this.collapse(false, false);
35717         while(this.firstChild){
35718             this.removeChild(this.firstChild);
35719         }
35720         this.childrenRendered = false;
35721         this.loaded = false;
35722         if(this.isHiddenRoot()){
35723             this.expanded = false;
35724         }
35725         this.expand(false, false, callback);
35726     }
35727 });/*
35728  * Based on:
35729  * Ext JS Library 1.1.1
35730  * Copyright(c) 2006-2007, Ext JS, LLC.
35731  *
35732  * Originally Released Under LGPL - original licence link has changed is not relivant.
35733  *
35734  * Fork - LGPL
35735  * <script type="text/javascript">
35736  */
35737  
35738 /**
35739  * @class Roo.tree.TreeNodeUI
35740  * @constructor
35741  * @param {Object} node The node to render
35742  * The TreeNode UI implementation is separate from the
35743  * tree implementation. Unless you are customizing the tree UI,
35744  * you should never have to use this directly.
35745  */
35746 Roo.tree.TreeNodeUI = function(node){
35747     this.node = node;
35748     this.rendered = false;
35749     this.animating = false;
35750     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35751 };
35752
35753 Roo.tree.TreeNodeUI.prototype = {
35754     removeChild : function(node){
35755         if(this.rendered){
35756             this.ctNode.removeChild(node.ui.getEl());
35757         }
35758     },
35759
35760     beforeLoad : function(){
35761          this.addClass("x-tree-node-loading");
35762     },
35763
35764     afterLoad : function(){
35765          this.removeClass("x-tree-node-loading");
35766     },
35767
35768     onTextChange : function(node, text, oldText){
35769         if(this.rendered){
35770             this.textNode.innerHTML = text;
35771         }
35772     },
35773
35774     onDisableChange : function(node, state){
35775         this.disabled = state;
35776         if(state){
35777             this.addClass("x-tree-node-disabled");
35778         }else{
35779             this.removeClass("x-tree-node-disabled");
35780         }
35781     },
35782
35783     onSelectedChange : function(state){
35784         if(state){
35785             this.focus();
35786             this.addClass("x-tree-selected");
35787         }else{
35788             //this.blur();
35789             this.removeClass("x-tree-selected");
35790         }
35791     },
35792
35793     onMove : function(tree, node, oldParent, newParent, index, refNode){
35794         this.childIndent = null;
35795         if(this.rendered){
35796             var targetNode = newParent.ui.getContainer();
35797             if(!targetNode){//target not rendered
35798                 this.holder = document.createElement("div");
35799                 this.holder.appendChild(this.wrap);
35800                 return;
35801             }
35802             var insertBefore = refNode ? refNode.ui.getEl() : null;
35803             if(insertBefore){
35804                 targetNode.insertBefore(this.wrap, insertBefore);
35805             }else{
35806                 targetNode.appendChild(this.wrap);
35807             }
35808             this.node.renderIndent(true);
35809         }
35810     },
35811
35812     addClass : function(cls){
35813         if(this.elNode){
35814             Roo.fly(this.elNode).addClass(cls);
35815         }
35816     },
35817
35818     removeClass : function(cls){
35819         if(this.elNode){
35820             Roo.fly(this.elNode).removeClass(cls);
35821         }
35822     },
35823
35824     remove : function(){
35825         if(this.rendered){
35826             this.holder = document.createElement("div");
35827             this.holder.appendChild(this.wrap);
35828         }
35829     },
35830
35831     fireEvent : function(){
35832         return this.node.fireEvent.apply(this.node, arguments);
35833     },
35834
35835     initEvents : function(){
35836         this.node.on("move", this.onMove, this);
35837         var E = Roo.EventManager;
35838         var a = this.anchor;
35839
35840         var el = Roo.fly(a, '_treeui');
35841
35842         if(Roo.isOpera){ // opera render bug ignores the CSS
35843             el.setStyle("text-decoration", "none");
35844         }
35845
35846         el.on("click", this.onClick, this);
35847         el.on("dblclick", this.onDblClick, this);
35848
35849         if(this.checkbox){
35850             Roo.EventManager.on(this.checkbox,
35851                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35852         }
35853
35854         el.on("contextmenu", this.onContextMenu, this);
35855
35856         var icon = Roo.fly(this.iconNode);
35857         icon.on("click", this.onClick, this);
35858         icon.on("dblclick", this.onDblClick, this);
35859         icon.on("contextmenu", this.onContextMenu, this);
35860         E.on(this.ecNode, "click", this.ecClick, this, true);
35861
35862         if(this.node.disabled){
35863             this.addClass("x-tree-node-disabled");
35864         }
35865         if(this.node.hidden){
35866             this.addClass("x-tree-node-disabled");
35867         }
35868         var ot = this.node.getOwnerTree();
35869         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
35870         if(dd && (!this.node.isRoot || ot.rootVisible)){
35871             Roo.dd.Registry.register(this.elNode, {
35872                 node: this.node,
35873                 handles: this.getDDHandles(),
35874                 isHandle: false
35875             });
35876         }
35877     },
35878
35879     getDDHandles : function(){
35880         return [this.iconNode, this.textNode];
35881     },
35882
35883     hide : function(){
35884         if(this.rendered){
35885             this.wrap.style.display = "none";
35886         }
35887     },
35888
35889     show : function(){
35890         if(this.rendered){
35891             this.wrap.style.display = "";
35892         }
35893     },
35894
35895     onContextMenu : function(e){
35896         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35897             e.preventDefault();
35898             this.focus();
35899             this.fireEvent("contextmenu", this.node, e);
35900         }
35901     },
35902
35903     onClick : function(e){
35904         if(this.dropping){
35905             e.stopEvent();
35906             return;
35907         }
35908         if(this.fireEvent("beforeclick", this.node, e) !== false){
35909             if(!this.disabled && this.node.attributes.href){
35910                 this.fireEvent("click", this.node, e);
35911                 return;
35912             }
35913             e.preventDefault();
35914             if(this.disabled){
35915                 return;
35916             }
35917
35918             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35919                 this.node.toggle();
35920             }
35921
35922             this.fireEvent("click", this.node, e);
35923         }else{
35924             e.stopEvent();
35925         }
35926     },
35927
35928     onDblClick : function(e){
35929         e.preventDefault();
35930         if(this.disabled){
35931             return;
35932         }
35933         if(this.checkbox){
35934             this.toggleCheck();
35935         }
35936         if(!this.animating && this.node.hasChildNodes()){
35937             this.node.toggle();
35938         }
35939         this.fireEvent("dblclick", this.node, e);
35940     },
35941
35942     onCheckChange : function(){
35943         var checked = this.checkbox.checked;
35944         this.node.attributes.checked = checked;
35945         this.fireEvent('checkchange', this.node, checked);
35946     },
35947
35948     ecClick : function(e){
35949         if(!this.animating && this.node.hasChildNodes()){
35950             this.node.toggle();
35951         }
35952     },
35953
35954     startDrop : function(){
35955         this.dropping = true;
35956     },
35957
35958     // delayed drop so the click event doesn't get fired on a drop
35959     endDrop : function(){
35960        setTimeout(function(){
35961            this.dropping = false;
35962        }.createDelegate(this), 50);
35963     },
35964
35965     expand : function(){
35966         this.updateExpandIcon();
35967         this.ctNode.style.display = "";
35968     },
35969
35970     focus : function(){
35971         if(!this.node.preventHScroll){
35972             try{this.anchor.focus();
35973             }catch(e){}
35974         }else if(!Roo.isIE){
35975             try{
35976                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35977                 var l = noscroll.scrollLeft;
35978                 this.anchor.focus();
35979                 noscroll.scrollLeft = l;
35980             }catch(e){}
35981         }
35982     },
35983
35984     toggleCheck : function(value){
35985         var cb = this.checkbox;
35986         if(cb){
35987             cb.checked = (value === undefined ? !cb.checked : value);
35988         }
35989     },
35990
35991     blur : function(){
35992         try{
35993             this.anchor.blur();
35994         }catch(e){}
35995     },
35996
35997     animExpand : function(callback){
35998         var ct = Roo.get(this.ctNode);
35999         ct.stopFx();
36000         if(!this.node.hasChildNodes()){
36001             this.updateExpandIcon();
36002             this.ctNode.style.display = "";
36003             Roo.callback(callback);
36004             return;
36005         }
36006         this.animating = true;
36007         this.updateExpandIcon();
36008
36009         ct.slideIn('t', {
36010            callback : function(){
36011                this.animating = false;
36012                Roo.callback(callback);
36013             },
36014             scope: this,
36015             duration: this.node.ownerTree.duration || .25
36016         });
36017     },
36018
36019     highlight : function(){
36020         var tree = this.node.getOwnerTree();
36021         Roo.fly(this.wrap).highlight(
36022             tree.hlColor || "C3DAF9",
36023             {endColor: tree.hlBaseColor}
36024         );
36025     },
36026
36027     collapse : function(){
36028         this.updateExpandIcon();
36029         this.ctNode.style.display = "none";
36030     },
36031
36032     animCollapse : function(callback){
36033         var ct = Roo.get(this.ctNode);
36034         ct.enableDisplayMode('block');
36035         ct.stopFx();
36036
36037         this.animating = true;
36038         this.updateExpandIcon();
36039
36040         ct.slideOut('t', {
36041             callback : function(){
36042                this.animating = false;
36043                Roo.callback(callback);
36044             },
36045             scope: this,
36046             duration: this.node.ownerTree.duration || .25
36047         });
36048     },
36049
36050     getContainer : function(){
36051         return this.ctNode;
36052     },
36053
36054     getEl : function(){
36055         return this.wrap;
36056     },
36057
36058     appendDDGhost : function(ghostNode){
36059         ghostNode.appendChild(this.elNode.cloneNode(true));
36060     },
36061
36062     getDDRepairXY : function(){
36063         return Roo.lib.Dom.getXY(this.iconNode);
36064     },
36065
36066     onRender : function(){
36067         this.render();
36068     },
36069
36070     render : function(bulkRender){
36071         var n = this.node, a = n.attributes;
36072         var targetNode = n.parentNode ?
36073               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
36074
36075         if(!this.rendered){
36076             this.rendered = true;
36077
36078             this.renderElements(n, a, targetNode, bulkRender);
36079
36080             if(a.qtip){
36081                if(this.textNode.setAttributeNS){
36082                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
36083                    if(a.qtipTitle){
36084                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
36085                    }
36086                }else{
36087                    this.textNode.setAttribute("ext:qtip", a.qtip);
36088                    if(a.qtipTitle){
36089                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
36090                    }
36091                }
36092             }else if(a.qtipCfg){
36093                 a.qtipCfg.target = Roo.id(this.textNode);
36094                 Roo.QuickTips.register(a.qtipCfg);
36095             }
36096             this.initEvents();
36097             if(!this.node.expanded){
36098                 this.updateExpandIcon();
36099             }
36100         }else{
36101             if(bulkRender === true) {
36102                 targetNode.appendChild(this.wrap);
36103             }
36104         }
36105     },
36106
36107     renderElements : function(n, a, targetNode, bulkRender)
36108     {
36109         // add some indent caching, this helps performance when rendering a large tree
36110         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36111         var t = n.getOwnerTree();
36112         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
36113         if (typeof(n.attributes.html) != 'undefined') {
36114             txt = n.attributes.html;
36115         }
36116         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
36117         var cb = typeof a.checked == 'boolean';
36118         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36119         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
36120             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
36121             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
36122             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
36123             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
36124             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
36125              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
36126                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
36127             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36128             "</li>"];
36129
36130         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36131             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36132                                 n.nextSibling.ui.getEl(), buf.join(""));
36133         }else{
36134             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36135         }
36136
36137         this.elNode = this.wrap.childNodes[0];
36138         this.ctNode = this.wrap.childNodes[1];
36139         var cs = this.elNode.childNodes;
36140         this.indentNode = cs[0];
36141         this.ecNode = cs[1];
36142         this.iconNode = cs[2];
36143         var index = 3;
36144         if(cb){
36145             this.checkbox = cs[3];
36146             index++;
36147         }
36148         this.anchor = cs[index];
36149         this.textNode = cs[index].firstChild;
36150     },
36151
36152     getAnchor : function(){
36153         return this.anchor;
36154     },
36155
36156     getTextEl : function(){
36157         return this.textNode;
36158     },
36159
36160     getIconEl : function(){
36161         return this.iconNode;
36162     },
36163
36164     isChecked : function(){
36165         return this.checkbox ? this.checkbox.checked : false;
36166     },
36167
36168     updateExpandIcon : function(){
36169         if(this.rendered){
36170             var n = this.node, c1, c2;
36171             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
36172             var hasChild = n.hasChildNodes();
36173             if(hasChild){
36174                 if(n.expanded){
36175                     cls += "-minus";
36176                     c1 = "x-tree-node-collapsed";
36177                     c2 = "x-tree-node-expanded";
36178                 }else{
36179                     cls += "-plus";
36180                     c1 = "x-tree-node-expanded";
36181                     c2 = "x-tree-node-collapsed";
36182                 }
36183                 if(this.wasLeaf){
36184                     this.removeClass("x-tree-node-leaf");
36185                     this.wasLeaf = false;
36186                 }
36187                 if(this.c1 != c1 || this.c2 != c2){
36188                     Roo.fly(this.elNode).replaceClass(c1, c2);
36189                     this.c1 = c1; this.c2 = c2;
36190                 }
36191             }else{
36192                 // this changes non-leafs into leafs if they have no children.
36193                 // it's not very rational behaviour..
36194                 
36195                 if(!this.wasLeaf && this.node.leaf){
36196                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
36197                     delete this.c1;
36198                     delete this.c2;
36199                     this.wasLeaf = true;
36200                 }
36201             }
36202             var ecc = "x-tree-ec-icon "+cls;
36203             if(this.ecc != ecc){
36204                 this.ecNode.className = ecc;
36205                 this.ecc = ecc;
36206             }
36207         }
36208     },
36209
36210     getChildIndent : function(){
36211         if(!this.childIndent){
36212             var buf = [];
36213             var p = this.node;
36214             while(p){
36215                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
36216                     if(!p.isLast()) {
36217                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
36218                     } else {
36219                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
36220                     }
36221                 }
36222                 p = p.parentNode;
36223             }
36224             this.childIndent = buf.join("");
36225         }
36226         return this.childIndent;
36227     },
36228
36229     renderIndent : function(){
36230         if(this.rendered){
36231             var indent = "";
36232             var p = this.node.parentNode;
36233             if(p){
36234                 indent = p.ui.getChildIndent();
36235             }
36236             if(this.indentMarkup != indent){ // don't rerender if not required
36237                 this.indentNode.innerHTML = indent;
36238                 this.indentMarkup = indent;
36239             }
36240             this.updateExpandIcon();
36241         }
36242     }
36243 };
36244
36245 Roo.tree.RootTreeNodeUI = function(){
36246     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
36247 };
36248 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
36249     render : function(){
36250         if(!this.rendered){
36251             var targetNode = this.node.ownerTree.innerCt.dom;
36252             this.node.expanded = true;
36253             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
36254             this.wrap = this.ctNode = targetNode.firstChild;
36255         }
36256     },
36257     collapse : function(){
36258     },
36259     expand : function(){
36260     }
36261 });/*
36262  * Based on:
36263  * Ext JS Library 1.1.1
36264  * Copyright(c) 2006-2007, Ext JS, LLC.
36265  *
36266  * Originally Released Under LGPL - original licence link has changed is not relivant.
36267  *
36268  * Fork - LGPL
36269  * <script type="text/javascript">
36270  */
36271 /**
36272  * @class Roo.tree.TreeLoader
36273  * @extends Roo.util.Observable
36274  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
36275  * nodes from a specified URL. The response must be a javascript Array definition
36276  * who's elements are node definition objects. eg:
36277  * <pre><code>
36278 {  success : true,
36279    data :      [
36280    
36281     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
36282     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
36283     ]
36284 }
36285
36286
36287 </code></pre>
36288  * <br><br>
36289  * The old style respose with just an array is still supported, but not recommended.
36290  * <br><br>
36291  *
36292  * A server request is sent, and child nodes are loaded only when a node is expanded.
36293  * The loading node's id is passed to the server under the parameter name "node" to
36294  * enable the server to produce the correct child nodes.
36295  * <br><br>
36296  * To pass extra parameters, an event handler may be attached to the "beforeload"
36297  * event, and the parameters specified in the TreeLoader's baseParams property:
36298  * <pre><code>
36299     myTreeLoader.on("beforeload", function(treeLoader, node) {
36300         this.baseParams.category = node.attributes.category;
36301     }, this);
36302     
36303 </code></pre>
36304  *
36305  * This would pass an HTTP parameter called "category" to the server containing
36306  * the value of the Node's "category" attribute.
36307  * @constructor
36308  * Creates a new Treeloader.
36309  * @param {Object} config A config object containing config properties.
36310  */
36311 Roo.tree.TreeLoader = function(config){
36312     this.baseParams = {};
36313     this.requestMethod = "POST";
36314     Roo.apply(this, config);
36315
36316     this.addEvents({
36317     
36318         /**
36319          * @event beforeload
36320          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
36321          * @param {Object} This TreeLoader object.
36322          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36323          * @param {Object} callback The callback function specified in the {@link #load} call.
36324          */
36325         beforeload : true,
36326         /**
36327          * @event load
36328          * Fires when the node has been successfuly loaded.
36329          * @param {Object} This TreeLoader object.
36330          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36331          * @param {Object} response The response object containing the data from the server.
36332          */
36333         load : true,
36334         /**
36335          * @event loadexception
36336          * Fires if the network request failed.
36337          * @param {Object} This TreeLoader object.
36338          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36339          * @param {Object} response The response object containing the data from the server.
36340          */
36341         loadexception : true,
36342         /**
36343          * @event create
36344          * Fires before a node is created, enabling you to return custom Node types 
36345          * @param {Object} This TreeLoader object.
36346          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36347          */
36348         create : true
36349     });
36350
36351     Roo.tree.TreeLoader.superclass.constructor.call(this);
36352 };
36353
36354 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36355     /**
36356     * @cfg {String} dataUrl The URL from which to request a Json string which
36357     * specifies an array of node definition object representing the child nodes
36358     * to be loaded.
36359     */
36360     /**
36361     * @cfg {String} requestMethod either GET or POST
36362     * defaults to POST (due to BC)
36363     * to be loaded.
36364     */
36365     /**
36366     * @cfg {Object} baseParams (optional) An object containing properties which
36367     * specify HTTP parameters to be passed to each request for child nodes.
36368     */
36369     /**
36370     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36371     * created by this loader. If the attributes sent by the server have an attribute in this object,
36372     * they take priority.
36373     */
36374     /**
36375     * @cfg {Object} uiProviders (optional) An object containing properties which
36376     * 
36377     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36378     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36379     * <i>uiProvider</i> attribute of a returned child node is a string rather
36380     * than a reference to a TreeNodeUI implementation, this that string value
36381     * is used as a property name in the uiProviders object. You can define the provider named
36382     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36383     */
36384     uiProviders : {},
36385
36386     /**
36387     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36388     * child nodes before loading.
36389     */
36390     clearOnLoad : true,
36391
36392     /**
36393     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36394     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36395     * Grid query { data : [ .....] }
36396     */
36397     
36398     root : false,
36399      /**
36400     * @cfg {String} queryParam (optional) 
36401     * Name of the query as it will be passed on the querystring (defaults to 'node')
36402     * eg. the request will be ?node=[id]
36403     */
36404     
36405     
36406     queryParam: false,
36407     
36408     /**
36409      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36410      * This is called automatically when a node is expanded, but may be used to reload
36411      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36412      * @param {Roo.tree.TreeNode} node
36413      * @param {Function} callback
36414      */
36415     load : function(node, callback){
36416         if(this.clearOnLoad){
36417             while(node.firstChild){
36418                 node.removeChild(node.firstChild);
36419             }
36420         }
36421         if(node.attributes.children){ // preloaded json children
36422             var cs = node.attributes.children;
36423             for(var i = 0, len = cs.length; i < len; i++){
36424                 node.appendChild(this.createNode(cs[i]));
36425             }
36426             if(typeof callback == "function"){
36427                 callback();
36428             }
36429         }else if(this.dataUrl){
36430             this.requestData(node, callback);
36431         }
36432     },
36433
36434     getParams: function(node){
36435         var buf = [], bp = this.baseParams;
36436         for(var key in bp){
36437             if(typeof bp[key] != "function"){
36438                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36439             }
36440         }
36441         var n = this.queryParam === false ? 'node' : this.queryParam;
36442         buf.push(n + "=", encodeURIComponent(node.id));
36443         return buf.join("");
36444     },
36445
36446     requestData : function(node, callback){
36447         if(this.fireEvent("beforeload", this, node, callback) !== false){
36448             this.transId = Roo.Ajax.request({
36449                 method:this.requestMethod,
36450                 url: this.dataUrl||this.url,
36451                 success: this.handleResponse,
36452                 failure: this.handleFailure,
36453                 scope: this,
36454                 argument: {callback: callback, node: node},
36455                 params: this.getParams(node)
36456             });
36457         }else{
36458             // if the load is cancelled, make sure we notify
36459             // the node that we are done
36460             if(typeof callback == "function"){
36461                 callback();
36462             }
36463         }
36464     },
36465
36466     isLoading : function(){
36467         return this.transId ? true : false;
36468     },
36469
36470     abort : function(){
36471         if(this.isLoading()){
36472             Roo.Ajax.abort(this.transId);
36473         }
36474     },
36475
36476     // private
36477     createNode : function(attr)
36478     {
36479         // apply baseAttrs, nice idea Corey!
36480         if(this.baseAttrs){
36481             Roo.applyIf(attr, this.baseAttrs);
36482         }
36483         if(this.applyLoader !== false){
36484             attr.loader = this;
36485         }
36486         // uiProvider = depreciated..
36487         
36488         if(typeof(attr.uiProvider) == 'string'){
36489            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36490                 /**  eval:var:attr */ eval(attr.uiProvider);
36491         }
36492         if(typeof(this.uiProviders['default']) != 'undefined') {
36493             attr.uiProvider = this.uiProviders['default'];
36494         }
36495         
36496         this.fireEvent('create', this, attr);
36497         
36498         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36499         return(attr.leaf ?
36500                         new Roo.tree.TreeNode(attr) :
36501                         new Roo.tree.AsyncTreeNode(attr));
36502     },
36503
36504     processResponse : function(response, node, callback)
36505     {
36506         var json = response.responseText;
36507         try {
36508             
36509             var o = Roo.decode(json);
36510             
36511             if (this.root === false && typeof(o.success) != undefined) {
36512                 this.root = 'data'; // the default behaviour for list like data..
36513                 }
36514                 
36515             if (this.root !== false &&  !o.success) {
36516                 // it's a failure condition.
36517                 var a = response.argument;
36518                 this.fireEvent("loadexception", this, a.node, response);
36519                 Roo.log("Load failed - should have a handler really");
36520                 return;
36521             }
36522             
36523             
36524             
36525             if (this.root !== false) {
36526                  o = o[this.root];
36527             }
36528             
36529             for(var i = 0, len = o.length; i < len; i++){
36530                 var n = this.createNode(o[i]);
36531                 if(n){
36532                     node.appendChild(n);
36533                 }
36534             }
36535             if(typeof callback == "function"){
36536                 callback(this, node);
36537             }
36538         }catch(e){
36539             this.handleFailure(response);
36540         }
36541     },
36542
36543     handleResponse : function(response){
36544         this.transId = false;
36545         var a = response.argument;
36546         this.processResponse(response, a.node, a.callback);
36547         this.fireEvent("load", this, a.node, response);
36548     },
36549
36550     handleFailure : function(response)
36551     {
36552         // should handle failure better..
36553         this.transId = false;
36554         var a = response.argument;
36555         this.fireEvent("loadexception", this, a.node, response);
36556         if(typeof a.callback == "function"){
36557             a.callback(this, a.node);
36558         }
36559     }
36560 });/*
36561  * Based on:
36562  * Ext JS Library 1.1.1
36563  * Copyright(c) 2006-2007, Ext JS, LLC.
36564  *
36565  * Originally Released Under LGPL - original licence link has changed is not relivant.
36566  *
36567  * Fork - LGPL
36568  * <script type="text/javascript">
36569  */
36570
36571 /**
36572 * @class Roo.tree.TreeFilter
36573 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36574 * @param {TreePanel} tree
36575 * @param {Object} config (optional)
36576  */
36577 Roo.tree.TreeFilter = function(tree, config){
36578     this.tree = tree;
36579     this.filtered = {};
36580     Roo.apply(this, config);
36581 };
36582
36583 Roo.tree.TreeFilter.prototype = {
36584     clearBlank:false,
36585     reverse:false,
36586     autoClear:false,
36587     remove:false,
36588
36589      /**
36590      * Filter the data by a specific attribute.
36591      * @param {String/RegExp} value Either string that the attribute value
36592      * should start with or a RegExp to test against the attribute
36593      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36594      * @param {TreeNode} startNode (optional) The node to start the filter at.
36595      */
36596     filter : function(value, attr, startNode){
36597         attr = attr || "text";
36598         var f;
36599         if(typeof value == "string"){
36600             var vlen = value.length;
36601             // auto clear empty filter
36602             if(vlen == 0 && this.clearBlank){
36603                 this.clear();
36604                 return;
36605             }
36606             value = value.toLowerCase();
36607             f = function(n){
36608                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36609             };
36610         }else if(value.exec){ // regex?
36611             f = function(n){
36612                 return value.test(n.attributes[attr]);
36613             };
36614         }else{
36615             throw 'Illegal filter type, must be string or regex';
36616         }
36617         this.filterBy(f, null, startNode);
36618         },
36619
36620     /**
36621      * Filter by a function. The passed function will be called with each
36622      * node in the tree (or from the startNode). If the function returns true, the node is kept
36623      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36624      * @param {Function} fn The filter function
36625      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36626      */
36627     filterBy : function(fn, scope, startNode){
36628         startNode = startNode || this.tree.root;
36629         if(this.autoClear){
36630             this.clear();
36631         }
36632         var af = this.filtered, rv = this.reverse;
36633         var f = function(n){
36634             if(n == startNode){
36635                 return true;
36636             }
36637             if(af[n.id]){
36638                 return false;
36639             }
36640             var m = fn.call(scope || n, n);
36641             if(!m || rv){
36642                 af[n.id] = n;
36643                 n.ui.hide();
36644                 return false;
36645             }
36646             return true;
36647         };
36648         startNode.cascade(f);
36649         if(this.remove){
36650            for(var id in af){
36651                if(typeof id != "function"){
36652                    var n = af[id];
36653                    if(n && n.parentNode){
36654                        n.parentNode.removeChild(n);
36655                    }
36656                }
36657            }
36658         }
36659     },
36660
36661     /**
36662      * Clears the current filter. Note: with the "remove" option
36663      * set a filter cannot be cleared.
36664      */
36665     clear : function(){
36666         var t = this.tree;
36667         var af = this.filtered;
36668         for(var id in af){
36669             if(typeof id != "function"){
36670                 var n = af[id];
36671                 if(n){
36672                     n.ui.show();
36673                 }
36674             }
36675         }
36676         this.filtered = {};
36677     }
36678 };
36679 /*
36680  * Based on:
36681  * Ext JS Library 1.1.1
36682  * Copyright(c) 2006-2007, Ext JS, LLC.
36683  *
36684  * Originally Released Under LGPL - original licence link has changed is not relivant.
36685  *
36686  * Fork - LGPL
36687  * <script type="text/javascript">
36688  */
36689  
36690
36691 /**
36692  * @class Roo.tree.TreeSorter
36693  * Provides sorting of nodes in a TreePanel
36694  * 
36695  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36696  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36697  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36698  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36699  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36700  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36701  * @constructor
36702  * @param {TreePanel} tree
36703  * @param {Object} config
36704  */
36705 Roo.tree.TreeSorter = function(tree, config){
36706     Roo.apply(this, config);
36707     tree.on("beforechildrenrendered", this.doSort, this);
36708     tree.on("append", this.updateSort, this);
36709     tree.on("insert", this.updateSort, this);
36710     
36711     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36712     var p = this.property || "text";
36713     var sortType = this.sortType;
36714     var fs = this.folderSort;
36715     var cs = this.caseSensitive === true;
36716     var leafAttr = this.leafAttr || 'leaf';
36717
36718     this.sortFn = function(n1, n2){
36719         if(fs){
36720             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36721                 return 1;
36722             }
36723             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36724                 return -1;
36725             }
36726         }
36727         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36728         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36729         if(v1 < v2){
36730                         return dsc ? +1 : -1;
36731                 }else if(v1 > v2){
36732                         return dsc ? -1 : +1;
36733         }else{
36734                 return 0;
36735         }
36736     };
36737 };
36738
36739 Roo.tree.TreeSorter.prototype = {
36740     doSort : function(node){
36741         node.sort(this.sortFn);
36742     },
36743     
36744     compareNodes : function(n1, n2){
36745         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36746     },
36747     
36748     updateSort : function(tree, node){
36749         if(node.childrenRendered){
36750             this.doSort.defer(1, this, [node]);
36751         }
36752     }
36753 };/*
36754  * Based on:
36755  * Ext JS Library 1.1.1
36756  * Copyright(c) 2006-2007, Ext JS, LLC.
36757  *
36758  * Originally Released Under LGPL - original licence link has changed is not relivant.
36759  *
36760  * Fork - LGPL
36761  * <script type="text/javascript">
36762  */
36763
36764 if(Roo.dd.DropZone){
36765     
36766 Roo.tree.TreeDropZone = function(tree, config){
36767     this.allowParentInsert = false;
36768     this.allowContainerDrop = false;
36769     this.appendOnly = false;
36770     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36771     this.tree = tree;
36772     this.lastInsertClass = "x-tree-no-status";
36773     this.dragOverData = {};
36774 };
36775
36776 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36777     ddGroup : "TreeDD",
36778     scroll:  true,
36779     
36780     expandDelay : 1000,
36781     
36782     expandNode : function(node){
36783         if(node.hasChildNodes() && !node.isExpanded()){
36784             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36785         }
36786     },
36787     
36788     queueExpand : function(node){
36789         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36790     },
36791     
36792     cancelExpand : function(){
36793         if(this.expandProcId){
36794             clearTimeout(this.expandProcId);
36795             this.expandProcId = false;
36796         }
36797     },
36798     
36799     isValidDropPoint : function(n, pt, dd, e, data){
36800         if(!n || !data){ return false; }
36801         var targetNode = n.node;
36802         var dropNode = data.node;
36803         // default drop rules
36804         if(!(targetNode && targetNode.isTarget && pt)){
36805             return false;
36806         }
36807         if(pt == "append" && targetNode.allowChildren === false){
36808             return false;
36809         }
36810         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36811             return false;
36812         }
36813         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36814             return false;
36815         }
36816         // reuse the object
36817         var overEvent = this.dragOverData;
36818         overEvent.tree = this.tree;
36819         overEvent.target = targetNode;
36820         overEvent.data = data;
36821         overEvent.point = pt;
36822         overEvent.source = dd;
36823         overEvent.rawEvent = e;
36824         overEvent.dropNode = dropNode;
36825         overEvent.cancel = false;  
36826         var result = this.tree.fireEvent("nodedragover", overEvent);
36827         return overEvent.cancel === false && result !== false;
36828     },
36829     
36830     getDropPoint : function(e, n, dd)
36831     {
36832         var tn = n.node;
36833         if(tn.isRoot){
36834             return tn.allowChildren !== false ? "append" : false; // always append for root
36835         }
36836         var dragEl = n.ddel;
36837         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36838         var y = Roo.lib.Event.getPageY(e);
36839         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36840         
36841         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36842         var noAppend = tn.allowChildren === false;
36843         if(this.appendOnly || tn.parentNode.allowChildren === false){
36844             return noAppend ? false : "append";
36845         }
36846         var noBelow = false;
36847         if(!this.allowParentInsert){
36848             noBelow = tn.hasChildNodes() && tn.isExpanded();
36849         }
36850         var q = (b - t) / (noAppend ? 2 : 3);
36851         if(y >= t && y < (t + q)){
36852             return "above";
36853         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36854             return "below";
36855         }else{
36856             return "append";
36857         }
36858     },
36859     
36860     onNodeEnter : function(n, dd, e, data)
36861     {
36862         this.cancelExpand();
36863     },
36864     
36865     onNodeOver : function(n, dd, e, data)
36866     {
36867        
36868         var pt = this.getDropPoint(e, n, dd);
36869         var node = n.node;
36870         
36871         // auto node expand check
36872         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36873             this.queueExpand(node);
36874         }else if(pt != "append"){
36875             this.cancelExpand();
36876         }
36877         
36878         // set the insert point style on the target node
36879         var returnCls = this.dropNotAllowed;
36880         if(this.isValidDropPoint(n, pt, dd, e, data)){
36881            if(pt){
36882                var el = n.ddel;
36883                var cls;
36884                if(pt == "above"){
36885                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36886                    cls = "x-tree-drag-insert-above";
36887                }else if(pt == "below"){
36888                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36889                    cls = "x-tree-drag-insert-below";
36890                }else{
36891                    returnCls = "x-tree-drop-ok-append";
36892                    cls = "x-tree-drag-append";
36893                }
36894                if(this.lastInsertClass != cls){
36895                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36896                    this.lastInsertClass = cls;
36897                }
36898            }
36899        }
36900        return returnCls;
36901     },
36902     
36903     onNodeOut : function(n, dd, e, data){
36904         
36905         this.cancelExpand();
36906         this.removeDropIndicators(n);
36907     },
36908     
36909     onNodeDrop : function(n, dd, e, data){
36910         var point = this.getDropPoint(e, n, dd);
36911         var targetNode = n.node;
36912         targetNode.ui.startDrop();
36913         if(!this.isValidDropPoint(n, point, dd, e, data)){
36914             targetNode.ui.endDrop();
36915             return false;
36916         }
36917         // first try to find the drop node
36918         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36919         var dropEvent = {
36920             tree : this.tree,
36921             target: targetNode,
36922             data: data,
36923             point: point,
36924             source: dd,
36925             rawEvent: e,
36926             dropNode: dropNode,
36927             cancel: !dropNode   
36928         };
36929         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36930         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36931             targetNode.ui.endDrop();
36932             return false;
36933         }
36934         // allow target changing
36935         targetNode = dropEvent.target;
36936         if(point == "append" && !targetNode.isExpanded()){
36937             targetNode.expand(false, null, function(){
36938                 this.completeDrop(dropEvent);
36939             }.createDelegate(this));
36940         }else{
36941             this.completeDrop(dropEvent);
36942         }
36943         return true;
36944     },
36945     
36946     completeDrop : function(de){
36947         var ns = de.dropNode, p = de.point, t = de.target;
36948         if(!(ns instanceof Array)){
36949             ns = [ns];
36950         }
36951         var n;
36952         for(var i = 0, len = ns.length; i < len; i++){
36953             n = ns[i];
36954             if(p == "above"){
36955                 t.parentNode.insertBefore(n, t);
36956             }else if(p == "below"){
36957                 t.parentNode.insertBefore(n, t.nextSibling);
36958             }else{
36959                 t.appendChild(n);
36960             }
36961         }
36962         n.ui.focus();
36963         if(this.tree.hlDrop){
36964             n.ui.highlight();
36965         }
36966         t.ui.endDrop();
36967         this.tree.fireEvent("nodedrop", de);
36968     },
36969     
36970     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36971         if(this.tree.hlDrop){
36972             dropNode.ui.focus();
36973             dropNode.ui.highlight();
36974         }
36975         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36976     },
36977     
36978     getTree : function(){
36979         return this.tree;
36980     },
36981     
36982     removeDropIndicators : function(n){
36983         if(n && n.ddel){
36984             var el = n.ddel;
36985             Roo.fly(el).removeClass([
36986                     "x-tree-drag-insert-above",
36987                     "x-tree-drag-insert-below",
36988                     "x-tree-drag-append"]);
36989             this.lastInsertClass = "_noclass";
36990         }
36991     },
36992     
36993     beforeDragDrop : function(target, e, id){
36994         this.cancelExpand();
36995         return true;
36996     },
36997     
36998     afterRepair : function(data){
36999         if(data && Roo.enableFx){
37000             data.node.ui.highlight();
37001         }
37002         this.hideProxy();
37003     } 
37004     
37005 });
37006
37007 }
37008 /*
37009  * Based on:
37010  * Ext JS Library 1.1.1
37011  * Copyright(c) 2006-2007, Ext JS, LLC.
37012  *
37013  * Originally Released Under LGPL - original licence link has changed is not relivant.
37014  *
37015  * Fork - LGPL
37016  * <script type="text/javascript">
37017  */
37018  
37019
37020 if(Roo.dd.DragZone){
37021 Roo.tree.TreeDragZone = function(tree, config){
37022     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
37023     this.tree = tree;
37024 };
37025
37026 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
37027     ddGroup : "TreeDD",
37028    
37029     onBeforeDrag : function(data, e){
37030         var n = data.node;
37031         return n && n.draggable && !n.disabled;
37032     },
37033      
37034     
37035     onInitDrag : function(e){
37036         var data = this.dragData;
37037         this.tree.getSelectionModel().select(data.node);
37038         this.proxy.update("");
37039         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
37040         this.tree.fireEvent("startdrag", this.tree, data.node, e);
37041     },
37042     
37043     getRepairXY : function(e, data){
37044         return data.node.ui.getDDRepairXY();
37045     },
37046     
37047     onEndDrag : function(data, e){
37048         this.tree.fireEvent("enddrag", this.tree, data.node, e);
37049         
37050         
37051     },
37052     
37053     onValidDrop : function(dd, e, id){
37054         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
37055         this.hideProxy();
37056     },
37057     
37058     beforeInvalidDrop : function(e, id){
37059         // this scrolls the original position back into view
37060         var sm = this.tree.getSelectionModel();
37061         sm.clearSelections();
37062         sm.select(this.dragData.node);
37063     }
37064 });
37065 }/*
37066  * Based on:
37067  * Ext JS Library 1.1.1
37068  * Copyright(c) 2006-2007, Ext JS, LLC.
37069  *
37070  * Originally Released Under LGPL - original licence link has changed is not relivant.
37071  *
37072  * Fork - LGPL
37073  * <script type="text/javascript">
37074  */
37075 /**
37076  * @class Roo.tree.TreeEditor
37077  * @extends Roo.Editor
37078  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
37079  * as the editor field.
37080  * @constructor
37081  * @param {Object} config (used to be the tree panel.)
37082  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
37083  * 
37084  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
37085  * @cfg {Roo.form.TextField|Object} field The field configuration
37086  *
37087  * 
37088  */
37089 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
37090     var tree = config;
37091     var field;
37092     if (oldconfig) { // old style..
37093         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
37094     } else {
37095         // new style..
37096         tree = config.tree;
37097         config.field = config.field  || {};
37098         config.field.xtype = 'TextField';
37099         field = Roo.factory(config.field, Roo.form);
37100     }
37101     config = config || {};
37102     
37103     
37104     this.addEvents({
37105         /**
37106          * @event beforenodeedit
37107          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
37108          * false from the handler of this event.
37109          * @param {Editor} this
37110          * @param {Roo.tree.Node} node 
37111          */
37112         "beforenodeedit" : true
37113     });
37114     
37115     //Roo.log(config);
37116     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
37117
37118     this.tree = tree;
37119
37120     tree.on('beforeclick', this.beforeNodeClick, this);
37121     tree.getTreeEl().on('mousedown', this.hide, this);
37122     this.on('complete', this.updateNode, this);
37123     this.on('beforestartedit', this.fitToTree, this);
37124     this.on('startedit', this.bindScroll, this, {delay:10});
37125     this.on('specialkey', this.onSpecialKey, this);
37126 };
37127
37128 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
37129     /**
37130      * @cfg {String} alignment
37131      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
37132      */
37133     alignment: "l-l",
37134     // inherit
37135     autoSize: false,
37136     /**
37137      * @cfg {Boolean} hideEl
37138      * True to hide the bound element while the editor is displayed (defaults to false)
37139      */
37140     hideEl : false,
37141     /**
37142      * @cfg {String} cls
37143      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
37144      */
37145     cls: "x-small-editor x-tree-editor",
37146     /**
37147      * @cfg {Boolean} shim
37148      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
37149      */
37150     shim:false,
37151     // inherit
37152     shadow:"frame",
37153     /**
37154      * @cfg {Number} maxWidth
37155      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
37156      * the containing tree element's size, it will be automatically limited for you to the container width, taking
37157      * scroll and client offsets into account prior to each edit.
37158      */
37159     maxWidth: 250,
37160
37161     editDelay : 350,
37162
37163     // private
37164     fitToTree : function(ed, el){
37165         var td = this.tree.getTreeEl().dom, nd = el.dom;
37166         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
37167             td.scrollLeft = nd.offsetLeft;
37168         }
37169         var w = Math.min(
37170                 this.maxWidth,
37171                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
37172         this.setSize(w, '');
37173         
37174         return this.fireEvent('beforenodeedit', this, this.editNode);
37175         
37176     },
37177
37178     // private
37179     triggerEdit : function(node){
37180         this.completeEdit();
37181         this.editNode = node;
37182         this.startEdit(node.ui.textNode, node.text);
37183     },
37184
37185     // private
37186     bindScroll : function(){
37187         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
37188     },
37189
37190     // private
37191     beforeNodeClick : function(node, e){
37192         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
37193         this.lastClick = new Date();
37194         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
37195             e.stopEvent();
37196             this.triggerEdit(node);
37197             return false;
37198         }
37199         return true;
37200     },
37201
37202     // private
37203     updateNode : function(ed, value){
37204         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
37205         this.editNode.setText(value);
37206     },
37207
37208     // private
37209     onHide : function(){
37210         Roo.tree.TreeEditor.superclass.onHide.call(this);
37211         if(this.editNode){
37212             this.editNode.ui.focus();
37213         }
37214     },
37215
37216     // private
37217     onSpecialKey : function(field, e){
37218         var k = e.getKey();
37219         if(k == e.ESC){
37220             e.stopEvent();
37221             this.cancelEdit();
37222         }else if(k == e.ENTER && !e.hasModifier()){
37223             e.stopEvent();
37224             this.completeEdit();
37225         }
37226     }
37227 });//<Script type="text/javascript">
37228 /*
37229  * Based on:
37230  * Ext JS Library 1.1.1
37231  * Copyright(c) 2006-2007, Ext JS, LLC.
37232  *
37233  * Originally Released Under LGPL - original licence link has changed is not relivant.
37234  *
37235  * Fork - LGPL
37236  * <script type="text/javascript">
37237  */
37238  
37239 /**
37240  * Not documented??? - probably should be...
37241  */
37242
37243 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
37244     //focus: Roo.emptyFn, // prevent odd scrolling behavior
37245     
37246     renderElements : function(n, a, targetNode, bulkRender){
37247         //consel.log("renderElements?");
37248         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37249
37250         var t = n.getOwnerTree();
37251         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
37252         
37253         var cols = t.columns;
37254         var bw = t.borderWidth;
37255         var c = cols[0];
37256         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37257          var cb = typeof a.checked == "boolean";
37258         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37259         var colcls = 'x-t-' + tid + '-c0';
37260         var buf = [
37261             '<li class="x-tree-node">',
37262             
37263                 
37264                 '<div class="x-tree-node-el ', a.cls,'">',
37265                     // extran...
37266                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
37267                 
37268                 
37269                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
37270                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
37271                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
37272                            (a.icon ? ' x-tree-node-inline-icon' : ''),
37273                            (a.iconCls ? ' '+a.iconCls : ''),
37274                            '" unselectable="on" />',
37275                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
37276                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
37277                              
37278                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37279                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
37280                             '<span unselectable="on" qtip="' + tx + '">',
37281                              tx,
37282                              '</span></a>' ,
37283                     '</div>',
37284                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37285                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
37286                  ];
37287         for(var i = 1, len = cols.length; i < len; i++){
37288             c = cols[i];
37289             colcls = 'x-t-' + tid + '-c' +i;
37290             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37291             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
37292                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
37293                       "</div>");
37294          }
37295          
37296          buf.push(
37297             '</a>',
37298             '<div class="x-clear"></div></div>',
37299             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37300             "</li>");
37301         
37302         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37303             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37304                                 n.nextSibling.ui.getEl(), buf.join(""));
37305         }else{
37306             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37307         }
37308         var el = this.wrap.firstChild;
37309         this.elRow = el;
37310         this.elNode = el.firstChild;
37311         this.ranchor = el.childNodes[1];
37312         this.ctNode = this.wrap.childNodes[1];
37313         var cs = el.firstChild.childNodes;
37314         this.indentNode = cs[0];
37315         this.ecNode = cs[1];
37316         this.iconNode = cs[2];
37317         var index = 3;
37318         if(cb){
37319             this.checkbox = cs[3];
37320             index++;
37321         }
37322         this.anchor = cs[index];
37323         
37324         this.textNode = cs[index].firstChild;
37325         
37326         //el.on("click", this.onClick, this);
37327         //el.on("dblclick", this.onDblClick, this);
37328         
37329         
37330        // console.log(this);
37331     },
37332     initEvents : function(){
37333         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37334         
37335             
37336         var a = this.ranchor;
37337
37338         var el = Roo.get(a);
37339
37340         if(Roo.isOpera){ // opera render bug ignores the CSS
37341             el.setStyle("text-decoration", "none");
37342         }
37343
37344         el.on("click", this.onClick, this);
37345         el.on("dblclick", this.onDblClick, this);
37346         el.on("contextmenu", this.onContextMenu, this);
37347         
37348     },
37349     
37350     /*onSelectedChange : function(state){
37351         if(state){
37352             this.focus();
37353             this.addClass("x-tree-selected");
37354         }else{
37355             //this.blur();
37356             this.removeClass("x-tree-selected");
37357         }
37358     },*/
37359     addClass : function(cls){
37360         if(this.elRow){
37361             Roo.fly(this.elRow).addClass(cls);
37362         }
37363         
37364     },
37365     
37366     
37367     removeClass : function(cls){
37368         if(this.elRow){
37369             Roo.fly(this.elRow).removeClass(cls);
37370         }
37371     }
37372
37373     
37374     
37375 });//<Script type="text/javascript">
37376
37377 /*
37378  * Based on:
37379  * Ext JS Library 1.1.1
37380  * Copyright(c) 2006-2007, Ext JS, LLC.
37381  *
37382  * Originally Released Under LGPL - original licence link has changed is not relivant.
37383  *
37384  * Fork - LGPL
37385  * <script type="text/javascript">
37386  */
37387  
37388
37389 /**
37390  * @class Roo.tree.ColumnTree
37391  * @extends Roo.data.TreePanel
37392  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37393  * @cfg {int} borderWidth  compined right/left border allowance
37394  * @constructor
37395  * @param {String/HTMLElement/Element} el The container element
37396  * @param {Object} config
37397  */
37398 Roo.tree.ColumnTree =  function(el, config)
37399 {
37400    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37401    this.addEvents({
37402         /**
37403         * @event resize
37404         * Fire this event on a container when it resizes
37405         * @param {int} w Width
37406         * @param {int} h Height
37407         */
37408        "resize" : true
37409     });
37410     this.on('resize', this.onResize, this);
37411 };
37412
37413 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37414     //lines:false,
37415     
37416     
37417     borderWidth: Roo.isBorderBox ? 0 : 2, 
37418     headEls : false,
37419     
37420     render : function(){
37421         // add the header.....
37422        
37423         Roo.tree.ColumnTree.superclass.render.apply(this);
37424         
37425         this.el.addClass('x-column-tree');
37426         
37427         this.headers = this.el.createChild(
37428             {cls:'x-tree-headers'},this.innerCt.dom);
37429    
37430         var cols = this.columns, c;
37431         var totalWidth = 0;
37432         this.headEls = [];
37433         var  len = cols.length;
37434         for(var i = 0; i < len; i++){
37435              c = cols[i];
37436              totalWidth += c.width;
37437             this.headEls.push(this.headers.createChild({
37438                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37439                  cn: {
37440                      cls:'x-tree-hd-text',
37441                      html: c.header
37442                  },
37443                  style:'width:'+(c.width-this.borderWidth)+'px;'
37444              }));
37445         }
37446         this.headers.createChild({cls:'x-clear'});
37447         // prevent floats from wrapping when clipped
37448         this.headers.setWidth(totalWidth);
37449         //this.innerCt.setWidth(totalWidth);
37450         this.innerCt.setStyle({ overflow: 'auto' });
37451         this.onResize(this.width, this.height);
37452              
37453         
37454     },
37455     onResize : function(w,h)
37456     {
37457         this.height = h;
37458         this.width = w;
37459         // resize cols..
37460         this.innerCt.setWidth(this.width);
37461         this.innerCt.setHeight(this.height-20);
37462         
37463         // headers...
37464         var cols = this.columns, c;
37465         var totalWidth = 0;
37466         var expEl = false;
37467         var len = cols.length;
37468         for(var i = 0; i < len; i++){
37469             c = cols[i];
37470             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37471                 // it's the expander..
37472                 expEl  = this.headEls[i];
37473                 continue;
37474             }
37475             totalWidth += c.width;
37476             
37477         }
37478         if (expEl) {
37479             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37480         }
37481         this.headers.setWidth(w-20);
37482
37483         
37484         
37485         
37486     }
37487 });
37488 /*
37489  * Based on:
37490  * Ext JS Library 1.1.1
37491  * Copyright(c) 2006-2007, Ext JS, LLC.
37492  *
37493  * Originally Released Under LGPL - original licence link has changed is not relivant.
37494  *
37495  * Fork - LGPL
37496  * <script type="text/javascript">
37497  */
37498  
37499 /**
37500  * @class Roo.menu.Menu
37501  * @extends Roo.util.Observable
37502  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37503  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37504  * @constructor
37505  * Creates a new Menu
37506  * @param {Object} config Configuration options
37507  */
37508 Roo.menu.Menu = function(config){
37509     
37510     Roo.menu.Menu.superclass.constructor.call(this, config);
37511     
37512     this.id = this.id || Roo.id();
37513     this.addEvents({
37514         /**
37515          * @event beforeshow
37516          * Fires before this menu is displayed
37517          * @param {Roo.menu.Menu} this
37518          */
37519         beforeshow : true,
37520         /**
37521          * @event beforehide
37522          * Fires before this menu is hidden
37523          * @param {Roo.menu.Menu} this
37524          */
37525         beforehide : true,
37526         /**
37527          * @event show
37528          * Fires after this menu is displayed
37529          * @param {Roo.menu.Menu} this
37530          */
37531         show : true,
37532         /**
37533          * @event hide
37534          * Fires after this menu is hidden
37535          * @param {Roo.menu.Menu} this
37536          */
37537         hide : true,
37538         /**
37539          * @event click
37540          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37541          * @param {Roo.menu.Menu} this
37542          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37543          * @param {Roo.EventObject} e
37544          */
37545         click : true,
37546         /**
37547          * @event mouseover
37548          * Fires when the mouse is hovering over this menu
37549          * @param {Roo.menu.Menu} this
37550          * @param {Roo.EventObject} e
37551          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37552          */
37553         mouseover : true,
37554         /**
37555          * @event mouseout
37556          * Fires when the mouse exits this menu
37557          * @param {Roo.menu.Menu} this
37558          * @param {Roo.EventObject} e
37559          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37560          */
37561         mouseout : true,
37562         /**
37563          * @event itemclick
37564          * Fires when a menu item contained in this menu is clicked
37565          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37566          * @param {Roo.EventObject} e
37567          */
37568         itemclick: true
37569     });
37570     if (this.registerMenu) {
37571         Roo.menu.MenuMgr.register(this);
37572     }
37573     
37574     var mis = this.items;
37575     this.items = new Roo.util.MixedCollection();
37576     if(mis){
37577         this.add.apply(this, mis);
37578     }
37579 };
37580
37581 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37582     /**
37583      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37584      */
37585     minWidth : 120,
37586     /**
37587      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37588      * for bottom-right shadow (defaults to "sides")
37589      */
37590     shadow : "sides",
37591     /**
37592      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37593      * this menu (defaults to "tl-tr?")
37594      */
37595     subMenuAlign : "tl-tr?",
37596     /**
37597      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37598      * relative to its element of origin (defaults to "tl-bl?")
37599      */
37600     defaultAlign : "tl-bl?",
37601     /**
37602      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37603      */
37604     allowOtherMenus : false,
37605     /**
37606      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37607      */
37608     registerMenu : true,
37609
37610     hidden:true,
37611
37612     // private
37613     render : function(){
37614         if(this.el){
37615             return;
37616         }
37617         var el = this.el = new Roo.Layer({
37618             cls: "x-menu",
37619             shadow:this.shadow,
37620             constrain: false,
37621             parentEl: this.parentEl || document.body,
37622             zindex:15000
37623         });
37624
37625         this.keyNav = new Roo.menu.MenuNav(this);
37626
37627         if(this.plain){
37628             el.addClass("x-menu-plain");
37629         }
37630         if(this.cls){
37631             el.addClass(this.cls);
37632         }
37633         // generic focus element
37634         this.focusEl = el.createChild({
37635             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37636         });
37637         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37638         //disabling touch- as it's causing issues ..
37639         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37640         ul.on('click'   , this.onClick, this);
37641         
37642         
37643         ul.on("mouseover", this.onMouseOver, this);
37644         ul.on("mouseout", this.onMouseOut, this);
37645         this.items.each(function(item){
37646             if (item.hidden) {
37647                 return;
37648             }
37649             
37650             var li = document.createElement("li");
37651             li.className = "x-menu-list-item";
37652             ul.dom.appendChild(li);
37653             item.render(li, this);
37654         }, this);
37655         this.ul = ul;
37656         this.autoWidth();
37657     },
37658
37659     // private
37660     autoWidth : function(){
37661         var el = this.el, ul = this.ul;
37662         if(!el){
37663             return;
37664         }
37665         var w = this.width;
37666         if(w){
37667             el.setWidth(w);
37668         }else if(Roo.isIE){
37669             el.setWidth(this.minWidth);
37670             var t = el.dom.offsetWidth; // force recalc
37671             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37672         }
37673     },
37674
37675     // private
37676     delayAutoWidth : function(){
37677         if(this.rendered){
37678             if(!this.awTask){
37679                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37680             }
37681             this.awTask.delay(20);
37682         }
37683     },
37684
37685     // private
37686     findTargetItem : function(e){
37687         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37688         if(t && t.menuItemId){
37689             return this.items.get(t.menuItemId);
37690         }
37691     },
37692
37693     // private
37694     onClick : function(e){
37695         Roo.log("menu.onClick");
37696         var t = this.findTargetItem(e);
37697         if(!t){
37698             return;
37699         }
37700         Roo.log(e);
37701         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37702             if(t == this.activeItem && t.shouldDeactivate(e)){
37703                 this.activeItem.deactivate();
37704                 delete this.activeItem;
37705                 return;
37706             }
37707             if(t.canActivate){
37708                 this.setActiveItem(t, true);
37709             }
37710             return;
37711             
37712             
37713         }
37714         
37715         t.onClick(e);
37716         this.fireEvent("click", this, t, e);
37717     },
37718
37719     // private
37720     setActiveItem : function(item, autoExpand){
37721         if(item != this.activeItem){
37722             if(this.activeItem){
37723                 this.activeItem.deactivate();
37724             }
37725             this.activeItem = item;
37726             item.activate(autoExpand);
37727         }else if(autoExpand){
37728             item.expandMenu();
37729         }
37730     },
37731
37732     // private
37733     tryActivate : function(start, step){
37734         var items = this.items;
37735         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37736             var item = items.get(i);
37737             if(!item.disabled && item.canActivate){
37738                 this.setActiveItem(item, false);
37739                 return item;
37740             }
37741         }
37742         return false;
37743     },
37744
37745     // private
37746     onMouseOver : function(e){
37747         var t;
37748         if(t = this.findTargetItem(e)){
37749             if(t.canActivate && !t.disabled){
37750                 this.setActiveItem(t, true);
37751             }
37752         }
37753         this.fireEvent("mouseover", this, e, t);
37754     },
37755
37756     // private
37757     onMouseOut : function(e){
37758         var t;
37759         if(t = this.findTargetItem(e)){
37760             if(t == this.activeItem && t.shouldDeactivate(e)){
37761                 this.activeItem.deactivate();
37762                 delete this.activeItem;
37763             }
37764         }
37765         this.fireEvent("mouseout", this, e, t);
37766     },
37767
37768     /**
37769      * Read-only.  Returns true if the menu is currently displayed, else false.
37770      * @type Boolean
37771      */
37772     isVisible : function(){
37773         return this.el && !this.hidden;
37774     },
37775
37776     /**
37777      * Displays this menu relative to another element
37778      * @param {String/HTMLElement/Roo.Element} element The element to align to
37779      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37780      * the element (defaults to this.defaultAlign)
37781      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37782      */
37783     show : function(el, pos, parentMenu){
37784         this.parentMenu = parentMenu;
37785         if(!this.el){
37786             this.render();
37787         }
37788         this.fireEvent("beforeshow", this);
37789         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37790     },
37791
37792     /**
37793      * Displays this menu at a specific xy position
37794      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37795      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37796      */
37797     showAt : function(xy, parentMenu, /* private: */_e){
37798         this.parentMenu = parentMenu;
37799         if(!this.el){
37800             this.render();
37801         }
37802         if(_e !== false){
37803             this.fireEvent("beforeshow", this);
37804             xy = this.el.adjustForConstraints(xy);
37805         }
37806         this.el.setXY(xy);
37807         this.el.show();
37808         this.hidden = false;
37809         this.focus();
37810         this.fireEvent("show", this);
37811     },
37812
37813     focus : function(){
37814         if(!this.hidden){
37815             this.doFocus.defer(50, this);
37816         }
37817     },
37818
37819     doFocus : function(){
37820         if(!this.hidden){
37821             this.focusEl.focus();
37822         }
37823     },
37824
37825     /**
37826      * Hides this menu and optionally all parent menus
37827      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37828      */
37829     hide : function(deep){
37830         if(this.el && this.isVisible()){
37831             this.fireEvent("beforehide", this);
37832             if(this.activeItem){
37833                 this.activeItem.deactivate();
37834                 this.activeItem = null;
37835             }
37836             this.el.hide();
37837             this.hidden = true;
37838             this.fireEvent("hide", this);
37839         }
37840         if(deep === true && this.parentMenu){
37841             this.parentMenu.hide(true);
37842         }
37843     },
37844
37845     /**
37846      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37847      * Any of the following are valid:
37848      * <ul>
37849      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37850      * <li>An HTMLElement object which will be converted to a menu item</li>
37851      * <li>A menu item config object that will be created as a new menu item</li>
37852      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37853      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37854      * </ul>
37855      * Usage:
37856      * <pre><code>
37857 // Create the menu
37858 var menu = new Roo.menu.Menu();
37859
37860 // Create a menu item to add by reference
37861 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37862
37863 // Add a bunch of items at once using different methods.
37864 // Only the last item added will be returned.
37865 var item = menu.add(
37866     menuItem,                // add existing item by ref
37867     'Dynamic Item',          // new TextItem
37868     '-',                     // new separator
37869     { text: 'Config Item' }  // new item by config
37870 );
37871 </code></pre>
37872      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37873      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37874      */
37875     add : function(){
37876         var a = arguments, l = a.length, item;
37877         for(var i = 0; i < l; i++){
37878             var el = a[i];
37879             if ((typeof(el) == "object") && el.xtype && el.xns) {
37880                 el = Roo.factory(el, Roo.menu);
37881             }
37882             
37883             if(el.render){ // some kind of Item
37884                 item = this.addItem(el);
37885             }else if(typeof el == "string"){ // string
37886                 if(el == "separator" || el == "-"){
37887                     item = this.addSeparator();
37888                 }else{
37889                     item = this.addText(el);
37890                 }
37891             }else if(el.tagName || el.el){ // element
37892                 item = this.addElement(el);
37893             }else if(typeof el == "object"){ // must be menu item config?
37894                 item = this.addMenuItem(el);
37895             }
37896         }
37897         return item;
37898     },
37899
37900     /**
37901      * Returns this menu's underlying {@link Roo.Element} object
37902      * @return {Roo.Element} The element
37903      */
37904     getEl : function(){
37905         if(!this.el){
37906             this.render();
37907         }
37908         return this.el;
37909     },
37910
37911     /**
37912      * Adds a separator bar to the menu
37913      * @return {Roo.menu.Item} The menu item that was added
37914      */
37915     addSeparator : function(){
37916         return this.addItem(new Roo.menu.Separator());
37917     },
37918
37919     /**
37920      * Adds an {@link Roo.Element} object to the menu
37921      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37922      * @return {Roo.menu.Item} The menu item that was added
37923      */
37924     addElement : function(el){
37925         return this.addItem(new Roo.menu.BaseItem(el));
37926     },
37927
37928     /**
37929      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37930      * @param {Roo.menu.Item} item The menu item to add
37931      * @return {Roo.menu.Item} The menu item that was added
37932      */
37933     addItem : function(item){
37934         this.items.add(item);
37935         if(this.ul){
37936             var li = document.createElement("li");
37937             li.className = "x-menu-list-item";
37938             this.ul.dom.appendChild(li);
37939             item.render(li, this);
37940             this.delayAutoWidth();
37941         }
37942         return item;
37943     },
37944
37945     /**
37946      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37947      * @param {Object} config A MenuItem config object
37948      * @return {Roo.menu.Item} The menu item that was added
37949      */
37950     addMenuItem : function(config){
37951         if(!(config instanceof Roo.menu.Item)){
37952             if(typeof config.checked == "boolean"){ // must be check menu item config?
37953                 config = new Roo.menu.CheckItem(config);
37954             }else{
37955                 config = new Roo.menu.Item(config);
37956             }
37957         }
37958         return this.addItem(config);
37959     },
37960
37961     /**
37962      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37963      * @param {String} text The text to display in the menu item
37964      * @return {Roo.menu.Item} The menu item that was added
37965      */
37966     addText : function(text){
37967         return this.addItem(new Roo.menu.TextItem({ text : text }));
37968     },
37969
37970     /**
37971      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37972      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37973      * @param {Roo.menu.Item} item The menu item to add
37974      * @return {Roo.menu.Item} The menu item that was added
37975      */
37976     insert : function(index, item){
37977         this.items.insert(index, item);
37978         if(this.ul){
37979             var li = document.createElement("li");
37980             li.className = "x-menu-list-item";
37981             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37982             item.render(li, this);
37983             this.delayAutoWidth();
37984         }
37985         return item;
37986     },
37987
37988     /**
37989      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37990      * @param {Roo.menu.Item} item The menu item to remove
37991      */
37992     remove : function(item){
37993         this.items.removeKey(item.id);
37994         item.destroy();
37995     },
37996
37997     /**
37998      * Removes and destroys all items in the menu
37999      */
38000     removeAll : function(){
38001         var f;
38002         while(f = this.items.first()){
38003             this.remove(f);
38004         }
38005     }
38006 });
38007
38008 // MenuNav is a private utility class used internally by the Menu
38009 Roo.menu.MenuNav = function(menu){
38010     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
38011     this.scope = this.menu = menu;
38012 };
38013
38014 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
38015     doRelay : function(e, h){
38016         var k = e.getKey();
38017         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
38018             this.menu.tryActivate(0, 1);
38019             return false;
38020         }
38021         return h.call(this.scope || this, e, this.menu);
38022     },
38023
38024     up : function(e, m){
38025         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
38026             m.tryActivate(m.items.length-1, -1);
38027         }
38028     },
38029
38030     down : function(e, m){
38031         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
38032             m.tryActivate(0, 1);
38033         }
38034     },
38035
38036     right : function(e, m){
38037         if(m.activeItem){
38038             m.activeItem.expandMenu(true);
38039         }
38040     },
38041
38042     left : function(e, m){
38043         m.hide();
38044         if(m.parentMenu && m.parentMenu.activeItem){
38045             m.parentMenu.activeItem.activate();
38046         }
38047     },
38048
38049     enter : function(e, m){
38050         if(m.activeItem){
38051             e.stopPropagation();
38052             m.activeItem.onClick(e);
38053             m.fireEvent("click", this, m.activeItem);
38054             return true;
38055         }
38056     }
38057 });/*
38058  * Based on:
38059  * Ext JS Library 1.1.1
38060  * Copyright(c) 2006-2007, Ext JS, LLC.
38061  *
38062  * Originally Released Under LGPL - original licence link has changed is not relivant.
38063  *
38064  * Fork - LGPL
38065  * <script type="text/javascript">
38066  */
38067  
38068 /**
38069  * @class Roo.menu.MenuMgr
38070  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
38071  * @singleton
38072  */
38073 Roo.menu.MenuMgr = function(){
38074    var menus, active, groups = {}, attached = false, lastShow = new Date();
38075
38076    // private - called when first menu is created
38077    function init(){
38078        menus = {};
38079        active = new Roo.util.MixedCollection();
38080        Roo.get(document).addKeyListener(27, function(){
38081            if(active.length > 0){
38082                hideAll();
38083            }
38084        });
38085    }
38086
38087    // private
38088    function hideAll(){
38089        if(active && active.length > 0){
38090            var c = active.clone();
38091            c.each(function(m){
38092                m.hide();
38093            });
38094        }
38095    }
38096
38097    // private
38098    function onHide(m){
38099        active.remove(m);
38100        if(active.length < 1){
38101            Roo.get(document).un("mousedown", onMouseDown);
38102            attached = false;
38103        }
38104    }
38105
38106    // private
38107    function onShow(m){
38108        var last = active.last();
38109        lastShow = new Date();
38110        active.add(m);
38111        if(!attached){
38112            Roo.get(document).on("mousedown", onMouseDown);
38113            attached = true;
38114        }
38115        if(m.parentMenu){
38116           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
38117           m.parentMenu.activeChild = m;
38118        }else if(last && last.isVisible()){
38119           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
38120        }
38121    }
38122
38123    // private
38124    function onBeforeHide(m){
38125        if(m.activeChild){
38126            m.activeChild.hide();
38127        }
38128        if(m.autoHideTimer){
38129            clearTimeout(m.autoHideTimer);
38130            delete m.autoHideTimer;
38131        }
38132    }
38133
38134    // private
38135    function onBeforeShow(m){
38136        var pm = m.parentMenu;
38137        if(!pm && !m.allowOtherMenus){
38138            hideAll();
38139        }else if(pm && pm.activeChild && active != m){
38140            pm.activeChild.hide();
38141        }
38142    }
38143
38144    // private
38145    function onMouseDown(e){
38146        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
38147            hideAll();
38148        }
38149    }
38150
38151    // private
38152    function onBeforeCheck(mi, state){
38153        if(state){
38154            var g = groups[mi.group];
38155            for(var i = 0, l = g.length; i < l; i++){
38156                if(g[i] != mi){
38157                    g[i].setChecked(false);
38158                }
38159            }
38160        }
38161    }
38162
38163    return {
38164
38165        /**
38166         * Hides all menus that are currently visible
38167         */
38168        hideAll : function(){
38169             hideAll();  
38170        },
38171
38172        // private
38173        register : function(menu){
38174            if(!menus){
38175                init();
38176            }
38177            menus[menu.id] = menu;
38178            menu.on("beforehide", onBeforeHide);
38179            menu.on("hide", onHide);
38180            menu.on("beforeshow", onBeforeShow);
38181            menu.on("show", onShow);
38182            var g = menu.group;
38183            if(g && menu.events["checkchange"]){
38184                if(!groups[g]){
38185                    groups[g] = [];
38186                }
38187                groups[g].push(menu);
38188                menu.on("checkchange", onCheck);
38189            }
38190        },
38191
38192         /**
38193          * Returns a {@link Roo.menu.Menu} object
38194          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
38195          * be used to generate and return a new Menu instance.
38196          */
38197        get : function(menu){
38198            if(typeof menu == "string"){ // menu id
38199                return menus[menu];
38200            }else if(menu.events){  // menu instance
38201                return menu;
38202            }else if(typeof menu.length == 'number'){ // array of menu items?
38203                return new Roo.menu.Menu({items:menu});
38204            }else{ // otherwise, must be a config
38205                return new Roo.menu.Menu(menu);
38206            }
38207        },
38208
38209        // private
38210        unregister : function(menu){
38211            delete menus[menu.id];
38212            menu.un("beforehide", onBeforeHide);
38213            menu.un("hide", onHide);
38214            menu.un("beforeshow", onBeforeShow);
38215            menu.un("show", onShow);
38216            var g = menu.group;
38217            if(g && menu.events["checkchange"]){
38218                groups[g].remove(menu);
38219                menu.un("checkchange", onCheck);
38220            }
38221        },
38222
38223        // private
38224        registerCheckable : function(menuItem){
38225            var g = menuItem.group;
38226            if(g){
38227                if(!groups[g]){
38228                    groups[g] = [];
38229                }
38230                groups[g].push(menuItem);
38231                menuItem.on("beforecheckchange", onBeforeCheck);
38232            }
38233        },
38234
38235        // private
38236        unregisterCheckable : function(menuItem){
38237            var g = menuItem.group;
38238            if(g){
38239                groups[g].remove(menuItem);
38240                menuItem.un("beforecheckchange", onBeforeCheck);
38241            }
38242        }
38243    };
38244 }();/*
38245  * Based on:
38246  * Ext JS Library 1.1.1
38247  * Copyright(c) 2006-2007, Ext JS, LLC.
38248  *
38249  * Originally Released Under LGPL - original licence link has changed is not relivant.
38250  *
38251  * Fork - LGPL
38252  * <script type="text/javascript">
38253  */
38254  
38255
38256 /**
38257  * @class Roo.menu.BaseItem
38258  * @extends Roo.Component
38259  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
38260  * management and base configuration options shared by all menu components.
38261  * @constructor
38262  * Creates a new BaseItem
38263  * @param {Object} config Configuration options
38264  */
38265 Roo.menu.BaseItem = function(config){
38266     Roo.menu.BaseItem.superclass.constructor.call(this, config);
38267
38268     this.addEvents({
38269         /**
38270          * @event click
38271          * Fires when this item is clicked
38272          * @param {Roo.menu.BaseItem} this
38273          * @param {Roo.EventObject} e
38274          */
38275         click: true,
38276         /**
38277          * @event activate
38278          * Fires when this item is activated
38279          * @param {Roo.menu.BaseItem} this
38280          */
38281         activate : true,
38282         /**
38283          * @event deactivate
38284          * Fires when this item is deactivated
38285          * @param {Roo.menu.BaseItem} this
38286          */
38287         deactivate : true
38288     });
38289
38290     if(this.handler){
38291         this.on("click", this.handler, this.scope, true);
38292     }
38293 };
38294
38295 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
38296     /**
38297      * @cfg {Function} handler
38298      * A function that will handle the click event of this menu item (defaults to undefined)
38299      */
38300     /**
38301      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
38302      */
38303     canActivate : false,
38304     
38305      /**
38306      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
38307      */
38308     hidden: false,
38309     
38310     /**
38311      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
38312      */
38313     activeClass : "x-menu-item-active",
38314     /**
38315      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
38316      */
38317     hideOnClick : true,
38318     /**
38319      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
38320      */
38321     hideDelay : 100,
38322
38323     // private
38324     ctype: "Roo.menu.BaseItem",
38325
38326     // private
38327     actionMode : "container",
38328
38329     // private
38330     render : function(container, parentMenu){
38331         this.parentMenu = parentMenu;
38332         Roo.menu.BaseItem.superclass.render.call(this, container);
38333         this.container.menuItemId = this.id;
38334     },
38335
38336     // private
38337     onRender : function(container, position){
38338         this.el = Roo.get(this.el);
38339         container.dom.appendChild(this.el.dom);
38340     },
38341
38342     // private
38343     onClick : function(e){
38344         if(!this.disabled && this.fireEvent("click", this, e) !== false
38345                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38346             this.handleClick(e);
38347         }else{
38348             e.stopEvent();
38349         }
38350     },
38351
38352     // private
38353     activate : function(){
38354         if(this.disabled){
38355             return false;
38356         }
38357         var li = this.container;
38358         li.addClass(this.activeClass);
38359         this.region = li.getRegion().adjust(2, 2, -2, -2);
38360         this.fireEvent("activate", this);
38361         return true;
38362     },
38363
38364     // private
38365     deactivate : function(){
38366         this.container.removeClass(this.activeClass);
38367         this.fireEvent("deactivate", this);
38368     },
38369
38370     // private
38371     shouldDeactivate : function(e){
38372         return !this.region || !this.region.contains(e.getPoint());
38373     },
38374
38375     // private
38376     handleClick : function(e){
38377         if(this.hideOnClick){
38378             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38379         }
38380     },
38381
38382     // private
38383     expandMenu : function(autoActivate){
38384         // do nothing
38385     },
38386
38387     // private
38388     hideMenu : function(){
38389         // do nothing
38390     }
38391 });/*
38392  * Based on:
38393  * Ext JS Library 1.1.1
38394  * Copyright(c) 2006-2007, Ext JS, LLC.
38395  *
38396  * Originally Released Under LGPL - original licence link has changed is not relivant.
38397  *
38398  * Fork - LGPL
38399  * <script type="text/javascript">
38400  */
38401  
38402 /**
38403  * @class Roo.menu.Adapter
38404  * @extends Roo.menu.BaseItem
38405  * 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.
38406  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38407  * @constructor
38408  * Creates a new Adapter
38409  * @param {Object} config Configuration options
38410  */
38411 Roo.menu.Adapter = function(component, config){
38412     Roo.menu.Adapter.superclass.constructor.call(this, config);
38413     this.component = component;
38414 };
38415 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38416     // private
38417     canActivate : true,
38418
38419     // private
38420     onRender : function(container, position){
38421         this.component.render(container);
38422         this.el = this.component.getEl();
38423     },
38424
38425     // private
38426     activate : function(){
38427         if(this.disabled){
38428             return false;
38429         }
38430         this.component.focus();
38431         this.fireEvent("activate", this);
38432         return true;
38433     },
38434
38435     // private
38436     deactivate : function(){
38437         this.fireEvent("deactivate", this);
38438     },
38439
38440     // private
38441     disable : function(){
38442         this.component.disable();
38443         Roo.menu.Adapter.superclass.disable.call(this);
38444     },
38445
38446     // private
38447     enable : function(){
38448         this.component.enable();
38449         Roo.menu.Adapter.superclass.enable.call(this);
38450     }
38451 });/*
38452  * Based on:
38453  * Ext JS Library 1.1.1
38454  * Copyright(c) 2006-2007, Ext JS, LLC.
38455  *
38456  * Originally Released Under LGPL - original licence link has changed is not relivant.
38457  *
38458  * Fork - LGPL
38459  * <script type="text/javascript">
38460  */
38461
38462 /**
38463  * @class Roo.menu.TextItem
38464  * @extends Roo.menu.BaseItem
38465  * Adds a static text string to a menu, usually used as either a heading or group separator.
38466  * Note: old style constructor with text is still supported.
38467  * 
38468  * @constructor
38469  * Creates a new TextItem
38470  * @param {Object} cfg Configuration
38471  */
38472 Roo.menu.TextItem = function(cfg){
38473     if (typeof(cfg) == 'string') {
38474         this.text = cfg;
38475     } else {
38476         Roo.apply(this,cfg);
38477     }
38478     
38479     Roo.menu.TextItem.superclass.constructor.call(this);
38480 };
38481
38482 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38483     /**
38484      * @cfg {String} text Text to show on item.
38485      */
38486     text : '',
38487     
38488     /**
38489      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38490      */
38491     hideOnClick : false,
38492     /**
38493      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38494      */
38495     itemCls : "x-menu-text",
38496
38497     // private
38498     onRender : function(){
38499         var s = document.createElement("span");
38500         s.className = this.itemCls;
38501         s.innerHTML = this.text;
38502         this.el = s;
38503         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38504     }
38505 });/*
38506  * Based on:
38507  * Ext JS Library 1.1.1
38508  * Copyright(c) 2006-2007, Ext JS, LLC.
38509  *
38510  * Originally Released Under LGPL - original licence link has changed is not relivant.
38511  *
38512  * Fork - LGPL
38513  * <script type="text/javascript">
38514  */
38515
38516 /**
38517  * @class Roo.menu.Separator
38518  * @extends Roo.menu.BaseItem
38519  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38520  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38521  * @constructor
38522  * @param {Object} config Configuration options
38523  */
38524 Roo.menu.Separator = function(config){
38525     Roo.menu.Separator.superclass.constructor.call(this, config);
38526 };
38527
38528 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38529     /**
38530      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38531      */
38532     itemCls : "x-menu-sep",
38533     /**
38534      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38535      */
38536     hideOnClick : false,
38537
38538     // private
38539     onRender : function(li){
38540         var s = document.createElement("span");
38541         s.className = this.itemCls;
38542         s.innerHTML = "&#160;";
38543         this.el = s;
38544         li.addClass("x-menu-sep-li");
38545         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38546     }
38547 });/*
38548  * Based on:
38549  * Ext JS Library 1.1.1
38550  * Copyright(c) 2006-2007, Ext JS, LLC.
38551  *
38552  * Originally Released Under LGPL - original licence link has changed is not relivant.
38553  *
38554  * Fork - LGPL
38555  * <script type="text/javascript">
38556  */
38557 /**
38558  * @class Roo.menu.Item
38559  * @extends Roo.menu.BaseItem
38560  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38561  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38562  * activation and click handling.
38563  * @constructor
38564  * Creates a new Item
38565  * @param {Object} config Configuration options
38566  */
38567 Roo.menu.Item = function(config){
38568     Roo.menu.Item.superclass.constructor.call(this, config);
38569     if(this.menu){
38570         this.menu = Roo.menu.MenuMgr.get(this.menu);
38571     }
38572 };
38573 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38574     
38575     /**
38576      * @cfg {String} text
38577      * The text to show on the menu item.
38578      */
38579     text: '',
38580      /**
38581      * @cfg {String} HTML to render in menu
38582      * The text to show on the menu item (HTML version).
38583      */
38584     html: '',
38585     /**
38586      * @cfg {String} icon
38587      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38588      */
38589     icon: undefined,
38590     /**
38591      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38592      */
38593     itemCls : "x-menu-item",
38594     /**
38595      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38596      */
38597     canActivate : true,
38598     /**
38599      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38600      */
38601     showDelay: 200,
38602     // doc'd in BaseItem
38603     hideDelay: 200,
38604
38605     // private
38606     ctype: "Roo.menu.Item",
38607     
38608     // private
38609     onRender : function(container, position){
38610         var el = document.createElement("a");
38611         el.hideFocus = true;
38612         el.unselectable = "on";
38613         el.href = this.href || "#";
38614         if(this.hrefTarget){
38615             el.target = this.hrefTarget;
38616         }
38617         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38618         
38619         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38620         
38621         el.innerHTML = String.format(
38622                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38623                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38624         this.el = el;
38625         Roo.menu.Item.superclass.onRender.call(this, container, position);
38626     },
38627
38628     /**
38629      * Sets the text to display in this menu item
38630      * @param {String} text The text to display
38631      * @param {Boolean} isHTML true to indicate text is pure html.
38632      */
38633     setText : function(text, isHTML){
38634         if (isHTML) {
38635             this.html = text;
38636         } else {
38637             this.text = text;
38638             this.html = '';
38639         }
38640         if(this.rendered){
38641             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38642      
38643             this.el.update(String.format(
38644                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38645                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38646             this.parentMenu.autoWidth();
38647         }
38648     },
38649
38650     // private
38651     handleClick : function(e){
38652         if(!this.href){ // if no link defined, stop the event automatically
38653             e.stopEvent();
38654         }
38655         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38656     },
38657
38658     // private
38659     activate : function(autoExpand){
38660         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38661             this.focus();
38662             if(autoExpand){
38663                 this.expandMenu();
38664             }
38665         }
38666         return true;
38667     },
38668
38669     // private
38670     shouldDeactivate : function(e){
38671         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38672             if(this.menu && this.menu.isVisible()){
38673                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38674             }
38675             return true;
38676         }
38677         return false;
38678     },
38679
38680     // private
38681     deactivate : function(){
38682         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38683         this.hideMenu();
38684     },
38685
38686     // private
38687     expandMenu : function(autoActivate){
38688         if(!this.disabled && this.menu){
38689             clearTimeout(this.hideTimer);
38690             delete this.hideTimer;
38691             if(!this.menu.isVisible() && !this.showTimer){
38692                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38693             }else if (this.menu.isVisible() && autoActivate){
38694                 this.menu.tryActivate(0, 1);
38695             }
38696         }
38697     },
38698
38699     // private
38700     deferExpand : function(autoActivate){
38701         delete this.showTimer;
38702         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38703         if(autoActivate){
38704             this.menu.tryActivate(0, 1);
38705         }
38706     },
38707
38708     // private
38709     hideMenu : function(){
38710         clearTimeout(this.showTimer);
38711         delete this.showTimer;
38712         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38713             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38714         }
38715     },
38716
38717     // private
38718     deferHide : function(){
38719         delete this.hideTimer;
38720         this.menu.hide();
38721     }
38722 });/*
38723  * Based on:
38724  * Ext JS Library 1.1.1
38725  * Copyright(c) 2006-2007, Ext JS, LLC.
38726  *
38727  * Originally Released Under LGPL - original licence link has changed is not relivant.
38728  *
38729  * Fork - LGPL
38730  * <script type="text/javascript">
38731  */
38732  
38733 /**
38734  * @class Roo.menu.CheckItem
38735  * @extends Roo.menu.Item
38736  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38737  * @constructor
38738  * Creates a new CheckItem
38739  * @param {Object} config Configuration options
38740  */
38741 Roo.menu.CheckItem = function(config){
38742     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38743     this.addEvents({
38744         /**
38745          * @event beforecheckchange
38746          * Fires before the checked value is set, providing an opportunity to cancel if needed
38747          * @param {Roo.menu.CheckItem} this
38748          * @param {Boolean} checked The new checked value that will be set
38749          */
38750         "beforecheckchange" : true,
38751         /**
38752          * @event checkchange
38753          * Fires after the checked value has been set
38754          * @param {Roo.menu.CheckItem} this
38755          * @param {Boolean} checked The checked value that was set
38756          */
38757         "checkchange" : true
38758     });
38759     if(this.checkHandler){
38760         this.on('checkchange', this.checkHandler, this.scope);
38761     }
38762 };
38763 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38764     /**
38765      * @cfg {String} group
38766      * All check items with the same group name will automatically be grouped into a single-select
38767      * radio button group (defaults to '')
38768      */
38769     /**
38770      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38771      */
38772     itemCls : "x-menu-item x-menu-check-item",
38773     /**
38774      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38775      */
38776     groupClass : "x-menu-group-item",
38777
38778     /**
38779      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38780      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38781      * initialized with checked = true will be rendered as checked.
38782      */
38783     checked: false,
38784
38785     // private
38786     ctype: "Roo.menu.CheckItem",
38787
38788     // private
38789     onRender : function(c){
38790         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38791         if(this.group){
38792             this.el.addClass(this.groupClass);
38793         }
38794         Roo.menu.MenuMgr.registerCheckable(this);
38795         if(this.checked){
38796             this.checked = false;
38797             this.setChecked(true, true);
38798         }
38799     },
38800
38801     // private
38802     destroy : function(){
38803         if(this.rendered){
38804             Roo.menu.MenuMgr.unregisterCheckable(this);
38805         }
38806         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38807     },
38808
38809     /**
38810      * Set the checked state of this item
38811      * @param {Boolean} checked The new checked value
38812      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38813      */
38814     setChecked : function(state, suppressEvent){
38815         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38816             if(this.container){
38817                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38818             }
38819             this.checked = state;
38820             if(suppressEvent !== true){
38821                 this.fireEvent("checkchange", this, state);
38822             }
38823         }
38824     },
38825
38826     // private
38827     handleClick : function(e){
38828        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38829            this.setChecked(!this.checked);
38830        }
38831        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38832     }
38833 });/*
38834  * Based on:
38835  * Ext JS Library 1.1.1
38836  * Copyright(c) 2006-2007, Ext JS, LLC.
38837  *
38838  * Originally Released Under LGPL - original licence link has changed is not relivant.
38839  *
38840  * Fork - LGPL
38841  * <script type="text/javascript">
38842  */
38843  
38844 /**
38845  * @class Roo.menu.DateItem
38846  * @extends Roo.menu.Adapter
38847  * A menu item that wraps the {@link Roo.DatPicker} component.
38848  * @constructor
38849  * Creates a new DateItem
38850  * @param {Object} config Configuration options
38851  */
38852 Roo.menu.DateItem = function(config){
38853     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38854     /** The Roo.DatePicker object @type Roo.DatePicker */
38855     this.picker = this.component;
38856     this.addEvents({select: true});
38857     
38858     this.picker.on("render", function(picker){
38859         picker.getEl().swallowEvent("click");
38860         picker.container.addClass("x-menu-date-item");
38861     });
38862
38863     this.picker.on("select", this.onSelect, this);
38864 };
38865
38866 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38867     // private
38868     onSelect : function(picker, date){
38869         this.fireEvent("select", this, date, picker);
38870         Roo.menu.DateItem.superclass.handleClick.call(this);
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.menu.ColorItem
38885  * @extends Roo.menu.Adapter
38886  * A menu item that wraps the {@link Roo.ColorPalette} component.
38887  * @constructor
38888  * Creates a new ColorItem
38889  * @param {Object} config Configuration options
38890  */
38891 Roo.menu.ColorItem = function(config){
38892     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38893     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38894     this.palette = this.component;
38895     this.relayEvents(this.palette, ["select"]);
38896     if(this.selectHandler){
38897         this.on('select', this.selectHandler, this.scope);
38898     }
38899 };
38900 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38901  * Based on:
38902  * Ext JS Library 1.1.1
38903  * Copyright(c) 2006-2007, Ext JS, LLC.
38904  *
38905  * Originally Released Under LGPL - original licence link has changed is not relivant.
38906  *
38907  * Fork - LGPL
38908  * <script type="text/javascript">
38909  */
38910  
38911
38912 /**
38913  * @class Roo.menu.DateMenu
38914  * @extends Roo.menu.Menu
38915  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38916  * @constructor
38917  * Creates a new DateMenu
38918  * @param {Object} config Configuration options
38919  */
38920 Roo.menu.DateMenu = function(config){
38921     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38922     this.plain = true;
38923     var di = new Roo.menu.DateItem(config);
38924     this.add(di);
38925     /**
38926      * The {@link Roo.DatePicker} instance for this DateMenu
38927      * @type DatePicker
38928      */
38929     this.picker = di.picker;
38930     /**
38931      * @event select
38932      * @param {DatePicker} picker
38933      * @param {Date} date
38934      */
38935     this.relayEvents(di, ["select"]);
38936     this.on('beforeshow', function(){
38937         if(this.picker){
38938             this.picker.hideMonthPicker(false);
38939         }
38940     }, this);
38941 };
38942 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38943     cls:'x-date-menu'
38944 });/*
38945  * Based on:
38946  * Ext JS Library 1.1.1
38947  * Copyright(c) 2006-2007, Ext JS, LLC.
38948  *
38949  * Originally Released Under LGPL - original licence link has changed is not relivant.
38950  *
38951  * Fork - LGPL
38952  * <script type="text/javascript">
38953  */
38954  
38955
38956 /**
38957  * @class Roo.menu.ColorMenu
38958  * @extends Roo.menu.Menu
38959  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38960  * @constructor
38961  * Creates a new ColorMenu
38962  * @param {Object} config Configuration options
38963  */
38964 Roo.menu.ColorMenu = function(config){
38965     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38966     this.plain = true;
38967     var ci = new Roo.menu.ColorItem(config);
38968     this.add(ci);
38969     /**
38970      * The {@link Roo.ColorPalette} instance for this ColorMenu
38971      * @type ColorPalette
38972      */
38973     this.palette = ci.palette;
38974     /**
38975      * @event select
38976      * @param {ColorPalette} palette
38977      * @param {String} color
38978      */
38979     this.relayEvents(ci, ["select"]);
38980 };
38981 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38982  * Based on:
38983  * Ext JS Library 1.1.1
38984  * Copyright(c) 2006-2007, Ext JS, LLC.
38985  *
38986  * Originally Released Under LGPL - original licence link has changed is not relivant.
38987  *
38988  * Fork - LGPL
38989  * <script type="text/javascript">
38990  */
38991  
38992 /**
38993  * @class Roo.form.TextItem
38994  * @extends Roo.BoxComponent
38995  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38996  * @constructor
38997  * Creates a new TextItem
38998  * @param {Object} config Configuration options
38999  */
39000 Roo.form.TextItem = function(config){
39001     Roo.form.TextItem.superclass.constructor.call(this, config);
39002 };
39003
39004 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
39005     
39006     /**
39007      * @cfg {String} tag the tag for this item (default div)
39008      */
39009     tag : 'div',
39010     /**
39011      * @cfg {String} html the content for this item
39012      */
39013     html : '',
39014     
39015     getAutoCreate : function()
39016     {
39017         var cfg = {
39018             id: this.id,
39019             tag: this.tag,
39020             html: this.html,
39021             cls: 'x-form-item'
39022         };
39023         
39024         return cfg;
39025         
39026     },
39027     
39028     onRender : function(ct, position)
39029     {
39030         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
39031         
39032         if(!this.el){
39033             var cfg = this.getAutoCreate();
39034             if(!cfg.name){
39035                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39036             }
39037             if (!cfg.name.length) {
39038                 delete cfg.name;
39039             }
39040             this.el = ct.createChild(cfg, position);
39041         }
39042     },
39043     /*
39044      * setHTML
39045      * @param {String} html update the Contents of the element.
39046      */
39047     setHTML : function(html)
39048     {
39049         this.fieldEl.dom.innerHTML = html;
39050     }
39051     
39052 });/*
39053  * Based on:
39054  * Ext JS Library 1.1.1
39055  * Copyright(c) 2006-2007, Ext JS, LLC.
39056  *
39057  * Originally Released Under LGPL - original licence link has changed is not relivant.
39058  *
39059  * Fork - LGPL
39060  * <script type="text/javascript">
39061  */
39062  
39063 /**
39064  * @class Roo.form.Field
39065  * @extends Roo.BoxComponent
39066  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39067  * @constructor
39068  * Creates a new Field
39069  * @param {Object} config Configuration options
39070  */
39071 Roo.form.Field = function(config){
39072     Roo.form.Field.superclass.constructor.call(this, config);
39073 };
39074
39075 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
39076     /**
39077      * @cfg {String} fieldLabel Label to use when rendering a form.
39078      */
39079        /**
39080      * @cfg {String} qtip Mouse over tip
39081      */
39082      
39083     /**
39084      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
39085      */
39086     invalidClass : "x-form-invalid",
39087     /**
39088      * @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")
39089      */
39090     invalidText : "The value in this field is invalid",
39091     /**
39092      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
39093      */
39094     focusClass : "x-form-focus",
39095     /**
39096      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
39097       automatic validation (defaults to "keyup").
39098      */
39099     validationEvent : "keyup",
39100     /**
39101      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
39102      */
39103     validateOnBlur : true,
39104     /**
39105      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
39106      */
39107     validationDelay : 250,
39108     /**
39109      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39110      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
39111      */
39112     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
39113     /**
39114      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
39115      */
39116     fieldClass : "x-form-field",
39117     /**
39118      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
39119      *<pre>
39120 Value         Description
39121 -----------   ----------------------------------------------------------------------
39122 qtip          Display a quick tip when the user hovers over the field
39123 title         Display a default browser title attribute popup
39124 under         Add a block div beneath the field containing the error text
39125 side          Add an error icon to the right of the field with a popup on hover
39126 [element id]  Add the error text directly to the innerHTML of the specified element
39127 </pre>
39128      */
39129     msgTarget : 'qtip',
39130     /**
39131      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
39132      */
39133     msgFx : 'normal',
39134
39135     /**
39136      * @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.
39137      */
39138     readOnly : false,
39139
39140     /**
39141      * @cfg {Boolean} disabled True to disable the field (defaults to false).
39142      */
39143     disabled : false,
39144
39145     /**
39146      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
39147      */
39148     inputType : undefined,
39149     
39150     /**
39151      * @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).
39152          */
39153         tabIndex : undefined,
39154         
39155     // private
39156     isFormField : true,
39157
39158     // private
39159     hasFocus : false,
39160     /**
39161      * @property {Roo.Element} fieldEl
39162      * Element Containing the rendered Field (with label etc.)
39163      */
39164     /**
39165      * @cfg {Mixed} value A value to initialize this field with.
39166      */
39167     value : undefined,
39168
39169     /**
39170      * @cfg {String} name The field's HTML name attribute.
39171      */
39172     /**
39173      * @cfg {String} cls A CSS class to apply to the field's underlying element.
39174      */
39175     // private
39176     loadedValue : false,
39177      
39178      
39179         // private ??
39180         initComponent : function(){
39181         Roo.form.Field.superclass.initComponent.call(this);
39182         this.addEvents({
39183             /**
39184              * @event focus
39185              * Fires when this field receives input focus.
39186              * @param {Roo.form.Field} this
39187              */
39188             focus : true,
39189             /**
39190              * @event blur
39191              * Fires when this field loses input focus.
39192              * @param {Roo.form.Field} this
39193              */
39194             blur : true,
39195             /**
39196              * @event specialkey
39197              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
39198              * {@link Roo.EventObject#getKey} to determine which key was pressed.
39199              * @param {Roo.form.Field} this
39200              * @param {Roo.EventObject} e The event object
39201              */
39202             specialkey : true,
39203             /**
39204              * @event change
39205              * Fires just before the field blurs if the field value has changed.
39206              * @param {Roo.form.Field} this
39207              * @param {Mixed} newValue The new value
39208              * @param {Mixed} oldValue The original value
39209              */
39210             change : true,
39211             /**
39212              * @event invalid
39213              * Fires after the field has been marked as invalid.
39214              * @param {Roo.form.Field} this
39215              * @param {String} msg The validation message
39216              */
39217             invalid : true,
39218             /**
39219              * @event valid
39220              * Fires after the field has been validated with no errors.
39221              * @param {Roo.form.Field} this
39222              */
39223             valid : true,
39224              /**
39225              * @event keyup
39226              * Fires after the key up
39227              * @param {Roo.form.Field} this
39228              * @param {Roo.EventObject}  e The event Object
39229              */
39230             keyup : true
39231         });
39232     },
39233
39234     /**
39235      * Returns the name attribute of the field if available
39236      * @return {String} name The field name
39237      */
39238     getName: function(){
39239          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
39240     },
39241
39242     // private
39243     onRender : function(ct, position){
39244         Roo.form.Field.superclass.onRender.call(this, ct, position);
39245         if(!this.el){
39246             var cfg = this.getAutoCreate();
39247             if(!cfg.name){
39248                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39249             }
39250             if (!cfg.name.length) {
39251                 delete cfg.name;
39252             }
39253             if(this.inputType){
39254                 cfg.type = this.inputType;
39255             }
39256             this.el = ct.createChild(cfg, position);
39257         }
39258         var type = this.el.dom.type;
39259         if(type){
39260             if(type == 'password'){
39261                 type = 'text';
39262             }
39263             this.el.addClass('x-form-'+type);
39264         }
39265         if(this.readOnly){
39266             this.el.dom.readOnly = true;
39267         }
39268         if(this.tabIndex !== undefined){
39269             this.el.dom.setAttribute('tabIndex', this.tabIndex);
39270         }
39271
39272         this.el.addClass([this.fieldClass, this.cls]);
39273         this.initValue();
39274     },
39275
39276     /**
39277      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
39278      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
39279      * @return {Roo.form.Field} this
39280      */
39281     applyTo : function(target){
39282         this.allowDomMove = false;
39283         this.el = Roo.get(target);
39284         this.render(this.el.dom.parentNode);
39285         return this;
39286     },
39287
39288     // private
39289     initValue : function(){
39290         if(this.value !== undefined){
39291             this.setValue(this.value);
39292         }else if(this.el.dom.value.length > 0){
39293             this.setValue(this.el.dom.value);
39294         }
39295     },
39296
39297     /**
39298      * Returns true if this field has been changed since it was originally loaded and is not disabled.
39299      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
39300      */
39301     isDirty : function() {
39302         if(this.disabled) {
39303             return false;
39304         }
39305         return String(this.getValue()) !== String(this.originalValue);
39306     },
39307
39308     /**
39309      * stores the current value in loadedValue
39310      */
39311     resetHasChanged : function()
39312     {
39313         this.loadedValue = String(this.getValue());
39314     },
39315     /**
39316      * checks the current value against the 'loaded' value.
39317      * Note - will return false if 'resetHasChanged' has not been called first.
39318      */
39319     hasChanged : function()
39320     {
39321         if(this.disabled || this.readOnly) {
39322             return false;
39323         }
39324         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
39325     },
39326     
39327     
39328     
39329     // private
39330     afterRender : function(){
39331         Roo.form.Field.superclass.afterRender.call(this);
39332         this.initEvents();
39333     },
39334
39335     // private
39336     fireKey : function(e){
39337         //Roo.log('field ' + e.getKey());
39338         if(e.isNavKeyPress()){
39339             this.fireEvent("specialkey", this, e);
39340         }
39341     },
39342
39343     /**
39344      * Resets the current field value to the originally loaded value and clears any validation messages
39345      */
39346     reset : function(){
39347         this.setValue(this.resetValue);
39348         this.originalValue = this.getValue();
39349         this.clearInvalid();
39350     },
39351
39352     // private
39353     initEvents : function(){
39354         // safari killled keypress - so keydown is now used..
39355         this.el.on("keydown" , this.fireKey,  this);
39356         this.el.on("focus", this.onFocus,  this);
39357         this.el.on("blur", this.onBlur,  this);
39358         this.el.relayEvent('keyup', this);
39359
39360         // reference to original value for reset
39361         this.originalValue = this.getValue();
39362         this.resetValue =  this.getValue();
39363     },
39364
39365     // private
39366     onFocus : function(){
39367         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39368             this.el.addClass(this.focusClass);
39369         }
39370         if(!this.hasFocus){
39371             this.hasFocus = true;
39372             this.startValue = this.getValue();
39373             this.fireEvent("focus", this);
39374         }
39375     },
39376
39377     beforeBlur : Roo.emptyFn,
39378
39379     // private
39380     onBlur : function(){
39381         this.beforeBlur();
39382         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39383             this.el.removeClass(this.focusClass);
39384         }
39385         this.hasFocus = false;
39386         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
39387             this.validate();
39388         }
39389         var v = this.getValue();
39390         if(String(v) !== String(this.startValue)){
39391             this.fireEvent('change', this, v, this.startValue);
39392         }
39393         this.fireEvent("blur", this);
39394     },
39395
39396     /**
39397      * Returns whether or not the field value is currently valid
39398      * @param {Boolean} preventMark True to disable marking the field invalid
39399      * @return {Boolean} True if the value is valid, else false
39400      */
39401     isValid : function(preventMark){
39402         if(this.disabled){
39403             return true;
39404         }
39405         var restore = this.preventMark;
39406         this.preventMark = preventMark === true;
39407         var v = this.validateValue(this.processValue(this.getRawValue()));
39408         this.preventMark = restore;
39409         return v;
39410     },
39411
39412     /**
39413      * Validates the field value
39414      * @return {Boolean} True if the value is valid, else false
39415      */
39416     validate : function(){
39417         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39418             this.clearInvalid();
39419             return true;
39420         }
39421         return false;
39422     },
39423
39424     processValue : function(value){
39425         return value;
39426     },
39427
39428     // private
39429     // Subclasses should provide the validation implementation by overriding this
39430     validateValue : function(value){
39431         return true;
39432     },
39433
39434     /**
39435      * Mark this field as invalid
39436      * @param {String} msg The validation message
39437      */
39438     markInvalid : function(msg){
39439         if(!this.rendered || this.preventMark){ // not rendered
39440             return;
39441         }
39442         
39443         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39444         
39445         obj.el.addClass(this.invalidClass);
39446         msg = msg || this.invalidText;
39447         switch(this.msgTarget){
39448             case 'qtip':
39449                 obj.el.dom.qtip = msg;
39450                 obj.el.dom.qclass = 'x-form-invalid-tip';
39451                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39452                     Roo.QuickTips.enable();
39453                 }
39454                 break;
39455             case 'title':
39456                 this.el.dom.title = msg;
39457                 break;
39458             case 'under':
39459                 if(!this.errorEl){
39460                     var elp = this.el.findParent('.x-form-element', 5, true);
39461                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39462                     this.errorEl.setWidth(elp.getWidth(true)-20);
39463                 }
39464                 this.errorEl.update(msg);
39465                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39466                 break;
39467             case 'side':
39468                 if(!this.errorIcon){
39469                     var elp = this.el.findParent('.x-form-element', 5, true);
39470                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39471                 }
39472                 this.alignErrorIcon();
39473                 this.errorIcon.dom.qtip = msg;
39474                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39475                 this.errorIcon.show();
39476                 this.on('resize', this.alignErrorIcon, this);
39477                 break;
39478             default:
39479                 var t = Roo.getDom(this.msgTarget);
39480                 t.innerHTML = msg;
39481                 t.style.display = this.msgDisplay;
39482                 break;
39483         }
39484         this.fireEvent('invalid', this, msg);
39485     },
39486
39487     // private
39488     alignErrorIcon : function(){
39489         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39490     },
39491
39492     /**
39493      * Clear any invalid styles/messages for this field
39494      */
39495     clearInvalid : function(){
39496         if(!this.rendered || this.preventMark){ // not rendered
39497             return;
39498         }
39499         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39500         
39501         obj.el.removeClass(this.invalidClass);
39502         switch(this.msgTarget){
39503             case 'qtip':
39504                 obj.el.dom.qtip = '';
39505                 break;
39506             case 'title':
39507                 this.el.dom.title = '';
39508                 break;
39509             case 'under':
39510                 if(this.errorEl){
39511                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39512                 }
39513                 break;
39514             case 'side':
39515                 if(this.errorIcon){
39516                     this.errorIcon.dom.qtip = '';
39517                     this.errorIcon.hide();
39518                     this.un('resize', this.alignErrorIcon, this);
39519                 }
39520                 break;
39521             default:
39522                 var t = Roo.getDom(this.msgTarget);
39523                 t.innerHTML = '';
39524                 t.style.display = 'none';
39525                 break;
39526         }
39527         this.fireEvent('valid', this);
39528     },
39529
39530     /**
39531      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39532      * @return {Mixed} value The field value
39533      */
39534     getRawValue : function(){
39535         var v = this.el.getValue();
39536         
39537         return v;
39538     },
39539
39540     /**
39541      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39542      * @return {Mixed} value The field value
39543      */
39544     getValue : function(){
39545         var v = this.el.getValue();
39546          
39547         return v;
39548     },
39549
39550     /**
39551      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39552      * @param {Mixed} value The value to set
39553      */
39554     setRawValue : function(v){
39555         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39556     },
39557
39558     /**
39559      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39560      * @param {Mixed} value The value to set
39561      */
39562     setValue : function(v){
39563         this.value = v;
39564         if(this.rendered){
39565             this.el.dom.value = (v === null || v === undefined ? '' : v);
39566              this.validate();
39567         }
39568     },
39569
39570     adjustSize : function(w, h){
39571         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39572         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39573         return s;
39574     },
39575
39576     adjustWidth : function(tag, w){
39577         tag = tag.toLowerCase();
39578         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39579             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39580                 if(tag == 'input'){
39581                     return w + 2;
39582                 }
39583                 if(tag == 'textarea'){
39584                     return w-2;
39585                 }
39586             }else if(Roo.isOpera){
39587                 if(tag == 'input'){
39588                     return w + 2;
39589                 }
39590                 if(tag == 'textarea'){
39591                     return w-2;
39592                 }
39593             }
39594         }
39595         return w;
39596     }
39597 });
39598
39599
39600 // anything other than normal should be considered experimental
39601 Roo.form.Field.msgFx = {
39602     normal : {
39603         show: function(msgEl, f){
39604             msgEl.setDisplayed('block');
39605         },
39606
39607         hide : function(msgEl, f){
39608             msgEl.setDisplayed(false).update('');
39609         }
39610     },
39611
39612     slide : {
39613         show: function(msgEl, f){
39614             msgEl.slideIn('t', {stopFx:true});
39615         },
39616
39617         hide : function(msgEl, f){
39618             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39619         }
39620     },
39621
39622     slideRight : {
39623         show: function(msgEl, f){
39624             msgEl.fixDisplay();
39625             msgEl.alignTo(f.el, 'tl-tr');
39626             msgEl.slideIn('l', {stopFx:true});
39627         },
39628
39629         hide : function(msgEl, f){
39630             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39631         }
39632     }
39633 };/*
39634  * Based on:
39635  * Ext JS Library 1.1.1
39636  * Copyright(c) 2006-2007, Ext JS, LLC.
39637  *
39638  * Originally Released Under LGPL - original licence link has changed is not relivant.
39639  *
39640  * Fork - LGPL
39641  * <script type="text/javascript">
39642  */
39643  
39644
39645 /**
39646  * @class Roo.form.TextField
39647  * @extends Roo.form.Field
39648  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39649  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39650  * @constructor
39651  * Creates a new TextField
39652  * @param {Object} config Configuration options
39653  */
39654 Roo.form.TextField = function(config){
39655     Roo.form.TextField.superclass.constructor.call(this, config);
39656     this.addEvents({
39657         /**
39658          * @event autosize
39659          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39660          * according to the default logic, but this event provides a hook for the developer to apply additional
39661          * logic at runtime to resize the field if needed.
39662              * @param {Roo.form.Field} this This text field
39663              * @param {Number} width The new field width
39664              */
39665         autosize : true
39666     });
39667 };
39668
39669 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39670     /**
39671      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39672      */
39673     grow : false,
39674     /**
39675      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39676      */
39677     growMin : 30,
39678     /**
39679      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39680      */
39681     growMax : 800,
39682     /**
39683      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39684      */
39685     vtype : null,
39686     /**
39687      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39688      */
39689     maskRe : null,
39690     /**
39691      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39692      */
39693     disableKeyFilter : false,
39694     /**
39695      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39696      */
39697     allowBlank : true,
39698     /**
39699      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39700      */
39701     minLength : 0,
39702     /**
39703      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39704      */
39705     maxLength : Number.MAX_VALUE,
39706     /**
39707      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39708      */
39709     minLengthText : "The minimum length for this field is {0}",
39710     /**
39711      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39712      */
39713     maxLengthText : "The maximum length for this field is {0}",
39714     /**
39715      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39716      */
39717     selectOnFocus : false,
39718     /**
39719      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
39720      */    
39721     allowLeadingSpace : false,
39722     /**
39723      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39724      */
39725     blankText : "This field is required",
39726     /**
39727      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39728      * If available, this function will be called only after the basic validators all return true, and will be passed the
39729      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39730      */
39731     validator : null,
39732     /**
39733      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39734      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39735      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39736      */
39737     regex : null,
39738     /**
39739      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39740      */
39741     regexText : "",
39742     /**
39743      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39744      */
39745     emptyText : null,
39746    
39747
39748     // private
39749     initEvents : function()
39750     {
39751         if (this.emptyText) {
39752             this.el.attr('placeholder', this.emptyText);
39753         }
39754         
39755         Roo.form.TextField.superclass.initEvents.call(this);
39756         if(this.validationEvent == 'keyup'){
39757             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39758             this.el.on('keyup', this.filterValidation, this);
39759         }
39760         else if(this.validationEvent !== false){
39761             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39762         }
39763         
39764         if(this.selectOnFocus){
39765             this.on("focus", this.preFocus, this);
39766         }
39767         if (!this.allowLeadingSpace) {
39768             this.on('blur', this.cleanLeadingSpace, this);
39769         }
39770         
39771         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39772             this.el.on("keypress", this.filterKeys, this);
39773         }
39774         if(this.grow){
39775             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39776             this.el.on("click", this.autoSize,  this);
39777         }
39778         if(this.el.is('input[type=password]') && Roo.isSafari){
39779             this.el.on('keydown', this.SafariOnKeyDown, this);
39780         }
39781     },
39782
39783     processValue : function(value){
39784         if(this.stripCharsRe){
39785             var newValue = value.replace(this.stripCharsRe, '');
39786             if(newValue !== value){
39787                 this.setRawValue(newValue);
39788                 return newValue;
39789             }
39790         }
39791         return value;
39792     },
39793
39794     filterValidation : function(e){
39795         if(!e.isNavKeyPress()){
39796             this.validationTask.delay(this.validationDelay);
39797         }
39798     },
39799
39800     // private
39801     onKeyUp : function(e){
39802         if(!e.isNavKeyPress()){
39803             this.autoSize();
39804         }
39805     },
39806     // private - clean the leading white space
39807     cleanLeadingSpace : function(e)
39808     {
39809         if ( this.inputType == 'file') {
39810             return;
39811         }
39812         
39813         this.setValue((this.getValue() + '').replace(/^\s+/,''));
39814     },
39815     /**
39816      * Resets the current field value to the originally-loaded value and clears any validation messages.
39817      *  
39818      */
39819     reset : function(){
39820         Roo.form.TextField.superclass.reset.call(this);
39821        
39822     }, 
39823     // private
39824     preFocus : function(){
39825         
39826         if(this.selectOnFocus){
39827             this.el.dom.select();
39828         }
39829     },
39830
39831     
39832     // private
39833     filterKeys : function(e){
39834         var k = e.getKey();
39835         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39836             return;
39837         }
39838         var c = e.getCharCode(), cc = String.fromCharCode(c);
39839         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39840             return;
39841         }
39842         if(!this.maskRe.test(cc)){
39843             e.stopEvent();
39844         }
39845     },
39846
39847     setValue : function(v){
39848         
39849         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39850         
39851         this.autoSize();
39852     },
39853
39854     /**
39855      * Validates a value according to the field's validation rules and marks the field as invalid
39856      * if the validation fails
39857      * @param {Mixed} value The value to validate
39858      * @return {Boolean} True if the value is valid, else false
39859      */
39860     validateValue : function(value){
39861         if(value.length < 1)  { // if it's blank
39862              if(this.allowBlank){
39863                 this.clearInvalid();
39864                 return true;
39865              }else{
39866                 this.markInvalid(this.blankText);
39867                 return false;
39868              }
39869         }
39870         if(value.length < this.minLength){
39871             this.markInvalid(String.format(this.minLengthText, this.minLength));
39872             return false;
39873         }
39874         if(value.length > this.maxLength){
39875             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39876             return false;
39877         }
39878         if(this.vtype){
39879             var vt = Roo.form.VTypes;
39880             if(!vt[this.vtype](value, this)){
39881                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39882                 return false;
39883             }
39884         }
39885         if(typeof this.validator == "function"){
39886             var msg = this.validator(value);
39887             if(msg !== true){
39888                 this.markInvalid(msg);
39889                 return false;
39890             }
39891         }
39892         if(this.regex && !this.regex.test(value)){
39893             this.markInvalid(this.regexText);
39894             return false;
39895         }
39896         return true;
39897     },
39898
39899     /**
39900      * Selects text in this field
39901      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39902      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39903      */
39904     selectText : function(start, end){
39905         var v = this.getRawValue();
39906         if(v.length > 0){
39907             start = start === undefined ? 0 : start;
39908             end = end === undefined ? v.length : end;
39909             var d = this.el.dom;
39910             if(d.setSelectionRange){
39911                 d.setSelectionRange(start, end);
39912             }else if(d.createTextRange){
39913                 var range = d.createTextRange();
39914                 range.moveStart("character", start);
39915                 range.moveEnd("character", v.length-end);
39916                 range.select();
39917             }
39918         }
39919     },
39920
39921     /**
39922      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39923      * This only takes effect if grow = true, and fires the autosize event.
39924      */
39925     autoSize : function(){
39926         if(!this.grow || !this.rendered){
39927             return;
39928         }
39929         if(!this.metrics){
39930             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39931         }
39932         var el = this.el;
39933         var v = el.dom.value;
39934         var d = document.createElement('div');
39935         d.appendChild(document.createTextNode(v));
39936         v = d.innerHTML;
39937         d = null;
39938         v += "&#160;";
39939         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39940         this.el.setWidth(w);
39941         this.fireEvent("autosize", this, w);
39942     },
39943     
39944     // private
39945     SafariOnKeyDown : function(event)
39946     {
39947         // this is a workaround for a password hang bug on chrome/ webkit.
39948         
39949         var isSelectAll = false;
39950         
39951         if(this.el.dom.selectionEnd > 0){
39952             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39953         }
39954         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39955             event.preventDefault();
39956             this.setValue('');
39957             return;
39958         }
39959         
39960         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39961             
39962             event.preventDefault();
39963             // this is very hacky as keydown always get's upper case.
39964             
39965             var cc = String.fromCharCode(event.getCharCode());
39966             
39967             
39968             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39969             
39970         }
39971         
39972         
39973     }
39974 });/*
39975  * Based on:
39976  * Ext JS Library 1.1.1
39977  * Copyright(c) 2006-2007, Ext JS, LLC.
39978  *
39979  * Originally Released Under LGPL - original licence link has changed is not relivant.
39980  *
39981  * Fork - LGPL
39982  * <script type="text/javascript">
39983  */
39984  
39985 /**
39986  * @class Roo.form.Hidden
39987  * @extends Roo.form.TextField
39988  * Simple Hidden element used on forms 
39989  * 
39990  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39991  * 
39992  * @constructor
39993  * Creates a new Hidden form element.
39994  * @param {Object} config Configuration options
39995  */
39996
39997
39998
39999 // easy hidden field...
40000 Roo.form.Hidden = function(config){
40001     Roo.form.Hidden.superclass.constructor.call(this, config);
40002 };
40003   
40004 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
40005     fieldLabel:      '',
40006     inputType:      'hidden',
40007     width:          50,
40008     allowBlank:     true,
40009     labelSeparator: '',
40010     hidden:         true,
40011     itemCls :       'x-form-item-display-none'
40012
40013
40014 });
40015
40016
40017 /*
40018  * Based on:
40019  * Ext JS Library 1.1.1
40020  * Copyright(c) 2006-2007, Ext JS, LLC.
40021  *
40022  * Originally Released Under LGPL - original licence link has changed is not relivant.
40023  *
40024  * Fork - LGPL
40025  * <script type="text/javascript">
40026  */
40027  
40028 /**
40029  * @class Roo.form.TriggerField
40030  * @extends Roo.form.TextField
40031  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
40032  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
40033  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
40034  * for which you can provide a custom implementation.  For example:
40035  * <pre><code>
40036 var trigger = new Roo.form.TriggerField();
40037 trigger.onTriggerClick = myTriggerFn;
40038 trigger.applyTo('my-field');
40039 </code></pre>
40040  *
40041  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
40042  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
40043  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40044  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
40045  * @constructor
40046  * Create a new TriggerField.
40047  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
40048  * to the base TextField)
40049  */
40050 Roo.form.TriggerField = function(config){
40051     this.mimicing = false;
40052     Roo.form.TriggerField.superclass.constructor.call(this, config);
40053 };
40054
40055 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
40056     /**
40057      * @cfg {String} triggerClass A CSS class to apply to the trigger
40058      */
40059     /**
40060      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40061      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
40062      */
40063     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
40064     /**
40065      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
40066      */
40067     hideTrigger:false,
40068
40069     /** @cfg {Boolean} grow @hide */
40070     /** @cfg {Number} growMin @hide */
40071     /** @cfg {Number} growMax @hide */
40072
40073     /**
40074      * @hide 
40075      * @method
40076      */
40077     autoSize: Roo.emptyFn,
40078     // private
40079     monitorTab : true,
40080     // private
40081     deferHeight : true,
40082
40083     
40084     actionMode : 'wrap',
40085     // private
40086     onResize : function(w, h){
40087         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
40088         if(typeof w == 'number'){
40089             var x = w - this.trigger.getWidth();
40090             this.el.setWidth(this.adjustWidth('input', x));
40091             this.trigger.setStyle('left', x+'px');
40092         }
40093     },
40094
40095     // private
40096     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40097
40098     // private
40099     getResizeEl : function(){
40100         return this.wrap;
40101     },
40102
40103     // private
40104     getPositionEl : function(){
40105         return this.wrap;
40106     },
40107
40108     // private
40109     alignErrorIcon : function(){
40110         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
40111     },
40112
40113     // private
40114     onRender : function(ct, position){
40115         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
40116         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
40117         this.trigger = this.wrap.createChild(this.triggerConfig ||
40118                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
40119         if(this.hideTrigger){
40120             this.trigger.setDisplayed(false);
40121         }
40122         this.initTrigger();
40123         if(!this.width){
40124             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
40125         }
40126     },
40127
40128     // private
40129     initTrigger : function(){
40130         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40131         this.trigger.addClassOnOver('x-form-trigger-over');
40132         this.trigger.addClassOnClick('x-form-trigger-click');
40133     },
40134
40135     // private
40136     onDestroy : function(){
40137         if(this.trigger){
40138             this.trigger.removeAllListeners();
40139             this.trigger.remove();
40140         }
40141         if(this.wrap){
40142             this.wrap.remove();
40143         }
40144         Roo.form.TriggerField.superclass.onDestroy.call(this);
40145     },
40146
40147     // private
40148     onFocus : function(){
40149         Roo.form.TriggerField.superclass.onFocus.call(this);
40150         if(!this.mimicing){
40151             this.wrap.addClass('x-trigger-wrap-focus');
40152             this.mimicing = true;
40153             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
40154             if(this.monitorTab){
40155                 this.el.on("keydown", this.checkTab, this);
40156             }
40157         }
40158     },
40159
40160     // private
40161     checkTab : function(e){
40162         if(e.getKey() == e.TAB){
40163             this.triggerBlur();
40164         }
40165     },
40166
40167     // private
40168     onBlur : function(){
40169         // do nothing
40170     },
40171
40172     // private
40173     mimicBlur : function(e, t){
40174         if(!this.wrap.contains(t) && this.validateBlur()){
40175             this.triggerBlur();
40176         }
40177     },
40178
40179     // private
40180     triggerBlur : function(){
40181         this.mimicing = false;
40182         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
40183         if(this.monitorTab){
40184             this.el.un("keydown", this.checkTab, this);
40185         }
40186         this.wrap.removeClass('x-trigger-wrap-focus');
40187         Roo.form.TriggerField.superclass.onBlur.call(this);
40188     },
40189
40190     // private
40191     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
40192     validateBlur : function(e, t){
40193         return true;
40194     },
40195
40196     // private
40197     onDisable : function(){
40198         Roo.form.TriggerField.superclass.onDisable.call(this);
40199         if(this.wrap){
40200             this.wrap.addClass('x-item-disabled');
40201         }
40202     },
40203
40204     // private
40205     onEnable : function(){
40206         Roo.form.TriggerField.superclass.onEnable.call(this);
40207         if(this.wrap){
40208             this.wrap.removeClass('x-item-disabled');
40209         }
40210     },
40211
40212     // private
40213     onShow : function(){
40214         var ae = this.getActionEl();
40215         
40216         if(ae){
40217             ae.dom.style.display = '';
40218             ae.dom.style.visibility = 'visible';
40219         }
40220     },
40221
40222     // private
40223     
40224     onHide : function(){
40225         var ae = this.getActionEl();
40226         ae.dom.style.display = 'none';
40227     },
40228
40229     /**
40230      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
40231      * by an implementing function.
40232      * @method
40233      * @param {EventObject} e
40234      */
40235     onTriggerClick : Roo.emptyFn
40236 });
40237
40238 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
40239 // to be extended by an implementing class.  For an example of implementing this class, see the custom
40240 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
40241 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
40242     initComponent : function(){
40243         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
40244
40245         this.triggerConfig = {
40246             tag:'span', cls:'x-form-twin-triggers', cn:[
40247             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
40248             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
40249         ]};
40250     },
40251
40252     getTrigger : function(index){
40253         return this.triggers[index];
40254     },
40255
40256     initTrigger : function(){
40257         var ts = this.trigger.select('.x-form-trigger', true);
40258         this.wrap.setStyle('overflow', 'hidden');
40259         var triggerField = this;
40260         ts.each(function(t, all, index){
40261             t.hide = function(){
40262                 var w = triggerField.wrap.getWidth();
40263                 this.dom.style.display = 'none';
40264                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40265             };
40266             t.show = function(){
40267                 var w = triggerField.wrap.getWidth();
40268                 this.dom.style.display = '';
40269                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40270             };
40271             var triggerIndex = 'Trigger'+(index+1);
40272
40273             if(this['hide'+triggerIndex]){
40274                 t.dom.style.display = 'none';
40275             }
40276             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
40277             t.addClassOnOver('x-form-trigger-over');
40278             t.addClassOnClick('x-form-trigger-click');
40279         }, this);
40280         this.triggers = ts.elements;
40281     },
40282
40283     onTrigger1Click : Roo.emptyFn,
40284     onTrigger2Click : Roo.emptyFn
40285 });/*
40286  * Based on:
40287  * Ext JS Library 1.1.1
40288  * Copyright(c) 2006-2007, Ext JS, LLC.
40289  *
40290  * Originally Released Under LGPL - original licence link has changed is not relivant.
40291  *
40292  * Fork - LGPL
40293  * <script type="text/javascript">
40294  */
40295  
40296 /**
40297  * @class Roo.form.TextArea
40298  * @extends Roo.form.TextField
40299  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
40300  * support for auto-sizing.
40301  * @constructor
40302  * Creates a new TextArea
40303  * @param {Object} config Configuration options
40304  */
40305 Roo.form.TextArea = function(config){
40306     Roo.form.TextArea.superclass.constructor.call(this, config);
40307     // these are provided exchanges for backwards compat
40308     // minHeight/maxHeight were replaced by growMin/growMax to be
40309     // compatible with TextField growing config values
40310     if(this.minHeight !== undefined){
40311         this.growMin = this.minHeight;
40312     }
40313     if(this.maxHeight !== undefined){
40314         this.growMax = this.maxHeight;
40315     }
40316 };
40317
40318 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
40319     /**
40320      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
40321      */
40322     growMin : 60,
40323     /**
40324      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
40325      */
40326     growMax: 1000,
40327     /**
40328      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
40329      * in the field (equivalent to setting overflow: hidden, defaults to false)
40330      */
40331     preventScrollbars: false,
40332     /**
40333      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40334      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
40335      */
40336
40337     // private
40338     onRender : function(ct, position){
40339         if(!this.el){
40340             this.defaultAutoCreate = {
40341                 tag: "textarea",
40342                 style:"width:300px;height:60px;",
40343                 autocomplete: "new-password"
40344             };
40345         }
40346         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
40347         if(this.grow){
40348             this.textSizeEl = Roo.DomHelper.append(document.body, {
40349                 tag: "pre", cls: "x-form-grow-sizer"
40350             });
40351             if(this.preventScrollbars){
40352                 this.el.setStyle("overflow", "hidden");
40353             }
40354             this.el.setHeight(this.growMin);
40355         }
40356     },
40357
40358     onDestroy : function(){
40359         if(this.textSizeEl){
40360             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
40361         }
40362         Roo.form.TextArea.superclass.onDestroy.call(this);
40363     },
40364
40365     // private
40366     onKeyUp : function(e){
40367         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
40368             this.autoSize();
40369         }
40370     },
40371
40372     /**
40373      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
40374      * This only takes effect if grow = true, and fires the autosize event if the height changes.
40375      */
40376     autoSize : function(){
40377         if(!this.grow || !this.textSizeEl){
40378             return;
40379         }
40380         var el = this.el;
40381         var v = el.dom.value;
40382         var ts = this.textSizeEl;
40383
40384         ts.innerHTML = '';
40385         ts.appendChild(document.createTextNode(v));
40386         v = ts.innerHTML;
40387
40388         Roo.fly(ts).setWidth(this.el.getWidth());
40389         if(v.length < 1){
40390             v = "&#160;&#160;";
40391         }else{
40392             if(Roo.isIE){
40393                 v = v.replace(/\n/g, '<p>&#160;</p>');
40394             }
40395             v += "&#160;\n&#160;";
40396         }
40397         ts.innerHTML = v;
40398         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
40399         if(h != this.lastHeight){
40400             this.lastHeight = h;
40401             this.el.setHeight(h);
40402             this.fireEvent("autosize", this, h);
40403         }
40404     }
40405 });/*
40406  * Based on:
40407  * Ext JS Library 1.1.1
40408  * Copyright(c) 2006-2007, Ext JS, LLC.
40409  *
40410  * Originally Released Under LGPL - original licence link has changed is not relivant.
40411  *
40412  * Fork - LGPL
40413  * <script type="text/javascript">
40414  */
40415  
40416
40417 /**
40418  * @class Roo.form.NumberField
40419  * @extends Roo.form.TextField
40420  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40421  * @constructor
40422  * Creates a new NumberField
40423  * @param {Object} config Configuration options
40424  */
40425 Roo.form.NumberField = function(config){
40426     Roo.form.NumberField.superclass.constructor.call(this, config);
40427 };
40428
40429 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40430     /**
40431      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40432      */
40433     fieldClass: "x-form-field x-form-num-field",
40434     /**
40435      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40436      */
40437     allowDecimals : true,
40438     /**
40439      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40440      */
40441     decimalSeparator : ".",
40442     /**
40443      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40444      */
40445     decimalPrecision : 2,
40446     /**
40447      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40448      */
40449     allowNegative : true,
40450     /**
40451      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40452      */
40453     minValue : Number.NEGATIVE_INFINITY,
40454     /**
40455      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40456      */
40457     maxValue : Number.MAX_VALUE,
40458     /**
40459      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40460      */
40461     minText : "The minimum value for this field is {0}",
40462     /**
40463      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40464      */
40465     maxText : "The maximum value for this field is {0}",
40466     /**
40467      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40468      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40469      */
40470     nanText : "{0} is not a valid number",
40471
40472     // private
40473     initEvents : function(){
40474         Roo.form.NumberField.superclass.initEvents.call(this);
40475         var allowed = "0123456789";
40476         if(this.allowDecimals){
40477             allowed += this.decimalSeparator;
40478         }
40479         if(this.allowNegative){
40480             allowed += "-";
40481         }
40482         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40483         var keyPress = function(e){
40484             var k = e.getKey();
40485             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40486                 return;
40487             }
40488             var c = e.getCharCode();
40489             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40490                 e.stopEvent();
40491             }
40492         };
40493         this.el.on("keypress", keyPress, this);
40494     },
40495
40496     // private
40497     validateValue : function(value){
40498         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40499             return false;
40500         }
40501         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40502              return true;
40503         }
40504         var num = this.parseValue(value);
40505         if(isNaN(num)){
40506             this.markInvalid(String.format(this.nanText, value));
40507             return false;
40508         }
40509         if(num < this.minValue){
40510             this.markInvalid(String.format(this.minText, this.minValue));
40511             return false;
40512         }
40513         if(num > this.maxValue){
40514             this.markInvalid(String.format(this.maxText, this.maxValue));
40515             return false;
40516         }
40517         return true;
40518     },
40519
40520     getValue : function(){
40521         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40522     },
40523
40524     // private
40525     parseValue : function(value){
40526         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40527         return isNaN(value) ? '' : value;
40528     },
40529
40530     // private
40531     fixPrecision : function(value){
40532         var nan = isNaN(value);
40533         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40534             return nan ? '' : value;
40535         }
40536         return parseFloat(value).toFixed(this.decimalPrecision);
40537     },
40538
40539     setValue : function(v){
40540         v = this.fixPrecision(v);
40541         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40542     },
40543
40544     // private
40545     decimalPrecisionFcn : function(v){
40546         return Math.floor(v);
40547     },
40548
40549     beforeBlur : function(){
40550         var v = this.parseValue(this.getRawValue());
40551         if(v){
40552             this.setValue(v);
40553         }
40554     }
40555 });/*
40556  * Based on:
40557  * Ext JS Library 1.1.1
40558  * Copyright(c) 2006-2007, Ext JS, LLC.
40559  *
40560  * Originally Released Under LGPL - original licence link has changed is not relivant.
40561  *
40562  * Fork - LGPL
40563  * <script type="text/javascript">
40564  */
40565  
40566 /**
40567  * @class Roo.form.DateField
40568  * @extends Roo.form.TriggerField
40569  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40570 * @constructor
40571 * Create a new DateField
40572 * @param {Object} config
40573  */
40574 Roo.form.DateField = function(config)
40575 {
40576     Roo.form.DateField.superclass.constructor.call(this, config);
40577     
40578       this.addEvents({
40579          
40580         /**
40581          * @event select
40582          * Fires when a date is selected
40583              * @param {Roo.form.DateField} combo This combo box
40584              * @param {Date} date The date selected
40585              */
40586         'select' : true
40587          
40588     });
40589     
40590     
40591     if(typeof this.minValue == "string") {
40592         this.minValue = this.parseDate(this.minValue);
40593     }
40594     if(typeof this.maxValue == "string") {
40595         this.maxValue = this.parseDate(this.maxValue);
40596     }
40597     this.ddMatch = null;
40598     if(this.disabledDates){
40599         var dd = this.disabledDates;
40600         var re = "(?:";
40601         for(var i = 0; i < dd.length; i++){
40602             re += dd[i];
40603             if(i != dd.length-1) {
40604                 re += "|";
40605             }
40606         }
40607         this.ddMatch = new RegExp(re + ")");
40608     }
40609 };
40610
40611 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40612     /**
40613      * @cfg {String} format
40614      * The default date format string which can be overriden for localization support.  The format must be
40615      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40616      */
40617     format : "m/d/y",
40618     /**
40619      * @cfg {String} altFormats
40620      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40621      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40622      */
40623     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40624     /**
40625      * @cfg {Array} disabledDays
40626      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40627      */
40628     disabledDays : null,
40629     /**
40630      * @cfg {String} disabledDaysText
40631      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40632      */
40633     disabledDaysText : "Disabled",
40634     /**
40635      * @cfg {Array} disabledDates
40636      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40637      * expression so they are very powerful. Some examples:
40638      * <ul>
40639      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40640      * <li>["03/08", "09/16"] would disable those days for every year</li>
40641      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40642      * <li>["03/../2006"] would disable every day in March 2006</li>
40643      * <li>["^03"] would disable every day in every March</li>
40644      * </ul>
40645      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40646      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40647      */
40648     disabledDates : null,
40649     /**
40650      * @cfg {String} disabledDatesText
40651      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40652      */
40653     disabledDatesText : "Disabled",
40654     /**
40655      * @cfg {Date/String} minValue
40656      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40657      * valid format (defaults to null).
40658      */
40659     minValue : null,
40660     /**
40661      * @cfg {Date/String} maxValue
40662      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40663      * valid format (defaults to null).
40664      */
40665     maxValue : null,
40666     /**
40667      * @cfg {String} minText
40668      * The error text to display when the date in the cell is before minValue (defaults to
40669      * 'The date in this field must be after {minValue}').
40670      */
40671     minText : "The date in this field must be equal to or after {0}",
40672     /**
40673      * @cfg {String} maxText
40674      * The error text to display when the date in the cell is after maxValue (defaults to
40675      * 'The date in this field must be before {maxValue}').
40676      */
40677     maxText : "The date in this field must be equal to or before {0}",
40678     /**
40679      * @cfg {String} invalidText
40680      * The error text to display when the date in the field is invalid (defaults to
40681      * '{value} is not a valid date - it must be in the format {format}').
40682      */
40683     invalidText : "{0} is not a valid date - it must be in the format {1}",
40684     /**
40685      * @cfg {String} triggerClass
40686      * An additional CSS class used to style the trigger button.  The trigger will always get the
40687      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40688      * which displays a calendar icon).
40689      */
40690     triggerClass : 'x-form-date-trigger',
40691     
40692
40693     /**
40694      * @cfg {Boolean} useIso
40695      * if enabled, then the date field will use a hidden field to store the 
40696      * real value as iso formated date. default (false)
40697      */ 
40698     useIso : false,
40699     /**
40700      * @cfg {String/Object} autoCreate
40701      * A DomHelper element spec, or true for a default element spec (defaults to
40702      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40703      */ 
40704     // private
40705     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40706     
40707     // private
40708     hiddenField: false,
40709     
40710     onRender : function(ct, position)
40711     {
40712         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40713         if (this.useIso) {
40714             //this.el.dom.removeAttribute('name'); 
40715             Roo.log("Changing name?");
40716             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40717             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40718                     'before', true);
40719             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40720             // prevent input submission
40721             this.hiddenName = this.name;
40722         }
40723             
40724             
40725     },
40726     
40727     // private
40728     validateValue : function(value)
40729     {
40730         value = this.formatDate(value);
40731         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40732             Roo.log('super failed');
40733             return false;
40734         }
40735         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40736              return true;
40737         }
40738         var svalue = value;
40739         value = this.parseDate(value);
40740         if(!value){
40741             Roo.log('parse date failed' + svalue);
40742             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40743             return false;
40744         }
40745         var time = value.getTime();
40746         if(this.minValue && time < this.minValue.getTime()){
40747             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40748             return false;
40749         }
40750         if(this.maxValue && time > this.maxValue.getTime()){
40751             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40752             return false;
40753         }
40754         if(this.disabledDays){
40755             var day = value.getDay();
40756             for(var i = 0; i < this.disabledDays.length; i++) {
40757                 if(day === this.disabledDays[i]){
40758                     this.markInvalid(this.disabledDaysText);
40759                     return false;
40760                 }
40761             }
40762         }
40763         var fvalue = this.formatDate(value);
40764         if(this.ddMatch && this.ddMatch.test(fvalue)){
40765             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40766             return false;
40767         }
40768         return true;
40769     },
40770
40771     // private
40772     // Provides logic to override the default TriggerField.validateBlur which just returns true
40773     validateBlur : function(){
40774         return !this.menu || !this.menu.isVisible();
40775     },
40776     
40777     getName: function()
40778     {
40779         // returns hidden if it's set..
40780         if (!this.rendered) {return ''};
40781         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40782         
40783     },
40784
40785     /**
40786      * Returns the current date value of the date field.
40787      * @return {Date} The date value
40788      */
40789     getValue : function(){
40790         
40791         return  this.hiddenField ?
40792                 this.hiddenField.value :
40793                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40794     },
40795
40796     /**
40797      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40798      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40799      * (the default format used is "m/d/y").
40800      * <br />Usage:
40801      * <pre><code>
40802 //All of these calls set the same date value (May 4, 2006)
40803
40804 //Pass a date object:
40805 var dt = new Date('5/4/06');
40806 dateField.setValue(dt);
40807
40808 //Pass a date string (default format):
40809 dateField.setValue('5/4/06');
40810
40811 //Pass a date string (custom format):
40812 dateField.format = 'Y-m-d';
40813 dateField.setValue('2006-5-4');
40814 </code></pre>
40815      * @param {String/Date} date The date or valid date string
40816      */
40817     setValue : function(date){
40818         if (this.hiddenField) {
40819             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40820         }
40821         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40822         // make sure the value field is always stored as a date..
40823         this.value = this.parseDate(date);
40824         
40825         
40826     },
40827
40828     // private
40829     parseDate : function(value){
40830         if(!value || value instanceof Date){
40831             return value;
40832         }
40833         var v = Date.parseDate(value, this.format);
40834          if (!v && this.useIso) {
40835             v = Date.parseDate(value, 'Y-m-d');
40836         }
40837         if(!v && this.altFormats){
40838             if(!this.altFormatsArray){
40839                 this.altFormatsArray = this.altFormats.split("|");
40840             }
40841             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40842                 v = Date.parseDate(value, this.altFormatsArray[i]);
40843             }
40844         }
40845         return v;
40846     },
40847
40848     // private
40849     formatDate : function(date, fmt){
40850         return (!date || !(date instanceof Date)) ?
40851                date : date.dateFormat(fmt || this.format);
40852     },
40853
40854     // private
40855     menuListeners : {
40856         select: function(m, d){
40857             
40858             this.setValue(d);
40859             this.fireEvent('select', this, d);
40860         },
40861         show : function(){ // retain focus styling
40862             this.onFocus();
40863         },
40864         hide : function(){
40865             this.focus.defer(10, this);
40866             var ml = this.menuListeners;
40867             this.menu.un("select", ml.select,  this);
40868             this.menu.un("show", ml.show,  this);
40869             this.menu.un("hide", ml.hide,  this);
40870         }
40871     },
40872
40873     // private
40874     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40875     onTriggerClick : function(){
40876         if(this.disabled){
40877             return;
40878         }
40879         if(this.menu == null){
40880             this.menu = new Roo.menu.DateMenu();
40881         }
40882         Roo.apply(this.menu.picker,  {
40883             showClear: this.allowBlank,
40884             minDate : this.minValue,
40885             maxDate : this.maxValue,
40886             disabledDatesRE : this.ddMatch,
40887             disabledDatesText : this.disabledDatesText,
40888             disabledDays : this.disabledDays,
40889             disabledDaysText : this.disabledDaysText,
40890             format : this.useIso ? 'Y-m-d' : this.format,
40891             minText : String.format(this.minText, this.formatDate(this.minValue)),
40892             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40893         });
40894         this.menu.on(Roo.apply({}, this.menuListeners, {
40895             scope:this
40896         }));
40897         this.menu.picker.setValue(this.getValue() || new Date());
40898         this.menu.show(this.el, "tl-bl?");
40899     },
40900
40901     beforeBlur : function(){
40902         var v = this.parseDate(this.getRawValue());
40903         if(v){
40904             this.setValue(v);
40905         }
40906     },
40907
40908     /*@
40909      * overide
40910      * 
40911      */
40912     isDirty : function() {
40913         if(this.disabled) {
40914             return false;
40915         }
40916         
40917         if(typeof(this.startValue) === 'undefined'){
40918             return false;
40919         }
40920         
40921         return String(this.getValue()) !== String(this.startValue);
40922         
40923     },
40924     // @overide
40925     cleanLeadingSpace : function(e)
40926     {
40927        return;
40928     }
40929     
40930 });/*
40931  * Based on:
40932  * Ext JS Library 1.1.1
40933  * Copyright(c) 2006-2007, Ext JS, LLC.
40934  *
40935  * Originally Released Under LGPL - original licence link has changed is not relivant.
40936  *
40937  * Fork - LGPL
40938  * <script type="text/javascript">
40939  */
40940  
40941 /**
40942  * @class Roo.form.MonthField
40943  * @extends Roo.form.TriggerField
40944  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40945 * @constructor
40946 * Create a new MonthField
40947 * @param {Object} config
40948  */
40949 Roo.form.MonthField = function(config){
40950     
40951     Roo.form.MonthField.superclass.constructor.call(this, config);
40952     
40953       this.addEvents({
40954          
40955         /**
40956          * @event select
40957          * Fires when a date is selected
40958              * @param {Roo.form.MonthFieeld} combo This combo box
40959              * @param {Date} date The date selected
40960              */
40961         'select' : true
40962          
40963     });
40964     
40965     
40966     if(typeof this.minValue == "string") {
40967         this.minValue = this.parseDate(this.minValue);
40968     }
40969     if(typeof this.maxValue == "string") {
40970         this.maxValue = this.parseDate(this.maxValue);
40971     }
40972     this.ddMatch = null;
40973     if(this.disabledDates){
40974         var dd = this.disabledDates;
40975         var re = "(?:";
40976         for(var i = 0; i < dd.length; i++){
40977             re += dd[i];
40978             if(i != dd.length-1) {
40979                 re += "|";
40980             }
40981         }
40982         this.ddMatch = new RegExp(re + ")");
40983     }
40984 };
40985
40986 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40987     /**
40988      * @cfg {String} format
40989      * The default date format string which can be overriden for localization support.  The format must be
40990      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40991      */
40992     format : "M Y",
40993     /**
40994      * @cfg {String} altFormats
40995      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40996      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40997      */
40998     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40999     /**
41000      * @cfg {Array} disabledDays
41001      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41002      */
41003     disabledDays : [0,1,2,3,4,5,6],
41004     /**
41005      * @cfg {String} disabledDaysText
41006      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41007      */
41008     disabledDaysText : "Disabled",
41009     /**
41010      * @cfg {Array} disabledDates
41011      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41012      * expression so they are very powerful. Some examples:
41013      * <ul>
41014      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41015      * <li>["03/08", "09/16"] would disable those days for every year</li>
41016      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41017      * <li>["03/../2006"] would disable every day in March 2006</li>
41018      * <li>["^03"] would disable every day in every March</li>
41019      * </ul>
41020      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41021      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41022      */
41023     disabledDates : null,
41024     /**
41025      * @cfg {String} disabledDatesText
41026      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41027      */
41028     disabledDatesText : "Disabled",
41029     /**
41030      * @cfg {Date/String} minValue
41031      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41032      * valid format (defaults to null).
41033      */
41034     minValue : null,
41035     /**
41036      * @cfg {Date/String} maxValue
41037      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41038      * valid format (defaults to null).
41039      */
41040     maxValue : null,
41041     /**
41042      * @cfg {String} minText
41043      * The error text to display when the date in the cell is before minValue (defaults to
41044      * 'The date in this field must be after {minValue}').
41045      */
41046     minText : "The date in this field must be equal to or after {0}",
41047     /**
41048      * @cfg {String} maxTextf
41049      * The error text to display when the date in the cell is after maxValue (defaults to
41050      * 'The date in this field must be before {maxValue}').
41051      */
41052     maxText : "The date in this field must be equal to or before {0}",
41053     /**
41054      * @cfg {String} invalidText
41055      * The error text to display when the date in the field is invalid (defaults to
41056      * '{value} is not a valid date - it must be in the format {format}').
41057      */
41058     invalidText : "{0} is not a valid date - it must be in the format {1}",
41059     /**
41060      * @cfg {String} triggerClass
41061      * An additional CSS class used to style the trigger button.  The trigger will always get the
41062      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41063      * which displays a calendar icon).
41064      */
41065     triggerClass : 'x-form-date-trigger',
41066     
41067
41068     /**
41069      * @cfg {Boolean} useIso
41070      * if enabled, then the date field will use a hidden field to store the 
41071      * real value as iso formated date. default (true)
41072      */ 
41073     useIso : true,
41074     /**
41075      * @cfg {String/Object} autoCreate
41076      * A DomHelper element spec, or true for a default element spec (defaults to
41077      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41078      */ 
41079     // private
41080     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
41081     
41082     // private
41083     hiddenField: false,
41084     
41085     hideMonthPicker : false,
41086     
41087     onRender : function(ct, position)
41088     {
41089         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
41090         if (this.useIso) {
41091             this.el.dom.removeAttribute('name'); 
41092             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41093                     'before', true);
41094             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41095             // prevent input submission
41096             this.hiddenName = this.name;
41097         }
41098             
41099             
41100     },
41101     
41102     // private
41103     validateValue : function(value)
41104     {
41105         value = this.formatDate(value);
41106         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
41107             return false;
41108         }
41109         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41110              return true;
41111         }
41112         var svalue = value;
41113         value = this.parseDate(value);
41114         if(!value){
41115             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41116             return false;
41117         }
41118         var time = value.getTime();
41119         if(this.minValue && time < this.minValue.getTime()){
41120             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41121             return false;
41122         }
41123         if(this.maxValue && time > this.maxValue.getTime()){
41124             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41125             return false;
41126         }
41127         /*if(this.disabledDays){
41128             var day = value.getDay();
41129             for(var i = 0; i < this.disabledDays.length; i++) {
41130                 if(day === this.disabledDays[i]){
41131                     this.markInvalid(this.disabledDaysText);
41132                     return false;
41133                 }
41134             }
41135         }
41136         */
41137         var fvalue = this.formatDate(value);
41138         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
41139             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41140             return false;
41141         }
41142         */
41143         return true;
41144     },
41145
41146     // private
41147     // Provides logic to override the default TriggerField.validateBlur which just returns true
41148     validateBlur : function(){
41149         return !this.menu || !this.menu.isVisible();
41150     },
41151
41152     /**
41153      * Returns the current date value of the date field.
41154      * @return {Date} The date value
41155      */
41156     getValue : function(){
41157         
41158         
41159         
41160         return  this.hiddenField ?
41161                 this.hiddenField.value :
41162                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
41163     },
41164
41165     /**
41166      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41167      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
41168      * (the default format used is "m/d/y").
41169      * <br />Usage:
41170      * <pre><code>
41171 //All of these calls set the same date value (May 4, 2006)
41172
41173 //Pass a date object:
41174 var dt = new Date('5/4/06');
41175 monthField.setValue(dt);
41176
41177 //Pass a date string (default format):
41178 monthField.setValue('5/4/06');
41179
41180 //Pass a date string (custom format):
41181 monthField.format = 'Y-m-d';
41182 monthField.setValue('2006-5-4');
41183 </code></pre>
41184      * @param {String/Date} date The date or valid date string
41185      */
41186     setValue : function(date){
41187         Roo.log('month setValue' + date);
41188         // can only be first of month..
41189         
41190         var val = this.parseDate(date);
41191         
41192         if (this.hiddenField) {
41193             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41194         }
41195         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41196         this.value = this.parseDate(date);
41197     },
41198
41199     // private
41200     parseDate : function(value){
41201         if(!value || value instanceof Date){
41202             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
41203             return value;
41204         }
41205         var v = Date.parseDate(value, this.format);
41206         if (!v && this.useIso) {
41207             v = Date.parseDate(value, 'Y-m-d');
41208         }
41209         if (v) {
41210             // 
41211             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
41212         }
41213         
41214         
41215         if(!v && this.altFormats){
41216             if(!this.altFormatsArray){
41217                 this.altFormatsArray = this.altFormats.split("|");
41218             }
41219             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41220                 v = Date.parseDate(value, this.altFormatsArray[i]);
41221             }
41222         }
41223         return v;
41224     },
41225
41226     // private
41227     formatDate : function(date, fmt){
41228         return (!date || !(date instanceof Date)) ?
41229                date : date.dateFormat(fmt || this.format);
41230     },
41231
41232     // private
41233     menuListeners : {
41234         select: function(m, d){
41235             this.setValue(d);
41236             this.fireEvent('select', this, d);
41237         },
41238         show : function(){ // retain focus styling
41239             this.onFocus();
41240         },
41241         hide : function(){
41242             this.focus.defer(10, this);
41243             var ml = this.menuListeners;
41244             this.menu.un("select", ml.select,  this);
41245             this.menu.un("show", ml.show,  this);
41246             this.menu.un("hide", ml.hide,  this);
41247         }
41248     },
41249     // private
41250     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41251     onTriggerClick : function(){
41252         if(this.disabled){
41253             return;
41254         }
41255         if(this.menu == null){
41256             this.menu = new Roo.menu.DateMenu();
41257            
41258         }
41259         
41260         Roo.apply(this.menu.picker,  {
41261             
41262             showClear: this.allowBlank,
41263             minDate : this.minValue,
41264             maxDate : this.maxValue,
41265             disabledDatesRE : this.ddMatch,
41266             disabledDatesText : this.disabledDatesText,
41267             
41268             format : this.useIso ? 'Y-m-d' : this.format,
41269             minText : String.format(this.minText, this.formatDate(this.minValue)),
41270             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41271             
41272         });
41273          this.menu.on(Roo.apply({}, this.menuListeners, {
41274             scope:this
41275         }));
41276        
41277         
41278         var m = this.menu;
41279         var p = m.picker;
41280         
41281         // hide month picker get's called when we called by 'before hide';
41282         
41283         var ignorehide = true;
41284         p.hideMonthPicker  = function(disableAnim){
41285             if (ignorehide) {
41286                 return;
41287             }
41288              if(this.monthPicker){
41289                 Roo.log("hideMonthPicker called");
41290                 if(disableAnim === true){
41291                     this.monthPicker.hide();
41292                 }else{
41293                     this.monthPicker.slideOut('t', {duration:.2});
41294                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
41295                     p.fireEvent("select", this, this.value);
41296                     m.hide();
41297                 }
41298             }
41299         }
41300         
41301         Roo.log('picker set value');
41302         Roo.log(this.getValue());
41303         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
41304         m.show(this.el, 'tl-bl?');
41305         ignorehide  = false;
41306         // this will trigger hideMonthPicker..
41307         
41308         
41309         // hidden the day picker
41310         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
41311         
41312         
41313         
41314       
41315         
41316         p.showMonthPicker.defer(100, p);
41317     
41318         
41319        
41320     },
41321
41322     beforeBlur : function(){
41323         var v = this.parseDate(this.getRawValue());
41324         if(v){
41325             this.setValue(v);
41326         }
41327     }
41328
41329     /** @cfg {Boolean} grow @hide */
41330     /** @cfg {Number} growMin @hide */
41331     /** @cfg {Number} growMax @hide */
41332     /**
41333      * @hide
41334      * @method autoSize
41335      */
41336 });/*
41337  * Based on:
41338  * Ext JS Library 1.1.1
41339  * Copyright(c) 2006-2007, Ext JS, LLC.
41340  *
41341  * Originally Released Under LGPL - original licence link has changed is not relivant.
41342  *
41343  * Fork - LGPL
41344  * <script type="text/javascript">
41345  */
41346  
41347
41348 /**
41349  * @class Roo.form.ComboBox
41350  * @extends Roo.form.TriggerField
41351  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
41352  * @constructor
41353  * Create a new ComboBox.
41354  * @param {Object} config Configuration options
41355  */
41356 Roo.form.ComboBox = function(config){
41357     Roo.form.ComboBox.superclass.constructor.call(this, config);
41358     this.addEvents({
41359         /**
41360          * @event expand
41361          * Fires when the dropdown list is expanded
41362              * @param {Roo.form.ComboBox} combo This combo box
41363              */
41364         'expand' : true,
41365         /**
41366          * @event collapse
41367          * Fires when the dropdown list is collapsed
41368              * @param {Roo.form.ComboBox} combo This combo box
41369              */
41370         'collapse' : true,
41371         /**
41372          * @event beforeselect
41373          * Fires before a list item is selected. Return false to cancel the selection.
41374              * @param {Roo.form.ComboBox} combo This combo box
41375              * @param {Roo.data.Record} record The data record returned from the underlying store
41376              * @param {Number} index The index of the selected item in the dropdown list
41377              */
41378         'beforeselect' : true,
41379         /**
41380          * @event select
41381          * Fires when a list item is selected
41382              * @param {Roo.form.ComboBox} combo This combo box
41383              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
41384              * @param {Number} index The index of the selected item in the dropdown list
41385              */
41386         'select' : true,
41387         /**
41388          * @event beforequery
41389          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
41390          * The event object passed has these properties:
41391              * @param {Roo.form.ComboBox} combo This combo box
41392              * @param {String} query The query
41393              * @param {Boolean} forceAll true to force "all" query
41394              * @param {Boolean} cancel true to cancel the query
41395              * @param {Object} e The query event object
41396              */
41397         'beforequery': true,
41398          /**
41399          * @event add
41400          * Fires when the 'add' icon is pressed (add a listener to enable add button)
41401              * @param {Roo.form.ComboBox} combo This combo box
41402              */
41403         'add' : true,
41404         /**
41405          * @event edit
41406          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
41407              * @param {Roo.form.ComboBox} combo This combo box
41408              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
41409              */
41410         'edit' : true
41411         
41412         
41413     });
41414     if(this.transform){
41415         this.allowDomMove = false;
41416         var s = Roo.getDom(this.transform);
41417         if(!this.hiddenName){
41418             this.hiddenName = s.name;
41419         }
41420         if(!this.store){
41421             this.mode = 'local';
41422             var d = [], opts = s.options;
41423             for(var i = 0, len = opts.length;i < len; i++){
41424                 var o = opts[i];
41425                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41426                 if(o.selected) {
41427                     this.value = value;
41428                 }
41429                 d.push([value, o.text]);
41430             }
41431             this.store = new Roo.data.SimpleStore({
41432                 'id': 0,
41433                 fields: ['value', 'text'],
41434                 data : d
41435             });
41436             this.valueField = 'value';
41437             this.displayField = 'text';
41438         }
41439         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41440         if(!this.lazyRender){
41441             this.target = true;
41442             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41443             s.parentNode.removeChild(s); // remove it
41444             this.render(this.el.parentNode);
41445         }else{
41446             s.parentNode.removeChild(s); // remove it
41447         }
41448
41449     }
41450     if (this.store) {
41451         this.store = Roo.factory(this.store, Roo.data);
41452     }
41453     
41454     this.selectedIndex = -1;
41455     if(this.mode == 'local'){
41456         if(config.queryDelay === undefined){
41457             this.queryDelay = 10;
41458         }
41459         if(config.minChars === undefined){
41460             this.minChars = 0;
41461         }
41462     }
41463 };
41464
41465 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41466     /**
41467      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41468      */
41469     /**
41470      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41471      * rendering into an Roo.Editor, defaults to false)
41472      */
41473     /**
41474      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41475      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41476      */
41477     /**
41478      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41479      */
41480     /**
41481      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41482      * the dropdown list (defaults to undefined, with no header element)
41483      */
41484
41485      /**
41486      * @cfg {String/Roo.Template} tpl The template to use to render the output
41487      */
41488      
41489     // private
41490     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41491     /**
41492      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41493      */
41494     listWidth: undefined,
41495     /**
41496      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41497      * mode = 'remote' or 'text' if mode = 'local')
41498      */
41499     displayField: undefined,
41500     /**
41501      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41502      * mode = 'remote' or 'value' if mode = 'local'). 
41503      * Note: use of a valueField requires the user make a selection
41504      * in order for a value to be mapped.
41505      */
41506     valueField: undefined,
41507     
41508     
41509     /**
41510      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41511      * field's data value (defaults to the underlying DOM element's name)
41512      */
41513     hiddenName: undefined,
41514     /**
41515      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41516      */
41517     listClass: '',
41518     /**
41519      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41520      */
41521     selectedClass: 'x-combo-selected',
41522     /**
41523      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41524      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41525      * which displays a downward arrow icon).
41526      */
41527     triggerClass : 'x-form-arrow-trigger',
41528     /**
41529      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41530      */
41531     shadow:'sides',
41532     /**
41533      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41534      * anchor positions (defaults to 'tl-bl')
41535      */
41536     listAlign: 'tl-bl?',
41537     /**
41538      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41539      */
41540     maxHeight: 300,
41541     /**
41542      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41543      * query specified by the allQuery config option (defaults to 'query')
41544      */
41545     triggerAction: 'query',
41546     /**
41547      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41548      * (defaults to 4, does not apply if editable = false)
41549      */
41550     minChars : 4,
41551     /**
41552      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41553      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41554      */
41555     typeAhead: false,
41556     /**
41557      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41558      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41559      */
41560     queryDelay: 500,
41561     /**
41562      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41563      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41564      */
41565     pageSize: 0,
41566     /**
41567      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41568      * when editable = true (defaults to false)
41569      */
41570     selectOnFocus:false,
41571     /**
41572      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41573      */
41574     queryParam: 'query',
41575     /**
41576      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41577      * when mode = 'remote' (defaults to 'Loading...')
41578      */
41579     loadingText: 'Loading...',
41580     /**
41581      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41582      */
41583     resizable: false,
41584     /**
41585      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41586      */
41587     handleHeight : 8,
41588     /**
41589      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41590      * traditional select (defaults to true)
41591      */
41592     editable: true,
41593     /**
41594      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41595      */
41596     allQuery: '',
41597     /**
41598      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41599      */
41600     mode: 'remote',
41601     /**
41602      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41603      * listWidth has a higher value)
41604      */
41605     minListWidth : 70,
41606     /**
41607      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41608      * allow the user to set arbitrary text into the field (defaults to false)
41609      */
41610     forceSelection:false,
41611     /**
41612      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41613      * if typeAhead = true (defaults to 250)
41614      */
41615     typeAheadDelay : 250,
41616     /**
41617      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41618      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41619      */
41620     valueNotFoundText : undefined,
41621     /**
41622      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41623      */
41624     blockFocus : false,
41625     
41626     /**
41627      * @cfg {Boolean} disableClear Disable showing of clear button.
41628      */
41629     disableClear : false,
41630     /**
41631      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41632      */
41633     alwaysQuery : false,
41634     
41635     //private
41636     addicon : false,
41637     editicon: false,
41638     
41639     // element that contains real text value.. (when hidden is used..)
41640      
41641     // private
41642     onRender : function(ct, position)
41643     {
41644         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41645         
41646         if(this.hiddenName){
41647             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41648                     'before', true);
41649             this.hiddenField.value =
41650                 this.hiddenValue !== undefined ? this.hiddenValue :
41651                 this.value !== undefined ? this.value : '';
41652
41653             // prevent input submission
41654             this.el.dom.removeAttribute('name');
41655              
41656              
41657         }
41658         
41659         if(Roo.isGecko){
41660             this.el.dom.setAttribute('autocomplete', 'off');
41661         }
41662
41663         var cls = 'x-combo-list';
41664
41665         this.list = new Roo.Layer({
41666             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41667         });
41668
41669         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41670         this.list.setWidth(lw);
41671         this.list.swallowEvent('mousewheel');
41672         this.assetHeight = 0;
41673
41674         if(this.title){
41675             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41676             this.assetHeight += this.header.getHeight();
41677         }
41678
41679         this.innerList = this.list.createChild({cls:cls+'-inner'});
41680         this.innerList.on('mouseover', this.onViewOver, this);
41681         this.innerList.on('mousemove', this.onViewMove, this);
41682         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41683         
41684         if(this.allowBlank && !this.pageSize && !this.disableClear){
41685             this.footer = this.list.createChild({cls:cls+'-ft'});
41686             this.pageTb = new Roo.Toolbar(this.footer);
41687            
41688         }
41689         if(this.pageSize){
41690             this.footer = this.list.createChild({cls:cls+'-ft'});
41691             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41692                     {pageSize: this.pageSize});
41693             
41694         }
41695         
41696         if (this.pageTb && this.allowBlank && !this.disableClear) {
41697             var _this = this;
41698             this.pageTb.add(new Roo.Toolbar.Fill(), {
41699                 cls: 'x-btn-icon x-btn-clear',
41700                 text: '&#160;',
41701                 handler: function()
41702                 {
41703                     _this.collapse();
41704                     _this.clearValue();
41705                     _this.onSelect(false, -1);
41706                 }
41707             });
41708         }
41709         if (this.footer) {
41710             this.assetHeight += this.footer.getHeight();
41711         }
41712         
41713
41714         if(!this.tpl){
41715             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41716         }
41717
41718         this.view = new Roo.View(this.innerList, this.tpl, {
41719             singleSelect:true,
41720             store: this.store,
41721             selectedClass: this.selectedClass
41722         });
41723
41724         this.view.on('click', this.onViewClick, this);
41725
41726         this.store.on('beforeload', this.onBeforeLoad, this);
41727         this.store.on('load', this.onLoad, this);
41728         this.store.on('loadexception', this.onLoadException, this);
41729
41730         if(this.resizable){
41731             this.resizer = new Roo.Resizable(this.list,  {
41732                pinned:true, handles:'se'
41733             });
41734             this.resizer.on('resize', function(r, w, h){
41735                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41736                 this.listWidth = w;
41737                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41738                 this.restrictHeight();
41739             }, this);
41740             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41741         }
41742         if(!this.editable){
41743             this.editable = true;
41744             this.setEditable(false);
41745         }  
41746         
41747         
41748         if (typeof(this.events.add.listeners) != 'undefined') {
41749             
41750             this.addicon = this.wrap.createChild(
41751                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41752        
41753             this.addicon.on('click', function(e) {
41754                 this.fireEvent('add', this);
41755             }, this);
41756         }
41757         if (typeof(this.events.edit.listeners) != 'undefined') {
41758             
41759             this.editicon = this.wrap.createChild(
41760                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41761             if (this.addicon) {
41762                 this.editicon.setStyle('margin-left', '40px');
41763             }
41764             this.editicon.on('click', function(e) {
41765                 
41766                 // we fire even  if inothing is selected..
41767                 this.fireEvent('edit', this, this.lastData );
41768                 
41769             }, this);
41770         }
41771         
41772         
41773         
41774     },
41775
41776     // private
41777     initEvents : function(){
41778         Roo.form.ComboBox.superclass.initEvents.call(this);
41779
41780         this.keyNav = new Roo.KeyNav(this.el, {
41781             "up" : function(e){
41782                 this.inKeyMode = true;
41783                 this.selectPrev();
41784             },
41785
41786             "down" : function(e){
41787                 if(!this.isExpanded()){
41788                     this.onTriggerClick();
41789                 }else{
41790                     this.inKeyMode = true;
41791                     this.selectNext();
41792                 }
41793             },
41794
41795             "enter" : function(e){
41796                 this.onViewClick();
41797                 //return true;
41798             },
41799
41800             "esc" : function(e){
41801                 this.collapse();
41802             },
41803
41804             "tab" : function(e){
41805                 this.onViewClick(false);
41806                 this.fireEvent("specialkey", this, e);
41807                 return true;
41808             },
41809
41810             scope : this,
41811
41812             doRelay : function(foo, bar, hname){
41813                 if(hname == 'down' || this.scope.isExpanded()){
41814                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41815                 }
41816                 return true;
41817             },
41818
41819             forceKeyDown: true
41820         });
41821         this.queryDelay = Math.max(this.queryDelay || 10,
41822                 this.mode == 'local' ? 10 : 250);
41823         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41824         if(this.typeAhead){
41825             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41826         }
41827         if(this.editable !== false){
41828             this.el.on("keyup", this.onKeyUp, this);
41829         }
41830         if(this.forceSelection){
41831             this.on('blur', this.doForce, this);
41832         }
41833     },
41834
41835     onDestroy : function(){
41836         if(this.view){
41837             this.view.setStore(null);
41838             this.view.el.removeAllListeners();
41839             this.view.el.remove();
41840             this.view.purgeListeners();
41841         }
41842         if(this.list){
41843             this.list.destroy();
41844         }
41845         if(this.store){
41846             this.store.un('beforeload', this.onBeforeLoad, this);
41847             this.store.un('load', this.onLoad, this);
41848             this.store.un('loadexception', this.onLoadException, this);
41849         }
41850         Roo.form.ComboBox.superclass.onDestroy.call(this);
41851     },
41852
41853     // private
41854     fireKey : function(e){
41855         if(e.isNavKeyPress() && !this.list.isVisible()){
41856             this.fireEvent("specialkey", this, e);
41857         }
41858     },
41859
41860     // private
41861     onResize: function(w, h){
41862         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41863         
41864         if(typeof w != 'number'){
41865             // we do not handle it!?!?
41866             return;
41867         }
41868         var tw = this.trigger.getWidth();
41869         tw += this.addicon ? this.addicon.getWidth() : 0;
41870         tw += this.editicon ? this.editicon.getWidth() : 0;
41871         var x = w - tw;
41872         this.el.setWidth( this.adjustWidth('input', x));
41873             
41874         this.trigger.setStyle('left', x+'px');
41875         
41876         if(this.list && this.listWidth === undefined){
41877             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41878             this.list.setWidth(lw);
41879             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41880         }
41881         
41882     
41883         
41884     },
41885
41886     /**
41887      * Allow or prevent the user from directly editing the field text.  If false is passed,
41888      * the user will only be able to select from the items defined in the dropdown list.  This method
41889      * is the runtime equivalent of setting the 'editable' config option at config time.
41890      * @param {Boolean} value True to allow the user to directly edit the field text
41891      */
41892     setEditable : function(value){
41893         if(value == this.editable){
41894             return;
41895         }
41896         this.editable = value;
41897         if(!value){
41898             this.el.dom.setAttribute('readOnly', true);
41899             this.el.on('mousedown', this.onTriggerClick,  this);
41900             this.el.addClass('x-combo-noedit');
41901         }else{
41902             this.el.dom.setAttribute('readOnly', false);
41903             this.el.un('mousedown', this.onTriggerClick,  this);
41904             this.el.removeClass('x-combo-noedit');
41905         }
41906     },
41907
41908     // private
41909     onBeforeLoad : function(){
41910         if(!this.hasFocus){
41911             return;
41912         }
41913         this.innerList.update(this.loadingText ?
41914                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41915         this.restrictHeight();
41916         this.selectedIndex = -1;
41917     },
41918
41919     // private
41920     onLoad : function(){
41921         if(!this.hasFocus){
41922             return;
41923         }
41924         if(this.store.getCount() > 0){
41925             this.expand();
41926             this.restrictHeight();
41927             if(this.lastQuery == this.allQuery){
41928                 if(this.editable){
41929                     this.el.dom.select();
41930                 }
41931                 if(!this.selectByValue(this.value, true)){
41932                     this.select(0, true);
41933                 }
41934             }else{
41935                 this.selectNext();
41936                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41937                     this.taTask.delay(this.typeAheadDelay);
41938                 }
41939             }
41940         }else{
41941             this.onEmptyResults();
41942         }
41943         //this.el.focus();
41944     },
41945     // private
41946     onLoadException : function()
41947     {
41948         this.collapse();
41949         Roo.log(this.store.reader.jsonData);
41950         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41951             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41952         }
41953         
41954         
41955     },
41956     // private
41957     onTypeAhead : function(){
41958         if(this.store.getCount() > 0){
41959             var r = this.store.getAt(0);
41960             var newValue = r.data[this.displayField];
41961             var len = newValue.length;
41962             var selStart = this.getRawValue().length;
41963             if(selStart != len){
41964                 this.setRawValue(newValue);
41965                 this.selectText(selStart, newValue.length);
41966             }
41967         }
41968     },
41969
41970     // private
41971     onSelect : function(record, index){
41972         if(this.fireEvent('beforeselect', this, record, index) !== false){
41973             this.setFromData(index > -1 ? record.data : false);
41974             this.collapse();
41975             this.fireEvent('select', this, record, index);
41976         }
41977     },
41978
41979     /**
41980      * Returns the currently selected field value or empty string if no value is set.
41981      * @return {String} value The selected value
41982      */
41983     getValue : function(){
41984         if(this.valueField){
41985             return typeof this.value != 'undefined' ? this.value : '';
41986         }
41987         return Roo.form.ComboBox.superclass.getValue.call(this);
41988     },
41989
41990     /**
41991      * Clears any text/value currently set in the field
41992      */
41993     clearValue : function(){
41994         if(this.hiddenField){
41995             this.hiddenField.value = '';
41996         }
41997         this.value = '';
41998         this.setRawValue('');
41999         this.lastSelectionText = '';
42000         
42001     },
42002
42003     /**
42004      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
42005      * will be displayed in the field.  If the value does not match the data value of an existing item,
42006      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
42007      * Otherwise the field will be blank (although the value will still be set).
42008      * @param {String} value The value to match
42009      */
42010     setValue : function(v){
42011         var text = v;
42012         if(this.valueField){
42013             var r = this.findRecord(this.valueField, v);
42014             if(r){
42015                 text = r.data[this.displayField];
42016             }else if(this.valueNotFoundText !== undefined){
42017                 text = this.valueNotFoundText;
42018             }
42019         }
42020         this.lastSelectionText = text;
42021         if(this.hiddenField){
42022             this.hiddenField.value = v;
42023         }
42024         Roo.form.ComboBox.superclass.setValue.call(this, text);
42025         this.value = v;
42026     },
42027     /**
42028      * @property {Object} the last set data for the element
42029      */
42030     
42031     lastData : false,
42032     /**
42033      * Sets the value of the field based on a object which is related to the record format for the store.
42034      * @param {Object} value the value to set as. or false on reset?
42035      */
42036     setFromData : function(o){
42037         var dv = ''; // display value
42038         var vv = ''; // value value..
42039         this.lastData = o;
42040         if (this.displayField) {
42041             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
42042         } else {
42043             // this is an error condition!!!
42044             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
42045         }
42046         
42047         if(this.valueField){
42048             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
42049         }
42050         if(this.hiddenField){
42051             this.hiddenField.value = vv;
42052             
42053             this.lastSelectionText = dv;
42054             Roo.form.ComboBox.superclass.setValue.call(this, dv);
42055             this.value = vv;
42056             return;
42057         }
42058         // no hidden field.. - we store the value in 'value', but still display
42059         // display field!!!!
42060         this.lastSelectionText = dv;
42061         Roo.form.ComboBox.superclass.setValue.call(this, dv);
42062         this.value = vv;
42063         
42064         
42065     },
42066     // private
42067     reset : function(){
42068         // overridden so that last data is reset..
42069         this.setValue(this.resetValue);
42070         this.originalValue = this.getValue();
42071         this.clearInvalid();
42072         this.lastData = false;
42073         if (this.view) {
42074             this.view.clearSelections();
42075         }
42076     },
42077     // private
42078     findRecord : function(prop, value){
42079         var record;
42080         if(this.store.getCount() > 0){
42081             this.store.each(function(r){
42082                 if(r.data[prop] == value){
42083                     record = r;
42084                     return false;
42085                 }
42086                 return true;
42087             });
42088         }
42089         return record;
42090     },
42091     
42092     getName: function()
42093     {
42094         // returns hidden if it's set..
42095         if (!this.rendered) {return ''};
42096         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42097         
42098     },
42099     // private
42100     onViewMove : function(e, t){
42101         this.inKeyMode = false;
42102     },
42103
42104     // private
42105     onViewOver : function(e, t){
42106         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
42107             return;
42108         }
42109         var item = this.view.findItemFromChild(t);
42110         if(item){
42111             var index = this.view.indexOf(item);
42112             this.select(index, false);
42113         }
42114     },
42115
42116     // private
42117     onViewClick : function(doFocus)
42118     {
42119         var index = this.view.getSelectedIndexes()[0];
42120         var r = this.store.getAt(index);
42121         if(r){
42122             this.onSelect(r, index);
42123         }
42124         if(doFocus !== false && !this.blockFocus){
42125             this.el.focus();
42126         }
42127     },
42128
42129     // private
42130     restrictHeight : function(){
42131         this.innerList.dom.style.height = '';
42132         var inner = this.innerList.dom;
42133         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
42134         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42135         this.list.beginUpdate();
42136         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
42137         this.list.alignTo(this.el, this.listAlign);
42138         this.list.endUpdate();
42139     },
42140
42141     // private
42142     onEmptyResults : function(){
42143         this.collapse();
42144     },
42145
42146     /**
42147      * Returns true if the dropdown list is expanded, else false.
42148      */
42149     isExpanded : function(){
42150         return this.list.isVisible();
42151     },
42152
42153     /**
42154      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
42155      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42156      * @param {String} value The data value of the item to select
42157      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42158      * selected item if it is not currently in view (defaults to true)
42159      * @return {Boolean} True if the value matched an item in the list, else false
42160      */
42161     selectByValue : function(v, scrollIntoView){
42162         if(v !== undefined && v !== null){
42163             var r = this.findRecord(this.valueField || this.displayField, v);
42164             if(r){
42165                 this.select(this.store.indexOf(r), scrollIntoView);
42166                 return true;
42167             }
42168         }
42169         return false;
42170     },
42171
42172     /**
42173      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
42174      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42175      * @param {Number} index The zero-based index of the list item to select
42176      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42177      * selected item if it is not currently in view (defaults to true)
42178      */
42179     select : function(index, scrollIntoView){
42180         this.selectedIndex = index;
42181         this.view.select(index);
42182         if(scrollIntoView !== false){
42183             var el = this.view.getNode(index);
42184             if(el){
42185                 this.innerList.scrollChildIntoView(el, false);
42186             }
42187         }
42188     },
42189
42190     // private
42191     selectNext : function(){
42192         var ct = this.store.getCount();
42193         if(ct > 0){
42194             if(this.selectedIndex == -1){
42195                 this.select(0);
42196             }else if(this.selectedIndex < ct-1){
42197                 this.select(this.selectedIndex+1);
42198             }
42199         }
42200     },
42201
42202     // private
42203     selectPrev : function(){
42204         var ct = this.store.getCount();
42205         if(ct > 0){
42206             if(this.selectedIndex == -1){
42207                 this.select(0);
42208             }else if(this.selectedIndex != 0){
42209                 this.select(this.selectedIndex-1);
42210             }
42211         }
42212     },
42213
42214     // private
42215     onKeyUp : function(e){
42216         if(this.editable !== false && !e.isSpecialKey()){
42217             this.lastKey = e.getKey();
42218             this.dqTask.delay(this.queryDelay);
42219         }
42220     },
42221
42222     // private
42223     validateBlur : function(){
42224         return !this.list || !this.list.isVisible();   
42225     },
42226
42227     // private
42228     initQuery : function(){
42229         this.doQuery(this.getRawValue());
42230     },
42231
42232     // private
42233     doForce : function(){
42234         if(this.el.dom.value.length > 0){
42235             this.el.dom.value =
42236                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
42237              
42238         }
42239     },
42240
42241     /**
42242      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
42243      * query allowing the query action to be canceled if needed.
42244      * @param {String} query The SQL query to execute
42245      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
42246      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
42247      * saved in the current store (defaults to false)
42248      */
42249     doQuery : function(q, forceAll){
42250         if(q === undefined || q === null){
42251             q = '';
42252         }
42253         var qe = {
42254             query: q,
42255             forceAll: forceAll,
42256             combo: this,
42257             cancel:false
42258         };
42259         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
42260             return false;
42261         }
42262         q = qe.query;
42263         forceAll = qe.forceAll;
42264         if(forceAll === true || (q.length >= this.minChars)){
42265             if(this.lastQuery != q || this.alwaysQuery){
42266                 this.lastQuery = q;
42267                 if(this.mode == 'local'){
42268                     this.selectedIndex = -1;
42269                     if(forceAll){
42270                         this.store.clearFilter();
42271                     }else{
42272                         this.store.filter(this.displayField, q);
42273                     }
42274                     this.onLoad();
42275                 }else{
42276                     this.store.baseParams[this.queryParam] = q;
42277                     this.store.load({
42278                         params: this.getParams(q)
42279                     });
42280                     this.expand();
42281                 }
42282             }else{
42283                 this.selectedIndex = -1;
42284                 this.onLoad();   
42285             }
42286         }
42287     },
42288
42289     // private
42290     getParams : function(q){
42291         var p = {};
42292         //p[this.queryParam] = q;
42293         if(this.pageSize){
42294             p.start = 0;
42295             p.limit = this.pageSize;
42296         }
42297         return p;
42298     },
42299
42300     /**
42301      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
42302      */
42303     collapse : function(){
42304         if(!this.isExpanded()){
42305             return;
42306         }
42307         this.list.hide();
42308         Roo.get(document).un('mousedown', this.collapseIf, this);
42309         Roo.get(document).un('mousewheel', this.collapseIf, this);
42310         if (!this.editable) {
42311             Roo.get(document).un('keydown', this.listKeyPress, this);
42312         }
42313         this.fireEvent('collapse', this);
42314     },
42315
42316     // private
42317     collapseIf : function(e){
42318         if(!e.within(this.wrap) && !e.within(this.list)){
42319             this.collapse();
42320         }
42321     },
42322
42323     /**
42324      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
42325      */
42326     expand : function(){
42327         if(this.isExpanded() || !this.hasFocus){
42328             return;
42329         }
42330         this.list.alignTo(this.el, this.listAlign);
42331         this.list.show();
42332         Roo.get(document).on('mousedown', this.collapseIf, this);
42333         Roo.get(document).on('mousewheel', this.collapseIf, this);
42334         if (!this.editable) {
42335             Roo.get(document).on('keydown', this.listKeyPress, this);
42336         }
42337         
42338         this.fireEvent('expand', this);
42339     },
42340
42341     // private
42342     // Implements the default empty TriggerField.onTriggerClick function
42343     onTriggerClick : function(){
42344         if(this.disabled){
42345             return;
42346         }
42347         if(this.isExpanded()){
42348             this.collapse();
42349             if (!this.blockFocus) {
42350                 this.el.focus();
42351             }
42352             
42353         }else {
42354             this.hasFocus = true;
42355             if(this.triggerAction == 'all') {
42356                 this.doQuery(this.allQuery, true);
42357             } else {
42358                 this.doQuery(this.getRawValue());
42359             }
42360             if (!this.blockFocus) {
42361                 this.el.focus();
42362             }
42363         }
42364     },
42365     listKeyPress : function(e)
42366     {
42367         //Roo.log('listkeypress');
42368         // scroll to first matching element based on key pres..
42369         if (e.isSpecialKey()) {
42370             return false;
42371         }
42372         var k = String.fromCharCode(e.getKey()).toUpperCase();
42373         //Roo.log(k);
42374         var match  = false;
42375         var csel = this.view.getSelectedNodes();
42376         var cselitem = false;
42377         if (csel.length) {
42378             var ix = this.view.indexOf(csel[0]);
42379             cselitem  = this.store.getAt(ix);
42380             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
42381                 cselitem = false;
42382             }
42383             
42384         }
42385         
42386         this.store.each(function(v) { 
42387             if (cselitem) {
42388                 // start at existing selection.
42389                 if (cselitem.id == v.id) {
42390                     cselitem = false;
42391                 }
42392                 return;
42393             }
42394                 
42395             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
42396                 match = this.store.indexOf(v);
42397                 return false;
42398             }
42399         }, this);
42400         
42401         if (match === false) {
42402             return true; // no more action?
42403         }
42404         // scroll to?
42405         this.view.select(match);
42406         var sn = Roo.get(this.view.getSelectedNodes()[0]);
42407         sn.scrollIntoView(sn.dom.parentNode, false);
42408     } 
42409
42410     /** 
42411     * @cfg {Boolean} grow 
42412     * @hide 
42413     */
42414     /** 
42415     * @cfg {Number} growMin 
42416     * @hide 
42417     */
42418     /** 
42419     * @cfg {Number} growMax 
42420     * @hide 
42421     */
42422     /**
42423      * @hide
42424      * @method autoSize
42425      */
42426 });/*
42427  * Copyright(c) 2010-2012, Roo J Solutions Limited
42428  *
42429  * Licence LGPL
42430  *
42431  */
42432
42433 /**
42434  * @class Roo.form.ComboBoxArray
42435  * @extends Roo.form.TextField
42436  * A facebook style adder... for lists of email / people / countries  etc...
42437  * pick multiple items from a combo box, and shows each one.
42438  *
42439  *  Fred [x]  Brian [x]  [Pick another |v]
42440  *
42441  *
42442  *  For this to work: it needs various extra information
42443  *    - normal combo problay has
42444  *      name, hiddenName
42445  *    + displayField, valueField
42446  *
42447  *    For our purpose...
42448  *
42449  *
42450  *   If we change from 'extends' to wrapping...
42451  *   
42452  *  
42453  *
42454  
42455  
42456  * @constructor
42457  * Create a new ComboBoxArray.
42458  * @param {Object} config Configuration options
42459  */
42460  
42461
42462 Roo.form.ComboBoxArray = function(config)
42463 {
42464     this.addEvents({
42465         /**
42466          * @event beforeremove
42467          * Fires before remove the value from the list
42468              * @param {Roo.form.ComboBoxArray} _self This combo box array
42469              * @param {Roo.form.ComboBoxArray.Item} item removed item
42470              */
42471         'beforeremove' : true,
42472         /**
42473          * @event remove
42474          * Fires when remove the value from the list
42475              * @param {Roo.form.ComboBoxArray} _self This combo box array
42476              * @param {Roo.form.ComboBoxArray.Item} item removed item
42477              */
42478         'remove' : true
42479         
42480         
42481     });
42482     
42483     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42484     
42485     this.items = new Roo.util.MixedCollection(false);
42486     
42487     // construct the child combo...
42488     
42489     
42490     
42491     
42492    
42493     
42494 }
42495
42496  
42497 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42498
42499     /**
42500      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
42501      */
42502     
42503     lastData : false,
42504     
42505     // behavies liek a hiddne field
42506     inputType:      'hidden',
42507     /**
42508      * @cfg {Number} width The width of the box that displays the selected element
42509      */ 
42510     width:          300,
42511
42512     
42513     
42514     /**
42515      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42516      */
42517     name : false,
42518     /**
42519      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42520      */
42521     hiddenName : false,
42522       /**
42523      * @cfg {String} seperator    The value seperator normally ',' 
42524      */
42525     seperator : ',',
42526     
42527     // private the array of items that are displayed..
42528     items  : false,
42529     // private - the hidden field el.
42530     hiddenEl : false,
42531     // private - the filed el..
42532     el : false,
42533     
42534     //validateValue : function() { return true; }, // all values are ok!
42535     //onAddClick: function() { },
42536     
42537     onRender : function(ct, position) 
42538     {
42539         
42540         // create the standard hidden element
42541         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42542         
42543         
42544         // give fake names to child combo;
42545         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42546         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
42547         
42548         this.combo = Roo.factory(this.combo, Roo.form);
42549         this.combo.onRender(ct, position);
42550         if (typeof(this.combo.width) != 'undefined') {
42551             this.combo.onResize(this.combo.width,0);
42552         }
42553         
42554         this.combo.initEvents();
42555         
42556         // assigned so form know we need to do this..
42557         this.store          = this.combo.store;
42558         this.valueField     = this.combo.valueField;
42559         this.displayField   = this.combo.displayField ;
42560         
42561         
42562         this.combo.wrap.addClass('x-cbarray-grp');
42563         
42564         var cbwrap = this.combo.wrap.createChild(
42565             {tag: 'div', cls: 'x-cbarray-cb'},
42566             this.combo.el.dom
42567         );
42568         
42569              
42570         this.hiddenEl = this.combo.wrap.createChild({
42571             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42572         });
42573         this.el = this.combo.wrap.createChild({
42574             tag: 'input',  type:'hidden' , name: this.name, value : ''
42575         });
42576          //   this.el.dom.removeAttribute("name");
42577         
42578         
42579         this.outerWrap = this.combo.wrap;
42580         this.wrap = cbwrap;
42581         
42582         this.outerWrap.setWidth(this.width);
42583         this.outerWrap.dom.removeChild(this.el.dom);
42584         
42585         this.wrap.dom.appendChild(this.el.dom);
42586         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42587         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42588         
42589         this.combo.trigger.setStyle('position','relative');
42590         this.combo.trigger.setStyle('left', '0px');
42591         this.combo.trigger.setStyle('top', '2px');
42592         
42593         this.combo.el.setStyle('vertical-align', 'text-bottom');
42594         
42595         //this.trigger.setStyle('vertical-align', 'top');
42596         
42597         // this should use the code from combo really... on('add' ....)
42598         if (this.adder) {
42599             
42600         
42601             this.adder = this.outerWrap.createChild(
42602                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42603             var _t = this;
42604             this.adder.on('click', function(e) {
42605                 _t.fireEvent('adderclick', this, e);
42606             }, _t);
42607         }
42608         //var _t = this;
42609         //this.adder.on('click', this.onAddClick, _t);
42610         
42611         
42612         this.combo.on('select', function(cb, rec, ix) {
42613             this.addItem(rec.data);
42614             
42615             cb.setValue('');
42616             cb.el.dom.value = '';
42617             //cb.lastData = rec.data;
42618             // add to list
42619             
42620         }, this);
42621         
42622         
42623     },
42624     
42625     
42626     getName: function()
42627     {
42628         // returns hidden if it's set..
42629         if (!this.rendered) {return ''};
42630         return  this.hiddenName ? this.hiddenName : this.name;
42631         
42632     },
42633     
42634     
42635     onResize: function(w, h){
42636         
42637         return;
42638         // not sure if this is needed..
42639         //this.combo.onResize(w,h);
42640         
42641         if(typeof w != 'number'){
42642             // we do not handle it!?!?
42643             return;
42644         }
42645         var tw = this.combo.trigger.getWidth();
42646         tw += this.addicon ? this.addicon.getWidth() : 0;
42647         tw += this.editicon ? this.editicon.getWidth() : 0;
42648         var x = w - tw;
42649         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42650             
42651         this.combo.trigger.setStyle('left', '0px');
42652         
42653         if(this.list && this.listWidth === undefined){
42654             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42655             this.list.setWidth(lw);
42656             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42657         }
42658         
42659     
42660         
42661     },
42662     
42663     addItem: function(rec)
42664     {
42665         var valueField = this.combo.valueField;
42666         var displayField = this.combo.displayField;
42667         
42668         if (this.items.indexOfKey(rec[valueField]) > -1) {
42669             //console.log("GOT " + rec.data.id);
42670             return;
42671         }
42672         
42673         var x = new Roo.form.ComboBoxArray.Item({
42674             //id : rec[this.idField],
42675             data : rec,
42676             displayField : displayField ,
42677             tipField : displayField ,
42678             cb : this
42679         });
42680         // use the 
42681         this.items.add(rec[valueField],x);
42682         // add it before the element..
42683         this.updateHiddenEl();
42684         x.render(this.outerWrap, this.wrap.dom);
42685         // add the image handler..
42686     },
42687     
42688     updateHiddenEl : function()
42689     {
42690         this.validate();
42691         if (!this.hiddenEl) {
42692             return;
42693         }
42694         var ar = [];
42695         var idField = this.combo.valueField;
42696         
42697         this.items.each(function(f) {
42698             ar.push(f.data[idField]);
42699         });
42700         this.hiddenEl.dom.value = ar.join(this.seperator);
42701         this.validate();
42702     },
42703     
42704     reset : function()
42705     {
42706         this.items.clear();
42707         
42708         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42709            el.remove();
42710         });
42711         
42712         this.el.dom.value = '';
42713         if (this.hiddenEl) {
42714             this.hiddenEl.dom.value = '';
42715         }
42716         
42717     },
42718     getValue: function()
42719     {
42720         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42721     },
42722     setValue: function(v) // not a valid action - must use addItems..
42723     {
42724         
42725         this.reset();
42726          
42727         if (this.store.isLocal && (typeof(v) == 'string')) {
42728             // then we can use the store to find the values..
42729             // comma seperated at present.. this needs to allow JSON based encoding..
42730             this.hiddenEl.value  = v;
42731             var v_ar = [];
42732             Roo.each(v.split(this.seperator), function(k) {
42733                 Roo.log("CHECK " + this.valueField + ',' + k);
42734                 var li = this.store.query(this.valueField, k);
42735                 if (!li.length) {
42736                     return;
42737                 }
42738                 var add = {};
42739                 add[this.valueField] = k;
42740                 add[this.displayField] = li.item(0).data[this.displayField];
42741                 
42742                 this.addItem(add);
42743             }, this) 
42744              
42745         }
42746         if (typeof(v) == 'object' ) {
42747             // then let's assume it's an array of objects..
42748             Roo.each(v, function(l) {
42749                 var add = l;
42750                 if (typeof(l) == 'string') {
42751                     add = {};
42752                     add[this.valueField] = l;
42753                     add[this.displayField] = l
42754                 }
42755                 this.addItem(add);
42756             }, this);
42757              
42758         }
42759         
42760         
42761     },
42762     setFromData: function(v)
42763     {
42764         // this recieves an object, if setValues is called.
42765         this.reset();
42766         this.el.dom.value = v[this.displayField];
42767         this.hiddenEl.dom.value = v[this.valueField];
42768         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42769             return;
42770         }
42771         var kv = v[this.valueField];
42772         var dv = v[this.displayField];
42773         kv = typeof(kv) != 'string' ? '' : kv;
42774         dv = typeof(dv) != 'string' ? '' : dv;
42775         
42776         
42777         var keys = kv.split(this.seperator);
42778         var display = dv.split(this.seperator);
42779         for (var i = 0 ; i < keys.length; i++) {
42780             add = {};
42781             add[this.valueField] = keys[i];
42782             add[this.displayField] = display[i];
42783             this.addItem(add);
42784         }
42785       
42786         
42787     },
42788     
42789     /**
42790      * Validates the combox array value
42791      * @return {Boolean} True if the value is valid, else false
42792      */
42793     validate : function(){
42794         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42795             this.clearInvalid();
42796             return true;
42797         }
42798         return false;
42799     },
42800     
42801     validateValue : function(value){
42802         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42803         
42804     },
42805     
42806     /*@
42807      * overide
42808      * 
42809      */
42810     isDirty : function() {
42811         if(this.disabled) {
42812             return false;
42813         }
42814         
42815         try {
42816             var d = Roo.decode(String(this.originalValue));
42817         } catch (e) {
42818             return String(this.getValue()) !== String(this.originalValue);
42819         }
42820         
42821         var originalValue = [];
42822         
42823         for (var i = 0; i < d.length; i++){
42824             originalValue.push(d[i][this.valueField]);
42825         }
42826         
42827         return String(this.getValue()) !== String(originalValue.join(this.seperator));
42828         
42829     }
42830     
42831 });
42832
42833
42834
42835 /**
42836  * @class Roo.form.ComboBoxArray.Item
42837  * @extends Roo.BoxComponent
42838  * A selected item in the list
42839  *  Fred [x]  Brian [x]  [Pick another |v]
42840  * 
42841  * @constructor
42842  * Create a new item.
42843  * @param {Object} config Configuration options
42844  */
42845  
42846 Roo.form.ComboBoxArray.Item = function(config) {
42847     config.id = Roo.id();
42848     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42849 }
42850
42851 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42852     data : {},
42853     cb: false,
42854     displayField : false,
42855     tipField : false,
42856     
42857     
42858     defaultAutoCreate : {
42859         tag: 'div',
42860         cls: 'x-cbarray-item',
42861         cn : [ 
42862             { tag: 'div' },
42863             {
42864                 tag: 'img',
42865                 width:16,
42866                 height : 16,
42867                 src : Roo.BLANK_IMAGE_URL ,
42868                 align: 'center'
42869             }
42870         ]
42871         
42872     },
42873     
42874  
42875     onRender : function(ct, position)
42876     {
42877         Roo.form.Field.superclass.onRender.call(this, ct, position);
42878         
42879         if(!this.el){
42880             var cfg = this.getAutoCreate();
42881             this.el = ct.createChild(cfg, position);
42882         }
42883         
42884         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42885         
42886         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42887             this.cb.renderer(this.data) :
42888             String.format('{0}',this.data[this.displayField]);
42889         
42890             
42891         this.el.child('div').dom.setAttribute('qtip',
42892                         String.format('{0}',this.data[this.tipField])
42893         );
42894         
42895         this.el.child('img').on('click', this.remove, this);
42896         
42897     },
42898    
42899     remove : function()
42900     {
42901         if(this.cb.disabled){
42902             return;
42903         }
42904         
42905         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42906             this.cb.items.remove(this);
42907             this.el.child('img').un('click', this.remove, this);
42908             this.el.remove();
42909             this.cb.updateHiddenEl();
42910
42911             this.cb.fireEvent('remove', this.cb, this);
42912         }
42913         
42914     }
42915 });/*
42916  * RooJS Library 1.1.1
42917  * Copyright(c) 2008-2011  Alan Knowles
42918  *
42919  * License - LGPL
42920  */
42921  
42922
42923 /**
42924  * @class Roo.form.ComboNested
42925  * @extends Roo.form.ComboBox
42926  * A combobox for that allows selection of nested items in a list,
42927  * eg.
42928  *
42929  *  Book
42930  *    -> red
42931  *    -> green
42932  *  Table
42933  *    -> square
42934  *      ->red
42935  *      ->green
42936  *    -> rectangle
42937  *      ->green
42938  *      
42939  * 
42940  * @constructor
42941  * Create a new ComboNested
42942  * @param {Object} config Configuration options
42943  */
42944 Roo.form.ComboNested = function(config){
42945     Roo.form.ComboCheck.superclass.constructor.call(this, config);
42946     // should verify some data...
42947     // like
42948     // hiddenName = required..
42949     // displayField = required
42950     // valudField == required
42951     var req= [ 'hiddenName', 'displayField', 'valueField' ];
42952     var _t = this;
42953     Roo.each(req, function(e) {
42954         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
42955             throw "Roo.form.ComboNested : missing value for: " + e;
42956         }
42957     });
42958      
42959     
42960 };
42961
42962 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
42963    
42964     /*
42965      * @config {Number} max Number of columns to show
42966      */
42967     
42968     maxColumns : 3,
42969    
42970     list : null, // the outermost div..
42971     innerLists : null, // the
42972     views : null,
42973     stores : null,
42974     // private
42975     loadingChildren : false,
42976     
42977     onRender : function(ct, position)
42978     {
42979         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
42980         
42981         if(this.hiddenName){
42982             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42983                     'before', true);
42984             this.hiddenField.value =
42985                 this.hiddenValue !== undefined ? this.hiddenValue :
42986                 this.value !== undefined ? this.value : '';
42987
42988             // prevent input submission
42989             this.el.dom.removeAttribute('name');
42990              
42991              
42992         }
42993         
42994         if(Roo.isGecko){
42995             this.el.dom.setAttribute('autocomplete', 'off');
42996         }
42997
42998         var cls = 'x-combo-list';
42999
43000         this.list = new Roo.Layer({
43001             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43002         });
43003
43004         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43005         this.list.setWidth(lw);
43006         this.list.swallowEvent('mousewheel');
43007         this.assetHeight = 0;
43008
43009         if(this.title){
43010             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43011             this.assetHeight += this.header.getHeight();
43012         }
43013         this.innerLists = [];
43014         this.views = [];
43015         this.stores = [];
43016         for (var i =0 ; i < this.maxColumns; i++) {
43017             this.onRenderList( cls, i);
43018         }
43019         
43020         // always needs footer, as we are going to have an 'OK' button.
43021         this.footer = this.list.createChild({cls:cls+'-ft'});
43022         this.pageTb = new Roo.Toolbar(this.footer);  
43023         var _this = this;
43024         this.pageTb.add(  {
43025             
43026             text: 'Done',
43027             handler: function()
43028             {
43029                 _this.collapse();
43030             }
43031         });
43032         
43033         if ( this.allowBlank && !this.disableClear) {
43034             
43035             this.pageTb.add(new Roo.Toolbar.Fill(), {
43036                 cls: 'x-btn-icon x-btn-clear',
43037                 text: '&#160;',
43038                 handler: function()
43039                 {
43040                     _this.collapse();
43041                     _this.clearValue();
43042                     _this.onSelect(false, -1);
43043                 }
43044             });
43045         }
43046         if (this.footer) {
43047             this.assetHeight += this.footer.getHeight();
43048         }
43049         
43050     },
43051     onRenderList : function (  cls, i)
43052     {
43053         
43054         var lw = Math.floor(
43055                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43056         );
43057         
43058         this.list.setWidth(lw); // default to '1'
43059
43060         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
43061         //il.on('mouseover', this.onViewOver, this, { list:  i });
43062         //il.on('mousemove', this.onViewMove, this, { list:  i });
43063         il.setWidth(lw);
43064         il.setStyle({ 'overflow-x' : 'hidden'});
43065
43066         if(!this.tpl){
43067             this.tpl = new Roo.Template({
43068                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
43069                 isEmpty: function (value, allValues) {
43070                     //Roo.log(value);
43071                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
43072                     return dl ? 'has-children' : 'no-children'
43073                 }
43074             });
43075         }
43076         
43077         var store  = this.store;
43078         if (i > 0) {
43079             store  = new Roo.data.SimpleStore({
43080                 //fields : this.store.reader.meta.fields,
43081                 reader : this.store.reader,
43082                 data : [ ]
43083             });
43084         }
43085         this.stores[i]  = store;
43086                   
43087         var view = this.views[i] = new Roo.View(
43088             il,
43089             this.tpl,
43090             {
43091                 singleSelect:true,
43092                 store: store,
43093                 selectedClass: this.selectedClass
43094             }
43095         );
43096         view.getEl().setWidth(lw);
43097         view.getEl().setStyle({
43098             position: i < 1 ? 'relative' : 'absolute',
43099             top: 0,
43100             left: (i * lw ) + 'px',
43101             display : i > 0 ? 'none' : 'block'
43102         });
43103         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
43104         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
43105         //view.on('click', this.onViewClick, this, { list : i });
43106
43107         store.on('beforeload', this.onBeforeLoad, this);
43108         store.on('load',  this.onLoad, this, { list  : i});
43109         store.on('loadexception', this.onLoadException, this);
43110
43111         // hide the other vies..
43112         
43113         
43114         
43115     },
43116       
43117     restrictHeight : function()
43118     {
43119         var mh = 0;
43120         Roo.each(this.innerLists, function(il,i) {
43121             var el = this.views[i].getEl();
43122             el.dom.style.height = '';
43123             var inner = el.dom;
43124             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
43125             // only adjust heights on other ones..
43126             mh = Math.max(h, mh);
43127             if (i < 1) {
43128                 
43129                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43130                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43131                
43132             }
43133             
43134             
43135         }, this);
43136         
43137         this.list.beginUpdate();
43138         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
43139         this.list.alignTo(this.el, this.listAlign);
43140         this.list.endUpdate();
43141         
43142     },
43143      
43144     
43145     // -- store handlers..
43146     // private
43147     onBeforeLoad : function()
43148     {
43149         if(!this.hasFocus){
43150             return;
43151         }
43152         this.innerLists[0].update(this.loadingText ?
43153                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43154         this.restrictHeight();
43155         this.selectedIndex = -1;
43156     },
43157     // private
43158     onLoad : function(a,b,c,d)
43159     {
43160         if (!this.loadingChildren) {
43161             // then we are loading the top level. - hide the children
43162             for (var i = 1;i < this.views.length; i++) {
43163                 this.views[i].getEl().setStyle({ display : 'none' });
43164             }
43165             var lw = Math.floor(
43166                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43167             );
43168         
43169              this.list.setWidth(lw); // default to '1'
43170
43171             
43172         }
43173         if(!this.hasFocus){
43174             return;
43175         }
43176         
43177         if(this.store.getCount() > 0) {
43178             this.expand();
43179             this.restrictHeight();   
43180         } else {
43181             this.onEmptyResults();
43182         }
43183         
43184         if (!this.loadingChildren) {
43185             this.selectActive();
43186         }
43187         /*
43188         this.stores[1].loadData([]);
43189         this.stores[2].loadData([]);
43190         this.views
43191         */    
43192     
43193         //this.el.focus();
43194     },
43195     
43196     
43197     // private
43198     onLoadException : function()
43199     {
43200         this.collapse();
43201         Roo.log(this.store.reader.jsonData);
43202         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43203             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43204         }
43205         
43206         
43207     },
43208     // no cleaning of leading spaces on blur here.
43209     cleanLeadingSpace : function(e) { },
43210     
43211
43212     onSelectChange : function (view, sels, opts )
43213     {
43214         var ix = view.getSelectedIndexes();
43215          
43216         if (opts.list > this.maxColumns - 2) {
43217             if (view.store.getCount()<  1) {
43218                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
43219
43220             } else  {
43221                 if (ix.length) {
43222                     // used to clear ?? but if we are loading unselected 
43223                     this.setFromData(view.store.getAt(ix[0]).data);
43224                 }
43225                 
43226             }
43227             
43228             return;
43229         }
43230         
43231         if (!ix.length) {
43232             // this get's fired when trigger opens..
43233            // this.setFromData({});
43234             var str = this.stores[opts.list+1];
43235             str.data.clear(); // removeall wihtout the fire events..
43236             return;
43237         }
43238         
43239         var rec = view.store.getAt(ix[0]);
43240          
43241         this.setFromData(rec.data);
43242         this.fireEvent('select', this, rec, ix[0]);
43243         
43244         var lw = Math.floor(
43245              (
43246                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
43247              ) / this.maxColumns
43248         );
43249         this.loadingChildren = true;
43250         this.stores[opts.list+1].loadDataFromChildren( rec );
43251         this.loadingChildren = false;
43252         var dl = this.stores[opts.list+1]. getTotalCount();
43253         
43254         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
43255         
43256         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
43257         for (var i = opts.list+2; i < this.views.length;i++) {
43258             this.views[i].getEl().setStyle({ display : 'none' });
43259         }
43260         
43261         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
43262         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
43263         
43264         if (this.isLoading) {
43265            // this.selectActive(opts.list);
43266         }
43267          
43268     },
43269     
43270     
43271     
43272     
43273     onDoubleClick : function()
43274     {
43275         this.collapse(); //??
43276     },
43277     
43278      
43279     
43280     
43281     
43282     // private
43283     recordToStack : function(store, prop, value, stack)
43284     {
43285         var cstore = new Roo.data.SimpleStore({
43286             //fields : this.store.reader.meta.fields, // we need array reader.. for
43287             reader : this.store.reader,
43288             data : [ ]
43289         });
43290         var _this = this;
43291         var record  = false;
43292         var srec = false;
43293         if(store.getCount() < 1){
43294             return false;
43295         }
43296         store.each(function(r){
43297             if(r.data[prop] == value){
43298                 record = r;
43299             srec = r;
43300                 return false;
43301             }
43302             if (r.data.cn && r.data.cn.length) {
43303                 cstore.loadDataFromChildren( r);
43304                 var cret = _this.recordToStack(cstore, prop, value, stack);
43305                 if (cret !== false) {
43306                     record = cret;
43307                     srec = r;
43308                     return false;
43309                 }
43310             }
43311              
43312             return true;
43313         });
43314         if (record == false) {
43315             return false
43316         }
43317         stack.unshift(srec);
43318         return record;
43319     },
43320     
43321     /*
43322      * find the stack of stores that match our value.
43323      *
43324      * 
43325      */
43326     
43327     selectActive : function ()
43328     {
43329         // if store is not loaded, then we will need to wait for that to happen first.
43330         var stack = [];
43331         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
43332         for (var i = 0; i < stack.length; i++ ) {
43333             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
43334         }
43335         
43336     }
43337         
43338          
43339     
43340     
43341     
43342     
43343 });/*
43344  * Based on:
43345  * Ext JS Library 1.1.1
43346  * Copyright(c) 2006-2007, Ext JS, LLC.
43347  *
43348  * Originally Released Under LGPL - original licence link has changed is not relivant.
43349  *
43350  * Fork - LGPL
43351  * <script type="text/javascript">
43352  */
43353 /**
43354  * @class Roo.form.Checkbox
43355  * @extends Roo.form.Field
43356  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
43357  * @constructor
43358  * Creates a new Checkbox
43359  * @param {Object} config Configuration options
43360  */
43361 Roo.form.Checkbox = function(config){
43362     Roo.form.Checkbox.superclass.constructor.call(this, config);
43363     this.addEvents({
43364         /**
43365          * @event check
43366          * Fires when the checkbox is checked or unchecked.
43367              * @param {Roo.form.Checkbox} this This checkbox
43368              * @param {Boolean} checked The new checked value
43369              */
43370         check : true
43371     });
43372 };
43373
43374 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
43375     /**
43376      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43377      */
43378     focusClass : undefined,
43379     /**
43380      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43381      */
43382     fieldClass: "x-form-field",
43383     /**
43384      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
43385      */
43386     checked: false,
43387     /**
43388      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43389      * {tag: "input", type: "checkbox", autocomplete: "off"})
43390      */
43391     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43392     /**
43393      * @cfg {String} boxLabel The text that appears beside the checkbox
43394      */
43395     boxLabel : "",
43396     /**
43397      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
43398      */  
43399     inputValue : '1',
43400     /**
43401      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
43402      */
43403      valueOff: '0', // value when not checked..
43404
43405     actionMode : 'viewEl', 
43406     //
43407     // private
43408     itemCls : 'x-menu-check-item x-form-item',
43409     groupClass : 'x-menu-group-item',
43410     inputType : 'hidden',
43411     
43412     
43413     inSetChecked: false, // check that we are not calling self...
43414     
43415     inputElement: false, // real input element?
43416     basedOn: false, // ????
43417     
43418     isFormField: true, // not sure where this is needed!!!!
43419
43420     onResize : function(){
43421         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43422         if(!this.boxLabel){
43423             this.el.alignTo(this.wrap, 'c-c');
43424         }
43425     },
43426
43427     initEvents : function(){
43428         Roo.form.Checkbox.superclass.initEvents.call(this);
43429         this.el.on("click", this.onClick,  this);
43430         this.el.on("change", this.onClick,  this);
43431     },
43432
43433
43434     getResizeEl : function(){
43435         return this.wrap;
43436     },
43437
43438     getPositionEl : function(){
43439         return this.wrap;
43440     },
43441
43442     // private
43443     onRender : function(ct, position){
43444         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43445         /*
43446         if(this.inputValue !== undefined){
43447             this.el.dom.value = this.inputValue;
43448         }
43449         */
43450         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43451         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43452         var viewEl = this.wrap.createChild({ 
43453             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43454         this.viewEl = viewEl;   
43455         this.wrap.on('click', this.onClick,  this); 
43456         
43457         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43458         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43459         
43460         
43461         
43462         if(this.boxLabel){
43463             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43464         //    viewEl.on('click', this.onClick,  this); 
43465         }
43466         //if(this.checked){
43467             this.setChecked(this.checked);
43468         //}else{
43469             //this.checked = this.el.dom;
43470         //}
43471
43472     },
43473
43474     // private
43475     initValue : Roo.emptyFn,
43476
43477     /**
43478      * Returns the checked state of the checkbox.
43479      * @return {Boolean} True if checked, else false
43480      */
43481     getValue : function(){
43482         if(this.el){
43483             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
43484         }
43485         return this.valueOff;
43486         
43487     },
43488
43489         // private
43490     onClick : function(){ 
43491         if (this.disabled) {
43492             return;
43493         }
43494         this.setChecked(!this.checked);
43495
43496         //if(this.el.dom.checked != this.checked){
43497         //    this.setValue(this.el.dom.checked);
43498        // }
43499     },
43500
43501     /**
43502      * Sets the checked state of the checkbox.
43503      * On is always based on a string comparison between inputValue and the param.
43504      * @param {Boolean/String} value - the value to set 
43505      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43506      */
43507     setValue : function(v,suppressEvent){
43508         
43509         
43510         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
43511         //if(this.el && this.el.dom){
43512         //    this.el.dom.checked = this.checked;
43513         //    this.el.dom.defaultChecked = this.checked;
43514         //}
43515         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
43516         //this.fireEvent("check", this, this.checked);
43517     },
43518     // private..
43519     setChecked : function(state,suppressEvent)
43520     {
43521         if (this.inSetChecked) {
43522             this.checked = state;
43523             return;
43524         }
43525         
43526     
43527         if(this.wrap){
43528             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
43529         }
43530         this.checked = state;
43531         if(suppressEvent !== true){
43532             this.fireEvent('check', this, state);
43533         }
43534         this.inSetChecked = true;
43535         this.el.dom.value = state ? this.inputValue : this.valueOff;
43536         this.inSetChecked = false;
43537         
43538     },
43539     // handle setting of hidden value by some other method!!?!?
43540     setFromHidden: function()
43541     {
43542         if(!this.el){
43543             return;
43544         }
43545         //console.log("SET FROM HIDDEN");
43546         //alert('setFrom hidden');
43547         this.setValue(this.el.dom.value);
43548     },
43549     
43550     onDestroy : function()
43551     {
43552         if(this.viewEl){
43553             Roo.get(this.viewEl).remove();
43554         }
43555          
43556         Roo.form.Checkbox.superclass.onDestroy.call(this);
43557     },
43558     
43559     setBoxLabel : function(str)
43560     {
43561         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
43562     }
43563
43564 });/*
43565  * Based on:
43566  * Ext JS Library 1.1.1
43567  * Copyright(c) 2006-2007, Ext JS, LLC.
43568  *
43569  * Originally Released Under LGPL - original licence link has changed is not relivant.
43570  *
43571  * Fork - LGPL
43572  * <script type="text/javascript">
43573  */
43574  
43575 /**
43576  * @class Roo.form.Radio
43577  * @extends Roo.form.Checkbox
43578  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
43579  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
43580  * @constructor
43581  * Creates a new Radio
43582  * @param {Object} config Configuration options
43583  */
43584 Roo.form.Radio = function(){
43585     Roo.form.Radio.superclass.constructor.apply(this, arguments);
43586 };
43587 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
43588     inputType: 'radio',
43589
43590     /**
43591      * If this radio is part of a group, it will return the selected value
43592      * @return {String}
43593      */
43594     getGroupValue : function(){
43595         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
43596     },
43597     
43598     
43599     onRender : function(ct, position){
43600         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43601         
43602         if(this.inputValue !== undefined){
43603             this.el.dom.value = this.inputValue;
43604         }
43605          
43606         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43607         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43608         //var viewEl = this.wrap.createChild({ 
43609         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43610         //this.viewEl = viewEl;   
43611         //this.wrap.on('click', this.onClick,  this); 
43612         
43613         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43614         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
43615         
43616         
43617         
43618         if(this.boxLabel){
43619             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43620         //    viewEl.on('click', this.onClick,  this); 
43621         }
43622          if(this.checked){
43623             this.el.dom.checked =   'checked' ;
43624         }
43625          
43626     } 
43627     
43628     
43629 });//<script type="text/javascript">
43630
43631 /*
43632  * Based  Ext JS Library 1.1.1
43633  * Copyright(c) 2006-2007, Ext JS, LLC.
43634  * LGPL
43635  *
43636  */
43637  
43638 /**
43639  * @class Roo.HtmlEditorCore
43640  * @extends Roo.Component
43641  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
43642  *
43643  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
43644  */
43645
43646 Roo.HtmlEditorCore = function(config){
43647     
43648     
43649     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
43650     
43651     
43652     this.addEvents({
43653         /**
43654          * @event initialize
43655          * Fires when the editor is fully initialized (including the iframe)
43656          * @param {Roo.HtmlEditorCore} this
43657          */
43658         initialize: true,
43659         /**
43660          * @event activate
43661          * Fires when the editor is first receives the focus. Any insertion must wait
43662          * until after this event.
43663          * @param {Roo.HtmlEditorCore} this
43664          */
43665         activate: true,
43666          /**
43667          * @event beforesync
43668          * Fires before the textarea is updated with content from the editor iframe. Return false
43669          * to cancel the sync.
43670          * @param {Roo.HtmlEditorCore} this
43671          * @param {String} html
43672          */
43673         beforesync: true,
43674          /**
43675          * @event beforepush
43676          * Fires before the iframe editor is updated with content from the textarea. Return false
43677          * to cancel the push.
43678          * @param {Roo.HtmlEditorCore} this
43679          * @param {String} html
43680          */
43681         beforepush: true,
43682          /**
43683          * @event sync
43684          * Fires when the textarea is updated with content from the editor iframe.
43685          * @param {Roo.HtmlEditorCore} this
43686          * @param {String} html
43687          */
43688         sync: true,
43689          /**
43690          * @event push
43691          * Fires when the iframe editor is updated with content from the textarea.
43692          * @param {Roo.HtmlEditorCore} this
43693          * @param {String} html
43694          */
43695         push: true,
43696         
43697         /**
43698          * @event editorevent
43699          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
43700          * @param {Roo.HtmlEditorCore} this
43701          */
43702         editorevent: true
43703         
43704     });
43705     
43706     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
43707     
43708     // defaults : white / black...
43709     this.applyBlacklists();
43710     
43711     
43712     
43713 };
43714
43715
43716 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
43717
43718
43719      /**
43720      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
43721      */
43722     
43723     owner : false,
43724     
43725      /**
43726      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
43727      *                        Roo.resizable.
43728      */
43729     resizable : false,
43730      /**
43731      * @cfg {Number} height (in pixels)
43732      */   
43733     height: 300,
43734    /**
43735      * @cfg {Number} width (in pixels)
43736      */   
43737     width: 500,
43738     
43739     /**
43740      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
43741      * 
43742      */
43743     stylesheets: false,
43744     
43745     // id of frame..
43746     frameId: false,
43747     
43748     // private properties
43749     validationEvent : false,
43750     deferHeight: true,
43751     initialized : false,
43752     activated : false,
43753     sourceEditMode : false,
43754     onFocus : Roo.emptyFn,
43755     iframePad:3,
43756     hideMode:'offsets',
43757     
43758     clearUp: true,
43759     
43760     // blacklist + whitelisted elements..
43761     black: false,
43762     white: false,
43763      
43764     bodyCls : '',
43765
43766     /**
43767      * Protected method that will not generally be called directly. It
43768      * is called when the editor initializes the iframe with HTML contents. Override this method if you
43769      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
43770      */
43771     getDocMarkup : function(){
43772         // body styles..
43773         var st = '';
43774         
43775         // inherit styels from page...?? 
43776         if (this.stylesheets === false) {
43777             
43778             Roo.get(document.head).select('style').each(function(node) {
43779                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43780             });
43781             
43782             Roo.get(document.head).select('link').each(function(node) { 
43783                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43784             });
43785             
43786         } else if (!this.stylesheets.length) {
43787                 // simple..
43788                 st = '<style type="text/css">' +
43789                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43790                    '</style>';
43791         } else {
43792             for (var i in this.stylesheets) { 
43793                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
43794             }
43795             
43796         }
43797         
43798         st +=  '<style type="text/css">' +
43799             'IMG { cursor: pointer } ' +
43800         '</style>';
43801
43802         var cls = 'roo-htmleditor-body';
43803         
43804         if(this.bodyCls.length){
43805             cls += ' ' + this.bodyCls;
43806         }
43807         
43808         return '<html><head>' + st  +
43809             //<style type="text/css">' +
43810             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43811             //'</style>' +
43812             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
43813     },
43814
43815     // private
43816     onRender : function(ct, position)
43817     {
43818         var _t = this;
43819         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
43820         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
43821         
43822         
43823         this.el.dom.style.border = '0 none';
43824         this.el.dom.setAttribute('tabIndex', -1);
43825         this.el.addClass('x-hidden hide');
43826         
43827         
43828         
43829         if(Roo.isIE){ // fix IE 1px bogus margin
43830             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43831         }
43832        
43833         
43834         this.frameId = Roo.id();
43835         
43836          
43837         
43838         var iframe = this.owner.wrap.createChild({
43839             tag: 'iframe',
43840             cls: 'form-control', // bootstrap..
43841             id: this.frameId,
43842             name: this.frameId,
43843             frameBorder : 'no',
43844             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43845         }, this.el
43846         );
43847         
43848         
43849         this.iframe = iframe.dom;
43850
43851          this.assignDocWin();
43852         
43853         this.doc.designMode = 'on';
43854        
43855         this.doc.open();
43856         this.doc.write(this.getDocMarkup());
43857         this.doc.close();
43858
43859         
43860         var task = { // must defer to wait for browser to be ready
43861             run : function(){
43862                 //console.log("run task?" + this.doc.readyState);
43863                 this.assignDocWin();
43864                 if(this.doc.body || this.doc.readyState == 'complete'){
43865                     try {
43866                         this.doc.designMode="on";
43867                     } catch (e) {
43868                         return;
43869                     }
43870                     Roo.TaskMgr.stop(task);
43871                     this.initEditor.defer(10, this);
43872                 }
43873             },
43874             interval : 10,
43875             duration: 10000,
43876             scope: this
43877         };
43878         Roo.TaskMgr.start(task);
43879
43880     },
43881
43882     // private
43883     onResize : function(w, h)
43884     {
43885          Roo.log('resize: ' +w + ',' + h );
43886         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43887         if(!this.iframe){
43888             return;
43889         }
43890         if(typeof w == 'number'){
43891             
43892             this.iframe.style.width = w + 'px';
43893         }
43894         if(typeof h == 'number'){
43895             
43896             this.iframe.style.height = h + 'px';
43897             if(this.doc){
43898                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43899             }
43900         }
43901         
43902     },
43903
43904     /**
43905      * Toggles the editor between standard and source edit mode.
43906      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43907      */
43908     toggleSourceEdit : function(sourceEditMode){
43909         
43910         this.sourceEditMode = sourceEditMode === true;
43911         
43912         if(this.sourceEditMode){
43913  
43914             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43915             
43916         }else{
43917             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43918             //this.iframe.className = '';
43919             this.deferFocus();
43920         }
43921         //this.setSize(this.owner.wrap.getSize());
43922         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43923     },
43924
43925     
43926   
43927
43928     /**
43929      * Protected method that will not generally be called directly. If you need/want
43930      * custom HTML cleanup, this is the method you should override.
43931      * @param {String} html The HTML to be cleaned
43932      * return {String} The cleaned HTML
43933      */
43934     cleanHtml : function(html){
43935         html = String(html);
43936         if(html.length > 5){
43937             if(Roo.isSafari){ // strip safari nonsense
43938                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
43939             }
43940         }
43941         if(html == '&nbsp;'){
43942             html = '';
43943         }
43944         return html;
43945     },
43946
43947     /**
43948      * HTML Editor -> Textarea
43949      * Protected method that will not generally be called directly. Syncs the contents
43950      * of the editor iframe with the textarea.
43951      */
43952     syncValue : function(){
43953         if(this.initialized){
43954             var bd = (this.doc.body || this.doc.documentElement);
43955             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43956             var html = bd.innerHTML;
43957             if(Roo.isSafari){
43958                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43959                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43960                 if(m && m[1]){
43961                     html = '<div style="'+m[0]+'">' + html + '</div>';
43962                 }
43963             }
43964             html = this.cleanHtml(html);
43965             // fix up the special chars.. normaly like back quotes in word...
43966             // however we do not want to do this with chinese..
43967             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
43968                 
43969                 var cc = match.charCodeAt();
43970
43971                 // Get the character value, handling surrogate pairs
43972                 if (match.length == 2) {
43973                     // It's a surrogate pair, calculate the Unicode code point
43974                     var high = match.charCodeAt(0) - 0xD800;
43975                     var low  = match.charCodeAt(1) - 0xDC00;
43976                     cc = (high * 0x400) + low + 0x10000;
43977                 }  else if (
43978                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43979                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43980                     (cc >= 0xf900 && cc < 0xfb00 )
43981                 ) {
43982                         return match;
43983                 }  
43984          
43985                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
43986                 return "&#" + cc + ";";
43987                 
43988                 
43989             });
43990             
43991             
43992              
43993             if(this.owner.fireEvent('beforesync', this, html) !== false){
43994                 this.el.dom.value = html;
43995                 this.owner.fireEvent('sync', this, html);
43996             }
43997         }
43998     },
43999
44000     /**
44001      * Protected method that will not generally be called directly. Pushes the value of the textarea
44002      * into the iframe editor.
44003      */
44004     pushValue : function(){
44005         if(this.initialized){
44006             var v = this.el.dom.value.trim();
44007             
44008 //            if(v.length < 1){
44009 //                v = '&#160;';
44010 //            }
44011             
44012             if(this.owner.fireEvent('beforepush', this, v) !== false){
44013                 var d = (this.doc.body || this.doc.documentElement);
44014                 d.innerHTML = v;
44015                 this.cleanUpPaste();
44016                 this.el.dom.value = d.innerHTML;
44017                 this.owner.fireEvent('push', this, v);
44018             }
44019         }
44020     },
44021
44022     // private
44023     deferFocus : function(){
44024         this.focus.defer(10, this);
44025     },
44026
44027     // doc'ed in Field
44028     focus : function(){
44029         if(this.win && !this.sourceEditMode){
44030             this.win.focus();
44031         }else{
44032             this.el.focus();
44033         }
44034     },
44035     
44036     assignDocWin: function()
44037     {
44038         var iframe = this.iframe;
44039         
44040          if(Roo.isIE){
44041             this.doc = iframe.contentWindow.document;
44042             this.win = iframe.contentWindow;
44043         } else {
44044 //            if (!Roo.get(this.frameId)) {
44045 //                return;
44046 //            }
44047 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44048 //            this.win = Roo.get(this.frameId).dom.contentWindow;
44049             
44050             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
44051                 return;
44052             }
44053             
44054             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44055             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
44056         }
44057     },
44058     
44059     // private
44060     initEditor : function(){
44061         //console.log("INIT EDITOR");
44062         this.assignDocWin();
44063         
44064         
44065         
44066         this.doc.designMode="on";
44067         this.doc.open();
44068         this.doc.write(this.getDocMarkup());
44069         this.doc.close();
44070         
44071         var dbody = (this.doc.body || this.doc.documentElement);
44072         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
44073         // this copies styles from the containing element into thsi one..
44074         // not sure why we need all of this..
44075         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
44076         
44077         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
44078         //ss['background-attachment'] = 'fixed'; // w3c
44079         dbody.bgProperties = 'fixed'; // ie
44080         //Roo.DomHelper.applyStyles(dbody, ss);
44081         Roo.EventManager.on(this.doc, {
44082             //'mousedown': this.onEditorEvent,
44083             'mouseup': this.onEditorEvent,
44084             'dblclick': this.onEditorEvent,
44085             'click': this.onEditorEvent,
44086             'keyup': this.onEditorEvent,
44087             buffer:100,
44088             scope: this
44089         });
44090         if(Roo.isGecko){
44091             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
44092         }
44093         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
44094             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
44095         }
44096         this.initialized = true;
44097
44098         this.owner.fireEvent('initialize', this);
44099         this.pushValue();
44100     },
44101
44102     // private
44103     onDestroy : function(){
44104         
44105         
44106         
44107         if(this.rendered){
44108             
44109             //for (var i =0; i < this.toolbars.length;i++) {
44110             //    // fixme - ask toolbars for heights?
44111             //    this.toolbars[i].onDestroy();
44112            // }
44113             
44114             //this.wrap.dom.innerHTML = '';
44115             //this.wrap.remove();
44116         }
44117     },
44118
44119     // private
44120     onFirstFocus : function(){
44121         
44122         this.assignDocWin();
44123         
44124         
44125         this.activated = true;
44126          
44127     
44128         if(Roo.isGecko){ // prevent silly gecko errors
44129             this.win.focus();
44130             var s = this.win.getSelection();
44131             if(!s.focusNode || s.focusNode.nodeType != 3){
44132                 var r = s.getRangeAt(0);
44133                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
44134                 r.collapse(true);
44135                 this.deferFocus();
44136             }
44137             try{
44138                 this.execCmd('useCSS', true);
44139                 this.execCmd('styleWithCSS', false);
44140             }catch(e){}
44141         }
44142         this.owner.fireEvent('activate', this);
44143     },
44144
44145     // private
44146     adjustFont: function(btn){
44147         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
44148         //if(Roo.isSafari){ // safari
44149         //    adjust *= 2;
44150        // }
44151         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
44152         if(Roo.isSafari){ // safari
44153             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
44154             v =  (v < 10) ? 10 : v;
44155             v =  (v > 48) ? 48 : v;
44156             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
44157             
44158         }
44159         
44160         
44161         v = Math.max(1, v+adjust);
44162         
44163         this.execCmd('FontSize', v  );
44164     },
44165
44166     onEditorEvent : function(e)
44167     {
44168         this.owner.fireEvent('editorevent', this, e);
44169       //  this.updateToolbar();
44170         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
44171     },
44172
44173     insertTag : function(tg)
44174     {
44175         // could be a bit smarter... -> wrap the current selected tRoo..
44176         if (tg.toLowerCase() == 'span' ||
44177             tg.toLowerCase() == 'code' ||
44178             tg.toLowerCase() == 'sup' ||
44179             tg.toLowerCase() == 'sub' 
44180             ) {
44181             
44182             range = this.createRange(this.getSelection());
44183             var wrappingNode = this.doc.createElement(tg.toLowerCase());
44184             wrappingNode.appendChild(range.extractContents());
44185             range.insertNode(wrappingNode);
44186
44187             return;
44188             
44189             
44190             
44191         }
44192         this.execCmd("formatblock",   tg);
44193         
44194     },
44195     
44196     insertText : function(txt)
44197     {
44198         
44199         
44200         var range = this.createRange();
44201         range.deleteContents();
44202                //alert(Sender.getAttribute('label'));
44203                
44204         range.insertNode(this.doc.createTextNode(txt));
44205     } ,
44206     
44207      
44208
44209     /**
44210      * Executes a Midas editor command on the editor document and performs necessary focus and
44211      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
44212      * @param {String} cmd The Midas command
44213      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44214      */
44215     relayCmd : function(cmd, value){
44216         this.win.focus();
44217         this.execCmd(cmd, value);
44218         this.owner.fireEvent('editorevent', this);
44219         //this.updateToolbar();
44220         this.owner.deferFocus();
44221     },
44222
44223     /**
44224      * Executes a Midas editor command directly on the editor document.
44225      * For visual commands, you should use {@link #relayCmd} instead.
44226      * <b>This should only be called after the editor is initialized.</b>
44227      * @param {String} cmd The Midas command
44228      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44229      */
44230     execCmd : function(cmd, value){
44231         this.doc.execCommand(cmd, false, value === undefined ? null : value);
44232         this.syncValue();
44233     },
44234  
44235  
44236    
44237     /**
44238      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
44239      * to insert tRoo.
44240      * @param {String} text | dom node.. 
44241      */
44242     insertAtCursor : function(text)
44243     {
44244         
44245         if(!this.activated){
44246             return;
44247         }
44248         /*
44249         if(Roo.isIE){
44250             this.win.focus();
44251             var r = this.doc.selection.createRange();
44252             if(r){
44253                 r.collapse(true);
44254                 r.pasteHTML(text);
44255                 this.syncValue();
44256                 this.deferFocus();
44257             
44258             }
44259             return;
44260         }
44261         */
44262         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
44263             this.win.focus();
44264             
44265             
44266             // from jquery ui (MIT licenced)
44267             var range, node;
44268             var win = this.win;
44269             
44270             if (win.getSelection && win.getSelection().getRangeAt) {
44271                 range = win.getSelection().getRangeAt(0);
44272                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
44273                 range.insertNode(node);
44274             } else if (win.document.selection && win.document.selection.createRange) {
44275                 // no firefox support
44276                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44277                 win.document.selection.createRange().pasteHTML(txt);
44278             } else {
44279                 // no firefox support
44280                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44281                 this.execCmd('InsertHTML', txt);
44282             } 
44283             
44284             this.syncValue();
44285             
44286             this.deferFocus();
44287         }
44288     },
44289  // private
44290     mozKeyPress : function(e){
44291         if(e.ctrlKey){
44292             var c = e.getCharCode(), cmd;
44293           
44294             if(c > 0){
44295                 c = String.fromCharCode(c).toLowerCase();
44296                 switch(c){
44297                     case 'b':
44298                         cmd = 'bold';
44299                         break;
44300                     case 'i':
44301                         cmd = 'italic';
44302                         break;
44303                     
44304                     case 'u':
44305                         cmd = 'underline';
44306                         break;
44307                     
44308                     case 'v':
44309                         this.cleanUpPaste.defer(100, this);
44310                         return;
44311                         
44312                 }
44313                 if(cmd){
44314                     this.win.focus();
44315                     this.execCmd(cmd);
44316                     this.deferFocus();
44317                     e.preventDefault();
44318                 }
44319                 
44320             }
44321         }
44322     },
44323
44324     // private
44325     fixKeys : function(){ // load time branching for fastest keydown performance
44326         if(Roo.isIE){
44327             return function(e){
44328                 var k = e.getKey(), r;
44329                 if(k == e.TAB){
44330                     e.stopEvent();
44331                     r = this.doc.selection.createRange();
44332                     if(r){
44333                         r.collapse(true);
44334                         r.pasteHTML('&#160;&#160;&#160;&#160;');
44335                         this.deferFocus();
44336                     }
44337                     return;
44338                 }
44339                 
44340                 if(k == e.ENTER){
44341                     r = this.doc.selection.createRange();
44342                     if(r){
44343                         var target = r.parentElement();
44344                         if(!target || target.tagName.toLowerCase() != 'li'){
44345                             e.stopEvent();
44346                             r.pasteHTML('<br />');
44347                             r.collapse(false);
44348                             r.select();
44349                         }
44350                     }
44351                 }
44352                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44353                     this.cleanUpPaste.defer(100, this);
44354                     return;
44355                 }
44356                 
44357                 
44358             };
44359         }else if(Roo.isOpera){
44360             return function(e){
44361                 var k = e.getKey();
44362                 if(k == e.TAB){
44363                     e.stopEvent();
44364                     this.win.focus();
44365                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
44366                     this.deferFocus();
44367                 }
44368                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44369                     this.cleanUpPaste.defer(100, this);
44370                     return;
44371                 }
44372                 
44373             };
44374         }else if(Roo.isSafari){
44375             return function(e){
44376                 var k = e.getKey();
44377                 
44378                 if(k == e.TAB){
44379                     e.stopEvent();
44380                     this.execCmd('InsertText','\t');
44381                     this.deferFocus();
44382                     return;
44383                 }
44384                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44385                     this.cleanUpPaste.defer(100, this);
44386                     return;
44387                 }
44388                 
44389              };
44390         }
44391     }(),
44392     
44393     getAllAncestors: function()
44394     {
44395         var p = this.getSelectedNode();
44396         var a = [];
44397         if (!p) {
44398             a.push(p); // push blank onto stack..
44399             p = this.getParentElement();
44400         }
44401         
44402         
44403         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
44404             a.push(p);
44405             p = p.parentNode;
44406         }
44407         a.push(this.doc.body);
44408         return a;
44409     },
44410     lastSel : false,
44411     lastSelNode : false,
44412     
44413     
44414     getSelection : function() 
44415     {
44416         this.assignDocWin();
44417         return Roo.isIE ? this.doc.selection : this.win.getSelection();
44418     },
44419     
44420     getSelectedNode: function() 
44421     {
44422         // this may only work on Gecko!!!
44423         
44424         // should we cache this!!!!
44425         
44426         
44427         
44428          
44429         var range = this.createRange(this.getSelection()).cloneRange();
44430         
44431         if (Roo.isIE) {
44432             var parent = range.parentElement();
44433             while (true) {
44434                 var testRange = range.duplicate();
44435                 testRange.moveToElementText(parent);
44436                 if (testRange.inRange(range)) {
44437                     break;
44438                 }
44439                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
44440                     break;
44441                 }
44442                 parent = parent.parentElement;
44443             }
44444             return parent;
44445         }
44446         
44447         // is ancestor a text element.
44448         var ac =  range.commonAncestorContainer;
44449         if (ac.nodeType == 3) {
44450             ac = ac.parentNode;
44451         }
44452         
44453         var ar = ac.childNodes;
44454          
44455         var nodes = [];
44456         var other_nodes = [];
44457         var has_other_nodes = false;
44458         for (var i=0;i<ar.length;i++) {
44459             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
44460                 continue;
44461             }
44462             // fullly contained node.
44463             
44464             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
44465                 nodes.push(ar[i]);
44466                 continue;
44467             }
44468             
44469             // probably selected..
44470             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
44471                 other_nodes.push(ar[i]);
44472                 continue;
44473             }
44474             // outer..
44475             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
44476                 continue;
44477             }
44478             
44479             
44480             has_other_nodes = true;
44481         }
44482         if (!nodes.length && other_nodes.length) {
44483             nodes= other_nodes;
44484         }
44485         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
44486             return false;
44487         }
44488         
44489         return nodes[0];
44490     },
44491     createRange: function(sel)
44492     {
44493         // this has strange effects when using with 
44494         // top toolbar - not sure if it's a great idea.
44495         //this.editor.contentWindow.focus();
44496         if (typeof sel != "undefined") {
44497             try {
44498                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
44499             } catch(e) {
44500                 return this.doc.createRange();
44501             }
44502         } else {
44503             return this.doc.createRange();
44504         }
44505     },
44506     getParentElement: function()
44507     {
44508         
44509         this.assignDocWin();
44510         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
44511         
44512         var range = this.createRange(sel);
44513          
44514         try {
44515             var p = range.commonAncestorContainer;
44516             while (p.nodeType == 3) { // text node
44517                 p = p.parentNode;
44518             }
44519             return p;
44520         } catch (e) {
44521             return null;
44522         }
44523     
44524     },
44525     /***
44526      *
44527      * Range intersection.. the hard stuff...
44528      *  '-1' = before
44529      *  '0' = hits..
44530      *  '1' = after.
44531      *         [ -- selected range --- ]
44532      *   [fail]                        [fail]
44533      *
44534      *    basically..
44535      *      if end is before start or  hits it. fail.
44536      *      if start is after end or hits it fail.
44537      *
44538      *   if either hits (but other is outside. - then it's not 
44539      *   
44540      *    
44541      **/
44542     
44543     
44544     // @see http://www.thismuchiknow.co.uk/?p=64.
44545     rangeIntersectsNode : function(range, node)
44546     {
44547         var nodeRange = node.ownerDocument.createRange();
44548         try {
44549             nodeRange.selectNode(node);
44550         } catch (e) {
44551             nodeRange.selectNodeContents(node);
44552         }
44553     
44554         var rangeStartRange = range.cloneRange();
44555         rangeStartRange.collapse(true);
44556     
44557         var rangeEndRange = range.cloneRange();
44558         rangeEndRange.collapse(false);
44559     
44560         var nodeStartRange = nodeRange.cloneRange();
44561         nodeStartRange.collapse(true);
44562     
44563         var nodeEndRange = nodeRange.cloneRange();
44564         nodeEndRange.collapse(false);
44565     
44566         return rangeStartRange.compareBoundaryPoints(
44567                  Range.START_TO_START, nodeEndRange) == -1 &&
44568                rangeEndRange.compareBoundaryPoints(
44569                  Range.START_TO_START, nodeStartRange) == 1;
44570         
44571          
44572     },
44573     rangeCompareNode : function(range, node)
44574     {
44575         var nodeRange = node.ownerDocument.createRange();
44576         try {
44577             nodeRange.selectNode(node);
44578         } catch (e) {
44579             nodeRange.selectNodeContents(node);
44580         }
44581         
44582         
44583         range.collapse(true);
44584     
44585         nodeRange.collapse(true);
44586      
44587         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
44588         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
44589          
44590         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
44591         
44592         var nodeIsBefore   =  ss == 1;
44593         var nodeIsAfter    = ee == -1;
44594         
44595         if (nodeIsBefore && nodeIsAfter) {
44596             return 0; // outer
44597         }
44598         if (!nodeIsBefore && nodeIsAfter) {
44599             return 1; //right trailed.
44600         }
44601         
44602         if (nodeIsBefore && !nodeIsAfter) {
44603             return 2;  // left trailed.
44604         }
44605         // fully contined.
44606         return 3;
44607     },
44608
44609     // private? - in a new class?
44610     cleanUpPaste :  function()
44611     {
44612         // cleans up the whole document..
44613         Roo.log('cleanuppaste');
44614         
44615         this.cleanUpChildren(this.doc.body);
44616         var clean = this.cleanWordChars(this.doc.body.innerHTML);
44617         if (clean != this.doc.body.innerHTML) {
44618             this.doc.body.innerHTML = clean;
44619         }
44620         
44621     },
44622     
44623     cleanWordChars : function(input) {// change the chars to hex code
44624         var he = Roo.HtmlEditorCore;
44625         
44626         var output = input;
44627         Roo.each(he.swapCodes, function(sw) { 
44628             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
44629             
44630             output = output.replace(swapper, sw[1]);
44631         });
44632         
44633         return output;
44634     },
44635     
44636     
44637     cleanUpChildren : function (n)
44638     {
44639         if (!n.childNodes.length) {
44640             return;
44641         }
44642         for (var i = n.childNodes.length-1; i > -1 ; i--) {
44643            this.cleanUpChild(n.childNodes[i]);
44644         }
44645     },
44646     
44647     
44648         
44649     
44650     cleanUpChild : function (node)
44651     {
44652         var ed = this;
44653         //console.log(node);
44654         if (node.nodeName == "#text") {
44655             // clean up silly Windows -- stuff?
44656             return; 
44657         }
44658         if (node.nodeName == "#comment") {
44659             node.parentNode.removeChild(node);
44660             // clean up silly Windows -- stuff?
44661             return; 
44662         }
44663         var lcname = node.tagName.toLowerCase();
44664         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
44665         // whitelist of tags..
44666         
44667         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
44668             // remove node.
44669             node.parentNode.removeChild(node);
44670             return;
44671             
44672         }
44673         
44674         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
44675         
44676         // spans with no attributes - just remove them..
44677         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
44678             remove_keep_children = true;
44679         }
44680         
44681         // remove <a name=....> as rendering on yahoo mailer is borked with this.
44682         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
44683         
44684         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
44685         //    remove_keep_children = true;
44686         //}
44687         
44688         if (remove_keep_children) {
44689             this.cleanUpChildren(node);
44690             // inserts everything just before this node...
44691             while (node.childNodes.length) {
44692                 var cn = node.childNodes[0];
44693                 node.removeChild(cn);
44694                 node.parentNode.insertBefore(cn, node);
44695             }
44696             node.parentNode.removeChild(node);
44697             return;
44698         }
44699         
44700         if (!node.attributes || !node.attributes.length) {
44701             
44702           
44703             
44704             
44705             this.cleanUpChildren(node);
44706             return;
44707         }
44708         
44709         function cleanAttr(n,v)
44710         {
44711             
44712             if (v.match(/^\./) || v.match(/^\//)) {
44713                 return;
44714             }
44715             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44716                 return;
44717             }
44718             if (v.match(/^#/)) {
44719                 return;
44720             }
44721             if (v.match(/^\{/)) { // allow template editing.
44722                 return;
44723             }
44724 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44725             node.removeAttribute(n);
44726             
44727         }
44728         
44729         var cwhite = this.cwhite;
44730         var cblack = this.cblack;
44731             
44732         function cleanStyle(n,v)
44733         {
44734             if (v.match(/expression/)) { //XSS?? should we even bother..
44735                 node.removeAttribute(n);
44736                 return;
44737             }
44738             
44739             var parts = v.split(/;/);
44740             var clean = [];
44741             
44742             Roo.each(parts, function(p) {
44743                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44744                 if (!p.length) {
44745                     return true;
44746                 }
44747                 var l = p.split(':').shift().replace(/\s+/g,'');
44748                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44749                 
44750                 if ( cwhite.length && cblack.indexOf(l) > -1) {
44751 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44752                     //node.removeAttribute(n);
44753                     return true;
44754                 }
44755                 //Roo.log()
44756                 // only allow 'c whitelisted system attributes'
44757                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
44758 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44759                     //node.removeAttribute(n);
44760                     return true;
44761                 }
44762                 
44763                 
44764                  
44765                 
44766                 clean.push(p);
44767                 return true;
44768             });
44769             if (clean.length) { 
44770                 node.setAttribute(n, clean.join(';'));
44771             } else {
44772                 node.removeAttribute(n);
44773             }
44774             
44775         }
44776         
44777         
44778         for (var i = node.attributes.length-1; i > -1 ; i--) {
44779             var a = node.attributes[i];
44780             //console.log(a);
44781             
44782             if (a.name.toLowerCase().substr(0,2)=='on')  {
44783                 node.removeAttribute(a.name);
44784                 continue;
44785             }
44786             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
44787                 node.removeAttribute(a.name);
44788                 continue;
44789             }
44790             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
44791                 cleanAttr(a.name,a.value); // fixme..
44792                 continue;
44793             }
44794             if (a.name == 'style') {
44795                 cleanStyle(a.name,a.value);
44796                 continue;
44797             }
44798             /// clean up MS crap..
44799             // tecnically this should be a list of valid class'es..
44800             
44801             
44802             if (a.name == 'class') {
44803                 if (a.value.match(/^Mso/)) {
44804                     node.removeAttribute('class');
44805                 }
44806                 
44807                 if (a.value.match(/^body$/)) {
44808                     node.removeAttribute('class');
44809                 }
44810                 continue;
44811             }
44812             
44813             // style cleanup!?
44814             // class cleanup?
44815             
44816         }
44817         
44818         
44819         this.cleanUpChildren(node);
44820         
44821         
44822     },
44823     
44824     /**
44825      * Clean up MS wordisms...
44826      */
44827     cleanWord : function(node)
44828     {
44829         if (!node) {
44830             this.cleanWord(this.doc.body);
44831             return;
44832         }
44833         
44834         if(
44835                 node.nodeName == 'SPAN' &&
44836                 !node.hasAttributes() &&
44837                 node.childNodes.length == 1 &&
44838                 node.firstChild.nodeName == "#text"  
44839         ) {
44840             var textNode = node.firstChild;
44841             node.removeChild(textNode);
44842             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44843                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44844             }
44845             node.parentNode.insertBefore(textNode, node);
44846             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44847                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44848             }
44849             node.parentNode.removeChild(node);
44850         }
44851         
44852         if (node.nodeName == "#text") {
44853             // clean up silly Windows -- stuff?
44854             return; 
44855         }
44856         if (node.nodeName == "#comment") {
44857             node.parentNode.removeChild(node);
44858             // clean up silly Windows -- stuff?
44859             return; 
44860         }
44861         
44862         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44863             node.parentNode.removeChild(node);
44864             return;
44865         }
44866         //Roo.log(node.tagName);
44867         // remove - but keep children..
44868         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44869             //Roo.log('-- removed');
44870             while (node.childNodes.length) {
44871                 var cn = node.childNodes[0];
44872                 node.removeChild(cn);
44873                 node.parentNode.insertBefore(cn, node);
44874                 // move node to parent - and clean it..
44875                 this.cleanWord(cn);
44876             }
44877             node.parentNode.removeChild(node);
44878             /// no need to iterate chidlren = it's got none..
44879             //this.iterateChildren(node, this.cleanWord);
44880             return;
44881         }
44882         // clean styles
44883         if (node.className.length) {
44884             
44885             var cn = node.className.split(/\W+/);
44886             var cna = [];
44887             Roo.each(cn, function(cls) {
44888                 if (cls.match(/Mso[a-zA-Z]+/)) {
44889                     return;
44890                 }
44891                 cna.push(cls);
44892             });
44893             node.className = cna.length ? cna.join(' ') : '';
44894             if (!cna.length) {
44895                 node.removeAttribute("class");
44896             }
44897         }
44898         
44899         if (node.hasAttribute("lang")) {
44900             node.removeAttribute("lang");
44901         }
44902         
44903         if (node.hasAttribute("style")) {
44904             
44905             var styles = node.getAttribute("style").split(";");
44906             var nstyle = [];
44907             Roo.each(styles, function(s) {
44908                 if (!s.match(/:/)) {
44909                     return;
44910                 }
44911                 var kv = s.split(":");
44912                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44913                     return;
44914                 }
44915                 // what ever is left... we allow.
44916                 nstyle.push(s);
44917             });
44918             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44919             if (!nstyle.length) {
44920                 node.removeAttribute('style');
44921             }
44922         }
44923         this.iterateChildren(node, this.cleanWord);
44924         
44925         
44926         
44927     },
44928     /**
44929      * iterateChildren of a Node, calling fn each time, using this as the scole..
44930      * @param {DomNode} node node to iterate children of.
44931      * @param {Function} fn method of this class to call on each item.
44932      */
44933     iterateChildren : function(node, fn)
44934     {
44935         if (!node.childNodes.length) {
44936                 return;
44937         }
44938         for (var i = node.childNodes.length-1; i > -1 ; i--) {
44939            fn.call(this, node.childNodes[i])
44940         }
44941     },
44942     
44943     
44944     /**
44945      * cleanTableWidths.
44946      *
44947      * Quite often pasting from word etc.. results in tables with column and widths.
44948      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44949      *
44950      */
44951     cleanTableWidths : function(node)
44952     {
44953          
44954          
44955         if (!node) {
44956             this.cleanTableWidths(this.doc.body);
44957             return;
44958         }
44959         
44960         // ignore list...
44961         if (node.nodeName == "#text" || node.nodeName == "#comment") {
44962             return; 
44963         }
44964         Roo.log(node.tagName);
44965         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
44966             this.iterateChildren(node, this.cleanTableWidths);
44967             return;
44968         }
44969         if (node.hasAttribute('width')) {
44970             node.removeAttribute('width');
44971         }
44972         
44973          
44974         if (node.hasAttribute("style")) {
44975             // pretty basic...
44976             
44977             var styles = node.getAttribute("style").split(";");
44978             var nstyle = [];
44979             Roo.each(styles, function(s) {
44980                 if (!s.match(/:/)) {
44981                     return;
44982                 }
44983                 var kv = s.split(":");
44984                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44985                     return;
44986                 }
44987                 // what ever is left... we allow.
44988                 nstyle.push(s);
44989             });
44990             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44991             if (!nstyle.length) {
44992                 node.removeAttribute('style');
44993             }
44994         }
44995         
44996         this.iterateChildren(node, this.cleanTableWidths);
44997         
44998         
44999     },
45000     
45001     
45002     
45003     
45004     domToHTML : function(currentElement, depth, nopadtext) {
45005         
45006         depth = depth || 0;
45007         nopadtext = nopadtext || false;
45008     
45009         if (!currentElement) {
45010             return this.domToHTML(this.doc.body);
45011         }
45012         
45013         //Roo.log(currentElement);
45014         var j;
45015         var allText = false;
45016         var nodeName = currentElement.nodeName;
45017         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
45018         
45019         if  (nodeName == '#text') {
45020             
45021             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
45022         }
45023         
45024         
45025         var ret = '';
45026         if (nodeName != 'BODY') {
45027              
45028             var i = 0;
45029             // Prints the node tagName, such as <A>, <IMG>, etc
45030             if (tagName) {
45031                 var attr = [];
45032                 for(i = 0; i < currentElement.attributes.length;i++) {
45033                     // quoting?
45034                     var aname = currentElement.attributes.item(i).name;
45035                     if (!currentElement.attributes.item(i).value.length) {
45036                         continue;
45037                     }
45038                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
45039                 }
45040                 
45041                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
45042             } 
45043             else {
45044                 
45045                 // eack
45046             }
45047         } else {
45048             tagName = false;
45049         }
45050         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
45051             return ret;
45052         }
45053         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
45054             nopadtext = true;
45055         }
45056         
45057         
45058         // Traverse the tree
45059         i = 0;
45060         var currentElementChild = currentElement.childNodes.item(i);
45061         var allText = true;
45062         var innerHTML  = '';
45063         lastnode = '';
45064         while (currentElementChild) {
45065             // Formatting code (indent the tree so it looks nice on the screen)
45066             var nopad = nopadtext;
45067             if (lastnode == 'SPAN') {
45068                 nopad  = true;
45069             }
45070             // text
45071             if  (currentElementChild.nodeName == '#text') {
45072                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
45073                 toadd = nopadtext ? toadd : toadd.trim();
45074                 if (!nopad && toadd.length > 80) {
45075                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
45076                 }
45077                 innerHTML  += toadd;
45078                 
45079                 i++;
45080                 currentElementChild = currentElement.childNodes.item(i);
45081                 lastNode = '';
45082                 continue;
45083             }
45084             allText = false;
45085             
45086             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
45087                 
45088             // Recursively traverse the tree structure of the child node
45089             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
45090             lastnode = currentElementChild.nodeName;
45091             i++;
45092             currentElementChild=currentElement.childNodes.item(i);
45093         }
45094         
45095         ret += innerHTML;
45096         
45097         if (!allText) {
45098                 // The remaining code is mostly for formatting the tree
45099             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
45100         }
45101         
45102         
45103         if (tagName) {
45104             ret+= "</"+tagName+">";
45105         }
45106         return ret;
45107         
45108     },
45109         
45110     applyBlacklists : function()
45111     {
45112         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
45113         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
45114         
45115         this.white = [];
45116         this.black = [];
45117         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
45118             if (b.indexOf(tag) > -1) {
45119                 return;
45120             }
45121             this.white.push(tag);
45122             
45123         }, this);
45124         
45125         Roo.each(w, function(tag) {
45126             if (b.indexOf(tag) > -1) {
45127                 return;
45128             }
45129             if (this.white.indexOf(tag) > -1) {
45130                 return;
45131             }
45132             this.white.push(tag);
45133             
45134         }, this);
45135         
45136         
45137         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
45138             if (w.indexOf(tag) > -1) {
45139                 return;
45140             }
45141             this.black.push(tag);
45142             
45143         }, this);
45144         
45145         Roo.each(b, function(tag) {
45146             if (w.indexOf(tag) > -1) {
45147                 return;
45148             }
45149             if (this.black.indexOf(tag) > -1) {
45150                 return;
45151             }
45152             this.black.push(tag);
45153             
45154         }, this);
45155         
45156         
45157         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
45158         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
45159         
45160         this.cwhite = [];
45161         this.cblack = [];
45162         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
45163             if (b.indexOf(tag) > -1) {
45164                 return;
45165             }
45166             this.cwhite.push(tag);
45167             
45168         }, this);
45169         
45170         Roo.each(w, function(tag) {
45171             if (b.indexOf(tag) > -1) {
45172                 return;
45173             }
45174             if (this.cwhite.indexOf(tag) > -1) {
45175                 return;
45176             }
45177             this.cwhite.push(tag);
45178             
45179         }, this);
45180         
45181         
45182         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
45183             if (w.indexOf(tag) > -1) {
45184                 return;
45185             }
45186             this.cblack.push(tag);
45187             
45188         }, this);
45189         
45190         Roo.each(b, function(tag) {
45191             if (w.indexOf(tag) > -1) {
45192                 return;
45193             }
45194             if (this.cblack.indexOf(tag) > -1) {
45195                 return;
45196             }
45197             this.cblack.push(tag);
45198             
45199         }, this);
45200     },
45201     
45202     setStylesheets : function(stylesheets)
45203     {
45204         if(typeof(stylesheets) == 'string'){
45205             Roo.get(this.iframe.contentDocument.head).createChild({
45206                 tag : 'link',
45207                 rel : 'stylesheet',
45208                 type : 'text/css',
45209                 href : stylesheets
45210             });
45211             
45212             return;
45213         }
45214         var _this = this;
45215      
45216         Roo.each(stylesheets, function(s) {
45217             if(!s.length){
45218                 return;
45219             }
45220             
45221             Roo.get(_this.iframe.contentDocument.head).createChild({
45222                 tag : 'link',
45223                 rel : 'stylesheet',
45224                 type : 'text/css',
45225                 href : s
45226             });
45227         });
45228
45229         
45230     },
45231     
45232     removeStylesheets : function()
45233     {
45234         var _this = this;
45235         
45236         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
45237             s.remove();
45238         });
45239     },
45240     
45241     setStyle : function(style)
45242     {
45243         Roo.get(this.iframe.contentDocument.head).createChild({
45244             tag : 'style',
45245             type : 'text/css',
45246             html : style
45247         });
45248
45249         return;
45250     }
45251     
45252     // hide stuff that is not compatible
45253     /**
45254      * @event blur
45255      * @hide
45256      */
45257     /**
45258      * @event change
45259      * @hide
45260      */
45261     /**
45262      * @event focus
45263      * @hide
45264      */
45265     /**
45266      * @event specialkey
45267      * @hide
45268      */
45269     /**
45270      * @cfg {String} fieldClass @hide
45271      */
45272     /**
45273      * @cfg {String} focusClass @hide
45274      */
45275     /**
45276      * @cfg {String} autoCreate @hide
45277      */
45278     /**
45279      * @cfg {String} inputType @hide
45280      */
45281     /**
45282      * @cfg {String} invalidClass @hide
45283      */
45284     /**
45285      * @cfg {String} invalidText @hide
45286      */
45287     /**
45288      * @cfg {String} msgFx @hide
45289      */
45290     /**
45291      * @cfg {String} validateOnBlur @hide
45292      */
45293 });
45294
45295 Roo.HtmlEditorCore.white = [
45296         'area', 'br', 'img', 'input', 'hr', 'wbr',
45297         
45298        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
45299        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
45300        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
45301        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
45302        'table',   'ul',         'xmp', 
45303        
45304        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
45305       'thead',   'tr', 
45306      
45307       'dir', 'menu', 'ol', 'ul', 'dl',
45308        
45309       'embed',  'object'
45310 ];
45311
45312
45313 Roo.HtmlEditorCore.black = [
45314     //    'embed',  'object', // enable - backend responsiblity to clean thiese
45315         'applet', // 
45316         'base',   'basefont', 'bgsound', 'blink',  'body', 
45317         'frame',  'frameset', 'head',    'html',   'ilayer', 
45318         'iframe', 'layer',  'link',     'meta',    'object',   
45319         'script', 'style' ,'title',  'xml' // clean later..
45320 ];
45321 Roo.HtmlEditorCore.clean = [
45322     'script', 'style', 'title', 'xml'
45323 ];
45324 Roo.HtmlEditorCore.remove = [
45325     'font'
45326 ];
45327 // attributes..
45328
45329 Roo.HtmlEditorCore.ablack = [
45330     'on'
45331 ];
45332     
45333 Roo.HtmlEditorCore.aclean = [ 
45334     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
45335 ];
45336
45337 // protocols..
45338 Roo.HtmlEditorCore.pwhite= [
45339         'http',  'https',  'mailto'
45340 ];
45341
45342 // white listed style attributes.
45343 Roo.HtmlEditorCore.cwhite= [
45344       //  'text-align', /// default is to allow most things..
45345       
45346          
45347 //        'font-size'//??
45348 ];
45349
45350 // black listed style attributes.
45351 Roo.HtmlEditorCore.cblack= [
45352       //  'font-size' -- this can be set by the project 
45353 ];
45354
45355
45356 Roo.HtmlEditorCore.swapCodes   =[ 
45357     [    8211, "&#8211;" ], 
45358     [    8212, "&#8212;" ], 
45359     [    8216,  "'" ],  
45360     [    8217, "'" ],  
45361     [    8220, '"' ],  
45362     [    8221, '"' ],  
45363     [    8226, "*" ],  
45364     [    8230, "..." ]
45365 ]; 
45366
45367     //<script type="text/javascript">
45368
45369 /*
45370  * Ext JS Library 1.1.1
45371  * Copyright(c) 2006-2007, Ext JS, LLC.
45372  * Licence LGPL
45373  * 
45374  */
45375  
45376  
45377 Roo.form.HtmlEditor = function(config){
45378     
45379     
45380     
45381     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
45382     
45383     if (!this.toolbars) {
45384         this.toolbars = [];
45385     }
45386     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
45387     
45388     
45389 };
45390
45391 /**
45392  * @class Roo.form.HtmlEditor
45393  * @extends Roo.form.Field
45394  * Provides a lightweight HTML Editor component.
45395  *
45396  * This has been tested on Fireforx / Chrome.. IE may not be so great..
45397  * 
45398  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
45399  * supported by this editor.</b><br/><br/>
45400  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
45401  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45402  */
45403 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
45404     /**
45405      * @cfg {Boolean} clearUp
45406      */
45407     clearUp : true,
45408       /**
45409      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
45410      */
45411     toolbars : false,
45412    
45413      /**
45414      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45415      *                        Roo.resizable.
45416      */
45417     resizable : false,
45418      /**
45419      * @cfg {Number} height (in pixels)
45420      */   
45421     height: 300,
45422    /**
45423      * @cfg {Number} width (in pixels)
45424      */   
45425     width: 500,
45426     
45427     /**
45428      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45429      * 
45430      */
45431     stylesheets: false,
45432     
45433     
45434      /**
45435      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
45436      * 
45437      */
45438     cblack: false,
45439     /**
45440      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
45441      * 
45442      */
45443     cwhite: false,
45444     
45445      /**
45446      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
45447      * 
45448      */
45449     black: false,
45450     /**
45451      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
45452      * 
45453      */
45454     white: false,
45455     
45456     // id of frame..
45457     frameId: false,
45458     
45459     // private properties
45460     validationEvent : false,
45461     deferHeight: true,
45462     initialized : false,
45463     activated : false,
45464     
45465     onFocus : Roo.emptyFn,
45466     iframePad:3,
45467     hideMode:'offsets',
45468     
45469     actionMode : 'container', // defaults to hiding it...
45470     
45471     defaultAutoCreate : { // modified by initCompnoent..
45472         tag: "textarea",
45473         style:"width:500px;height:300px;",
45474         autocomplete: "new-password"
45475     },
45476
45477     // private
45478     initComponent : function(){
45479         this.addEvents({
45480             /**
45481              * @event initialize
45482              * Fires when the editor is fully initialized (including the iframe)
45483              * @param {HtmlEditor} this
45484              */
45485             initialize: true,
45486             /**
45487              * @event activate
45488              * Fires when the editor is first receives the focus. Any insertion must wait
45489              * until after this event.
45490              * @param {HtmlEditor} this
45491              */
45492             activate: true,
45493              /**
45494              * @event beforesync
45495              * Fires before the textarea is updated with content from the editor iframe. Return false
45496              * to cancel the sync.
45497              * @param {HtmlEditor} this
45498              * @param {String} html
45499              */
45500             beforesync: true,
45501              /**
45502              * @event beforepush
45503              * Fires before the iframe editor is updated with content from the textarea. Return false
45504              * to cancel the push.
45505              * @param {HtmlEditor} this
45506              * @param {String} html
45507              */
45508             beforepush: true,
45509              /**
45510              * @event sync
45511              * Fires when the textarea is updated with content from the editor iframe.
45512              * @param {HtmlEditor} this
45513              * @param {String} html
45514              */
45515             sync: true,
45516              /**
45517              * @event push
45518              * Fires when the iframe editor is updated with content from the textarea.
45519              * @param {HtmlEditor} this
45520              * @param {String} html
45521              */
45522             push: true,
45523              /**
45524              * @event editmodechange
45525              * Fires when the editor switches edit modes
45526              * @param {HtmlEditor} this
45527              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
45528              */
45529             editmodechange: true,
45530             /**
45531              * @event editorevent
45532              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45533              * @param {HtmlEditor} this
45534              */
45535             editorevent: true,
45536             /**
45537              * @event firstfocus
45538              * Fires when on first focus - needed by toolbars..
45539              * @param {HtmlEditor} this
45540              */
45541             firstfocus: true,
45542             /**
45543              * @event autosave
45544              * Auto save the htmlEditor value as a file into Events
45545              * @param {HtmlEditor} this
45546              */
45547             autosave: true,
45548             /**
45549              * @event savedpreview
45550              * preview the saved version of htmlEditor
45551              * @param {HtmlEditor} this
45552              */
45553             savedpreview: true,
45554             
45555             /**
45556             * @event stylesheetsclick
45557             * Fires when press the Sytlesheets button
45558             * @param {Roo.HtmlEditorCore} this
45559             */
45560             stylesheetsclick: true
45561         });
45562         this.defaultAutoCreate =  {
45563             tag: "textarea",
45564             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
45565             autocomplete: "new-password"
45566         };
45567     },
45568
45569     /**
45570      * Protected method that will not generally be called directly. It
45571      * is called when the editor creates its toolbar. Override this method if you need to
45572      * add custom toolbar buttons.
45573      * @param {HtmlEditor} editor
45574      */
45575     createToolbar : function(editor){
45576         Roo.log("create toolbars");
45577         if (!editor.toolbars || !editor.toolbars.length) {
45578             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
45579         }
45580         
45581         for (var i =0 ; i < editor.toolbars.length;i++) {
45582             editor.toolbars[i] = Roo.factory(
45583                     typeof(editor.toolbars[i]) == 'string' ?
45584                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
45585                 Roo.form.HtmlEditor);
45586             editor.toolbars[i].init(editor);
45587         }
45588          
45589         
45590     },
45591
45592      
45593     // private
45594     onRender : function(ct, position)
45595     {
45596         var _t = this;
45597         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
45598         
45599         this.wrap = this.el.wrap({
45600             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
45601         });
45602         
45603         this.editorcore.onRender(ct, position);
45604          
45605         if (this.resizable) {
45606             this.resizeEl = new Roo.Resizable(this.wrap, {
45607                 pinned : true,
45608                 wrap: true,
45609                 dynamic : true,
45610                 minHeight : this.height,
45611                 height: this.height,
45612                 handles : this.resizable,
45613                 width: this.width,
45614                 listeners : {
45615                     resize : function(r, w, h) {
45616                         _t.onResize(w,h); // -something
45617                     }
45618                 }
45619             });
45620             
45621         }
45622         this.createToolbar(this);
45623        
45624         
45625         if(!this.width){
45626             this.setSize(this.wrap.getSize());
45627         }
45628         if (this.resizeEl) {
45629             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
45630             // should trigger onReize..
45631         }
45632         
45633         this.keyNav = new Roo.KeyNav(this.el, {
45634             
45635             "tab" : function(e){
45636                 e.preventDefault();
45637                 
45638                 var value = this.getValue();
45639                 
45640                 var start = this.el.dom.selectionStart;
45641                 var end = this.el.dom.selectionEnd;
45642                 
45643                 if(!e.shiftKey){
45644                     
45645                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
45646                     this.el.dom.setSelectionRange(end + 1, end + 1);
45647                     return;
45648                 }
45649                 
45650                 var f = value.substring(0, start).split("\t");
45651                 
45652                 if(f.pop().length != 0){
45653                     return;
45654                 }
45655                 
45656                 this.setValue(f.join("\t") + value.substring(end));
45657                 this.el.dom.setSelectionRange(start - 1, start - 1);
45658                 
45659             },
45660             
45661             "home" : function(e){
45662                 e.preventDefault();
45663                 
45664                 var curr = this.el.dom.selectionStart;
45665                 var lines = this.getValue().split("\n");
45666                 
45667                 if(!lines.length){
45668                     return;
45669                 }
45670                 
45671                 if(e.ctrlKey){
45672                     this.el.dom.setSelectionRange(0, 0);
45673                     return;
45674                 }
45675                 
45676                 var pos = 0;
45677                 
45678                 for (var i = 0; i < lines.length;i++) {
45679                     pos += lines[i].length;
45680                     
45681                     if(i != 0){
45682                         pos += 1;
45683                     }
45684                     
45685                     if(pos < curr){
45686                         continue;
45687                     }
45688                     
45689                     pos -= lines[i].length;
45690                     
45691                     break;
45692                 }
45693                 
45694                 if(!e.shiftKey){
45695                     this.el.dom.setSelectionRange(pos, pos);
45696                     return;
45697                 }
45698                 
45699                 this.el.dom.selectionStart = pos;
45700                 this.el.dom.selectionEnd = curr;
45701             },
45702             
45703             "end" : function(e){
45704                 e.preventDefault();
45705                 
45706                 var curr = this.el.dom.selectionStart;
45707                 var lines = this.getValue().split("\n");
45708                 
45709                 if(!lines.length){
45710                     return;
45711                 }
45712                 
45713                 if(e.ctrlKey){
45714                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
45715                     return;
45716                 }
45717                 
45718                 var pos = 0;
45719                 
45720                 for (var i = 0; i < lines.length;i++) {
45721                     
45722                     pos += lines[i].length;
45723                     
45724                     if(i != 0){
45725                         pos += 1;
45726                     }
45727                     
45728                     if(pos < curr){
45729                         continue;
45730                     }
45731                     
45732                     break;
45733                 }
45734                 
45735                 if(!e.shiftKey){
45736                     this.el.dom.setSelectionRange(pos, pos);
45737                     return;
45738                 }
45739                 
45740                 this.el.dom.selectionStart = curr;
45741                 this.el.dom.selectionEnd = pos;
45742             },
45743
45744             scope : this,
45745
45746             doRelay : function(foo, bar, hname){
45747                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45748             },
45749
45750             forceKeyDown: true
45751         });
45752         
45753 //        if(this.autosave && this.w){
45754 //            this.autoSaveFn = setInterval(this.autosave, 1000);
45755 //        }
45756     },
45757
45758     // private
45759     onResize : function(w, h)
45760     {
45761         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
45762         var ew = false;
45763         var eh = false;
45764         
45765         if(this.el ){
45766             if(typeof w == 'number'){
45767                 var aw = w - this.wrap.getFrameWidth('lr');
45768                 this.el.setWidth(this.adjustWidth('textarea', aw));
45769                 ew = aw;
45770             }
45771             if(typeof h == 'number'){
45772                 var tbh = 0;
45773                 for (var i =0; i < this.toolbars.length;i++) {
45774                     // fixme - ask toolbars for heights?
45775                     tbh += this.toolbars[i].tb.el.getHeight();
45776                     if (this.toolbars[i].footer) {
45777                         tbh += this.toolbars[i].footer.el.getHeight();
45778                     }
45779                 }
45780                 
45781                 
45782                 
45783                 
45784                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
45785                 ah -= 5; // knock a few pixes off for look..
45786 //                Roo.log(ah);
45787                 this.el.setHeight(this.adjustWidth('textarea', ah));
45788                 var eh = ah;
45789             }
45790         }
45791         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
45792         this.editorcore.onResize(ew,eh);
45793         
45794     },
45795
45796     /**
45797      * Toggles the editor between standard and source edit mode.
45798      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45799      */
45800     toggleSourceEdit : function(sourceEditMode)
45801     {
45802         this.editorcore.toggleSourceEdit(sourceEditMode);
45803         
45804         if(this.editorcore.sourceEditMode){
45805             Roo.log('editor - showing textarea');
45806             
45807 //            Roo.log('in');
45808 //            Roo.log(this.syncValue());
45809             this.editorcore.syncValue();
45810             this.el.removeClass('x-hidden');
45811             this.el.dom.removeAttribute('tabIndex');
45812             this.el.focus();
45813             
45814             for (var i = 0; i < this.toolbars.length; i++) {
45815                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45816                     this.toolbars[i].tb.hide();
45817                     this.toolbars[i].footer.hide();
45818                 }
45819             }
45820             
45821         }else{
45822             Roo.log('editor - hiding textarea');
45823 //            Roo.log('out')
45824 //            Roo.log(this.pushValue()); 
45825             this.editorcore.pushValue();
45826             
45827             this.el.addClass('x-hidden');
45828             this.el.dom.setAttribute('tabIndex', -1);
45829             
45830             for (var i = 0; i < this.toolbars.length; i++) {
45831                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45832                     this.toolbars[i].tb.show();
45833                     this.toolbars[i].footer.show();
45834                 }
45835             }
45836             
45837             //this.deferFocus();
45838         }
45839         
45840         this.setSize(this.wrap.getSize());
45841         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
45842         
45843         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
45844     },
45845  
45846     // private (for BoxComponent)
45847     adjustSize : Roo.BoxComponent.prototype.adjustSize,
45848
45849     // private (for BoxComponent)
45850     getResizeEl : function(){
45851         return this.wrap;
45852     },
45853
45854     // private (for BoxComponent)
45855     getPositionEl : function(){
45856         return this.wrap;
45857     },
45858
45859     // private
45860     initEvents : function(){
45861         this.originalValue = this.getValue();
45862     },
45863
45864     /**
45865      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45866      * @method
45867      */
45868     markInvalid : Roo.emptyFn,
45869     /**
45870      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45871      * @method
45872      */
45873     clearInvalid : Roo.emptyFn,
45874
45875     setValue : function(v){
45876         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45877         this.editorcore.pushValue();
45878     },
45879
45880      
45881     // private
45882     deferFocus : function(){
45883         this.focus.defer(10, this);
45884     },
45885
45886     // doc'ed in Field
45887     focus : function(){
45888         this.editorcore.focus();
45889         
45890     },
45891       
45892
45893     // private
45894     onDestroy : function(){
45895         
45896         
45897         
45898         if(this.rendered){
45899             
45900             for (var i =0; i < this.toolbars.length;i++) {
45901                 // fixme - ask toolbars for heights?
45902                 this.toolbars[i].onDestroy();
45903             }
45904             
45905             this.wrap.dom.innerHTML = '';
45906             this.wrap.remove();
45907         }
45908     },
45909
45910     // private
45911     onFirstFocus : function(){
45912         //Roo.log("onFirstFocus");
45913         this.editorcore.onFirstFocus();
45914          for (var i =0; i < this.toolbars.length;i++) {
45915             this.toolbars[i].onFirstFocus();
45916         }
45917         
45918     },
45919     
45920     // private
45921     syncValue : function()
45922     {
45923         this.editorcore.syncValue();
45924     },
45925     
45926     pushValue : function()
45927     {
45928         this.editorcore.pushValue();
45929     },
45930     
45931     setStylesheets : function(stylesheets)
45932     {
45933         this.editorcore.setStylesheets(stylesheets);
45934     },
45935     
45936     removeStylesheets : function()
45937     {
45938         this.editorcore.removeStylesheets();
45939     }
45940      
45941     
45942     // hide stuff that is not compatible
45943     /**
45944      * @event blur
45945      * @hide
45946      */
45947     /**
45948      * @event change
45949      * @hide
45950      */
45951     /**
45952      * @event focus
45953      * @hide
45954      */
45955     /**
45956      * @event specialkey
45957      * @hide
45958      */
45959     /**
45960      * @cfg {String} fieldClass @hide
45961      */
45962     /**
45963      * @cfg {String} focusClass @hide
45964      */
45965     /**
45966      * @cfg {String} autoCreate @hide
45967      */
45968     /**
45969      * @cfg {String} inputType @hide
45970      */
45971     /**
45972      * @cfg {String} invalidClass @hide
45973      */
45974     /**
45975      * @cfg {String} invalidText @hide
45976      */
45977     /**
45978      * @cfg {String} msgFx @hide
45979      */
45980     /**
45981      * @cfg {String} validateOnBlur @hide
45982      */
45983 });
45984  
45985     // <script type="text/javascript">
45986 /*
45987  * Based on
45988  * Ext JS Library 1.1.1
45989  * Copyright(c) 2006-2007, Ext JS, LLC.
45990  *  
45991  
45992  */
45993
45994 /**
45995  * @class Roo.form.HtmlEditorToolbar1
45996  * Basic Toolbar
45997  * 
45998  * Usage:
45999  *
46000  new Roo.form.HtmlEditor({
46001     ....
46002     toolbars : [
46003         new Roo.form.HtmlEditorToolbar1({
46004             disable : { fonts: 1 , format: 1, ..., ... , ...],
46005             btns : [ .... ]
46006         })
46007     }
46008      
46009  * 
46010  * @cfg {Object} disable List of elements to disable..
46011  * @cfg {Array} btns List of additional buttons.
46012  * 
46013  * 
46014  * NEEDS Extra CSS? 
46015  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
46016  */
46017  
46018 Roo.form.HtmlEditor.ToolbarStandard = function(config)
46019 {
46020     
46021     Roo.apply(this, config);
46022     
46023     // default disabled, based on 'good practice'..
46024     this.disable = this.disable || {};
46025     Roo.applyIf(this.disable, {
46026         fontSize : true,
46027         colors : true,
46028         specialElements : true
46029     });
46030     
46031     
46032     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46033     // dont call parent... till later.
46034 }
46035
46036 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
46037     
46038     tb: false,
46039     
46040     rendered: false,
46041     
46042     editor : false,
46043     editorcore : false,
46044     /**
46045      * @cfg {Object} disable  List of toolbar elements to disable
46046          
46047      */
46048     disable : false,
46049     
46050     
46051      /**
46052      * @cfg {String} createLinkText The default text for the create link prompt
46053      */
46054     createLinkText : 'Please enter the URL for the link:',
46055     /**
46056      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
46057      */
46058     defaultLinkValue : 'http:/'+'/',
46059    
46060     
46061       /**
46062      * @cfg {Array} fontFamilies An array of available font families
46063      */
46064     fontFamilies : [
46065         'Arial',
46066         'Courier New',
46067         'Tahoma',
46068         'Times New Roman',
46069         'Verdana'
46070     ],
46071     
46072     specialChars : [
46073            "&#169;",
46074           "&#174;",     
46075           "&#8482;",    
46076           "&#163;" ,    
46077          // "&#8212;",    
46078           "&#8230;",    
46079           "&#247;" ,    
46080         //  "&#225;" ,     ?? a acute?
46081            "&#8364;"    , //Euro
46082        //   "&#8220;"    ,
46083         //  "&#8221;"    ,
46084         //  "&#8226;"    ,
46085           "&#176;"  //   , // degrees
46086
46087          // "&#233;"     , // e ecute
46088          // "&#250;"     , // u ecute?
46089     ],
46090     
46091     specialElements : [
46092         {
46093             text: "Insert Table",
46094             xtype: 'MenuItem',
46095             xns : Roo.Menu,
46096             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
46097                 
46098         },
46099         {    
46100             text: "Insert Image",
46101             xtype: 'MenuItem',
46102             xns : Roo.Menu,
46103             ihtml : '<img src="about:blank"/>'
46104             
46105         }
46106         
46107          
46108     ],
46109     
46110     
46111     inputElements : [ 
46112             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
46113             "input:submit", "input:button", "select", "textarea", "label" ],
46114     formats : [
46115         ["p"] ,  
46116         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
46117         ["pre"],[ "code"], 
46118         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
46119         ['div'],['span'],
46120         ['sup'],['sub']
46121     ],
46122     
46123     cleanStyles : [
46124         "font-size"
46125     ],
46126      /**
46127      * @cfg {String} defaultFont default font to use.
46128      */
46129     defaultFont: 'tahoma',
46130    
46131     fontSelect : false,
46132     
46133     
46134     formatCombo : false,
46135     
46136     init : function(editor)
46137     {
46138         this.editor = editor;
46139         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46140         var editorcore = this.editorcore;
46141         
46142         var _t = this;
46143         
46144         var fid = editorcore.frameId;
46145         var etb = this;
46146         function btn(id, toggle, handler){
46147             var xid = fid + '-'+ id ;
46148             return {
46149                 id : xid,
46150                 cmd : id,
46151                 cls : 'x-btn-icon x-edit-'+id,
46152                 enableToggle:toggle !== false,
46153                 scope: _t, // was editor...
46154                 handler:handler||_t.relayBtnCmd,
46155                 clickEvent:'mousedown',
46156                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46157                 tabIndex:-1
46158             };
46159         }
46160         
46161         
46162         
46163         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46164         this.tb = tb;
46165          // stop form submits
46166         tb.el.on('click', function(e){
46167             e.preventDefault(); // what does this do?
46168         });
46169
46170         if(!this.disable.font) { // && !Roo.isSafari){
46171             /* why no safari for fonts 
46172             editor.fontSelect = tb.el.createChild({
46173                 tag:'select',
46174                 tabIndex: -1,
46175                 cls:'x-font-select',
46176                 html: this.createFontOptions()
46177             });
46178             
46179             editor.fontSelect.on('change', function(){
46180                 var font = editor.fontSelect.dom.value;
46181                 editor.relayCmd('fontname', font);
46182                 editor.deferFocus();
46183             }, editor);
46184             
46185             tb.add(
46186                 editor.fontSelect.dom,
46187                 '-'
46188             );
46189             */
46190             
46191         };
46192         if(!this.disable.formats){
46193             this.formatCombo = new Roo.form.ComboBox({
46194                 store: new Roo.data.SimpleStore({
46195                     id : 'tag',
46196                     fields: ['tag'],
46197                     data : this.formats // from states.js
46198                 }),
46199                 blockFocus : true,
46200                 name : '',
46201                 //autoCreate : {tag: "div",  size: "20"},
46202                 displayField:'tag',
46203                 typeAhead: false,
46204                 mode: 'local',
46205                 editable : false,
46206                 triggerAction: 'all',
46207                 emptyText:'Add tag',
46208                 selectOnFocus:true,
46209                 width:135,
46210                 listeners : {
46211                     'select': function(c, r, i) {
46212                         editorcore.insertTag(r.get('tag'));
46213                         editor.focus();
46214                     }
46215                 }
46216
46217             });
46218             tb.addField(this.formatCombo);
46219             
46220         }
46221         
46222         if(!this.disable.format){
46223             tb.add(
46224                 btn('bold'),
46225                 btn('italic'),
46226                 btn('underline'),
46227                 btn('strikethrough')
46228             );
46229         };
46230         if(!this.disable.fontSize){
46231             tb.add(
46232                 '-',
46233                 
46234                 
46235                 btn('increasefontsize', false, editorcore.adjustFont),
46236                 btn('decreasefontsize', false, editorcore.adjustFont)
46237             );
46238         };
46239         
46240         
46241         if(!this.disable.colors){
46242             tb.add(
46243                 '-', {
46244                     id:editorcore.frameId +'-forecolor',
46245                     cls:'x-btn-icon x-edit-forecolor',
46246                     clickEvent:'mousedown',
46247                     tooltip: this.buttonTips['forecolor'] || undefined,
46248                     tabIndex:-1,
46249                     menu : new Roo.menu.ColorMenu({
46250                         allowReselect: true,
46251                         focus: Roo.emptyFn,
46252                         value:'000000',
46253                         plain:true,
46254                         selectHandler: function(cp, color){
46255                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
46256                             editor.deferFocus();
46257                         },
46258                         scope: editorcore,
46259                         clickEvent:'mousedown'
46260                     })
46261                 }, {
46262                     id:editorcore.frameId +'backcolor',
46263                     cls:'x-btn-icon x-edit-backcolor',
46264                     clickEvent:'mousedown',
46265                     tooltip: this.buttonTips['backcolor'] || undefined,
46266                     tabIndex:-1,
46267                     menu : new Roo.menu.ColorMenu({
46268                         focus: Roo.emptyFn,
46269                         value:'FFFFFF',
46270                         plain:true,
46271                         allowReselect: true,
46272                         selectHandler: function(cp, color){
46273                             if(Roo.isGecko){
46274                                 editorcore.execCmd('useCSS', false);
46275                                 editorcore.execCmd('hilitecolor', color);
46276                                 editorcore.execCmd('useCSS', true);
46277                                 editor.deferFocus();
46278                             }else{
46279                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
46280                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
46281                                 editor.deferFocus();
46282                             }
46283                         },
46284                         scope:editorcore,
46285                         clickEvent:'mousedown'
46286                     })
46287                 }
46288             );
46289         };
46290         // now add all the items...
46291         
46292
46293         if(!this.disable.alignments){
46294             tb.add(
46295                 '-',
46296                 btn('justifyleft'),
46297                 btn('justifycenter'),
46298                 btn('justifyright')
46299             );
46300         };
46301
46302         //if(!Roo.isSafari){
46303             if(!this.disable.links){
46304                 tb.add(
46305                     '-',
46306                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
46307                 );
46308             };
46309
46310             if(!this.disable.lists){
46311                 tb.add(
46312                     '-',
46313                     btn('insertorderedlist'),
46314                     btn('insertunorderedlist')
46315                 );
46316             }
46317             if(!this.disable.sourceEdit){
46318                 tb.add(
46319                     '-',
46320                     btn('sourceedit', true, function(btn){
46321                         this.toggleSourceEdit(btn.pressed);
46322                     })
46323                 );
46324             }
46325         //}
46326         
46327         var smenu = { };
46328         // special menu.. - needs to be tidied up..
46329         if (!this.disable.special) {
46330             smenu = {
46331                 text: "&#169;",
46332                 cls: 'x-edit-none',
46333                 
46334                 menu : {
46335                     items : []
46336                 }
46337             };
46338             for (var i =0; i < this.specialChars.length; i++) {
46339                 smenu.menu.items.push({
46340                     
46341                     html: this.specialChars[i],
46342                     handler: function(a,b) {
46343                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
46344                         //editor.insertAtCursor(a.html);
46345                         
46346                     },
46347                     tabIndex:-1
46348                 });
46349             }
46350             
46351             
46352             tb.add(smenu);
46353             
46354             
46355         }
46356         
46357         var cmenu = { };
46358         if (!this.disable.cleanStyles) {
46359             cmenu = {
46360                 cls: 'x-btn-icon x-btn-clear',
46361                 
46362                 menu : {
46363                     items : []
46364                 }
46365             };
46366             for (var i =0; i < this.cleanStyles.length; i++) {
46367                 cmenu.menu.items.push({
46368                     actiontype : this.cleanStyles[i],
46369                     html: 'Remove ' + this.cleanStyles[i],
46370                     handler: function(a,b) {
46371 //                        Roo.log(a);
46372 //                        Roo.log(b);
46373                         var c = Roo.get(editorcore.doc.body);
46374                         c.select('[style]').each(function(s) {
46375                             s.dom.style.removeProperty(a.actiontype);
46376                         });
46377                         editorcore.syncValue();
46378                     },
46379                     tabIndex:-1
46380                 });
46381             }
46382              cmenu.menu.items.push({
46383                 actiontype : 'tablewidths',
46384                 html: 'Remove Table Widths',
46385                 handler: function(a,b) {
46386                     editorcore.cleanTableWidths();
46387                     editorcore.syncValue();
46388                 },
46389                 tabIndex:-1
46390             });
46391             cmenu.menu.items.push({
46392                 actiontype : 'word',
46393                 html: 'Remove MS Word Formating',
46394                 handler: function(a,b) {
46395                     editorcore.cleanWord();
46396                     editorcore.syncValue();
46397                 },
46398                 tabIndex:-1
46399             });
46400             
46401             cmenu.menu.items.push({
46402                 actiontype : 'all',
46403                 html: 'Remove All Styles',
46404                 handler: function(a,b) {
46405                     
46406                     var c = Roo.get(editorcore.doc.body);
46407                     c.select('[style]').each(function(s) {
46408                         s.dom.removeAttribute('style');
46409                     });
46410                     editorcore.syncValue();
46411                 },
46412                 tabIndex:-1
46413             });
46414             
46415             cmenu.menu.items.push({
46416                 actiontype : 'all',
46417                 html: 'Remove All CSS Classes',
46418                 handler: function(a,b) {
46419                     
46420                     var c = Roo.get(editorcore.doc.body);
46421                     c.select('[class]').each(function(s) {
46422                         s.dom.removeAttribute('class');
46423                     });
46424                     editorcore.cleanWord();
46425                     editorcore.syncValue();
46426                 },
46427                 tabIndex:-1
46428             });
46429             
46430              cmenu.menu.items.push({
46431                 actiontype : 'tidy',
46432                 html: 'Tidy HTML Source',
46433                 handler: function(a,b) {
46434                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
46435                     editorcore.syncValue();
46436                 },
46437                 tabIndex:-1
46438             });
46439             
46440             
46441             tb.add(cmenu);
46442         }
46443          
46444         if (!this.disable.specialElements) {
46445             var semenu = {
46446                 text: "Other;",
46447                 cls: 'x-edit-none',
46448                 menu : {
46449                     items : []
46450                 }
46451             };
46452             for (var i =0; i < this.specialElements.length; i++) {
46453                 semenu.menu.items.push(
46454                     Roo.apply({ 
46455                         handler: function(a,b) {
46456                             editor.insertAtCursor(this.ihtml);
46457                         }
46458                     }, this.specialElements[i])
46459                 );
46460                     
46461             }
46462             
46463             tb.add(semenu);
46464             
46465             
46466         }
46467          
46468         
46469         if (this.btns) {
46470             for(var i =0; i< this.btns.length;i++) {
46471                 var b = Roo.factory(this.btns[i],Roo.form);
46472                 b.cls =  'x-edit-none';
46473                 
46474                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
46475                     b.cls += ' x-init-enable';
46476                 }
46477                 
46478                 b.scope = editorcore;
46479                 tb.add(b);
46480             }
46481         
46482         }
46483         
46484         
46485         
46486         // disable everything...
46487         
46488         this.tb.items.each(function(item){
46489             
46490            if(
46491                 item.id != editorcore.frameId+ '-sourceedit' && 
46492                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
46493             ){
46494                 
46495                 item.disable();
46496             }
46497         });
46498         this.rendered = true;
46499         
46500         // the all the btns;
46501         editor.on('editorevent', this.updateToolbar, this);
46502         // other toolbars need to implement this..
46503         //editor.on('editmodechange', this.updateToolbar, this);
46504     },
46505     
46506     
46507     relayBtnCmd : function(btn) {
46508         this.editorcore.relayCmd(btn.cmd);
46509     },
46510     // private used internally
46511     createLink : function(){
46512         Roo.log("create link?");
46513         var url = prompt(this.createLinkText, this.defaultLinkValue);
46514         if(url && url != 'http:/'+'/'){
46515             this.editorcore.relayCmd('createlink', url);
46516         }
46517     },
46518
46519     
46520     /**
46521      * Protected method that will not generally be called directly. It triggers
46522      * a toolbar update by reading the markup state of the current selection in the editor.
46523      */
46524     updateToolbar: function(){
46525
46526         if(!this.editorcore.activated){
46527             this.editor.onFirstFocus();
46528             return;
46529         }
46530
46531         var btns = this.tb.items.map, 
46532             doc = this.editorcore.doc,
46533             frameId = this.editorcore.frameId;
46534
46535         if(!this.disable.font && !Roo.isSafari){
46536             /*
46537             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
46538             if(name != this.fontSelect.dom.value){
46539                 this.fontSelect.dom.value = name;
46540             }
46541             */
46542         }
46543         if(!this.disable.format){
46544             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
46545             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
46546             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
46547             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
46548         }
46549         if(!this.disable.alignments){
46550             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
46551             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
46552             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
46553         }
46554         if(!Roo.isSafari && !this.disable.lists){
46555             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
46556             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
46557         }
46558         
46559         var ans = this.editorcore.getAllAncestors();
46560         if (this.formatCombo) {
46561             
46562             
46563             var store = this.formatCombo.store;
46564             this.formatCombo.setValue("");
46565             for (var i =0; i < ans.length;i++) {
46566                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
46567                     // select it..
46568                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
46569                     break;
46570                 }
46571             }
46572         }
46573         
46574         
46575         
46576         // hides menus... - so this cant be on a menu...
46577         Roo.menu.MenuMgr.hideAll();
46578
46579         //this.editorsyncValue();
46580     },
46581    
46582     
46583     createFontOptions : function(){
46584         var buf = [], fs = this.fontFamilies, ff, lc;
46585         
46586         
46587         
46588         for(var i = 0, len = fs.length; i< len; i++){
46589             ff = fs[i];
46590             lc = ff.toLowerCase();
46591             buf.push(
46592                 '<option value="',lc,'" style="font-family:',ff,';"',
46593                     (this.defaultFont == lc ? ' selected="true">' : '>'),
46594                     ff,
46595                 '</option>'
46596             );
46597         }
46598         return buf.join('');
46599     },
46600     
46601     toggleSourceEdit : function(sourceEditMode){
46602         
46603         Roo.log("toolbar toogle");
46604         if(sourceEditMode === undefined){
46605             sourceEditMode = !this.sourceEditMode;
46606         }
46607         this.sourceEditMode = sourceEditMode === true;
46608         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
46609         // just toggle the button?
46610         if(btn.pressed !== this.sourceEditMode){
46611             btn.toggle(this.sourceEditMode);
46612             return;
46613         }
46614         
46615         if(sourceEditMode){
46616             Roo.log("disabling buttons");
46617             this.tb.items.each(function(item){
46618                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
46619                     item.disable();
46620                 }
46621             });
46622           
46623         }else{
46624             Roo.log("enabling buttons");
46625             if(this.editorcore.initialized){
46626                 this.tb.items.each(function(item){
46627                     item.enable();
46628                 });
46629             }
46630             
46631         }
46632         Roo.log("calling toggole on editor");
46633         // tell the editor that it's been pressed..
46634         this.editor.toggleSourceEdit(sourceEditMode);
46635        
46636     },
46637      /**
46638      * Object collection of toolbar tooltips for the buttons in the editor. The key
46639      * is the command id associated with that button and the value is a valid QuickTips object.
46640      * For example:
46641 <pre><code>
46642 {
46643     bold : {
46644         title: 'Bold (Ctrl+B)',
46645         text: 'Make the selected text bold.',
46646         cls: 'x-html-editor-tip'
46647     },
46648     italic : {
46649         title: 'Italic (Ctrl+I)',
46650         text: 'Make the selected text italic.',
46651         cls: 'x-html-editor-tip'
46652     },
46653     ...
46654 </code></pre>
46655     * @type Object
46656      */
46657     buttonTips : {
46658         bold : {
46659             title: 'Bold (Ctrl+B)',
46660             text: 'Make the selected text bold.',
46661             cls: 'x-html-editor-tip'
46662         },
46663         italic : {
46664             title: 'Italic (Ctrl+I)',
46665             text: 'Make the selected text italic.',
46666             cls: 'x-html-editor-tip'
46667         },
46668         underline : {
46669             title: 'Underline (Ctrl+U)',
46670             text: 'Underline the selected text.',
46671             cls: 'x-html-editor-tip'
46672         },
46673         strikethrough : {
46674             title: 'Strikethrough',
46675             text: 'Strikethrough the selected text.',
46676             cls: 'x-html-editor-tip'
46677         },
46678         increasefontsize : {
46679             title: 'Grow Text',
46680             text: 'Increase the font size.',
46681             cls: 'x-html-editor-tip'
46682         },
46683         decreasefontsize : {
46684             title: 'Shrink Text',
46685             text: 'Decrease the font size.',
46686             cls: 'x-html-editor-tip'
46687         },
46688         backcolor : {
46689             title: 'Text Highlight Color',
46690             text: 'Change the background color of the selected text.',
46691             cls: 'x-html-editor-tip'
46692         },
46693         forecolor : {
46694             title: 'Font Color',
46695             text: 'Change the color of the selected text.',
46696             cls: 'x-html-editor-tip'
46697         },
46698         justifyleft : {
46699             title: 'Align Text Left',
46700             text: 'Align text to the left.',
46701             cls: 'x-html-editor-tip'
46702         },
46703         justifycenter : {
46704             title: 'Center Text',
46705             text: 'Center text in the editor.',
46706             cls: 'x-html-editor-tip'
46707         },
46708         justifyright : {
46709             title: 'Align Text Right',
46710             text: 'Align text to the right.',
46711             cls: 'x-html-editor-tip'
46712         },
46713         insertunorderedlist : {
46714             title: 'Bullet List',
46715             text: 'Start a bulleted list.',
46716             cls: 'x-html-editor-tip'
46717         },
46718         insertorderedlist : {
46719             title: 'Numbered List',
46720             text: 'Start a numbered list.',
46721             cls: 'x-html-editor-tip'
46722         },
46723         createlink : {
46724             title: 'Hyperlink',
46725             text: 'Make the selected text a hyperlink.',
46726             cls: 'x-html-editor-tip'
46727         },
46728         sourceedit : {
46729             title: 'Source Edit',
46730             text: 'Switch to source editing mode.',
46731             cls: 'x-html-editor-tip'
46732         }
46733     },
46734     // private
46735     onDestroy : function(){
46736         if(this.rendered){
46737             
46738             this.tb.items.each(function(item){
46739                 if(item.menu){
46740                     item.menu.removeAll();
46741                     if(item.menu.el){
46742                         item.menu.el.destroy();
46743                     }
46744                 }
46745                 item.destroy();
46746             });
46747              
46748         }
46749     },
46750     onFirstFocus: function() {
46751         this.tb.items.each(function(item){
46752            item.enable();
46753         });
46754     }
46755 });
46756
46757
46758
46759
46760 // <script type="text/javascript">
46761 /*
46762  * Based on
46763  * Ext JS Library 1.1.1
46764  * Copyright(c) 2006-2007, Ext JS, LLC.
46765  *  
46766  
46767  */
46768
46769  
46770 /**
46771  * @class Roo.form.HtmlEditor.ToolbarContext
46772  * Context Toolbar
46773  * 
46774  * Usage:
46775  *
46776  new Roo.form.HtmlEditor({
46777     ....
46778     toolbars : [
46779         { xtype: 'ToolbarStandard', styles : {} }
46780         { xtype: 'ToolbarContext', disable : {} }
46781     ]
46782 })
46783
46784      
46785  * 
46786  * @config : {Object} disable List of elements to disable.. (not done yet.)
46787  * @config : {Object} styles  Map of styles available.
46788  * 
46789  */
46790
46791 Roo.form.HtmlEditor.ToolbarContext = function(config)
46792 {
46793     
46794     Roo.apply(this, config);
46795     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46796     // dont call parent... till later.
46797     this.styles = this.styles || {};
46798 }
46799
46800  
46801
46802 Roo.form.HtmlEditor.ToolbarContext.types = {
46803     'IMG' : {
46804         width : {
46805             title: "Width",
46806             width: 40
46807         },
46808         height:  {
46809             title: "Height",
46810             width: 40
46811         },
46812         align: {
46813             title: "Align",
46814             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
46815             width : 80
46816             
46817         },
46818         border: {
46819             title: "Border",
46820             width: 40
46821         },
46822         alt: {
46823             title: "Alt",
46824             width: 120
46825         },
46826         src : {
46827             title: "Src",
46828             width: 220
46829         }
46830         
46831     },
46832     'A' : {
46833         name : {
46834             title: "Name",
46835             width: 50
46836         },
46837         target:  {
46838             title: "Target",
46839             width: 120
46840         },
46841         href:  {
46842             title: "Href",
46843             width: 220
46844         } // border?
46845         
46846     },
46847     'TABLE' : {
46848         rows : {
46849             title: "Rows",
46850             width: 20
46851         },
46852         cols : {
46853             title: "Cols",
46854             width: 20
46855         },
46856         width : {
46857             title: "Width",
46858             width: 40
46859         },
46860         height : {
46861             title: "Height",
46862             width: 40
46863         },
46864         border : {
46865             title: "Border",
46866             width: 20
46867         }
46868     },
46869     'TD' : {
46870         width : {
46871             title: "Width",
46872             width: 40
46873         },
46874         height : {
46875             title: "Height",
46876             width: 40
46877         },   
46878         align: {
46879             title: "Align",
46880             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46881             width: 80
46882         },
46883         valign: {
46884             title: "Valign",
46885             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46886             width: 80
46887         },
46888         colspan: {
46889             title: "Colspan",
46890             width: 20
46891             
46892         },
46893          'font-family'  : {
46894             title : "Font",
46895             style : 'fontFamily',
46896             displayField: 'display',
46897             optname : 'font-family',
46898             width: 140
46899         }
46900     },
46901     'INPUT' : {
46902         name : {
46903             title: "name",
46904             width: 120
46905         },
46906         value : {
46907             title: "Value",
46908             width: 120
46909         },
46910         width : {
46911             title: "Width",
46912             width: 40
46913         }
46914     },
46915     'LABEL' : {
46916         'for' : {
46917             title: "For",
46918             width: 120
46919         }
46920     },
46921     'TEXTAREA' : {
46922           name : {
46923             title: "name",
46924             width: 120
46925         },
46926         rows : {
46927             title: "Rows",
46928             width: 20
46929         },
46930         cols : {
46931             title: "Cols",
46932             width: 20
46933         }
46934     },
46935     'SELECT' : {
46936         name : {
46937             title: "name",
46938             width: 120
46939         },
46940         selectoptions : {
46941             title: "Options",
46942             width: 200
46943         }
46944     },
46945     
46946     // should we really allow this??
46947     // should this just be 
46948     'BODY' : {
46949         title : {
46950             title: "Title",
46951             width: 200,
46952             disabled : true
46953         }
46954     },
46955     'SPAN' : {
46956         'font-family'  : {
46957             title : "Font",
46958             style : 'fontFamily',
46959             displayField: 'display',
46960             optname : 'font-family',
46961             width: 140
46962         }
46963     },
46964     'DIV' : {
46965         'font-family'  : {
46966             title : "Font",
46967             style : 'fontFamily',
46968             displayField: 'display',
46969             optname : 'font-family',
46970             width: 140
46971         }
46972     },
46973      'P' : {
46974         'font-family'  : {
46975             title : "Font",
46976             style : 'fontFamily',
46977             displayField: 'display',
46978             optname : 'font-family',
46979             width: 140
46980         }
46981     },
46982     
46983     '*' : {
46984         // empty..
46985     }
46986
46987 };
46988
46989 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
46990 Roo.form.HtmlEditor.ToolbarContext.stores = false;
46991
46992 Roo.form.HtmlEditor.ToolbarContext.options = {
46993         'font-family'  : [ 
46994                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
46995                 [ 'Courier New', 'Courier New'],
46996                 [ 'Tahoma', 'Tahoma'],
46997                 [ 'Times New Roman,serif', 'Times'],
46998                 [ 'Verdana','Verdana' ]
46999         ]
47000 };
47001
47002 // fixme - these need to be configurable..
47003  
47004
47005 //Roo.form.HtmlEditor.ToolbarContext.types
47006
47007
47008 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
47009     
47010     tb: false,
47011     
47012     rendered: false,
47013     
47014     editor : false,
47015     editorcore : false,
47016     /**
47017      * @cfg {Object} disable  List of toolbar elements to disable
47018          
47019      */
47020     disable : false,
47021     /**
47022      * @cfg {Object} styles List of styles 
47023      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
47024      *
47025      * These must be defined in the page, so they get rendered correctly..
47026      * .headline { }
47027      * TD.underline { }
47028      * 
47029      */
47030     styles : false,
47031     
47032     options: false,
47033     
47034     toolbars : false,
47035     
47036     init : function(editor)
47037     {
47038         this.editor = editor;
47039         this.editorcore = editor.editorcore ? editor.editorcore : editor;
47040         var editorcore = this.editorcore;
47041         
47042         var fid = editorcore.frameId;
47043         var etb = this;
47044         function btn(id, toggle, handler){
47045             var xid = fid + '-'+ id ;
47046             return {
47047                 id : xid,
47048                 cmd : id,
47049                 cls : 'x-btn-icon x-edit-'+id,
47050                 enableToggle:toggle !== false,
47051                 scope: editorcore, // was editor...
47052                 handler:handler||editorcore.relayBtnCmd,
47053                 clickEvent:'mousedown',
47054                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47055                 tabIndex:-1
47056             };
47057         }
47058         // create a new element.
47059         var wdiv = editor.wrap.createChild({
47060                 tag: 'div'
47061             }, editor.wrap.dom.firstChild.nextSibling, true);
47062         
47063         // can we do this more than once??
47064         
47065          // stop form submits
47066       
47067  
47068         // disable everything...
47069         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47070         this.toolbars = {};
47071            
47072         for (var i in  ty) {
47073           
47074             this.toolbars[i] = this.buildToolbar(ty[i],i);
47075         }
47076         this.tb = this.toolbars.BODY;
47077         this.tb.el.show();
47078         this.buildFooter();
47079         this.footer.show();
47080         editor.on('hide', function( ) { this.footer.hide() }, this);
47081         editor.on('show', function( ) { this.footer.show() }, this);
47082         
47083          
47084         this.rendered = true;
47085         
47086         // the all the btns;
47087         editor.on('editorevent', this.updateToolbar, this);
47088         // other toolbars need to implement this..
47089         //editor.on('editmodechange', this.updateToolbar, this);
47090     },
47091     
47092     
47093     
47094     /**
47095      * Protected method that will not generally be called directly. It triggers
47096      * a toolbar update by reading the markup state of the current selection in the editor.
47097      *
47098      * Note you can force an update by calling on('editorevent', scope, false)
47099      */
47100     updateToolbar: function(editor,ev,sel){
47101
47102         //Roo.log(ev);
47103         // capture mouse up - this is handy for selecting images..
47104         // perhaps should go somewhere else...
47105         if(!this.editorcore.activated){
47106              this.editor.onFirstFocus();
47107             return;
47108         }
47109         
47110         
47111         
47112         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
47113         // selectNode - might want to handle IE?
47114         if (ev &&
47115             (ev.type == 'mouseup' || ev.type == 'click' ) &&
47116             ev.target && ev.target.tagName == 'IMG') {
47117             // they have click on an image...
47118             // let's see if we can change the selection...
47119             sel = ev.target;
47120          
47121               var nodeRange = sel.ownerDocument.createRange();
47122             try {
47123                 nodeRange.selectNode(sel);
47124             } catch (e) {
47125                 nodeRange.selectNodeContents(sel);
47126             }
47127             //nodeRange.collapse(true);
47128             var s = this.editorcore.win.getSelection();
47129             s.removeAllRanges();
47130             s.addRange(nodeRange);
47131         }  
47132         
47133       
47134         var updateFooter = sel ? false : true;
47135         
47136         
47137         var ans = this.editorcore.getAllAncestors();
47138         
47139         // pick
47140         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47141         
47142         if (!sel) { 
47143             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
47144             sel = sel ? sel : this.editorcore.doc.body;
47145             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
47146             
47147         }
47148         // pick a menu that exists..
47149         var tn = sel.tagName.toUpperCase();
47150         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
47151         
47152         tn = sel.tagName.toUpperCase();
47153         
47154         var lastSel = this.tb.selectedNode;
47155         
47156         this.tb.selectedNode = sel;
47157         
47158         // if current menu does not match..
47159         
47160         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
47161                 
47162             this.tb.el.hide();
47163             ///console.log("show: " + tn);
47164             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
47165             this.tb.el.show();
47166             // update name
47167             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
47168             
47169             
47170             // update attributes
47171             if (this.tb.fields) {
47172                 this.tb.fields.each(function(e) {
47173                     if (e.stylename) {
47174                         e.setValue(sel.style[e.stylename]);
47175                         return;
47176                     } 
47177                    e.setValue(sel.getAttribute(e.attrname));
47178                 });
47179             }
47180             
47181             var hasStyles = false;
47182             for(var i in this.styles) {
47183                 hasStyles = true;
47184                 break;
47185             }
47186             
47187             // update styles
47188             if (hasStyles) { 
47189                 var st = this.tb.fields.item(0);
47190                 
47191                 st.store.removeAll();
47192                
47193                 
47194                 var cn = sel.className.split(/\s+/);
47195                 
47196                 var avs = [];
47197                 if (this.styles['*']) {
47198                     
47199                     Roo.each(this.styles['*'], function(v) {
47200                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47201                     });
47202                 }
47203                 if (this.styles[tn]) { 
47204                     Roo.each(this.styles[tn], function(v) {
47205                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47206                     });
47207                 }
47208                 
47209                 st.store.loadData(avs);
47210                 st.collapse();
47211                 st.setValue(cn);
47212             }
47213             // flag our selected Node.
47214             this.tb.selectedNode = sel;
47215            
47216            
47217             Roo.menu.MenuMgr.hideAll();
47218
47219         }
47220         
47221         if (!updateFooter) {
47222             //this.footDisp.dom.innerHTML = ''; 
47223             return;
47224         }
47225         // update the footer
47226         //
47227         var html = '';
47228         
47229         this.footerEls = ans.reverse();
47230         Roo.each(this.footerEls, function(a,i) {
47231             if (!a) { return; }
47232             html += html.length ? ' &gt; '  :  '';
47233             
47234             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
47235             
47236         });
47237        
47238         // 
47239         var sz = this.footDisp.up('td').getSize();
47240         this.footDisp.dom.style.width = (sz.width -10) + 'px';
47241         this.footDisp.dom.style.marginLeft = '5px';
47242         
47243         this.footDisp.dom.style.overflow = 'hidden';
47244         
47245         this.footDisp.dom.innerHTML = html;
47246             
47247         //this.editorsyncValue();
47248     },
47249      
47250     
47251    
47252        
47253     // private
47254     onDestroy : function(){
47255         if(this.rendered){
47256             
47257             this.tb.items.each(function(item){
47258                 if(item.menu){
47259                     item.menu.removeAll();
47260                     if(item.menu.el){
47261                         item.menu.el.destroy();
47262                     }
47263                 }
47264                 item.destroy();
47265             });
47266              
47267         }
47268     },
47269     onFirstFocus: function() {
47270         // need to do this for all the toolbars..
47271         this.tb.items.each(function(item){
47272            item.enable();
47273         });
47274     },
47275     buildToolbar: function(tlist, nm)
47276     {
47277         var editor = this.editor;
47278         var editorcore = this.editorcore;
47279          // create a new element.
47280         var wdiv = editor.wrap.createChild({
47281                 tag: 'div'
47282             }, editor.wrap.dom.firstChild.nextSibling, true);
47283         
47284        
47285         var tb = new Roo.Toolbar(wdiv);
47286         // add the name..
47287         
47288         tb.add(nm+ ":&nbsp;");
47289         
47290         var styles = [];
47291         for(var i in this.styles) {
47292             styles.push(i);
47293         }
47294         
47295         // styles...
47296         if (styles && styles.length) {
47297             
47298             // this needs a multi-select checkbox...
47299             tb.addField( new Roo.form.ComboBox({
47300                 store: new Roo.data.SimpleStore({
47301                     id : 'val',
47302                     fields: ['val', 'selected'],
47303                     data : [] 
47304                 }),
47305                 name : '-roo-edit-className',
47306                 attrname : 'className',
47307                 displayField: 'val',
47308                 typeAhead: false,
47309                 mode: 'local',
47310                 editable : false,
47311                 triggerAction: 'all',
47312                 emptyText:'Select Style',
47313                 selectOnFocus:true,
47314                 width: 130,
47315                 listeners : {
47316                     'select': function(c, r, i) {
47317                         // initial support only for on class per el..
47318                         tb.selectedNode.className =  r ? r.get('val') : '';
47319                         editorcore.syncValue();
47320                     }
47321                 }
47322     
47323             }));
47324         }
47325         
47326         var tbc = Roo.form.HtmlEditor.ToolbarContext;
47327         var tbops = tbc.options;
47328         
47329         for (var i in tlist) {
47330             
47331             var item = tlist[i];
47332             tb.add(item.title + ":&nbsp;");
47333             
47334             
47335             //optname == used so you can configure the options available..
47336             var opts = item.opts ? item.opts : false;
47337             if (item.optname) {
47338                 opts = tbops[item.optname];
47339            
47340             }
47341             
47342             if (opts) {
47343                 // opts == pulldown..
47344                 tb.addField( new Roo.form.ComboBox({
47345                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
47346                         id : 'val',
47347                         fields: ['val', 'display'],
47348                         data : opts  
47349                     }),
47350                     name : '-roo-edit-' + i,
47351                     attrname : i,
47352                     stylename : item.style ? item.style : false,
47353                     displayField: item.displayField ? item.displayField : 'val',
47354                     valueField :  'val',
47355                     typeAhead: false,
47356                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
47357                     editable : false,
47358                     triggerAction: 'all',
47359                     emptyText:'Select',
47360                     selectOnFocus:true,
47361                     width: item.width ? item.width  : 130,
47362                     listeners : {
47363                         'select': function(c, r, i) {
47364                             if (c.stylename) {
47365                                 tb.selectedNode.style[c.stylename] =  r.get('val');
47366                                 return;
47367                             }
47368                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
47369                         }
47370                     }
47371
47372                 }));
47373                 continue;
47374                     
47375                  
47376                 
47377                 tb.addField( new Roo.form.TextField({
47378                     name: i,
47379                     width: 100,
47380                     //allowBlank:false,
47381                     value: ''
47382                 }));
47383                 continue;
47384             }
47385             tb.addField( new Roo.form.TextField({
47386                 name: '-roo-edit-' + i,
47387                 attrname : i,
47388                 
47389                 width: item.width,
47390                 //allowBlank:true,
47391                 value: '',
47392                 listeners: {
47393                     'change' : function(f, nv, ov) {
47394                         tb.selectedNode.setAttribute(f.attrname, nv);
47395                         editorcore.syncValue();
47396                     }
47397                 }
47398             }));
47399              
47400         }
47401         
47402         var _this = this;
47403         
47404         if(nm == 'BODY'){
47405             tb.addSeparator();
47406         
47407             tb.addButton( {
47408                 text: 'Stylesheets',
47409
47410                 listeners : {
47411                     click : function ()
47412                     {
47413                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
47414                     }
47415                 }
47416             });
47417         }
47418         
47419         tb.addFill();
47420         tb.addButton( {
47421             text: 'Remove Tag',
47422     
47423             listeners : {
47424                 click : function ()
47425                 {
47426                     // remove
47427                     // undo does not work.
47428                      
47429                     var sn = tb.selectedNode;
47430                     
47431                     var pn = sn.parentNode;
47432                     
47433                     var stn =  sn.childNodes[0];
47434                     var en = sn.childNodes[sn.childNodes.length - 1 ];
47435                     while (sn.childNodes.length) {
47436                         var node = sn.childNodes[0];
47437                         sn.removeChild(node);
47438                         //Roo.log(node);
47439                         pn.insertBefore(node, sn);
47440                         
47441                     }
47442                     pn.removeChild(sn);
47443                     var range = editorcore.createRange();
47444         
47445                     range.setStart(stn,0);
47446                     range.setEnd(en,0); //????
47447                     //range.selectNode(sel);
47448                     
47449                     
47450                     var selection = editorcore.getSelection();
47451                     selection.removeAllRanges();
47452                     selection.addRange(range);
47453                     
47454                     
47455                     
47456                     //_this.updateToolbar(null, null, pn);
47457                     _this.updateToolbar(null, null, null);
47458                     _this.footDisp.dom.innerHTML = ''; 
47459                 }
47460             }
47461             
47462                     
47463                 
47464             
47465         });
47466         
47467         
47468         tb.el.on('click', function(e){
47469             e.preventDefault(); // what does this do?
47470         });
47471         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
47472         tb.el.hide();
47473         tb.name = nm;
47474         // dont need to disable them... as they will get hidden
47475         return tb;
47476          
47477         
47478     },
47479     buildFooter : function()
47480     {
47481         
47482         var fel = this.editor.wrap.createChild();
47483         this.footer = new Roo.Toolbar(fel);
47484         // toolbar has scrolly on left / right?
47485         var footDisp= new Roo.Toolbar.Fill();
47486         var _t = this;
47487         this.footer.add(
47488             {
47489                 text : '&lt;',
47490                 xtype: 'Button',
47491                 handler : function() {
47492                     _t.footDisp.scrollTo('left',0,true)
47493                 }
47494             }
47495         );
47496         this.footer.add( footDisp );
47497         this.footer.add( 
47498             {
47499                 text : '&gt;',
47500                 xtype: 'Button',
47501                 handler : function() {
47502                     // no animation..
47503                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
47504                 }
47505             }
47506         );
47507         var fel = Roo.get(footDisp.el);
47508         fel.addClass('x-editor-context');
47509         this.footDispWrap = fel; 
47510         this.footDispWrap.overflow  = 'hidden';
47511         
47512         this.footDisp = fel.createChild();
47513         this.footDispWrap.on('click', this.onContextClick, this)
47514         
47515         
47516     },
47517     onContextClick : function (ev,dom)
47518     {
47519         ev.preventDefault();
47520         var  cn = dom.className;
47521         //Roo.log(cn);
47522         if (!cn.match(/x-ed-loc-/)) {
47523             return;
47524         }
47525         var n = cn.split('-').pop();
47526         var ans = this.footerEls;
47527         var sel = ans[n];
47528         
47529          // pick
47530         var range = this.editorcore.createRange();
47531         
47532         range.selectNodeContents(sel);
47533         //range.selectNode(sel);
47534         
47535         
47536         var selection = this.editorcore.getSelection();
47537         selection.removeAllRanges();
47538         selection.addRange(range);
47539         
47540         
47541         
47542         this.updateToolbar(null, null, sel);
47543         
47544         
47545     }
47546     
47547     
47548     
47549     
47550     
47551 });
47552
47553
47554
47555
47556
47557 /*
47558  * Based on:
47559  * Ext JS Library 1.1.1
47560  * Copyright(c) 2006-2007, Ext JS, LLC.
47561  *
47562  * Originally Released Under LGPL - original licence link has changed is not relivant.
47563  *
47564  * Fork - LGPL
47565  * <script type="text/javascript">
47566  */
47567  
47568 /**
47569  * @class Roo.form.BasicForm
47570  * @extends Roo.util.Observable
47571  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
47572  * @constructor
47573  * @param {String/HTMLElement/Roo.Element} el The form element or its id
47574  * @param {Object} config Configuration options
47575  */
47576 Roo.form.BasicForm = function(el, config){
47577     this.allItems = [];
47578     this.childForms = [];
47579     Roo.apply(this, config);
47580     /*
47581      * The Roo.form.Field items in this form.
47582      * @type MixedCollection
47583      */
47584      
47585      
47586     this.items = new Roo.util.MixedCollection(false, function(o){
47587         return o.id || (o.id = Roo.id());
47588     });
47589     this.addEvents({
47590         /**
47591          * @event beforeaction
47592          * Fires before any action is performed. Return false to cancel the action.
47593          * @param {Form} this
47594          * @param {Action} action The action to be performed
47595          */
47596         beforeaction: true,
47597         /**
47598          * @event actionfailed
47599          * Fires when an action fails.
47600          * @param {Form} this
47601          * @param {Action} action The action that failed
47602          */
47603         actionfailed : true,
47604         /**
47605          * @event actioncomplete
47606          * Fires when an action is completed.
47607          * @param {Form} this
47608          * @param {Action} action The action that completed
47609          */
47610         actioncomplete : true
47611     });
47612     if(el){
47613         this.initEl(el);
47614     }
47615     Roo.form.BasicForm.superclass.constructor.call(this);
47616     
47617     Roo.form.BasicForm.popover.apply();
47618 };
47619
47620 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
47621     /**
47622      * @cfg {String} method
47623      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
47624      */
47625     /**
47626      * @cfg {DataReader} reader
47627      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
47628      * This is optional as there is built-in support for processing JSON.
47629      */
47630     /**
47631      * @cfg {DataReader} errorReader
47632      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
47633      * This is completely optional as there is built-in support for processing JSON.
47634      */
47635     /**
47636      * @cfg {String} url
47637      * The URL to use for form actions if one isn't supplied in the action options.
47638      */
47639     /**
47640      * @cfg {Boolean} fileUpload
47641      * Set to true if this form is a file upload.
47642      */
47643      
47644     /**
47645      * @cfg {Object} baseParams
47646      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
47647      */
47648      /**
47649      
47650     /**
47651      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
47652      */
47653     timeout: 30,
47654
47655     // private
47656     activeAction : null,
47657
47658     /**
47659      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
47660      * or setValues() data instead of when the form was first created.
47661      */
47662     trackResetOnLoad : false,
47663     
47664     
47665     /**
47666      * childForms - used for multi-tab forms
47667      * @type {Array}
47668      */
47669     childForms : false,
47670     
47671     /**
47672      * allItems - full list of fields.
47673      * @type {Array}
47674      */
47675     allItems : false,
47676     
47677     /**
47678      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
47679      * element by passing it or its id or mask the form itself by passing in true.
47680      * @type Mixed
47681      */
47682     waitMsgTarget : false,
47683     
47684     /**
47685      * @type Boolean
47686      */
47687     disableMask : false,
47688     
47689     /**
47690      * @cfg {Boolean} errorMask (true|false) default false
47691      */
47692     errorMask : false,
47693     
47694     /**
47695      * @cfg {Number} maskOffset Default 100
47696      */
47697     maskOffset : 100,
47698
47699     // private
47700     initEl : function(el){
47701         this.el = Roo.get(el);
47702         this.id = this.el.id || Roo.id();
47703         this.el.on('submit', this.onSubmit, this);
47704         this.el.addClass('x-form');
47705     },
47706
47707     // private
47708     onSubmit : function(e){
47709         e.stopEvent();
47710     },
47711
47712     /**
47713      * Returns true if client-side validation on the form is successful.
47714      * @return Boolean
47715      */
47716     isValid : function(){
47717         var valid = true;
47718         var target = false;
47719         this.items.each(function(f){
47720             if(f.validate()){
47721                 return;
47722             }
47723             
47724             valid = false;
47725                 
47726             if(!target && f.el.isVisible(true)){
47727                 target = f;
47728             }
47729         });
47730         
47731         if(this.errorMask && !valid){
47732             Roo.form.BasicForm.popover.mask(this, target);
47733         }
47734         
47735         return valid;
47736     },
47737     /**
47738      * Returns array of invalid form fields.
47739      * @return Array
47740      */
47741     
47742     invalidFields : function()
47743     {
47744         var ret = [];
47745         this.items.each(function(f){
47746             if(f.validate()){
47747                 return;
47748             }
47749             ret.push(f);
47750             
47751         });
47752         
47753         return ret;
47754     },
47755     
47756     
47757     /**
47758      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
47759      * @return Boolean
47760      */
47761     isDirty : function(){
47762         var dirty = false;
47763         this.items.each(function(f){
47764            if(f.isDirty()){
47765                dirty = true;
47766                return false;
47767            }
47768         });
47769         return dirty;
47770     },
47771     
47772     /**
47773      * Returns true if any fields in this form have changed since their original load. (New version)
47774      * @return Boolean
47775      */
47776     
47777     hasChanged : function()
47778     {
47779         var dirty = false;
47780         this.items.each(function(f){
47781            if(f.hasChanged()){
47782                dirty = true;
47783                return false;
47784            }
47785         });
47786         return dirty;
47787         
47788     },
47789     /**
47790      * Resets all hasChanged to 'false' -
47791      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
47792      * So hasChanged storage is only to be used for this purpose
47793      * @return Boolean
47794      */
47795     resetHasChanged : function()
47796     {
47797         this.items.each(function(f){
47798            f.resetHasChanged();
47799         });
47800         
47801     },
47802     
47803     
47804     /**
47805      * Performs a predefined action (submit or load) or custom actions you define on this form.
47806      * @param {String} actionName The name of the action type
47807      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
47808      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
47809      * accept other config options):
47810      * <pre>
47811 Property          Type             Description
47812 ----------------  ---------------  ----------------------------------------------------------------------------------
47813 url               String           The url for the action (defaults to the form's url)
47814 method            String           The form method to use (defaults to the form's method, or POST if not defined)
47815 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
47816 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
47817                                    validate the form on the client (defaults to false)
47818      * </pre>
47819      * @return {BasicForm} this
47820      */
47821     doAction : function(action, options){
47822         if(typeof action == 'string'){
47823             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
47824         }
47825         if(this.fireEvent('beforeaction', this, action) !== false){
47826             this.beforeAction(action);
47827             action.run.defer(100, action);
47828         }
47829         return this;
47830     },
47831
47832     /**
47833      * Shortcut to do a submit action.
47834      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47835      * @return {BasicForm} this
47836      */
47837     submit : function(options){
47838         this.doAction('submit', options);
47839         return this;
47840     },
47841
47842     /**
47843      * Shortcut to do a load action.
47844      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47845      * @return {BasicForm} this
47846      */
47847     load : function(options){
47848         this.doAction('load', options);
47849         return this;
47850     },
47851
47852     /**
47853      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
47854      * @param {Record} record The record to edit
47855      * @return {BasicForm} this
47856      */
47857     updateRecord : function(record){
47858         record.beginEdit();
47859         var fs = record.fields;
47860         fs.each(function(f){
47861             var field = this.findField(f.name);
47862             if(field){
47863                 record.set(f.name, field.getValue());
47864             }
47865         }, this);
47866         record.endEdit();
47867         return this;
47868     },
47869
47870     /**
47871      * Loads an Roo.data.Record into this form.
47872      * @param {Record} record The record to load
47873      * @return {BasicForm} this
47874      */
47875     loadRecord : function(record){
47876         this.setValues(record.data);
47877         return this;
47878     },
47879
47880     // private
47881     beforeAction : function(action){
47882         var o = action.options;
47883         
47884         if(!this.disableMask) {
47885             if(this.waitMsgTarget === true){
47886                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
47887             }else if(this.waitMsgTarget){
47888                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
47889                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
47890             }else {
47891                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
47892             }
47893         }
47894         
47895          
47896     },
47897
47898     // private
47899     afterAction : function(action, success){
47900         this.activeAction = null;
47901         var o = action.options;
47902         
47903         if(!this.disableMask) {
47904             if(this.waitMsgTarget === true){
47905                 this.el.unmask();
47906             }else if(this.waitMsgTarget){
47907                 this.waitMsgTarget.unmask();
47908             }else{
47909                 Roo.MessageBox.updateProgress(1);
47910                 Roo.MessageBox.hide();
47911             }
47912         }
47913         
47914         if(success){
47915             if(o.reset){
47916                 this.reset();
47917             }
47918             Roo.callback(o.success, o.scope, [this, action]);
47919             this.fireEvent('actioncomplete', this, action);
47920             
47921         }else{
47922             
47923             // failure condition..
47924             // we have a scenario where updates need confirming.
47925             // eg. if a locking scenario exists..
47926             // we look for { errors : { needs_confirm : true }} in the response.
47927             if (
47928                 (typeof(action.result) != 'undefined')  &&
47929                 (typeof(action.result.errors) != 'undefined')  &&
47930                 (typeof(action.result.errors.needs_confirm) != 'undefined')
47931            ){
47932                 var _t = this;
47933                 Roo.MessageBox.confirm(
47934                     "Change requires confirmation",
47935                     action.result.errorMsg,
47936                     function(r) {
47937                         if (r != 'yes') {
47938                             return;
47939                         }
47940                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
47941                     }
47942                     
47943                 );
47944                 
47945                 
47946                 
47947                 return;
47948             }
47949             
47950             Roo.callback(o.failure, o.scope, [this, action]);
47951             // show an error message if no failed handler is set..
47952             if (!this.hasListener('actionfailed')) {
47953                 Roo.MessageBox.alert("Error",
47954                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
47955                         action.result.errorMsg :
47956                         "Saving Failed, please check your entries or try again"
47957                 );
47958             }
47959             
47960             this.fireEvent('actionfailed', this, action);
47961         }
47962         
47963     },
47964
47965     /**
47966      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
47967      * @param {String} id The value to search for
47968      * @return Field
47969      */
47970     findField : function(id){
47971         var field = this.items.get(id);
47972         if(!field){
47973             this.items.each(function(f){
47974                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
47975                     field = f;
47976                     return false;
47977                 }
47978             });
47979         }
47980         return field || null;
47981     },
47982
47983     /**
47984      * Add a secondary form to this one, 
47985      * Used to provide tabbed forms. One form is primary, with hidden values 
47986      * which mirror the elements from the other forms.
47987      * 
47988      * @param {Roo.form.Form} form to add.
47989      * 
47990      */
47991     addForm : function(form)
47992     {
47993        
47994         if (this.childForms.indexOf(form) > -1) {
47995             // already added..
47996             return;
47997         }
47998         this.childForms.push(form);
47999         var n = '';
48000         Roo.each(form.allItems, function (fe) {
48001             
48002             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
48003             if (this.findField(n)) { // already added..
48004                 return;
48005             }
48006             var add = new Roo.form.Hidden({
48007                 name : n
48008             });
48009             add.render(this.el);
48010             
48011             this.add( add );
48012         }, this);
48013         
48014     },
48015     /**
48016      * Mark fields in this form invalid in bulk.
48017      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
48018      * @return {BasicForm} this
48019      */
48020     markInvalid : function(errors){
48021         if(errors instanceof Array){
48022             for(var i = 0, len = errors.length; i < len; i++){
48023                 var fieldError = errors[i];
48024                 var f = this.findField(fieldError.id);
48025                 if(f){
48026                     f.markInvalid(fieldError.msg);
48027                 }
48028             }
48029         }else{
48030             var field, id;
48031             for(id in errors){
48032                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
48033                     field.markInvalid(errors[id]);
48034                 }
48035             }
48036         }
48037         Roo.each(this.childForms || [], function (f) {
48038             f.markInvalid(errors);
48039         });
48040         
48041         return this;
48042     },
48043
48044     /**
48045      * Set values for fields in this form in bulk.
48046      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
48047      * @return {BasicForm} this
48048      */
48049     setValues : function(values){
48050         if(values instanceof Array){ // array of objects
48051             for(var i = 0, len = values.length; i < len; i++){
48052                 var v = values[i];
48053                 var f = this.findField(v.id);
48054                 if(f){
48055                     f.setValue(v.value);
48056                     if(this.trackResetOnLoad){
48057                         f.originalValue = f.getValue();
48058                     }
48059                 }
48060             }
48061         }else{ // object hash
48062             var field, id;
48063             for(id in values){
48064                 if(typeof values[id] != 'function' && (field = this.findField(id))){
48065                     
48066                     if (field.setFromData && 
48067                         field.valueField && 
48068                         field.displayField &&
48069                         // combos' with local stores can 
48070                         // be queried via setValue()
48071                         // to set their value..
48072                         (field.store && !field.store.isLocal)
48073                         ) {
48074                         // it's a combo
48075                         var sd = { };
48076                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
48077                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
48078                         field.setFromData(sd);
48079                         
48080                     } else {
48081                         field.setValue(values[id]);
48082                     }
48083                     
48084                     
48085                     if(this.trackResetOnLoad){
48086                         field.originalValue = field.getValue();
48087                     }
48088                 }
48089             }
48090         }
48091         this.resetHasChanged();
48092         
48093         
48094         Roo.each(this.childForms || [], function (f) {
48095             f.setValues(values);
48096             f.resetHasChanged();
48097         });
48098                 
48099         return this;
48100     },
48101  
48102     /**
48103      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
48104      * they are returned as an array.
48105      * @param {Boolean} asString
48106      * @return {Object}
48107      */
48108     getValues : function(asString){
48109         if (this.childForms) {
48110             // copy values from the child forms
48111             Roo.each(this.childForms, function (f) {
48112                 this.setValues(f.getValues());
48113             }, this);
48114         }
48115         
48116         // use formdata
48117         if (typeof(FormData) != 'undefined' && asString !== true) {
48118             // this relies on a 'recent' version of chrome apparently...
48119             try {
48120                 var fd = (new FormData(this.el.dom)).entries();
48121                 var ret = {};
48122                 var ent = fd.next();
48123                 while (!ent.done) {
48124                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
48125                     ent = fd.next();
48126                 };
48127                 return ret;
48128             } catch(e) {
48129                 
48130             }
48131             
48132         }
48133         
48134         
48135         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
48136         if(asString === true){
48137             return fs;
48138         }
48139         return Roo.urlDecode(fs);
48140     },
48141     
48142     /**
48143      * Returns the fields in this form as an object with key/value pairs. 
48144      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
48145      * @return {Object}
48146      */
48147     getFieldValues : function(with_hidden)
48148     {
48149         if (this.childForms) {
48150             // copy values from the child forms
48151             // should this call getFieldValues - probably not as we do not currently copy
48152             // hidden fields when we generate..
48153             Roo.each(this.childForms, function (f) {
48154                 this.setValues(f.getValues());
48155             }, this);
48156         }
48157         
48158         var ret = {};
48159         this.items.each(function(f){
48160             if (!f.getName()) {
48161                 return;
48162             }
48163             var v = f.getValue();
48164             if (f.inputType =='radio') {
48165                 if (typeof(ret[f.getName()]) == 'undefined') {
48166                     ret[f.getName()] = ''; // empty..
48167                 }
48168                 
48169                 if (!f.el.dom.checked) {
48170                     return;
48171                     
48172                 }
48173                 v = f.el.dom.value;
48174                 
48175             }
48176             
48177             // not sure if this supported any more..
48178             if ((typeof(v) == 'object') && f.getRawValue) {
48179                 v = f.getRawValue() ; // dates..
48180             }
48181             // combo boxes where name != hiddenName...
48182             if (f.name != f.getName()) {
48183                 ret[f.name] = f.getRawValue();
48184             }
48185             ret[f.getName()] = v;
48186         });
48187         
48188         return ret;
48189     },
48190
48191     /**
48192      * Clears all invalid messages in this form.
48193      * @return {BasicForm} this
48194      */
48195     clearInvalid : function(){
48196         this.items.each(function(f){
48197            f.clearInvalid();
48198         });
48199         
48200         Roo.each(this.childForms || [], function (f) {
48201             f.clearInvalid();
48202         });
48203         
48204         
48205         return this;
48206     },
48207
48208     /**
48209      * Resets this form.
48210      * @return {BasicForm} this
48211      */
48212     reset : function(){
48213         this.items.each(function(f){
48214             f.reset();
48215         });
48216         
48217         Roo.each(this.childForms || [], function (f) {
48218             f.reset();
48219         });
48220         this.resetHasChanged();
48221         
48222         return this;
48223     },
48224
48225     /**
48226      * Add Roo.form components to this form.
48227      * @param {Field} field1
48228      * @param {Field} field2 (optional)
48229      * @param {Field} etc (optional)
48230      * @return {BasicForm} this
48231      */
48232     add : function(){
48233         this.items.addAll(Array.prototype.slice.call(arguments, 0));
48234         return this;
48235     },
48236
48237
48238     /**
48239      * Removes a field from the items collection (does NOT remove its markup).
48240      * @param {Field} field
48241      * @return {BasicForm} this
48242      */
48243     remove : function(field){
48244         this.items.remove(field);
48245         return this;
48246     },
48247
48248     /**
48249      * Looks at the fields in this form, checks them for an id attribute,
48250      * and calls applyTo on the existing dom element with that id.
48251      * @return {BasicForm} this
48252      */
48253     render : function(){
48254         this.items.each(function(f){
48255             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
48256                 f.applyTo(f.id);
48257             }
48258         });
48259         return this;
48260     },
48261
48262     /**
48263      * Calls {@link Ext#apply} for all fields in this form with the passed object.
48264      * @param {Object} values
48265      * @return {BasicForm} this
48266      */
48267     applyToFields : function(o){
48268         this.items.each(function(f){
48269            Roo.apply(f, o);
48270         });
48271         return this;
48272     },
48273
48274     /**
48275      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
48276      * @param {Object} values
48277      * @return {BasicForm} this
48278      */
48279     applyIfToFields : function(o){
48280         this.items.each(function(f){
48281            Roo.applyIf(f, o);
48282         });
48283         return this;
48284     }
48285 });
48286
48287 // back compat
48288 Roo.BasicForm = Roo.form.BasicForm;
48289
48290 Roo.apply(Roo.form.BasicForm, {
48291     
48292     popover : {
48293         
48294         padding : 5,
48295         
48296         isApplied : false,
48297         
48298         isMasked : false,
48299         
48300         form : false,
48301         
48302         target : false,
48303         
48304         intervalID : false,
48305         
48306         maskEl : false,
48307         
48308         apply : function()
48309         {
48310             if(this.isApplied){
48311                 return;
48312             }
48313             
48314             this.maskEl = {
48315                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
48316                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
48317                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
48318                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
48319             };
48320             
48321             this.maskEl.top.enableDisplayMode("block");
48322             this.maskEl.left.enableDisplayMode("block");
48323             this.maskEl.bottom.enableDisplayMode("block");
48324             this.maskEl.right.enableDisplayMode("block");
48325             
48326             Roo.get(document.body).on('click', function(){
48327                 this.unmask();
48328             }, this);
48329             
48330             Roo.get(document.body).on('touchstart', function(){
48331                 this.unmask();
48332             }, this);
48333             
48334             this.isApplied = true
48335         },
48336         
48337         mask : function(form, target)
48338         {
48339             this.form = form;
48340             
48341             this.target = target;
48342             
48343             if(!this.form.errorMask || !target.el){
48344                 return;
48345             }
48346             
48347             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
48348             
48349             var ot = this.target.el.calcOffsetsTo(scrollable);
48350             
48351             var scrollTo = ot[1] - this.form.maskOffset;
48352             
48353             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
48354             
48355             scrollable.scrollTo('top', scrollTo);
48356             
48357             var el = this.target.wrap || this.target.el;
48358             
48359             var box = el.getBox();
48360             
48361             this.maskEl.top.setStyle('position', 'absolute');
48362             this.maskEl.top.setStyle('z-index', 10000);
48363             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
48364             this.maskEl.top.setLeft(0);
48365             this.maskEl.top.setTop(0);
48366             this.maskEl.top.show();
48367             
48368             this.maskEl.left.setStyle('position', 'absolute');
48369             this.maskEl.left.setStyle('z-index', 10000);
48370             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
48371             this.maskEl.left.setLeft(0);
48372             this.maskEl.left.setTop(box.y - this.padding);
48373             this.maskEl.left.show();
48374
48375             this.maskEl.bottom.setStyle('position', 'absolute');
48376             this.maskEl.bottom.setStyle('z-index', 10000);
48377             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
48378             this.maskEl.bottom.setLeft(0);
48379             this.maskEl.bottom.setTop(box.bottom + this.padding);
48380             this.maskEl.bottom.show();
48381
48382             this.maskEl.right.setStyle('position', 'absolute');
48383             this.maskEl.right.setStyle('z-index', 10000);
48384             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
48385             this.maskEl.right.setLeft(box.right + this.padding);
48386             this.maskEl.right.setTop(box.y - this.padding);
48387             this.maskEl.right.show();
48388
48389             this.intervalID = window.setInterval(function() {
48390                 Roo.form.BasicForm.popover.unmask();
48391             }, 10000);
48392
48393             window.onwheel = function(){ return false;};
48394             
48395             (function(){ this.isMasked = true; }).defer(500, this);
48396             
48397         },
48398         
48399         unmask : function()
48400         {
48401             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
48402                 return;
48403             }
48404             
48405             this.maskEl.top.setStyle('position', 'absolute');
48406             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
48407             this.maskEl.top.hide();
48408
48409             this.maskEl.left.setStyle('position', 'absolute');
48410             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
48411             this.maskEl.left.hide();
48412
48413             this.maskEl.bottom.setStyle('position', 'absolute');
48414             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
48415             this.maskEl.bottom.hide();
48416
48417             this.maskEl.right.setStyle('position', 'absolute');
48418             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
48419             this.maskEl.right.hide();
48420             
48421             window.onwheel = function(){ return true;};
48422             
48423             if(this.intervalID){
48424                 window.clearInterval(this.intervalID);
48425                 this.intervalID = false;
48426             }
48427             
48428             this.isMasked = false;
48429             
48430         }
48431         
48432     }
48433     
48434 });/*
48435  * Based on:
48436  * Ext JS Library 1.1.1
48437  * Copyright(c) 2006-2007, Ext JS, LLC.
48438  *
48439  * Originally Released Under LGPL - original licence link has changed is not relivant.
48440  *
48441  * Fork - LGPL
48442  * <script type="text/javascript">
48443  */
48444
48445 /**
48446  * @class Roo.form.Form
48447  * @extends Roo.form.BasicForm
48448  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
48449  * @constructor
48450  * @param {Object} config Configuration options
48451  */
48452 Roo.form.Form = function(config){
48453     var xitems =  [];
48454     if (config.items) {
48455         xitems = config.items;
48456         delete config.items;
48457     }
48458    
48459     
48460     Roo.form.Form.superclass.constructor.call(this, null, config);
48461     this.url = this.url || this.action;
48462     if(!this.root){
48463         this.root = new Roo.form.Layout(Roo.applyIf({
48464             id: Roo.id()
48465         }, config));
48466     }
48467     this.active = this.root;
48468     /**
48469      * Array of all the buttons that have been added to this form via {@link addButton}
48470      * @type Array
48471      */
48472     this.buttons = [];
48473     this.allItems = [];
48474     this.addEvents({
48475         /**
48476          * @event clientvalidation
48477          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
48478          * @param {Form} this
48479          * @param {Boolean} valid true if the form has passed client-side validation
48480          */
48481         clientvalidation: true,
48482         /**
48483          * @event rendered
48484          * Fires when the form is rendered
48485          * @param {Roo.form.Form} form
48486          */
48487         rendered : true
48488     });
48489     
48490     if (this.progressUrl) {
48491             // push a hidden field onto the list of fields..
48492             this.addxtype( {
48493                     xns: Roo.form, 
48494                     xtype : 'Hidden', 
48495                     name : 'UPLOAD_IDENTIFIER' 
48496             });
48497         }
48498         
48499     
48500     Roo.each(xitems, this.addxtype, this);
48501     
48502 };
48503
48504 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
48505     /**
48506      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
48507      */
48508     /**
48509      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
48510      */
48511     /**
48512      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
48513      */
48514     buttonAlign:'center',
48515
48516     /**
48517      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
48518      */
48519     minButtonWidth:75,
48520
48521     /**
48522      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
48523      * This property cascades to child containers if not set.
48524      */
48525     labelAlign:'left',
48526
48527     /**
48528      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
48529      * fires a looping event with that state. This is required to bind buttons to the valid
48530      * state using the config value formBind:true on the button.
48531      */
48532     monitorValid : false,
48533
48534     /**
48535      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
48536      */
48537     monitorPoll : 200,
48538     
48539     /**
48540      * @cfg {String} progressUrl - Url to return progress data 
48541      */
48542     
48543     progressUrl : false,
48544     /**
48545      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
48546      * sending a formdata with extra parameters - eg uploaded elements.
48547      */
48548     
48549     formData : false,
48550     
48551     /**
48552      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
48553      * fields are added and the column is closed. If no fields are passed the column remains open
48554      * until end() is called.
48555      * @param {Object} config The config to pass to the column
48556      * @param {Field} field1 (optional)
48557      * @param {Field} field2 (optional)
48558      * @param {Field} etc (optional)
48559      * @return Column The column container object
48560      */
48561     column : function(c){
48562         var col = new Roo.form.Column(c);
48563         this.start(col);
48564         if(arguments.length > 1){ // duplicate code required because of Opera
48565             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48566             this.end();
48567         }
48568         return col;
48569     },
48570
48571     /**
48572      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
48573      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
48574      * until end() is called.
48575      * @param {Object} config The config to pass to the fieldset
48576      * @param {Field} field1 (optional)
48577      * @param {Field} field2 (optional)
48578      * @param {Field} etc (optional)
48579      * @return FieldSet The fieldset container object
48580      */
48581     fieldset : function(c){
48582         var fs = new Roo.form.FieldSet(c);
48583         this.start(fs);
48584         if(arguments.length > 1){ // duplicate code required because of Opera
48585             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48586             this.end();
48587         }
48588         return fs;
48589     },
48590
48591     /**
48592      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
48593      * fields are added and the container is closed. If no fields are passed the container remains open
48594      * until end() is called.
48595      * @param {Object} config The config to pass to the Layout
48596      * @param {Field} field1 (optional)
48597      * @param {Field} field2 (optional)
48598      * @param {Field} etc (optional)
48599      * @return Layout The container object
48600      */
48601     container : function(c){
48602         var l = new Roo.form.Layout(c);
48603         this.start(l);
48604         if(arguments.length > 1){ // duplicate code required because of Opera
48605             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48606             this.end();
48607         }
48608         return l;
48609     },
48610
48611     /**
48612      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
48613      * @param {Object} container A Roo.form.Layout or subclass of Layout
48614      * @return {Form} this
48615      */
48616     start : function(c){
48617         // cascade label info
48618         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
48619         this.active.stack.push(c);
48620         c.ownerCt = this.active;
48621         this.active = c;
48622         return this;
48623     },
48624
48625     /**
48626      * Closes the current open container
48627      * @return {Form} this
48628      */
48629     end : function(){
48630         if(this.active == this.root){
48631             return this;
48632         }
48633         this.active = this.active.ownerCt;
48634         return this;
48635     },
48636
48637     /**
48638      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
48639      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
48640      * as the label of the field.
48641      * @param {Field} field1
48642      * @param {Field} field2 (optional)
48643      * @param {Field} etc. (optional)
48644      * @return {Form} this
48645      */
48646     add : function(){
48647         this.active.stack.push.apply(this.active.stack, arguments);
48648         this.allItems.push.apply(this.allItems,arguments);
48649         var r = [];
48650         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
48651             if(a[i].isFormField){
48652                 r.push(a[i]);
48653             }
48654         }
48655         if(r.length > 0){
48656             Roo.form.Form.superclass.add.apply(this, r);
48657         }
48658         return this;
48659     },
48660     
48661
48662     
48663     
48664     
48665      /**
48666      * Find any element that has been added to a form, using it's ID or name
48667      * This can include framesets, columns etc. along with regular fields..
48668      * @param {String} id - id or name to find.
48669      
48670      * @return {Element} e - or false if nothing found.
48671      */
48672     findbyId : function(id)
48673     {
48674         var ret = false;
48675         if (!id) {
48676             return ret;
48677         }
48678         Roo.each(this.allItems, function(f){
48679             if (f.id == id || f.name == id ){
48680                 ret = f;
48681                 return false;
48682             }
48683         });
48684         return ret;
48685     },
48686
48687     
48688     
48689     /**
48690      * Render this form into the passed container. This should only be called once!
48691      * @param {String/HTMLElement/Element} container The element this component should be rendered into
48692      * @return {Form} this
48693      */
48694     render : function(ct)
48695     {
48696         
48697         
48698         
48699         ct = Roo.get(ct);
48700         var o = this.autoCreate || {
48701             tag: 'form',
48702             method : this.method || 'POST',
48703             id : this.id || Roo.id()
48704         };
48705         this.initEl(ct.createChild(o));
48706
48707         this.root.render(this.el);
48708         
48709        
48710              
48711         this.items.each(function(f){
48712             f.render('x-form-el-'+f.id);
48713         });
48714
48715         if(this.buttons.length > 0){
48716             // tables are required to maintain order and for correct IE layout
48717             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
48718                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
48719                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
48720             }}, null, true);
48721             var tr = tb.getElementsByTagName('tr')[0];
48722             for(var i = 0, len = this.buttons.length; i < len; i++) {
48723                 var b = this.buttons[i];
48724                 var td = document.createElement('td');
48725                 td.className = 'x-form-btn-td';
48726                 b.render(tr.appendChild(td));
48727             }
48728         }
48729         if(this.monitorValid){ // initialize after render
48730             this.startMonitoring();
48731         }
48732         this.fireEvent('rendered', this);
48733         return this;
48734     },
48735
48736     /**
48737      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
48738      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
48739      * object or a valid Roo.DomHelper element config
48740      * @param {Function} handler The function called when the button is clicked
48741      * @param {Object} scope (optional) The scope of the handler function
48742      * @return {Roo.Button}
48743      */
48744     addButton : function(config, handler, scope){
48745         var bc = {
48746             handler: handler,
48747             scope: scope,
48748             minWidth: this.minButtonWidth,
48749             hideParent:true
48750         };
48751         if(typeof config == "string"){
48752             bc.text = config;
48753         }else{
48754             Roo.apply(bc, config);
48755         }
48756         var btn = new Roo.Button(null, bc);
48757         this.buttons.push(btn);
48758         return btn;
48759     },
48760
48761      /**
48762      * Adds a series of form elements (using the xtype property as the factory method.
48763      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
48764      * @param {Object} config 
48765      */
48766     
48767     addxtype : function()
48768     {
48769         var ar = Array.prototype.slice.call(arguments, 0);
48770         var ret = false;
48771         for(var i = 0; i < ar.length; i++) {
48772             if (!ar[i]) {
48773                 continue; // skip -- if this happends something invalid got sent, we 
48774                 // should ignore it, as basically that interface element will not show up
48775                 // and that should be pretty obvious!!
48776             }
48777             
48778             if (Roo.form[ar[i].xtype]) {
48779                 ar[i].form = this;
48780                 var fe = Roo.factory(ar[i], Roo.form);
48781                 if (!ret) {
48782                     ret = fe;
48783                 }
48784                 fe.form = this;
48785                 if (fe.store) {
48786                     fe.store.form = this;
48787                 }
48788                 if (fe.isLayout) {  
48789                          
48790                     this.start(fe);
48791                     this.allItems.push(fe);
48792                     if (fe.items && fe.addxtype) {
48793                         fe.addxtype.apply(fe, fe.items);
48794                         delete fe.items;
48795                     }
48796                      this.end();
48797                     continue;
48798                 }
48799                 
48800                 
48801                  
48802                 this.add(fe);
48803               //  console.log('adding ' + ar[i].xtype);
48804             }
48805             if (ar[i].xtype == 'Button') {  
48806                 //console.log('adding button');
48807                 //console.log(ar[i]);
48808                 this.addButton(ar[i]);
48809                 this.allItems.push(fe);
48810                 continue;
48811             }
48812             
48813             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
48814                 alert('end is not supported on xtype any more, use items');
48815             //    this.end();
48816             //    //console.log('adding end');
48817             }
48818             
48819         }
48820         return ret;
48821     },
48822     
48823     /**
48824      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
48825      * option "monitorValid"
48826      */
48827     startMonitoring : function(){
48828         if(!this.bound){
48829             this.bound = true;
48830             Roo.TaskMgr.start({
48831                 run : this.bindHandler,
48832                 interval : this.monitorPoll || 200,
48833                 scope: this
48834             });
48835         }
48836     },
48837
48838     /**
48839      * Stops monitoring of the valid state of this form
48840      */
48841     stopMonitoring : function(){
48842         this.bound = false;
48843     },
48844
48845     // private
48846     bindHandler : function(){
48847         if(!this.bound){
48848             return false; // stops binding
48849         }
48850         var valid = true;
48851         this.items.each(function(f){
48852             if(!f.isValid(true)){
48853                 valid = false;
48854                 return false;
48855             }
48856         });
48857         for(var i = 0, len = this.buttons.length; i < len; i++){
48858             var btn = this.buttons[i];
48859             if(btn.formBind === true && btn.disabled === valid){
48860                 btn.setDisabled(!valid);
48861             }
48862         }
48863         this.fireEvent('clientvalidation', this, valid);
48864     }
48865     
48866     
48867     
48868     
48869     
48870     
48871     
48872     
48873 });
48874
48875
48876 // back compat
48877 Roo.Form = Roo.form.Form;
48878 /*
48879  * Based on:
48880  * Ext JS Library 1.1.1
48881  * Copyright(c) 2006-2007, Ext JS, LLC.
48882  *
48883  * Originally Released Under LGPL - original licence link has changed is not relivant.
48884  *
48885  * Fork - LGPL
48886  * <script type="text/javascript">
48887  */
48888
48889 // as we use this in bootstrap.
48890 Roo.namespace('Roo.form');
48891  /**
48892  * @class Roo.form.Action
48893  * Internal Class used to handle form actions
48894  * @constructor
48895  * @param {Roo.form.BasicForm} el The form element or its id
48896  * @param {Object} config Configuration options
48897  */
48898
48899  
48900  
48901 // define the action interface
48902 Roo.form.Action = function(form, options){
48903     this.form = form;
48904     this.options = options || {};
48905 };
48906 /**
48907  * Client Validation Failed
48908  * @const 
48909  */
48910 Roo.form.Action.CLIENT_INVALID = 'client';
48911 /**
48912  * Server Validation Failed
48913  * @const 
48914  */
48915 Roo.form.Action.SERVER_INVALID = 'server';
48916  /**
48917  * Connect to Server Failed
48918  * @const 
48919  */
48920 Roo.form.Action.CONNECT_FAILURE = 'connect';
48921 /**
48922  * Reading Data from Server Failed
48923  * @const 
48924  */
48925 Roo.form.Action.LOAD_FAILURE = 'load';
48926
48927 Roo.form.Action.prototype = {
48928     type : 'default',
48929     failureType : undefined,
48930     response : undefined,
48931     result : undefined,
48932
48933     // interface method
48934     run : function(options){
48935
48936     },
48937
48938     // interface method
48939     success : function(response){
48940
48941     },
48942
48943     // interface method
48944     handleResponse : function(response){
48945
48946     },
48947
48948     // default connection failure
48949     failure : function(response){
48950         
48951         this.response = response;
48952         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48953         this.form.afterAction(this, false);
48954     },
48955
48956     processResponse : function(response){
48957         this.response = response;
48958         if(!response.responseText){
48959             return true;
48960         }
48961         this.result = this.handleResponse(response);
48962         return this.result;
48963     },
48964
48965     // utility functions used internally
48966     getUrl : function(appendParams){
48967         var url = this.options.url || this.form.url || this.form.el.dom.action;
48968         if(appendParams){
48969             var p = this.getParams();
48970             if(p){
48971                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
48972             }
48973         }
48974         return url;
48975     },
48976
48977     getMethod : function(){
48978         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
48979     },
48980
48981     getParams : function(){
48982         var bp = this.form.baseParams;
48983         var p = this.options.params;
48984         if(p){
48985             if(typeof p == "object"){
48986                 p = Roo.urlEncode(Roo.applyIf(p, bp));
48987             }else if(typeof p == 'string' && bp){
48988                 p += '&' + Roo.urlEncode(bp);
48989             }
48990         }else if(bp){
48991             p = Roo.urlEncode(bp);
48992         }
48993         return p;
48994     },
48995
48996     createCallback : function(){
48997         return {
48998             success: this.success,
48999             failure: this.failure,
49000             scope: this,
49001             timeout: (this.form.timeout*1000),
49002             upload: this.form.fileUpload ? this.success : undefined
49003         };
49004     }
49005 };
49006
49007 Roo.form.Action.Submit = function(form, options){
49008     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
49009 };
49010
49011 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
49012     type : 'submit',
49013
49014     haveProgress : false,
49015     uploadComplete : false,
49016     
49017     // uploadProgress indicator.
49018     uploadProgress : function()
49019     {
49020         if (!this.form.progressUrl) {
49021             return;
49022         }
49023         
49024         if (!this.haveProgress) {
49025             Roo.MessageBox.progress("Uploading", "Uploading");
49026         }
49027         if (this.uploadComplete) {
49028            Roo.MessageBox.hide();
49029            return;
49030         }
49031         
49032         this.haveProgress = true;
49033    
49034         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
49035         
49036         var c = new Roo.data.Connection();
49037         c.request({
49038             url : this.form.progressUrl,
49039             params: {
49040                 id : uid
49041             },
49042             method: 'GET',
49043             success : function(req){
49044                //console.log(data);
49045                 var rdata = false;
49046                 var edata;
49047                 try  {
49048                    rdata = Roo.decode(req.responseText)
49049                 } catch (e) {
49050                     Roo.log("Invalid data from server..");
49051                     Roo.log(edata);
49052                     return;
49053                 }
49054                 if (!rdata || !rdata.success) {
49055                     Roo.log(rdata);
49056                     Roo.MessageBox.alert(Roo.encode(rdata));
49057                     return;
49058                 }
49059                 var data = rdata.data;
49060                 
49061                 if (this.uploadComplete) {
49062                    Roo.MessageBox.hide();
49063                    return;
49064                 }
49065                    
49066                 if (data){
49067                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
49068                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
49069                     );
49070                 }
49071                 this.uploadProgress.defer(2000,this);
49072             },
49073        
49074             failure: function(data) {
49075                 Roo.log('progress url failed ');
49076                 Roo.log(data);
49077             },
49078             scope : this
49079         });
49080            
49081     },
49082     
49083     
49084     run : function()
49085     {
49086         // run get Values on the form, so it syncs any secondary forms.
49087         this.form.getValues();
49088         
49089         var o = this.options;
49090         var method = this.getMethod();
49091         var isPost = method == 'POST';
49092         if(o.clientValidation === false || this.form.isValid()){
49093             
49094             if (this.form.progressUrl) {
49095                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
49096                     (new Date() * 1) + '' + Math.random());
49097                     
49098             } 
49099             
49100             
49101             Roo.Ajax.request(Roo.apply(this.createCallback(), {
49102                 form:this.form.el.dom,
49103                 url:this.getUrl(!isPost),
49104                 method: method,
49105                 params:isPost ? this.getParams() : null,
49106                 isUpload: this.form.fileUpload,
49107                 formData : this.form.formData
49108             }));
49109             
49110             this.uploadProgress();
49111
49112         }else if (o.clientValidation !== false){ // client validation failed
49113             this.failureType = Roo.form.Action.CLIENT_INVALID;
49114             this.form.afterAction(this, false);
49115         }
49116     },
49117
49118     success : function(response)
49119     {
49120         this.uploadComplete= true;
49121         if (this.haveProgress) {
49122             Roo.MessageBox.hide();
49123         }
49124         
49125         
49126         var result = this.processResponse(response);
49127         if(result === true || result.success){
49128             this.form.afterAction(this, true);
49129             return;
49130         }
49131         if(result.errors){
49132             this.form.markInvalid(result.errors);
49133             this.failureType = Roo.form.Action.SERVER_INVALID;
49134         }
49135         this.form.afterAction(this, false);
49136     },
49137     failure : function(response)
49138     {
49139         this.uploadComplete= true;
49140         if (this.haveProgress) {
49141             Roo.MessageBox.hide();
49142         }
49143         
49144         this.response = response;
49145         this.failureType = Roo.form.Action.CONNECT_FAILURE;
49146         this.form.afterAction(this, false);
49147     },
49148     
49149     handleResponse : function(response){
49150         if(this.form.errorReader){
49151             var rs = this.form.errorReader.read(response);
49152             var errors = [];
49153             if(rs.records){
49154                 for(var i = 0, len = rs.records.length; i < len; i++) {
49155                     var r = rs.records[i];
49156                     errors[i] = r.data;
49157                 }
49158             }
49159             if(errors.length < 1){
49160                 errors = null;
49161             }
49162             return {
49163                 success : rs.success,
49164                 errors : errors
49165             };
49166         }
49167         var ret = false;
49168         try {
49169             ret = Roo.decode(response.responseText);
49170         } catch (e) {
49171             ret = {
49172                 success: false,
49173                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
49174                 errors : []
49175             };
49176         }
49177         return ret;
49178         
49179     }
49180 });
49181
49182
49183 Roo.form.Action.Load = function(form, options){
49184     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
49185     this.reader = this.form.reader;
49186 };
49187
49188 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
49189     type : 'load',
49190
49191     run : function(){
49192         
49193         Roo.Ajax.request(Roo.apply(
49194                 this.createCallback(), {
49195                     method:this.getMethod(),
49196                     url:this.getUrl(false),
49197                     params:this.getParams()
49198         }));
49199     },
49200
49201     success : function(response){
49202         
49203         var result = this.processResponse(response);
49204         if(result === true || !result.success || !result.data){
49205             this.failureType = Roo.form.Action.LOAD_FAILURE;
49206             this.form.afterAction(this, false);
49207             return;
49208         }
49209         this.form.clearInvalid();
49210         this.form.setValues(result.data);
49211         this.form.afterAction(this, true);
49212     },
49213
49214     handleResponse : function(response){
49215         if(this.form.reader){
49216             var rs = this.form.reader.read(response);
49217             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
49218             return {
49219                 success : rs.success,
49220                 data : data
49221             };
49222         }
49223         return Roo.decode(response.responseText);
49224     }
49225 });
49226
49227 Roo.form.Action.ACTION_TYPES = {
49228     'load' : Roo.form.Action.Load,
49229     'submit' : Roo.form.Action.Submit
49230 };/*
49231  * Based on:
49232  * Ext JS Library 1.1.1
49233  * Copyright(c) 2006-2007, Ext JS, LLC.
49234  *
49235  * Originally Released Under LGPL - original licence link has changed is not relivant.
49236  *
49237  * Fork - LGPL
49238  * <script type="text/javascript">
49239  */
49240  
49241 /**
49242  * @class Roo.form.Layout
49243  * @extends Roo.Component
49244  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
49245  * @constructor
49246  * @param {Object} config Configuration options
49247  */
49248 Roo.form.Layout = function(config){
49249     var xitems = [];
49250     if (config.items) {
49251         xitems = config.items;
49252         delete config.items;
49253     }
49254     Roo.form.Layout.superclass.constructor.call(this, config);
49255     this.stack = [];
49256     Roo.each(xitems, this.addxtype, this);
49257      
49258 };
49259
49260 Roo.extend(Roo.form.Layout, Roo.Component, {
49261     /**
49262      * @cfg {String/Object} autoCreate
49263      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
49264      */
49265     /**
49266      * @cfg {String/Object/Function} style
49267      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
49268      * a function which returns such a specification.
49269      */
49270     /**
49271      * @cfg {String} labelAlign
49272      * Valid values are "left," "top" and "right" (defaults to "left")
49273      */
49274     /**
49275      * @cfg {Number} labelWidth
49276      * Fixed width in pixels of all field labels (defaults to undefined)
49277      */
49278     /**
49279      * @cfg {Boolean} clear
49280      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
49281      */
49282     clear : true,
49283     /**
49284      * @cfg {String} labelSeparator
49285      * The separator to use after field labels (defaults to ':')
49286      */
49287     labelSeparator : ':',
49288     /**
49289      * @cfg {Boolean} hideLabels
49290      * True to suppress the display of field labels in this layout (defaults to false)
49291      */
49292     hideLabels : false,
49293
49294     // private
49295     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
49296     
49297     isLayout : true,
49298     
49299     // private
49300     onRender : function(ct, position){
49301         if(this.el){ // from markup
49302             this.el = Roo.get(this.el);
49303         }else {  // generate
49304             var cfg = this.getAutoCreate();
49305             this.el = ct.createChild(cfg, position);
49306         }
49307         if(this.style){
49308             this.el.applyStyles(this.style);
49309         }
49310         if(this.labelAlign){
49311             this.el.addClass('x-form-label-'+this.labelAlign);
49312         }
49313         if(this.hideLabels){
49314             this.labelStyle = "display:none";
49315             this.elementStyle = "padding-left:0;";
49316         }else{
49317             if(typeof this.labelWidth == 'number'){
49318                 this.labelStyle = "width:"+this.labelWidth+"px;";
49319                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
49320             }
49321             if(this.labelAlign == 'top'){
49322                 this.labelStyle = "width:auto;";
49323                 this.elementStyle = "padding-left:0;";
49324             }
49325         }
49326         var stack = this.stack;
49327         var slen = stack.length;
49328         if(slen > 0){
49329             if(!this.fieldTpl){
49330                 var t = new Roo.Template(
49331                     '<div class="x-form-item {5}">',
49332                         '<label for="{0}" style="{2}">{1}{4}</label>',
49333                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49334                         '</div>',
49335                     '</div><div class="x-form-clear-left"></div>'
49336                 );
49337                 t.disableFormats = true;
49338                 t.compile();
49339                 Roo.form.Layout.prototype.fieldTpl = t;
49340             }
49341             for(var i = 0; i < slen; i++) {
49342                 if(stack[i].isFormField){
49343                     this.renderField(stack[i]);
49344                 }else{
49345                     this.renderComponent(stack[i]);
49346                 }
49347             }
49348         }
49349         if(this.clear){
49350             this.el.createChild({cls:'x-form-clear'});
49351         }
49352     },
49353
49354     // private
49355     renderField : function(f){
49356         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
49357                f.id, //0
49358                f.fieldLabel, //1
49359                f.labelStyle||this.labelStyle||'', //2
49360                this.elementStyle||'', //3
49361                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
49362                f.itemCls||this.itemCls||''  //5
49363        ], true).getPrevSibling());
49364     },
49365
49366     // private
49367     renderComponent : function(c){
49368         c.render(c.isLayout ? this.el : this.el.createChild());    
49369     },
49370     /**
49371      * Adds a object form elements (using the xtype property as the factory method.)
49372      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
49373      * @param {Object} config 
49374      */
49375     addxtype : function(o)
49376     {
49377         // create the lement.
49378         o.form = this.form;
49379         var fe = Roo.factory(o, Roo.form);
49380         this.form.allItems.push(fe);
49381         this.stack.push(fe);
49382         
49383         if (fe.isFormField) {
49384             this.form.items.add(fe);
49385         }
49386          
49387         return fe;
49388     }
49389 });
49390
49391 /**
49392  * @class Roo.form.Column
49393  * @extends Roo.form.Layout
49394  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
49395  * @constructor
49396  * @param {Object} config Configuration options
49397  */
49398 Roo.form.Column = function(config){
49399     Roo.form.Column.superclass.constructor.call(this, config);
49400 };
49401
49402 Roo.extend(Roo.form.Column, Roo.form.Layout, {
49403     /**
49404      * @cfg {Number/String} width
49405      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49406      */
49407     /**
49408      * @cfg {String/Object} autoCreate
49409      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
49410      */
49411
49412     // private
49413     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
49414
49415     // private
49416     onRender : function(ct, position){
49417         Roo.form.Column.superclass.onRender.call(this, ct, position);
49418         if(this.width){
49419             this.el.setWidth(this.width);
49420         }
49421     }
49422 });
49423
49424
49425 /**
49426  * @class Roo.form.Row
49427  * @extends Roo.form.Layout
49428  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
49429  * @constructor
49430  * @param {Object} config Configuration options
49431  */
49432
49433  
49434 Roo.form.Row = function(config){
49435     Roo.form.Row.superclass.constructor.call(this, config);
49436 };
49437  
49438 Roo.extend(Roo.form.Row, Roo.form.Layout, {
49439       /**
49440      * @cfg {Number/String} width
49441      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49442      */
49443     /**
49444      * @cfg {Number/String} height
49445      * The fixed height of the column in pixels or CSS value (defaults to "auto")
49446      */
49447     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
49448     
49449     padWidth : 20,
49450     // private
49451     onRender : function(ct, position){
49452         //console.log('row render');
49453         if(!this.rowTpl){
49454             var t = new Roo.Template(
49455                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
49456                     '<label for="{0}" style="{2}">{1}{4}</label>',
49457                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49458                     '</div>',
49459                 '</div>'
49460             );
49461             t.disableFormats = true;
49462             t.compile();
49463             Roo.form.Layout.prototype.rowTpl = t;
49464         }
49465         this.fieldTpl = this.rowTpl;
49466         
49467         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
49468         var labelWidth = 100;
49469         
49470         if ((this.labelAlign != 'top')) {
49471             if (typeof this.labelWidth == 'number') {
49472                 labelWidth = this.labelWidth
49473             }
49474             this.padWidth =  20 + labelWidth;
49475             
49476         }
49477         
49478         Roo.form.Column.superclass.onRender.call(this, ct, position);
49479         if(this.width){
49480             this.el.setWidth(this.width);
49481         }
49482         if(this.height){
49483             this.el.setHeight(this.height);
49484         }
49485     },
49486     
49487     // private
49488     renderField : function(f){
49489         f.fieldEl = this.fieldTpl.append(this.el, [
49490                f.id, f.fieldLabel,
49491                f.labelStyle||this.labelStyle||'',
49492                this.elementStyle||'',
49493                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
49494                f.itemCls||this.itemCls||'',
49495                f.width ? f.width + this.padWidth : 160 + this.padWidth
49496        ],true);
49497     }
49498 });
49499  
49500
49501 /**
49502  * @class Roo.form.FieldSet
49503  * @extends Roo.form.Layout
49504  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
49505  * @constructor
49506  * @param {Object} config Configuration options
49507  */
49508 Roo.form.FieldSet = function(config){
49509     Roo.form.FieldSet.superclass.constructor.call(this, config);
49510 };
49511
49512 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
49513     /**
49514      * @cfg {String} legend
49515      * The text to display as the legend for the FieldSet (defaults to '')
49516      */
49517     /**
49518      * @cfg {String/Object} autoCreate
49519      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
49520      */
49521
49522     // private
49523     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
49524
49525     // private
49526     onRender : function(ct, position){
49527         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
49528         if(this.legend){
49529             this.setLegend(this.legend);
49530         }
49531     },
49532
49533     // private
49534     setLegend : function(text){
49535         if(this.rendered){
49536             this.el.child('legend').update(text);
49537         }
49538     }
49539 });/*
49540  * Based on:
49541  * Ext JS Library 1.1.1
49542  * Copyright(c) 2006-2007, Ext JS, LLC.
49543  *
49544  * Originally Released Under LGPL - original licence link has changed is not relivant.
49545  *
49546  * Fork - LGPL
49547  * <script type="text/javascript">
49548  */
49549 /**
49550  * @class Roo.form.VTypes
49551  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
49552  * @singleton
49553  */
49554 Roo.form.VTypes = function(){
49555     // closure these in so they are only created once.
49556     var alpha = /^[a-zA-Z_]+$/;
49557     var alphanum = /^[a-zA-Z0-9_]+$/;
49558     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
49559     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
49560
49561     // All these messages and functions are configurable
49562     return {
49563         /**
49564          * The function used to validate email addresses
49565          * @param {String} value The email address
49566          */
49567         'email' : function(v){
49568             return email.test(v);
49569         },
49570         /**
49571          * The error text to display when the email validation function returns false
49572          * @type String
49573          */
49574         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
49575         /**
49576          * The keystroke filter mask to be applied on email input
49577          * @type RegExp
49578          */
49579         'emailMask' : /[a-z0-9_\.\-@]/i,
49580
49581         /**
49582          * The function used to validate URLs
49583          * @param {String} value The URL
49584          */
49585         'url' : function(v){
49586             return url.test(v);
49587         },
49588         /**
49589          * The error text to display when the url validation function returns false
49590          * @type String
49591          */
49592         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
49593         
49594         /**
49595          * The function used to validate alpha values
49596          * @param {String} value The value
49597          */
49598         'alpha' : function(v){
49599             return alpha.test(v);
49600         },
49601         /**
49602          * The error text to display when the alpha validation function returns false
49603          * @type String
49604          */
49605         'alphaText' : 'This field should only contain letters and _',
49606         /**
49607          * The keystroke filter mask to be applied on alpha input
49608          * @type RegExp
49609          */
49610         'alphaMask' : /[a-z_]/i,
49611
49612         /**
49613          * The function used to validate alphanumeric values
49614          * @param {String} value The value
49615          */
49616         'alphanum' : function(v){
49617             return alphanum.test(v);
49618         },
49619         /**
49620          * The error text to display when the alphanumeric validation function returns false
49621          * @type String
49622          */
49623         'alphanumText' : 'This field should only contain letters, numbers and _',
49624         /**
49625          * The keystroke filter mask to be applied on alphanumeric input
49626          * @type RegExp
49627          */
49628         'alphanumMask' : /[a-z0-9_]/i
49629     };
49630 }();//<script type="text/javascript">
49631
49632 /**
49633  * @class Roo.form.FCKeditor
49634  * @extends Roo.form.TextArea
49635  * Wrapper around the FCKEditor http://www.fckeditor.net
49636  * @constructor
49637  * Creates a new FCKeditor
49638  * @param {Object} config Configuration options
49639  */
49640 Roo.form.FCKeditor = function(config){
49641     Roo.form.FCKeditor.superclass.constructor.call(this, config);
49642     this.addEvents({
49643          /**
49644          * @event editorinit
49645          * Fired when the editor is initialized - you can add extra handlers here..
49646          * @param {FCKeditor} this
49647          * @param {Object} the FCK object.
49648          */
49649         editorinit : true
49650     });
49651     
49652     
49653 };
49654 Roo.form.FCKeditor.editors = { };
49655 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
49656 {
49657     //defaultAutoCreate : {
49658     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
49659     //},
49660     // private
49661     /**
49662      * @cfg {Object} fck options - see fck manual for details.
49663      */
49664     fckconfig : false,
49665     
49666     /**
49667      * @cfg {Object} fck toolbar set (Basic or Default)
49668      */
49669     toolbarSet : 'Basic',
49670     /**
49671      * @cfg {Object} fck BasePath
49672      */ 
49673     basePath : '/fckeditor/',
49674     
49675     
49676     frame : false,
49677     
49678     value : '',
49679     
49680    
49681     onRender : function(ct, position)
49682     {
49683         if(!this.el){
49684             this.defaultAutoCreate = {
49685                 tag: "textarea",
49686                 style:"width:300px;height:60px;",
49687                 autocomplete: "new-password"
49688             };
49689         }
49690         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
49691         /*
49692         if(this.grow){
49693             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
49694             if(this.preventScrollbars){
49695                 this.el.setStyle("overflow", "hidden");
49696             }
49697             this.el.setHeight(this.growMin);
49698         }
49699         */
49700         //console.log('onrender' + this.getId() );
49701         Roo.form.FCKeditor.editors[this.getId()] = this;
49702          
49703
49704         this.replaceTextarea() ;
49705         
49706     },
49707     
49708     getEditor : function() {
49709         return this.fckEditor;
49710     },
49711     /**
49712      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
49713      * @param {Mixed} value The value to set
49714      */
49715     
49716     
49717     setValue : function(value)
49718     {
49719         //console.log('setValue: ' + value);
49720         
49721         if(typeof(value) == 'undefined') { // not sure why this is happending...
49722             return;
49723         }
49724         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49725         
49726         //if(!this.el || !this.getEditor()) {
49727         //    this.value = value;
49728             //this.setValue.defer(100,this,[value]);    
49729         //    return;
49730         //} 
49731         
49732         if(!this.getEditor()) {
49733             return;
49734         }
49735         
49736         this.getEditor().SetData(value);
49737         
49738         //
49739
49740     },
49741
49742     /**
49743      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
49744      * @return {Mixed} value The field value
49745      */
49746     getValue : function()
49747     {
49748         
49749         if (this.frame && this.frame.dom.style.display == 'none') {
49750             return Roo.form.FCKeditor.superclass.getValue.call(this);
49751         }
49752         
49753         if(!this.el || !this.getEditor()) {
49754            
49755            // this.getValue.defer(100,this); 
49756             return this.value;
49757         }
49758        
49759         
49760         var value=this.getEditor().GetData();
49761         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49762         return Roo.form.FCKeditor.superclass.getValue.call(this);
49763         
49764
49765     },
49766
49767     /**
49768      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
49769      * @return {Mixed} value The field value
49770      */
49771     getRawValue : function()
49772     {
49773         if (this.frame && this.frame.dom.style.display == 'none') {
49774             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49775         }
49776         
49777         if(!this.el || !this.getEditor()) {
49778             //this.getRawValue.defer(100,this); 
49779             return this.value;
49780             return;
49781         }
49782         
49783         
49784         
49785         var value=this.getEditor().GetData();
49786         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
49787         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49788          
49789     },
49790     
49791     setSize : function(w,h) {
49792         
49793         
49794         
49795         //if (this.frame && this.frame.dom.style.display == 'none') {
49796         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49797         //    return;
49798         //}
49799         //if(!this.el || !this.getEditor()) {
49800         //    this.setSize.defer(100,this, [w,h]); 
49801         //    return;
49802         //}
49803         
49804         
49805         
49806         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49807         
49808         this.frame.dom.setAttribute('width', w);
49809         this.frame.dom.setAttribute('height', h);
49810         this.frame.setSize(w,h);
49811         
49812     },
49813     
49814     toggleSourceEdit : function(value) {
49815         
49816       
49817          
49818         this.el.dom.style.display = value ? '' : 'none';
49819         this.frame.dom.style.display = value ?  'none' : '';
49820         
49821     },
49822     
49823     
49824     focus: function(tag)
49825     {
49826         if (this.frame.dom.style.display == 'none') {
49827             return Roo.form.FCKeditor.superclass.focus.call(this);
49828         }
49829         if(!this.el || !this.getEditor()) {
49830             this.focus.defer(100,this, [tag]); 
49831             return;
49832         }
49833         
49834         
49835         
49836         
49837         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
49838         this.getEditor().Focus();
49839         if (tgs.length) {
49840             if (!this.getEditor().Selection.GetSelection()) {
49841                 this.focus.defer(100,this, [tag]); 
49842                 return;
49843             }
49844             
49845             
49846             var r = this.getEditor().EditorDocument.createRange();
49847             r.setStart(tgs[0],0);
49848             r.setEnd(tgs[0],0);
49849             this.getEditor().Selection.GetSelection().removeAllRanges();
49850             this.getEditor().Selection.GetSelection().addRange(r);
49851             this.getEditor().Focus();
49852         }
49853         
49854     },
49855     
49856     
49857     
49858     replaceTextarea : function()
49859     {
49860         if ( document.getElementById( this.getId() + '___Frame' ) ) {
49861             return ;
49862         }
49863         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
49864         //{
49865             // We must check the elements firstly using the Id and then the name.
49866         var oTextarea = document.getElementById( this.getId() );
49867         
49868         var colElementsByName = document.getElementsByName( this.getId() ) ;
49869          
49870         oTextarea.style.display = 'none' ;
49871
49872         if ( oTextarea.tabIndex ) {            
49873             this.TabIndex = oTextarea.tabIndex ;
49874         }
49875         
49876         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
49877         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
49878         this.frame = Roo.get(this.getId() + '___Frame')
49879     },
49880     
49881     _getConfigHtml : function()
49882     {
49883         var sConfig = '' ;
49884
49885         for ( var o in this.fckconfig ) {
49886             sConfig += sConfig.length > 0  ? '&amp;' : '';
49887             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
49888         }
49889
49890         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
49891     },
49892     
49893     
49894     _getIFrameHtml : function()
49895     {
49896         var sFile = 'fckeditor.html' ;
49897         /* no idea what this is about..
49898         try
49899         {
49900             if ( (/fcksource=true/i).test( window.top.location.search ) )
49901                 sFile = 'fckeditor.original.html' ;
49902         }
49903         catch (e) { 
49904         */
49905
49906         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
49907         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
49908         
49909         
49910         var html = '<iframe id="' + this.getId() +
49911             '___Frame" src="' + sLink +
49912             '" width="' + this.width +
49913             '" height="' + this.height + '"' +
49914             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
49915             ' frameborder="0" scrolling="no"></iframe>' ;
49916
49917         return html ;
49918     },
49919     
49920     _insertHtmlBefore : function( html, element )
49921     {
49922         if ( element.insertAdjacentHTML )       {
49923             // IE
49924             element.insertAdjacentHTML( 'beforeBegin', html ) ;
49925         } else { // Gecko
49926             var oRange = document.createRange() ;
49927             oRange.setStartBefore( element ) ;
49928             var oFragment = oRange.createContextualFragment( html );
49929             element.parentNode.insertBefore( oFragment, element ) ;
49930         }
49931     }
49932     
49933     
49934   
49935     
49936     
49937     
49938     
49939
49940 });
49941
49942 //Roo.reg('fckeditor', Roo.form.FCKeditor);
49943
49944 function FCKeditor_OnComplete(editorInstance){
49945     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
49946     f.fckEditor = editorInstance;
49947     //console.log("loaded");
49948     f.fireEvent('editorinit', f, editorInstance);
49949
49950   
49951
49952  
49953
49954
49955
49956
49957
49958
49959
49960
49961
49962
49963
49964
49965
49966
49967
49968 //<script type="text/javascript">
49969 /**
49970  * @class Roo.form.GridField
49971  * @extends Roo.form.Field
49972  * Embed a grid (or editable grid into a form)
49973  * STATUS ALPHA
49974  * 
49975  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
49976  * it needs 
49977  * xgrid.store = Roo.data.Store
49978  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
49979  * xgrid.store.reader = Roo.data.JsonReader 
49980  * 
49981  * 
49982  * @constructor
49983  * Creates a new GridField
49984  * @param {Object} config Configuration options
49985  */
49986 Roo.form.GridField = function(config){
49987     Roo.form.GridField.superclass.constructor.call(this, config);
49988      
49989 };
49990
49991 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
49992     /**
49993      * @cfg {Number} width  - used to restrict width of grid..
49994      */
49995     width : 100,
49996     /**
49997      * @cfg {Number} height - used to restrict height of grid..
49998      */
49999     height : 50,
50000      /**
50001      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
50002          * 
50003          *}
50004      */
50005     xgrid : false, 
50006     /**
50007      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50008      * {tag: "input", type: "checkbox", autocomplete: "off"})
50009      */
50010    // defaultAutoCreate : { tag: 'div' },
50011     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
50012     /**
50013      * @cfg {String} addTitle Text to include for adding a title.
50014      */
50015     addTitle : false,
50016     //
50017     onResize : function(){
50018         Roo.form.Field.superclass.onResize.apply(this, arguments);
50019     },
50020
50021     initEvents : function(){
50022         // Roo.form.Checkbox.superclass.initEvents.call(this);
50023         // has no events...
50024        
50025     },
50026
50027
50028     getResizeEl : function(){
50029         return this.wrap;
50030     },
50031
50032     getPositionEl : function(){
50033         return this.wrap;
50034     },
50035
50036     // private
50037     onRender : function(ct, position){
50038         
50039         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
50040         var style = this.style;
50041         delete this.style;
50042         
50043         Roo.form.GridField.superclass.onRender.call(this, ct, position);
50044         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
50045         this.viewEl = this.wrap.createChild({ tag: 'div' });
50046         if (style) {
50047             this.viewEl.applyStyles(style);
50048         }
50049         if (this.width) {
50050             this.viewEl.setWidth(this.width);
50051         }
50052         if (this.height) {
50053             this.viewEl.setHeight(this.height);
50054         }
50055         //if(this.inputValue !== undefined){
50056         //this.setValue(this.value);
50057         
50058         
50059         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
50060         
50061         
50062         this.grid.render();
50063         this.grid.getDataSource().on('remove', this.refreshValue, this);
50064         this.grid.getDataSource().on('update', this.refreshValue, this);
50065         this.grid.on('afteredit', this.refreshValue, this);
50066  
50067     },
50068      
50069     
50070     /**
50071      * Sets the value of the item. 
50072      * @param {String} either an object  or a string..
50073      */
50074     setValue : function(v){
50075         //this.value = v;
50076         v = v || []; // empty set..
50077         // this does not seem smart - it really only affects memoryproxy grids..
50078         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
50079             var ds = this.grid.getDataSource();
50080             // assumes a json reader..
50081             var data = {}
50082             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
50083             ds.loadData( data);
50084         }
50085         // clear selection so it does not get stale.
50086         if (this.grid.sm) { 
50087             this.grid.sm.clearSelections();
50088         }
50089         
50090         Roo.form.GridField.superclass.setValue.call(this, v);
50091         this.refreshValue();
50092         // should load data in the grid really....
50093     },
50094     
50095     // private
50096     refreshValue: function() {
50097          var val = [];
50098         this.grid.getDataSource().each(function(r) {
50099             val.push(r.data);
50100         });
50101         this.el.dom.value = Roo.encode(val);
50102     }
50103     
50104      
50105     
50106     
50107 });/*
50108  * Based on:
50109  * Ext JS Library 1.1.1
50110  * Copyright(c) 2006-2007, Ext JS, LLC.
50111  *
50112  * Originally Released Under LGPL - original licence link has changed is not relivant.
50113  *
50114  * Fork - LGPL
50115  * <script type="text/javascript">
50116  */
50117 /**
50118  * @class Roo.form.DisplayField
50119  * @extends Roo.form.Field
50120  * A generic Field to display non-editable data.
50121  * @cfg {Boolean} closable (true|false) default false
50122  * @constructor
50123  * Creates a new Display Field item.
50124  * @param {Object} config Configuration options
50125  */
50126 Roo.form.DisplayField = function(config){
50127     Roo.form.DisplayField.superclass.constructor.call(this, config);
50128     
50129     this.addEvents({
50130         /**
50131          * @event close
50132          * Fires after the click the close btn
50133              * @param {Roo.form.DisplayField} this
50134              */
50135         close : true
50136     });
50137 };
50138
50139 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
50140     inputType:      'hidden',
50141     allowBlank:     true,
50142     readOnly:         true,
50143     
50144  
50145     /**
50146      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50147      */
50148     focusClass : undefined,
50149     /**
50150      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50151      */
50152     fieldClass: 'x-form-field',
50153     
50154      /**
50155      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
50156      */
50157     valueRenderer: undefined,
50158     
50159     width: 100,
50160     /**
50161      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50162      * {tag: "input", type: "checkbox", autocomplete: "off"})
50163      */
50164      
50165  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
50166  
50167     closable : false,
50168     
50169     onResize : function(){
50170         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
50171         
50172     },
50173
50174     initEvents : function(){
50175         // Roo.form.Checkbox.superclass.initEvents.call(this);
50176         // has no events...
50177         
50178         if(this.closable){
50179             this.closeEl.on('click', this.onClose, this);
50180         }
50181        
50182     },
50183
50184
50185     getResizeEl : function(){
50186         return this.wrap;
50187     },
50188
50189     getPositionEl : function(){
50190         return this.wrap;
50191     },
50192
50193     // private
50194     onRender : function(ct, position){
50195         
50196         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
50197         //if(this.inputValue !== undefined){
50198         this.wrap = this.el.wrap();
50199         
50200         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
50201         
50202         if(this.closable){
50203             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
50204         }
50205         
50206         if (this.bodyStyle) {
50207             this.viewEl.applyStyles(this.bodyStyle);
50208         }
50209         //this.viewEl.setStyle('padding', '2px');
50210         
50211         this.setValue(this.value);
50212         
50213     },
50214 /*
50215     // private
50216     initValue : Roo.emptyFn,
50217
50218   */
50219
50220         // private
50221     onClick : function(){
50222         
50223     },
50224
50225     /**
50226      * Sets the checked state of the checkbox.
50227      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
50228      */
50229     setValue : function(v){
50230         this.value = v;
50231         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
50232         // this might be called before we have a dom element..
50233         if (!this.viewEl) {
50234             return;
50235         }
50236         this.viewEl.dom.innerHTML = html;
50237         Roo.form.DisplayField.superclass.setValue.call(this, v);
50238
50239     },
50240     
50241     onClose : function(e)
50242     {
50243         e.preventDefault();
50244         
50245         this.fireEvent('close', this);
50246     }
50247 });/*
50248  * 
50249  * Licence- LGPL
50250  * 
50251  */
50252
50253 /**
50254  * @class Roo.form.DayPicker
50255  * @extends Roo.form.Field
50256  * A Day picker show [M] [T] [W] ....
50257  * @constructor
50258  * Creates a new Day Picker
50259  * @param {Object} config Configuration options
50260  */
50261 Roo.form.DayPicker= function(config){
50262     Roo.form.DayPicker.superclass.constructor.call(this, config);
50263      
50264 };
50265
50266 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
50267     /**
50268      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50269      */
50270     focusClass : undefined,
50271     /**
50272      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50273      */
50274     fieldClass: "x-form-field",
50275    
50276     /**
50277      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50278      * {tag: "input", type: "checkbox", autocomplete: "off"})
50279      */
50280     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
50281     
50282    
50283     actionMode : 'viewEl', 
50284     //
50285     // private
50286  
50287     inputType : 'hidden',
50288     
50289      
50290     inputElement: false, // real input element?
50291     basedOn: false, // ????
50292     
50293     isFormField: true, // not sure where this is needed!!!!
50294
50295     onResize : function(){
50296         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
50297         if(!this.boxLabel){
50298             this.el.alignTo(this.wrap, 'c-c');
50299         }
50300     },
50301
50302     initEvents : function(){
50303         Roo.form.Checkbox.superclass.initEvents.call(this);
50304         this.el.on("click", this.onClick,  this);
50305         this.el.on("change", this.onClick,  this);
50306     },
50307
50308
50309     getResizeEl : function(){
50310         return this.wrap;
50311     },
50312
50313     getPositionEl : function(){
50314         return this.wrap;
50315     },
50316
50317     
50318     // private
50319     onRender : function(ct, position){
50320         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
50321        
50322         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
50323         
50324         var r1 = '<table><tr>';
50325         var r2 = '<tr class="x-form-daypick-icons">';
50326         for (var i=0; i < 7; i++) {
50327             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
50328             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
50329         }
50330         
50331         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
50332         viewEl.select('img').on('click', this.onClick, this);
50333         this.viewEl = viewEl;   
50334         
50335         
50336         // this will not work on Chrome!!!
50337         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
50338         this.el.on('propertychange', this.setFromHidden,  this);  //ie
50339         
50340         
50341           
50342
50343     },
50344
50345     // private
50346     initValue : Roo.emptyFn,
50347
50348     /**
50349      * Returns the checked state of the checkbox.
50350      * @return {Boolean} True if checked, else false
50351      */
50352     getValue : function(){
50353         return this.el.dom.value;
50354         
50355     },
50356
50357         // private
50358     onClick : function(e){ 
50359         //this.setChecked(!this.checked);
50360         Roo.get(e.target).toggleClass('x-menu-item-checked');
50361         this.refreshValue();
50362         //if(this.el.dom.checked != this.checked){
50363         //    this.setValue(this.el.dom.checked);
50364        // }
50365     },
50366     
50367     // private
50368     refreshValue : function()
50369     {
50370         var val = '';
50371         this.viewEl.select('img',true).each(function(e,i,n)  {
50372             val += e.is(".x-menu-item-checked") ? String(n) : '';
50373         });
50374         this.setValue(val, true);
50375     },
50376
50377     /**
50378      * Sets the checked state of the checkbox.
50379      * On is always based on a string comparison between inputValue and the param.
50380      * @param {Boolean/String} value - the value to set 
50381      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
50382      */
50383     setValue : function(v,suppressEvent){
50384         if (!this.el.dom) {
50385             return;
50386         }
50387         var old = this.el.dom.value ;
50388         this.el.dom.value = v;
50389         if (suppressEvent) {
50390             return ;
50391         }
50392          
50393         // update display..
50394         this.viewEl.select('img',true).each(function(e,i,n)  {
50395             
50396             var on = e.is(".x-menu-item-checked");
50397             var newv = v.indexOf(String(n)) > -1;
50398             if (on != newv) {
50399                 e.toggleClass('x-menu-item-checked');
50400             }
50401             
50402         });
50403         
50404         
50405         this.fireEvent('change', this, v, old);
50406         
50407         
50408     },
50409    
50410     // handle setting of hidden value by some other method!!?!?
50411     setFromHidden: function()
50412     {
50413         if(!this.el){
50414             return;
50415         }
50416         //console.log("SET FROM HIDDEN");
50417         //alert('setFrom hidden');
50418         this.setValue(this.el.dom.value);
50419     },
50420     
50421     onDestroy : function()
50422     {
50423         if(this.viewEl){
50424             Roo.get(this.viewEl).remove();
50425         }
50426          
50427         Roo.form.DayPicker.superclass.onDestroy.call(this);
50428     }
50429
50430 });/*
50431  * RooJS Library 1.1.1
50432  * Copyright(c) 2008-2011  Alan Knowles
50433  *
50434  * License - LGPL
50435  */
50436  
50437
50438 /**
50439  * @class Roo.form.ComboCheck
50440  * @extends Roo.form.ComboBox
50441  * A combobox for multiple select items.
50442  *
50443  * FIXME - could do with a reset button..
50444  * 
50445  * @constructor
50446  * Create a new ComboCheck
50447  * @param {Object} config Configuration options
50448  */
50449 Roo.form.ComboCheck = function(config){
50450     Roo.form.ComboCheck.superclass.constructor.call(this, config);
50451     // should verify some data...
50452     // like
50453     // hiddenName = required..
50454     // displayField = required
50455     // valudField == required
50456     var req= [ 'hiddenName', 'displayField', 'valueField' ];
50457     var _t = this;
50458     Roo.each(req, function(e) {
50459         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
50460             throw "Roo.form.ComboCheck : missing value for: " + e;
50461         }
50462     });
50463     
50464     
50465 };
50466
50467 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
50468      
50469      
50470     editable : false,
50471      
50472     selectedClass: 'x-menu-item-checked', 
50473     
50474     // private
50475     onRender : function(ct, position){
50476         var _t = this;
50477         
50478         
50479         
50480         if(!this.tpl){
50481             var cls = 'x-combo-list';
50482
50483             
50484             this.tpl =  new Roo.Template({
50485                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
50486                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
50487                    '<span>{' + this.displayField + '}</span>' +
50488                     '</div>' 
50489                 
50490             });
50491         }
50492  
50493         
50494         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
50495         this.view.singleSelect = false;
50496         this.view.multiSelect = true;
50497         this.view.toggleSelect = true;
50498         this.pageTb.add(new Roo.Toolbar.Fill(), {
50499             
50500             text: 'Done',
50501             handler: function()
50502             {
50503                 _t.collapse();
50504             }
50505         });
50506     },
50507     
50508     onViewOver : function(e, t){
50509         // do nothing...
50510         return;
50511         
50512     },
50513     
50514     onViewClick : function(doFocus,index){
50515         return;
50516         
50517     },
50518     select: function () {
50519         //Roo.log("SELECT CALLED");
50520     },
50521      
50522     selectByValue : function(xv, scrollIntoView){
50523         var ar = this.getValueArray();
50524         var sels = [];
50525         
50526         Roo.each(ar, function(v) {
50527             if(v === undefined || v === null){
50528                 return;
50529             }
50530             var r = this.findRecord(this.valueField, v);
50531             if(r){
50532                 sels.push(this.store.indexOf(r))
50533                 
50534             }
50535         },this);
50536         this.view.select(sels);
50537         return false;
50538     },
50539     
50540     
50541     
50542     onSelect : function(record, index){
50543        // Roo.log("onselect Called");
50544        // this is only called by the clear button now..
50545         this.view.clearSelections();
50546         this.setValue('[]');
50547         if (this.value != this.valueBefore) {
50548             this.fireEvent('change', this, this.value, this.valueBefore);
50549             this.valueBefore = this.value;
50550         }
50551     },
50552     getValueArray : function()
50553     {
50554         var ar = [] ;
50555         
50556         try {
50557             //Roo.log(this.value);
50558             if (typeof(this.value) == 'undefined') {
50559                 return [];
50560             }
50561             var ar = Roo.decode(this.value);
50562             return  ar instanceof Array ? ar : []; //?? valid?
50563             
50564         } catch(e) {
50565             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
50566             return [];
50567         }
50568          
50569     },
50570     expand : function ()
50571     {
50572         
50573         Roo.form.ComboCheck.superclass.expand.call(this);
50574         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
50575         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
50576         
50577
50578     },
50579     
50580     collapse : function(){
50581         Roo.form.ComboCheck.superclass.collapse.call(this);
50582         var sl = this.view.getSelectedIndexes();
50583         var st = this.store;
50584         var nv = [];
50585         var tv = [];
50586         var r;
50587         Roo.each(sl, function(i) {
50588             r = st.getAt(i);
50589             nv.push(r.get(this.valueField));
50590         },this);
50591         this.setValue(Roo.encode(nv));
50592         if (this.value != this.valueBefore) {
50593
50594             this.fireEvent('change', this, this.value, this.valueBefore);
50595             this.valueBefore = this.value;
50596         }
50597         
50598     },
50599     
50600     setValue : function(v){
50601         // Roo.log(v);
50602         this.value = v;
50603         
50604         var vals = this.getValueArray();
50605         var tv = [];
50606         Roo.each(vals, function(k) {
50607             var r = this.findRecord(this.valueField, k);
50608             if(r){
50609                 tv.push(r.data[this.displayField]);
50610             }else if(this.valueNotFoundText !== undefined){
50611                 tv.push( this.valueNotFoundText );
50612             }
50613         },this);
50614        // Roo.log(tv);
50615         
50616         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
50617         this.hiddenField.value = v;
50618         this.value = v;
50619     }
50620     
50621 });/*
50622  * Based on:
50623  * Ext JS Library 1.1.1
50624  * Copyright(c) 2006-2007, Ext JS, LLC.
50625  *
50626  * Originally Released Under LGPL - original licence link has changed is not relivant.
50627  *
50628  * Fork - LGPL
50629  * <script type="text/javascript">
50630  */
50631  
50632 /**
50633  * @class Roo.form.Signature
50634  * @extends Roo.form.Field
50635  * Signature field.  
50636  * @constructor
50637  * 
50638  * @param {Object} config Configuration options
50639  */
50640
50641 Roo.form.Signature = function(config){
50642     Roo.form.Signature.superclass.constructor.call(this, config);
50643     
50644     this.addEvents({// not in used??
50645          /**
50646          * @event confirm
50647          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
50648              * @param {Roo.form.Signature} combo This combo box
50649              */
50650         'confirm' : true,
50651         /**
50652          * @event reset
50653          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
50654              * @param {Roo.form.ComboBox} combo This combo box
50655              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
50656              */
50657         'reset' : true
50658     });
50659 };
50660
50661 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
50662     /**
50663      * @cfg {Object} labels Label to use when rendering a form.
50664      * defaults to 
50665      * labels : { 
50666      *      clear : "Clear",
50667      *      confirm : "Confirm"
50668      *  }
50669      */
50670     labels : { 
50671         clear : "Clear",
50672         confirm : "Confirm"
50673     },
50674     /**
50675      * @cfg {Number} width The signature panel width (defaults to 300)
50676      */
50677     width: 300,
50678     /**
50679      * @cfg {Number} height The signature panel height (defaults to 100)
50680      */
50681     height : 100,
50682     /**
50683      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
50684      */
50685     allowBlank : false,
50686     
50687     //private
50688     // {Object} signPanel The signature SVG panel element (defaults to {})
50689     signPanel : {},
50690     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
50691     isMouseDown : false,
50692     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
50693     isConfirmed : false,
50694     // {String} signatureTmp SVG mapping string (defaults to empty string)
50695     signatureTmp : '',
50696     
50697     
50698     defaultAutoCreate : { // modified by initCompnoent..
50699         tag: "input",
50700         type:"hidden"
50701     },
50702
50703     // private
50704     onRender : function(ct, position){
50705         
50706         Roo.form.Signature.superclass.onRender.call(this, ct, position);
50707         
50708         this.wrap = this.el.wrap({
50709             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
50710         });
50711         
50712         this.createToolbar(this);
50713         this.signPanel = this.wrap.createChild({
50714                 tag: 'div',
50715                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
50716             }, this.el
50717         );
50718             
50719         this.svgID = Roo.id();
50720         this.svgEl = this.signPanel.createChild({
50721               xmlns : 'http://www.w3.org/2000/svg',
50722               tag : 'svg',
50723               id : this.svgID + "-svg",
50724               width: this.width,
50725               height: this.height,
50726               viewBox: '0 0 '+this.width+' '+this.height,
50727               cn : [
50728                 {
50729                     tag: "rect",
50730                     id: this.svgID + "-svg-r",
50731                     width: this.width,
50732                     height: this.height,
50733                     fill: "#ffa"
50734                 },
50735                 {
50736                     tag: "line",
50737                     id: this.svgID + "-svg-l",
50738                     x1: "0", // start
50739                     y1: (this.height*0.8), // start set the line in 80% of height
50740                     x2: this.width, // end
50741                     y2: (this.height*0.8), // end set the line in 80% of height
50742                     'stroke': "#666",
50743                     'stroke-width': "1",
50744                     'stroke-dasharray': "3",
50745                     'shape-rendering': "crispEdges",
50746                     'pointer-events': "none"
50747                 },
50748                 {
50749                     tag: "path",
50750                     id: this.svgID + "-svg-p",
50751                     'stroke': "navy",
50752                     'stroke-width': "3",
50753                     'fill': "none",
50754                     'pointer-events': 'none'
50755                 }
50756               ]
50757         });
50758         this.createSVG();
50759         this.svgBox = this.svgEl.dom.getScreenCTM();
50760     },
50761     createSVG : function(){ 
50762         var svg = this.signPanel;
50763         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
50764         var t = this;
50765
50766         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
50767         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
50768         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
50769         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
50770         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
50771         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
50772         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
50773         
50774     },
50775     isTouchEvent : function(e){
50776         return e.type.match(/^touch/);
50777     },
50778     getCoords : function (e) {
50779         var pt    = this.svgEl.dom.createSVGPoint();
50780         pt.x = e.clientX; 
50781         pt.y = e.clientY;
50782         if (this.isTouchEvent(e)) {
50783             pt.x =  e.targetTouches[0].clientX;
50784             pt.y = e.targetTouches[0].clientY;
50785         }
50786         var a = this.svgEl.dom.getScreenCTM();
50787         var b = a.inverse();
50788         var mx = pt.matrixTransform(b);
50789         return mx.x + ',' + mx.y;
50790     },
50791     //mouse event headler 
50792     down : function (e) {
50793         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
50794         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
50795         
50796         this.isMouseDown = true;
50797         
50798         e.preventDefault();
50799     },
50800     move : function (e) {
50801         if (this.isMouseDown) {
50802             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
50803             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
50804         }
50805         
50806         e.preventDefault();
50807     },
50808     up : function (e) {
50809         this.isMouseDown = false;
50810         var sp = this.signatureTmp.split(' ');
50811         
50812         if(sp.length > 1){
50813             if(!sp[sp.length-2].match(/^L/)){
50814                 sp.pop();
50815                 sp.pop();
50816                 sp.push("");
50817                 this.signatureTmp = sp.join(" ");
50818             }
50819         }
50820         if(this.getValue() != this.signatureTmp){
50821             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50822             this.isConfirmed = false;
50823         }
50824         e.preventDefault();
50825     },
50826     
50827     /**
50828      * Protected method that will not generally be called directly. It
50829      * is called when the editor creates its toolbar. Override this method if you need to
50830      * add custom toolbar buttons.
50831      * @param {HtmlEditor} editor
50832      */
50833     createToolbar : function(editor){
50834          function btn(id, toggle, handler){
50835             var xid = fid + '-'+ id ;
50836             return {
50837                 id : xid,
50838                 cmd : id,
50839                 cls : 'x-btn-icon x-edit-'+id,
50840                 enableToggle:toggle !== false,
50841                 scope: editor, // was editor...
50842                 handler:handler||editor.relayBtnCmd,
50843                 clickEvent:'mousedown',
50844                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50845                 tabIndex:-1
50846             };
50847         }
50848         
50849         
50850         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50851         this.tb = tb;
50852         this.tb.add(
50853            {
50854                 cls : ' x-signature-btn x-signature-'+id,
50855                 scope: editor, // was editor...
50856                 handler: this.reset,
50857                 clickEvent:'mousedown',
50858                 text: this.labels.clear
50859             },
50860             {
50861                  xtype : 'Fill',
50862                  xns: Roo.Toolbar
50863             }, 
50864             {
50865                 cls : '  x-signature-btn x-signature-'+id,
50866                 scope: editor, // was editor...
50867                 handler: this.confirmHandler,
50868                 clickEvent:'mousedown',
50869                 text: this.labels.confirm
50870             }
50871         );
50872     
50873     },
50874     //public
50875     /**
50876      * when user is clicked confirm then show this image.....
50877      * 
50878      * @return {String} Image Data URI
50879      */
50880     getImageDataURI : function(){
50881         var svg = this.svgEl.dom.parentNode.innerHTML;
50882         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
50883         return src; 
50884     },
50885     /**
50886      * 
50887      * @return {Boolean} this.isConfirmed
50888      */
50889     getConfirmed : function(){
50890         return this.isConfirmed;
50891     },
50892     /**
50893      * 
50894      * @return {Number} this.width
50895      */
50896     getWidth : function(){
50897         return this.width;
50898     },
50899     /**
50900      * 
50901      * @return {Number} this.height
50902      */
50903     getHeight : function(){
50904         return this.height;
50905     },
50906     // private
50907     getSignature : function(){
50908         return this.signatureTmp;
50909     },
50910     // private
50911     reset : function(){
50912         this.signatureTmp = '';
50913         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50914         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
50915         this.isConfirmed = false;
50916         Roo.form.Signature.superclass.reset.call(this);
50917     },
50918     setSignature : function(s){
50919         this.signatureTmp = s;
50920         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50921         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
50922         this.setValue(s);
50923         this.isConfirmed = false;
50924         Roo.form.Signature.superclass.reset.call(this);
50925     }, 
50926     test : function(){
50927 //        Roo.log(this.signPanel.dom.contentWindow.up())
50928     },
50929     //private
50930     setConfirmed : function(){
50931         
50932         
50933         
50934 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
50935     },
50936     // private
50937     confirmHandler : function(){
50938         if(!this.getSignature()){
50939             return;
50940         }
50941         
50942         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
50943         this.setValue(this.getSignature());
50944         this.isConfirmed = true;
50945         
50946         this.fireEvent('confirm', this);
50947     },
50948     // private
50949     // Subclasses should provide the validation implementation by overriding this
50950     validateValue : function(value){
50951         if(this.allowBlank){
50952             return true;
50953         }
50954         
50955         if(this.isConfirmed){
50956             return true;
50957         }
50958         return false;
50959     }
50960 });/*
50961  * Based on:
50962  * Ext JS Library 1.1.1
50963  * Copyright(c) 2006-2007, Ext JS, LLC.
50964  *
50965  * Originally Released Under LGPL - original licence link has changed is not relivant.
50966  *
50967  * Fork - LGPL
50968  * <script type="text/javascript">
50969  */
50970  
50971
50972 /**
50973  * @class Roo.form.ComboBox
50974  * @extends Roo.form.TriggerField
50975  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
50976  * @constructor
50977  * Create a new ComboBox.
50978  * @param {Object} config Configuration options
50979  */
50980 Roo.form.Select = function(config){
50981     Roo.form.Select.superclass.constructor.call(this, config);
50982      
50983 };
50984
50985 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
50986     /**
50987      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
50988      */
50989     /**
50990      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
50991      * rendering into an Roo.Editor, defaults to false)
50992      */
50993     /**
50994      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
50995      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
50996      */
50997     /**
50998      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
50999      */
51000     /**
51001      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
51002      * the dropdown list (defaults to undefined, with no header element)
51003      */
51004
51005      /**
51006      * @cfg {String/Roo.Template} tpl The template to use to render the output
51007      */
51008      
51009     // private
51010     defaultAutoCreate : {tag: "select"  },
51011     /**
51012      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
51013      */
51014     listWidth: undefined,
51015     /**
51016      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
51017      * mode = 'remote' or 'text' if mode = 'local')
51018      */
51019     displayField: undefined,
51020     /**
51021      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
51022      * mode = 'remote' or 'value' if mode = 'local'). 
51023      * Note: use of a valueField requires the user make a selection
51024      * in order for a value to be mapped.
51025      */
51026     valueField: undefined,
51027     
51028     
51029     /**
51030      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
51031      * field's data value (defaults to the underlying DOM element's name)
51032      */
51033     hiddenName: undefined,
51034     /**
51035      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
51036      */
51037     listClass: '',
51038     /**
51039      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
51040      */
51041     selectedClass: 'x-combo-selected',
51042     /**
51043      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
51044      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
51045      * which displays a downward arrow icon).
51046      */
51047     triggerClass : 'x-form-arrow-trigger',
51048     /**
51049      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
51050      */
51051     shadow:'sides',
51052     /**
51053      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
51054      * anchor positions (defaults to 'tl-bl')
51055      */
51056     listAlign: 'tl-bl?',
51057     /**
51058      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
51059      */
51060     maxHeight: 300,
51061     /**
51062      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
51063      * query specified by the allQuery config option (defaults to 'query')
51064      */
51065     triggerAction: 'query',
51066     /**
51067      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
51068      * (defaults to 4, does not apply if editable = false)
51069      */
51070     minChars : 4,
51071     /**
51072      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
51073      * delay (typeAheadDelay) if it matches a known value (defaults to false)
51074      */
51075     typeAhead: false,
51076     /**
51077      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
51078      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
51079      */
51080     queryDelay: 500,
51081     /**
51082      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
51083      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
51084      */
51085     pageSize: 0,
51086     /**
51087      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
51088      * when editable = true (defaults to false)
51089      */
51090     selectOnFocus:false,
51091     /**
51092      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
51093      */
51094     queryParam: 'query',
51095     /**
51096      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
51097      * when mode = 'remote' (defaults to 'Loading...')
51098      */
51099     loadingText: 'Loading...',
51100     /**
51101      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
51102      */
51103     resizable: false,
51104     /**
51105      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
51106      */
51107     handleHeight : 8,
51108     /**
51109      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
51110      * traditional select (defaults to true)
51111      */
51112     editable: true,
51113     /**
51114      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
51115      */
51116     allQuery: '',
51117     /**
51118      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
51119      */
51120     mode: 'remote',
51121     /**
51122      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
51123      * listWidth has a higher value)
51124      */
51125     minListWidth : 70,
51126     /**
51127      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
51128      * allow the user to set arbitrary text into the field (defaults to false)
51129      */
51130     forceSelection:false,
51131     /**
51132      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
51133      * if typeAhead = true (defaults to 250)
51134      */
51135     typeAheadDelay : 250,
51136     /**
51137      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
51138      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
51139      */
51140     valueNotFoundText : undefined,
51141     
51142     /**
51143      * @cfg {String} defaultValue The value displayed after loading the store.
51144      */
51145     defaultValue: '',
51146     
51147     /**
51148      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
51149      */
51150     blockFocus : false,
51151     
51152     /**
51153      * @cfg {Boolean} disableClear Disable showing of clear button.
51154      */
51155     disableClear : false,
51156     /**
51157      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
51158      */
51159     alwaysQuery : false,
51160     
51161     //private
51162     addicon : false,
51163     editicon: false,
51164     
51165     // element that contains real text value.. (when hidden is used..)
51166      
51167     // private
51168     onRender : function(ct, position){
51169         Roo.form.Field.prototype.onRender.call(this, ct, position);
51170         
51171         if(this.store){
51172             this.store.on('beforeload', this.onBeforeLoad, this);
51173             this.store.on('load', this.onLoad, this);
51174             this.store.on('loadexception', this.onLoadException, this);
51175             this.store.load({});
51176         }
51177         
51178         
51179         
51180     },
51181
51182     // private
51183     initEvents : function(){
51184         //Roo.form.ComboBox.superclass.initEvents.call(this);
51185  
51186     },
51187
51188     onDestroy : function(){
51189        
51190         if(this.store){
51191             this.store.un('beforeload', this.onBeforeLoad, this);
51192             this.store.un('load', this.onLoad, this);
51193             this.store.un('loadexception', this.onLoadException, this);
51194         }
51195         //Roo.form.ComboBox.superclass.onDestroy.call(this);
51196     },
51197
51198     // private
51199     fireKey : function(e){
51200         if(e.isNavKeyPress() && !this.list.isVisible()){
51201             this.fireEvent("specialkey", this, e);
51202         }
51203     },
51204
51205     // private
51206     onResize: function(w, h){
51207         
51208         return; 
51209     
51210         
51211     },
51212
51213     /**
51214      * Allow or prevent the user from directly editing the field text.  If false is passed,
51215      * the user will only be able to select from the items defined in the dropdown list.  This method
51216      * is the runtime equivalent of setting the 'editable' config option at config time.
51217      * @param {Boolean} value True to allow the user to directly edit the field text
51218      */
51219     setEditable : function(value){
51220          
51221     },
51222
51223     // private
51224     onBeforeLoad : function(){
51225         
51226         Roo.log("Select before load");
51227         return;
51228     
51229         this.innerList.update(this.loadingText ?
51230                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
51231         //this.restrictHeight();
51232         this.selectedIndex = -1;
51233     },
51234
51235     // private
51236     onLoad : function(){
51237
51238     
51239         var dom = this.el.dom;
51240         dom.innerHTML = '';
51241          var od = dom.ownerDocument;
51242          
51243         if (this.emptyText) {
51244             var op = od.createElement('option');
51245             op.setAttribute('value', '');
51246             op.innerHTML = String.format('{0}', this.emptyText);
51247             dom.appendChild(op);
51248         }
51249         if(this.store.getCount() > 0){
51250            
51251             var vf = this.valueField;
51252             var df = this.displayField;
51253             this.store.data.each(function(r) {
51254                 // which colmsn to use... testing - cdoe / title..
51255                 var op = od.createElement('option');
51256                 op.setAttribute('value', r.data[vf]);
51257                 op.innerHTML = String.format('{0}', r.data[df]);
51258                 dom.appendChild(op);
51259             });
51260             if (typeof(this.defaultValue != 'undefined')) {
51261                 this.setValue(this.defaultValue);
51262             }
51263             
51264              
51265         }else{
51266             //this.onEmptyResults();
51267         }
51268         //this.el.focus();
51269     },
51270     // private
51271     onLoadException : function()
51272     {
51273         dom.innerHTML = '';
51274             
51275         Roo.log("Select on load exception");
51276         return;
51277     
51278         this.collapse();
51279         Roo.log(this.store.reader.jsonData);
51280         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
51281             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
51282         }
51283         
51284         
51285     },
51286     // private
51287     onTypeAhead : function(){
51288          
51289     },
51290
51291     // private
51292     onSelect : function(record, index){
51293         Roo.log('on select?');
51294         return;
51295         if(this.fireEvent('beforeselect', this, record, index) !== false){
51296             this.setFromData(index > -1 ? record.data : false);
51297             this.collapse();
51298             this.fireEvent('select', this, record, index);
51299         }
51300     },
51301
51302     /**
51303      * Returns the currently selected field value or empty string if no value is set.
51304      * @return {String} value The selected value
51305      */
51306     getValue : function(){
51307         var dom = this.el.dom;
51308         this.value = dom.options[dom.selectedIndex].value;
51309         return this.value;
51310         
51311     },
51312
51313     /**
51314      * Clears any text/value currently set in the field
51315      */
51316     clearValue : function(){
51317         this.value = '';
51318         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
51319         
51320     },
51321
51322     /**
51323      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
51324      * will be displayed in the field.  If the value does not match the data value of an existing item,
51325      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
51326      * Otherwise the field will be blank (although the value will still be set).
51327      * @param {String} value The value to match
51328      */
51329     setValue : function(v){
51330         var d = this.el.dom;
51331         for (var i =0; i < d.options.length;i++) {
51332             if (v == d.options[i].value) {
51333                 d.selectedIndex = i;
51334                 this.value = v;
51335                 return;
51336             }
51337         }
51338         this.clearValue();
51339     },
51340     /**
51341      * @property {Object} the last set data for the element
51342      */
51343     
51344     lastData : false,
51345     /**
51346      * Sets the value of the field based on a object which is related to the record format for the store.
51347      * @param {Object} value the value to set as. or false on reset?
51348      */
51349     setFromData : function(o){
51350         Roo.log('setfrom data?');
51351          
51352         
51353         
51354     },
51355     // private
51356     reset : function(){
51357         this.clearValue();
51358     },
51359     // private
51360     findRecord : function(prop, value){
51361         
51362         return false;
51363     
51364         var record;
51365         if(this.store.getCount() > 0){
51366             this.store.each(function(r){
51367                 if(r.data[prop] == value){
51368                     record = r;
51369                     return false;
51370                 }
51371                 return true;
51372             });
51373         }
51374         return record;
51375     },
51376     
51377     getName: function()
51378     {
51379         // returns hidden if it's set..
51380         if (!this.rendered) {return ''};
51381         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
51382         
51383     },
51384      
51385
51386     
51387
51388     // private
51389     onEmptyResults : function(){
51390         Roo.log('empty results');
51391         //this.collapse();
51392     },
51393
51394     /**
51395      * Returns true if the dropdown list is expanded, else false.
51396      */
51397     isExpanded : function(){
51398         return false;
51399     },
51400
51401     /**
51402      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
51403      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51404      * @param {String} value The data value of the item to select
51405      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51406      * selected item if it is not currently in view (defaults to true)
51407      * @return {Boolean} True if the value matched an item in the list, else false
51408      */
51409     selectByValue : function(v, scrollIntoView){
51410         Roo.log('select By Value');
51411         return false;
51412     
51413         if(v !== undefined && v !== null){
51414             var r = this.findRecord(this.valueField || this.displayField, v);
51415             if(r){
51416                 this.select(this.store.indexOf(r), scrollIntoView);
51417                 return true;
51418             }
51419         }
51420         return false;
51421     },
51422
51423     /**
51424      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
51425      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51426      * @param {Number} index The zero-based index of the list item to select
51427      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51428      * selected item if it is not currently in view (defaults to true)
51429      */
51430     select : function(index, scrollIntoView){
51431         Roo.log('select ');
51432         return  ;
51433         
51434         this.selectedIndex = index;
51435         this.view.select(index);
51436         if(scrollIntoView !== false){
51437             var el = this.view.getNode(index);
51438             if(el){
51439                 this.innerList.scrollChildIntoView(el, false);
51440             }
51441         }
51442     },
51443
51444       
51445
51446     // private
51447     validateBlur : function(){
51448         
51449         return;
51450         
51451     },
51452
51453     // private
51454     initQuery : function(){
51455         this.doQuery(this.getRawValue());
51456     },
51457
51458     // private
51459     doForce : function(){
51460         if(this.el.dom.value.length > 0){
51461             this.el.dom.value =
51462                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
51463              
51464         }
51465     },
51466
51467     /**
51468      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
51469      * query allowing the query action to be canceled if needed.
51470      * @param {String} query The SQL query to execute
51471      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
51472      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
51473      * saved in the current store (defaults to false)
51474      */
51475     doQuery : function(q, forceAll){
51476         
51477         Roo.log('doQuery?');
51478         if(q === undefined || q === null){
51479             q = '';
51480         }
51481         var qe = {
51482             query: q,
51483             forceAll: forceAll,
51484             combo: this,
51485             cancel:false
51486         };
51487         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
51488             return false;
51489         }
51490         q = qe.query;
51491         forceAll = qe.forceAll;
51492         if(forceAll === true || (q.length >= this.minChars)){
51493             if(this.lastQuery != q || this.alwaysQuery){
51494                 this.lastQuery = q;
51495                 if(this.mode == 'local'){
51496                     this.selectedIndex = -1;
51497                     if(forceAll){
51498                         this.store.clearFilter();
51499                     }else{
51500                         this.store.filter(this.displayField, q);
51501                     }
51502                     this.onLoad();
51503                 }else{
51504                     this.store.baseParams[this.queryParam] = q;
51505                     this.store.load({
51506                         params: this.getParams(q)
51507                     });
51508                     this.expand();
51509                 }
51510             }else{
51511                 this.selectedIndex = -1;
51512                 this.onLoad();   
51513             }
51514         }
51515     },
51516
51517     // private
51518     getParams : function(q){
51519         var p = {};
51520         //p[this.queryParam] = q;
51521         if(this.pageSize){
51522             p.start = 0;
51523             p.limit = this.pageSize;
51524         }
51525         return p;
51526     },
51527
51528     /**
51529      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
51530      */
51531     collapse : function(){
51532         
51533     },
51534
51535     // private
51536     collapseIf : function(e){
51537         
51538     },
51539
51540     /**
51541      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
51542      */
51543     expand : function(){
51544         
51545     } ,
51546
51547     // private
51548      
51549
51550     /** 
51551     * @cfg {Boolean} grow 
51552     * @hide 
51553     */
51554     /** 
51555     * @cfg {Number} growMin 
51556     * @hide 
51557     */
51558     /** 
51559     * @cfg {Number} growMax 
51560     * @hide 
51561     */
51562     /**
51563      * @hide
51564      * @method autoSize
51565      */
51566     
51567     setWidth : function()
51568     {
51569         
51570     },
51571     getResizeEl : function(){
51572         return this.el;
51573     }
51574 });//<script type="text/javasscript">
51575  
51576
51577 /**
51578  * @class Roo.DDView
51579  * A DnD enabled version of Roo.View.
51580  * @param {Element/String} container The Element in which to create the View.
51581  * @param {String} tpl The template string used to create the markup for each element of the View
51582  * @param {Object} config The configuration properties. These include all the config options of
51583  * {@link Roo.View} plus some specific to this class.<br>
51584  * <p>
51585  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
51586  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
51587  * <p>
51588  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
51589 .x-view-drag-insert-above {
51590         border-top:1px dotted #3366cc;
51591 }
51592 .x-view-drag-insert-below {
51593         border-bottom:1px dotted #3366cc;
51594 }
51595 </code></pre>
51596  * 
51597  */
51598  
51599 Roo.DDView = function(container, tpl, config) {
51600     Roo.DDView.superclass.constructor.apply(this, arguments);
51601     this.getEl().setStyle("outline", "0px none");
51602     this.getEl().unselectable();
51603     if (this.dragGroup) {
51604         this.setDraggable(this.dragGroup.split(","));
51605     }
51606     if (this.dropGroup) {
51607         this.setDroppable(this.dropGroup.split(","));
51608     }
51609     if (this.deletable) {
51610         this.setDeletable();
51611     }
51612     this.isDirtyFlag = false;
51613         this.addEvents({
51614                 "drop" : true
51615         });
51616 };
51617
51618 Roo.extend(Roo.DDView, Roo.View, {
51619 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
51620 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
51621 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
51622 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
51623
51624         isFormField: true,
51625
51626         reset: Roo.emptyFn,
51627         
51628         clearInvalid: Roo.form.Field.prototype.clearInvalid,
51629
51630         validate: function() {
51631                 return true;
51632         },
51633         
51634         destroy: function() {
51635                 this.purgeListeners();
51636                 this.getEl.removeAllListeners();
51637                 this.getEl().remove();
51638                 if (this.dragZone) {
51639                         if (this.dragZone.destroy) {
51640                                 this.dragZone.destroy();
51641                         }
51642                 }
51643                 if (this.dropZone) {
51644                         if (this.dropZone.destroy) {
51645                                 this.dropZone.destroy();
51646                         }
51647                 }
51648         },
51649
51650 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
51651         getName: function() {
51652                 return this.name;
51653         },
51654
51655 /**     Loads the View from a JSON string representing the Records to put into the Store. */
51656         setValue: function(v) {
51657                 if (!this.store) {
51658                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
51659                 }
51660                 var data = {};
51661                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
51662                 this.store.proxy = new Roo.data.MemoryProxy(data);
51663                 this.store.load();
51664         },
51665
51666 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
51667         getValue: function() {
51668                 var result = '(';
51669                 this.store.each(function(rec) {
51670                         result += rec.id + ',';
51671                 });
51672                 return result.substr(0, result.length - 1) + ')';
51673         },
51674         
51675         getIds: function() {
51676                 var i = 0, result = new Array(this.store.getCount());
51677                 this.store.each(function(rec) {
51678                         result[i++] = rec.id;
51679                 });
51680                 return result;
51681         },
51682         
51683         isDirty: function() {
51684                 return this.isDirtyFlag;
51685         },
51686
51687 /**
51688  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
51689  *      whole Element becomes the target, and this causes the drop gesture to append.
51690  */
51691     getTargetFromEvent : function(e) {
51692                 var target = e.getTarget();
51693                 while ((target !== null) && (target.parentNode != this.el.dom)) {
51694                 target = target.parentNode;
51695                 }
51696                 if (!target) {
51697                         target = this.el.dom.lastChild || this.el.dom;
51698                 }
51699                 return target;
51700     },
51701
51702 /**
51703  *      Create the drag data which consists of an object which has the property "ddel" as
51704  *      the drag proxy element. 
51705  */
51706     getDragData : function(e) {
51707         var target = this.findItemFromChild(e.getTarget());
51708                 if(target) {
51709                         this.handleSelection(e);
51710                         var selNodes = this.getSelectedNodes();
51711             var dragData = {
51712                 source: this,
51713                 copy: this.copy || (this.allowCopy && e.ctrlKey),
51714                 nodes: selNodes,
51715                 records: []
51716                         };
51717                         var selectedIndices = this.getSelectedIndexes();
51718                         for (var i = 0; i < selectedIndices.length; i++) {
51719                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
51720                         }
51721                         if (selNodes.length == 1) {
51722                                 dragData.ddel = target.cloneNode(true); // the div element
51723                         } else {
51724                                 var div = document.createElement('div'); // create the multi element drag "ghost"
51725                                 div.className = 'multi-proxy';
51726                                 for (var i = 0, len = selNodes.length; i < len; i++) {
51727                                         div.appendChild(selNodes[i].cloneNode(true));
51728                                 }
51729                                 dragData.ddel = div;
51730                         }
51731             //console.log(dragData)
51732             //console.log(dragData.ddel.innerHTML)
51733                         return dragData;
51734                 }
51735         //console.log('nodragData')
51736                 return false;
51737     },
51738     
51739 /**     Specify to which ddGroup items in this DDView may be dragged. */
51740     setDraggable: function(ddGroup) {
51741         if (ddGroup instanceof Array) {
51742                 Roo.each(ddGroup, this.setDraggable, this);
51743                 return;
51744         }
51745         if (this.dragZone) {
51746                 this.dragZone.addToGroup(ddGroup);
51747         } else {
51748                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
51749                                 containerScroll: true,
51750                                 ddGroup: ddGroup 
51751
51752                         });
51753 //                      Draggability implies selection. DragZone's mousedown selects the element.
51754                         if (!this.multiSelect) { this.singleSelect = true; }
51755
51756 //                      Wire the DragZone's handlers up to methods in *this*
51757                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
51758                 }
51759     },
51760
51761 /**     Specify from which ddGroup this DDView accepts drops. */
51762     setDroppable: function(ddGroup) {
51763         if (ddGroup instanceof Array) {
51764                 Roo.each(ddGroup, this.setDroppable, this);
51765                 return;
51766         }
51767         if (this.dropZone) {
51768                 this.dropZone.addToGroup(ddGroup);
51769         } else {
51770                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
51771                                 containerScroll: true,
51772                                 ddGroup: ddGroup
51773                         });
51774
51775 //                      Wire the DropZone's handlers up to methods in *this*
51776                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
51777                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
51778                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
51779                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
51780                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
51781                 }
51782     },
51783
51784 /**     Decide whether to drop above or below a View node. */
51785     getDropPoint : function(e, n, dd){
51786         if (n == this.el.dom) { return "above"; }
51787                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
51788                 var c = t + (b - t) / 2;
51789                 var y = Roo.lib.Event.getPageY(e);
51790                 if(y <= c) {
51791                         return "above";
51792                 }else{
51793                         return "below";
51794                 }
51795     },
51796
51797     onNodeEnter : function(n, dd, e, data){
51798                 return false;
51799     },
51800     
51801     onNodeOver : function(n, dd, e, data){
51802                 var pt = this.getDropPoint(e, n, dd);
51803                 // set the insert point style on the target node
51804                 var dragElClass = this.dropNotAllowed;
51805                 if (pt) {
51806                         var targetElClass;
51807                         if (pt == "above"){
51808                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
51809                                 targetElClass = "x-view-drag-insert-above";
51810                         } else {
51811                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
51812                                 targetElClass = "x-view-drag-insert-below";
51813                         }
51814                         if (this.lastInsertClass != targetElClass){
51815                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
51816                                 this.lastInsertClass = targetElClass;
51817                         }
51818                 }
51819                 return dragElClass;
51820         },
51821
51822     onNodeOut : function(n, dd, e, data){
51823                 this.removeDropIndicators(n);
51824     },
51825
51826     onNodeDrop : function(n, dd, e, data){
51827         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
51828                 return false;
51829         }
51830         var pt = this.getDropPoint(e, n, dd);
51831                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
51832                 if (pt == "below") { insertAt++; }
51833                 for (var i = 0; i < data.records.length; i++) {
51834                         var r = data.records[i];
51835                         var dup = this.store.getById(r.id);
51836                         if (dup && (dd != this.dragZone)) {
51837                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
51838                         } else {
51839                                 if (data.copy) {
51840                                         this.store.insert(insertAt++, r.copy());
51841                                 } else {
51842                                         data.source.isDirtyFlag = true;
51843                                         r.store.remove(r);
51844                                         this.store.insert(insertAt++, r);
51845                                 }
51846                                 this.isDirtyFlag = true;
51847                         }
51848                 }
51849                 this.dragZone.cachedTarget = null;
51850                 return true;
51851     },
51852
51853     removeDropIndicators : function(n){
51854                 if(n){
51855                         Roo.fly(n).removeClass([
51856                                 "x-view-drag-insert-above",
51857                                 "x-view-drag-insert-below"]);
51858                         this.lastInsertClass = "_noclass";
51859                 }
51860     },
51861
51862 /**
51863  *      Utility method. Add a delete option to the DDView's context menu.
51864  *      @param {String} imageUrl The URL of the "delete" icon image.
51865  */
51866         setDeletable: function(imageUrl) {
51867                 if (!this.singleSelect && !this.multiSelect) {
51868                         this.singleSelect = true;
51869                 }
51870                 var c = this.getContextMenu();
51871                 this.contextMenu.on("itemclick", function(item) {
51872                         switch (item.id) {
51873                                 case "delete":
51874                                         this.remove(this.getSelectedIndexes());
51875                                         break;
51876                         }
51877                 }, this);
51878                 this.contextMenu.add({
51879                         icon: imageUrl,
51880                         id: "delete",
51881                         text: 'Delete'
51882                 });
51883         },
51884         
51885 /**     Return the context menu for this DDView. */
51886         getContextMenu: function() {
51887                 if (!this.contextMenu) {
51888 //                      Create the View's context menu
51889                         this.contextMenu = new Roo.menu.Menu({
51890                                 id: this.id + "-contextmenu"
51891                         });
51892                         this.el.on("contextmenu", this.showContextMenu, this);
51893                 }
51894                 return this.contextMenu;
51895         },
51896         
51897         disableContextMenu: function() {
51898                 if (this.contextMenu) {
51899                         this.el.un("contextmenu", this.showContextMenu, this);
51900                 }
51901         },
51902
51903         showContextMenu: function(e, item) {
51904         item = this.findItemFromChild(e.getTarget());
51905                 if (item) {
51906                         e.stopEvent();
51907                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
51908                         this.contextMenu.showAt(e.getXY());
51909             }
51910     },
51911
51912 /**
51913  *      Remove {@link Roo.data.Record}s at the specified indices.
51914  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
51915  */
51916     remove: function(selectedIndices) {
51917                 selectedIndices = [].concat(selectedIndices);
51918                 for (var i = 0; i < selectedIndices.length; i++) {
51919                         var rec = this.store.getAt(selectedIndices[i]);
51920                         this.store.remove(rec);
51921                 }
51922     },
51923
51924 /**
51925  *      Double click fires the event, but also, if this is draggable, and there is only one other
51926  *      related DropZone, it transfers the selected node.
51927  */
51928     onDblClick : function(e){
51929         var item = this.findItemFromChild(e.getTarget());
51930         if(item){
51931             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
51932                 return false;
51933             }
51934             if (this.dragGroup) {
51935                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
51936                     while (targets.indexOf(this.dropZone) > -1) {
51937                             targets.remove(this.dropZone);
51938                                 }
51939                     if (targets.length == 1) {
51940                                         this.dragZone.cachedTarget = null;
51941                         var el = Roo.get(targets[0].getEl());
51942                         var box = el.getBox(true);
51943                         targets[0].onNodeDrop(el.dom, {
51944                                 target: el.dom,
51945                                 xy: [box.x, box.y + box.height - 1]
51946                         }, null, this.getDragData(e));
51947                     }
51948                 }
51949         }
51950     },
51951     
51952     handleSelection: function(e) {
51953                 this.dragZone.cachedTarget = null;
51954         var item = this.findItemFromChild(e.getTarget());
51955         if (!item) {
51956                 this.clearSelections(true);
51957                 return;
51958         }
51959                 if (item && (this.multiSelect || this.singleSelect)){
51960                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
51961                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
51962                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
51963                                 this.unselect(item);
51964                         } else {
51965                                 this.select(item, this.multiSelect && e.ctrlKey);
51966                                 this.lastSelection = item;
51967                         }
51968                 }
51969     },
51970
51971     onItemClick : function(item, index, e){
51972                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
51973                         return false;
51974                 }
51975                 return true;
51976     },
51977
51978     unselect : function(nodeInfo, suppressEvent){
51979                 var node = this.getNode(nodeInfo);
51980                 if(node && this.isSelected(node)){
51981                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
51982                                 Roo.fly(node).removeClass(this.selectedClass);
51983                                 this.selections.remove(node);
51984                                 if(!suppressEvent){
51985                                         this.fireEvent("selectionchange", this, this.selections);
51986                                 }
51987                         }
51988                 }
51989     }
51990 });
51991 /*
51992  * Based on:
51993  * Ext JS Library 1.1.1
51994  * Copyright(c) 2006-2007, Ext JS, LLC.
51995  *
51996  * Originally Released Under LGPL - original licence link has changed is not relivant.
51997  *
51998  * Fork - LGPL
51999  * <script type="text/javascript">
52000  */
52001  
52002 /**
52003  * @class Roo.LayoutManager
52004  * @extends Roo.util.Observable
52005  * Base class for layout managers.
52006  */
52007 Roo.LayoutManager = function(container, config){
52008     Roo.LayoutManager.superclass.constructor.call(this);
52009     this.el = Roo.get(container);
52010     // ie scrollbar fix
52011     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
52012         document.body.scroll = "no";
52013     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
52014         this.el.position('relative');
52015     }
52016     this.id = this.el.id;
52017     this.el.addClass("x-layout-container");
52018     /** false to disable window resize monitoring @type Boolean */
52019     this.monitorWindowResize = true;
52020     this.regions = {};
52021     this.addEvents({
52022         /**
52023          * @event layout
52024          * Fires when a layout is performed. 
52025          * @param {Roo.LayoutManager} this
52026          */
52027         "layout" : true,
52028         /**
52029          * @event regionresized
52030          * Fires when the user resizes a region. 
52031          * @param {Roo.LayoutRegion} region The resized region
52032          * @param {Number} newSize The new size (width for east/west, height for north/south)
52033          */
52034         "regionresized" : true,
52035         /**
52036          * @event regioncollapsed
52037          * Fires when a region is collapsed. 
52038          * @param {Roo.LayoutRegion} region The collapsed region
52039          */
52040         "regioncollapsed" : true,
52041         /**
52042          * @event regionexpanded
52043          * Fires when a region is expanded.  
52044          * @param {Roo.LayoutRegion} region The expanded region
52045          */
52046         "regionexpanded" : true
52047     });
52048     this.updating = false;
52049     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
52050 };
52051
52052 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
52053     /**
52054      * Returns true if this layout is currently being updated
52055      * @return {Boolean}
52056      */
52057     isUpdating : function(){
52058         return this.updating; 
52059     },
52060     
52061     /**
52062      * Suspend the LayoutManager from doing auto-layouts while
52063      * making multiple add or remove calls
52064      */
52065     beginUpdate : function(){
52066         this.updating = true;    
52067     },
52068     
52069     /**
52070      * Restore auto-layouts and optionally disable the manager from performing a layout
52071      * @param {Boolean} noLayout true to disable a layout update 
52072      */
52073     endUpdate : function(noLayout){
52074         this.updating = false;
52075         if(!noLayout){
52076             this.layout();
52077         }    
52078     },
52079     
52080     layout: function(){
52081         
52082     },
52083     
52084     onRegionResized : function(region, newSize){
52085         this.fireEvent("regionresized", region, newSize);
52086         this.layout();
52087     },
52088     
52089     onRegionCollapsed : function(region){
52090         this.fireEvent("regioncollapsed", region);
52091     },
52092     
52093     onRegionExpanded : function(region){
52094         this.fireEvent("regionexpanded", region);
52095     },
52096         
52097     /**
52098      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
52099      * performs box-model adjustments.
52100      * @return {Object} The size as an object {width: (the width), height: (the height)}
52101      */
52102     getViewSize : function(){
52103         var size;
52104         if(this.el.dom != document.body){
52105             size = this.el.getSize();
52106         }else{
52107             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
52108         }
52109         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
52110         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
52111         return size;
52112     },
52113     
52114     /**
52115      * Returns the Element this layout is bound to.
52116      * @return {Roo.Element}
52117      */
52118     getEl : function(){
52119         return this.el;
52120     },
52121     
52122     /**
52123      * Returns the specified region.
52124      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
52125      * @return {Roo.LayoutRegion}
52126      */
52127     getRegion : function(target){
52128         return this.regions[target.toLowerCase()];
52129     },
52130     
52131     onWindowResize : function(){
52132         if(this.monitorWindowResize){
52133             this.layout();
52134         }
52135     }
52136 });/*
52137  * Based on:
52138  * Ext JS Library 1.1.1
52139  * Copyright(c) 2006-2007, Ext JS, LLC.
52140  *
52141  * Originally Released Under LGPL - original licence link has changed is not relivant.
52142  *
52143  * Fork - LGPL
52144  * <script type="text/javascript">
52145  */
52146 /**
52147  * @class Roo.BorderLayout
52148  * @extends Roo.LayoutManager
52149  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
52150  * please see: <br><br>
52151  * <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>
52152  * <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>
52153  * Example:
52154  <pre><code>
52155  var layout = new Roo.BorderLayout(document.body, {
52156     north: {
52157         initialSize: 25,
52158         titlebar: false
52159     },
52160     west: {
52161         split:true,
52162         initialSize: 200,
52163         minSize: 175,
52164         maxSize: 400,
52165         titlebar: true,
52166         collapsible: true
52167     },
52168     east: {
52169         split:true,
52170         initialSize: 202,
52171         minSize: 175,
52172         maxSize: 400,
52173         titlebar: true,
52174         collapsible: true
52175     },
52176     south: {
52177         split:true,
52178         initialSize: 100,
52179         minSize: 100,
52180         maxSize: 200,
52181         titlebar: true,
52182         collapsible: true
52183     },
52184     center: {
52185         titlebar: true,
52186         autoScroll:true,
52187         resizeTabs: true,
52188         minTabWidth: 50,
52189         preferredTabWidth: 150
52190     }
52191 });
52192
52193 // shorthand
52194 var CP = Roo.ContentPanel;
52195
52196 layout.beginUpdate();
52197 layout.add("north", new CP("north", "North"));
52198 layout.add("south", new CP("south", {title: "South", closable: true}));
52199 layout.add("west", new CP("west", {title: "West"}));
52200 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
52201 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
52202 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
52203 layout.getRegion("center").showPanel("center1");
52204 layout.endUpdate();
52205 </code></pre>
52206
52207 <b>The container the layout is rendered into can be either the body element or any other element.
52208 If it is not the body element, the container needs to either be an absolute positioned element,
52209 or you will need to add "position:relative" to the css of the container.  You will also need to specify
52210 the container size if it is not the body element.</b>
52211
52212 * @constructor
52213 * Create a new BorderLayout
52214 * @param {String/HTMLElement/Element} container The container this layout is bound to
52215 * @param {Object} config Configuration options
52216  */
52217 Roo.BorderLayout = function(container, config){
52218     config = config || {};
52219     Roo.BorderLayout.superclass.constructor.call(this, container, config);
52220     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
52221     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
52222         var target = this.factory.validRegions[i];
52223         if(config[target]){
52224             this.addRegion(target, config[target]);
52225         }
52226     }
52227 };
52228
52229 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
52230     /**
52231      * Creates and adds a new region if it doesn't already exist.
52232      * @param {String} target The target region key (north, south, east, west or center).
52233      * @param {Object} config The regions config object
52234      * @return {BorderLayoutRegion} The new region
52235      */
52236     addRegion : function(target, config){
52237         if(!this.regions[target]){
52238             var r = this.factory.create(target, this, config);
52239             this.bindRegion(target, r);
52240         }
52241         return this.regions[target];
52242     },
52243
52244     // private (kinda)
52245     bindRegion : function(name, r){
52246         this.regions[name] = r;
52247         r.on("visibilitychange", this.layout, this);
52248         r.on("paneladded", this.layout, this);
52249         r.on("panelremoved", this.layout, this);
52250         r.on("invalidated", this.layout, this);
52251         r.on("resized", this.onRegionResized, this);
52252         r.on("collapsed", this.onRegionCollapsed, this);
52253         r.on("expanded", this.onRegionExpanded, this);
52254     },
52255
52256     /**
52257      * Performs a layout update.
52258      */
52259     layout : function(){
52260         if(this.updating) {
52261             return;
52262         }
52263         var size = this.getViewSize();
52264         var w = size.width;
52265         var h = size.height;
52266         var centerW = w;
52267         var centerH = h;
52268         var centerY = 0;
52269         var centerX = 0;
52270         //var x = 0, y = 0;
52271
52272         var rs = this.regions;
52273         var north = rs["north"];
52274         var south = rs["south"]; 
52275         var west = rs["west"];
52276         var east = rs["east"];
52277         var center = rs["center"];
52278         //if(this.hideOnLayout){ // not supported anymore
52279             //c.el.setStyle("display", "none");
52280         //}
52281         if(north && north.isVisible()){
52282             var b = north.getBox();
52283             var m = north.getMargins();
52284             b.width = w - (m.left+m.right);
52285             b.x = m.left;
52286             b.y = m.top;
52287             centerY = b.height + b.y + m.bottom;
52288             centerH -= centerY;
52289             north.updateBox(this.safeBox(b));
52290         }
52291         if(south && south.isVisible()){
52292             var b = south.getBox();
52293             var m = south.getMargins();
52294             b.width = w - (m.left+m.right);
52295             b.x = m.left;
52296             var totalHeight = (b.height + m.top + m.bottom);
52297             b.y = h - totalHeight + m.top;
52298             centerH -= totalHeight;
52299             south.updateBox(this.safeBox(b));
52300         }
52301         if(west && west.isVisible()){
52302             var b = west.getBox();
52303             var m = west.getMargins();
52304             b.height = centerH - (m.top+m.bottom);
52305             b.x = m.left;
52306             b.y = centerY + m.top;
52307             var totalWidth = (b.width + m.left + m.right);
52308             centerX += totalWidth;
52309             centerW -= totalWidth;
52310             west.updateBox(this.safeBox(b));
52311         }
52312         if(east && east.isVisible()){
52313             var b = east.getBox();
52314             var m = east.getMargins();
52315             b.height = centerH - (m.top+m.bottom);
52316             var totalWidth = (b.width + m.left + m.right);
52317             b.x = w - totalWidth + m.left;
52318             b.y = centerY + m.top;
52319             centerW -= totalWidth;
52320             east.updateBox(this.safeBox(b));
52321         }
52322         if(center){
52323             var m = center.getMargins();
52324             var centerBox = {
52325                 x: centerX + m.left,
52326                 y: centerY + m.top,
52327                 width: centerW - (m.left+m.right),
52328                 height: centerH - (m.top+m.bottom)
52329             };
52330             //if(this.hideOnLayout){
52331                 //center.el.setStyle("display", "block");
52332             //}
52333             center.updateBox(this.safeBox(centerBox));
52334         }
52335         this.el.repaint();
52336         this.fireEvent("layout", this);
52337     },
52338
52339     // private
52340     safeBox : function(box){
52341         box.width = Math.max(0, box.width);
52342         box.height = Math.max(0, box.height);
52343         return box;
52344     },
52345
52346     /**
52347      * Adds a ContentPanel (or subclass) to this layout.
52348      * @param {String} target The target region key (north, south, east, west or center).
52349      * @param {Roo.ContentPanel} panel The panel to add
52350      * @return {Roo.ContentPanel} The added panel
52351      */
52352     add : function(target, panel){
52353          
52354         target = target.toLowerCase();
52355         return this.regions[target].add(panel);
52356     },
52357
52358     /**
52359      * Remove a ContentPanel (or subclass) to this layout.
52360      * @param {String} target The target region key (north, south, east, west or center).
52361      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
52362      * @return {Roo.ContentPanel} The removed panel
52363      */
52364     remove : function(target, panel){
52365         target = target.toLowerCase();
52366         return this.regions[target].remove(panel);
52367     },
52368
52369     /**
52370      * Searches all regions for a panel with the specified id
52371      * @param {String} panelId
52372      * @return {Roo.ContentPanel} The panel or null if it wasn't found
52373      */
52374     findPanel : function(panelId){
52375         var rs = this.regions;
52376         for(var target in rs){
52377             if(typeof rs[target] != "function"){
52378                 var p = rs[target].getPanel(panelId);
52379                 if(p){
52380                     return p;
52381                 }
52382             }
52383         }
52384         return null;
52385     },
52386
52387     /**
52388      * Searches all regions for a panel with the specified id and activates (shows) it.
52389      * @param {String/ContentPanel} panelId The panels id or the panel itself
52390      * @return {Roo.ContentPanel} The shown panel or null
52391      */
52392     showPanel : function(panelId) {
52393       var rs = this.regions;
52394       for(var target in rs){
52395          var r = rs[target];
52396          if(typeof r != "function"){
52397             if(r.hasPanel(panelId)){
52398                return r.showPanel(panelId);
52399             }
52400          }
52401       }
52402       return null;
52403    },
52404
52405    /**
52406      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
52407      * @param {Roo.state.Provider} provider (optional) An alternate state provider
52408      */
52409     restoreState : function(provider){
52410         if(!provider){
52411             provider = Roo.state.Manager;
52412         }
52413         var sm = new Roo.LayoutStateManager();
52414         sm.init(this, provider);
52415     },
52416
52417     /**
52418      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
52419      * object should contain properties for each region to add ContentPanels to, and each property's value should be
52420      * a valid ContentPanel config object.  Example:
52421      * <pre><code>
52422 // Create the main layout
52423 var layout = new Roo.BorderLayout('main-ct', {
52424     west: {
52425         split:true,
52426         minSize: 175,
52427         titlebar: true
52428     },
52429     center: {
52430         title:'Components'
52431     }
52432 }, 'main-ct');
52433
52434 // Create and add multiple ContentPanels at once via configs
52435 layout.batchAdd({
52436    west: {
52437        id: 'source-files',
52438        autoCreate:true,
52439        title:'Ext Source Files',
52440        autoScroll:true,
52441        fitToFrame:true
52442    },
52443    center : {
52444        el: cview,
52445        autoScroll:true,
52446        fitToFrame:true,
52447        toolbar: tb,
52448        resizeEl:'cbody'
52449    }
52450 });
52451 </code></pre>
52452      * @param {Object} regions An object containing ContentPanel configs by region name
52453      */
52454     batchAdd : function(regions){
52455         this.beginUpdate();
52456         for(var rname in regions){
52457             var lr = this.regions[rname];
52458             if(lr){
52459                 this.addTypedPanels(lr, regions[rname]);
52460             }
52461         }
52462         this.endUpdate();
52463     },
52464
52465     // private
52466     addTypedPanels : function(lr, ps){
52467         if(typeof ps == 'string'){
52468             lr.add(new Roo.ContentPanel(ps));
52469         }
52470         else if(ps instanceof Array){
52471             for(var i =0, len = ps.length; i < len; i++){
52472                 this.addTypedPanels(lr, ps[i]);
52473             }
52474         }
52475         else if(!ps.events){ // raw config?
52476             var el = ps.el;
52477             delete ps.el; // prevent conflict
52478             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
52479         }
52480         else {  // panel object assumed!
52481             lr.add(ps);
52482         }
52483     },
52484     /**
52485      * Adds a xtype elements to the layout.
52486      * <pre><code>
52487
52488 layout.addxtype({
52489        xtype : 'ContentPanel',
52490        region: 'west',
52491        items: [ .... ]
52492    }
52493 );
52494
52495 layout.addxtype({
52496         xtype : 'NestedLayoutPanel',
52497         region: 'west',
52498         layout: {
52499            center: { },
52500            west: { }   
52501         },
52502         items : [ ... list of content panels or nested layout panels.. ]
52503    }
52504 );
52505 </code></pre>
52506      * @param {Object} cfg Xtype definition of item to add.
52507      */
52508     addxtype : function(cfg)
52509     {
52510         // basically accepts a pannel...
52511         // can accept a layout region..!?!?
52512         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
52513         
52514         if (!cfg.xtype.match(/Panel$/)) {
52515             return false;
52516         }
52517         var ret = false;
52518         
52519         if (typeof(cfg.region) == 'undefined') {
52520             Roo.log("Failed to add Panel, region was not set");
52521             Roo.log(cfg);
52522             return false;
52523         }
52524         var region = cfg.region;
52525         delete cfg.region;
52526         
52527           
52528         var xitems = [];
52529         if (cfg.items) {
52530             xitems = cfg.items;
52531             delete cfg.items;
52532         }
52533         var nb = false;
52534         
52535         switch(cfg.xtype) 
52536         {
52537             case 'ContentPanel':  // ContentPanel (el, cfg)
52538             case 'ScrollPanel':  // ContentPanel (el, cfg)
52539             case 'ViewPanel': 
52540                 if(cfg.autoCreate) {
52541                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52542                 } else {
52543                     var el = this.el.createChild();
52544                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
52545                 }
52546                 
52547                 this.add(region, ret);
52548                 break;
52549             
52550             
52551             case 'TreePanel': // our new panel!
52552                 cfg.el = this.el.createChild();
52553                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52554                 this.add(region, ret);
52555                 break;
52556             
52557             case 'NestedLayoutPanel': 
52558                 // create a new Layout (which is  a Border Layout...
52559                 var el = this.el.createChild();
52560                 var clayout = cfg.layout;
52561                 delete cfg.layout;
52562                 clayout.items   = clayout.items  || [];
52563                 // replace this exitems with the clayout ones..
52564                 xitems = clayout.items;
52565                  
52566                 
52567                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
52568                     cfg.background = false;
52569                 }
52570                 var layout = new Roo.BorderLayout(el, clayout);
52571                 
52572                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
52573                 //console.log('adding nested layout panel '  + cfg.toSource());
52574                 this.add(region, ret);
52575                 nb = {}; /// find first...
52576                 break;
52577                 
52578             case 'GridPanel': 
52579             
52580                 // needs grid and region
52581                 
52582                 //var el = this.getRegion(region).el.createChild();
52583                 var el = this.el.createChild();
52584                 // create the grid first...
52585                 
52586                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
52587                 delete cfg.grid;
52588                 if (region == 'center' && this.active ) {
52589                     cfg.background = false;
52590                 }
52591                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
52592                 
52593                 this.add(region, ret);
52594                 if (cfg.background) {
52595                     ret.on('activate', function(gp) {
52596                         if (!gp.grid.rendered) {
52597                             gp.grid.render();
52598                         }
52599                     });
52600                 } else {
52601                     grid.render();
52602                 }
52603                 break;
52604            
52605            
52606            
52607                 
52608                 
52609                 
52610             default:
52611                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
52612                     
52613                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52614                     this.add(region, ret);
52615                 } else {
52616                 
52617                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
52618                     return null;
52619                 }
52620                 
52621              // GridPanel (grid, cfg)
52622             
52623         }
52624         this.beginUpdate();
52625         // add children..
52626         var region = '';
52627         var abn = {};
52628         Roo.each(xitems, function(i)  {
52629             region = nb && i.region ? i.region : false;
52630             
52631             var add = ret.addxtype(i);
52632            
52633             if (region) {
52634                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
52635                 if (!i.background) {
52636                     abn[region] = nb[region] ;
52637                 }
52638             }
52639             
52640         });
52641         this.endUpdate();
52642
52643         // make the last non-background panel active..
52644         //if (nb) { Roo.log(abn); }
52645         if (nb) {
52646             
52647             for(var r in abn) {
52648                 region = this.getRegion(r);
52649                 if (region) {
52650                     // tried using nb[r], but it does not work..
52651                      
52652                     region.showPanel(abn[r]);
52653                    
52654                 }
52655             }
52656         }
52657         return ret;
52658         
52659     }
52660 });
52661
52662 /**
52663  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
52664  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
52665  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
52666  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
52667  * <pre><code>
52668 // shorthand
52669 var CP = Roo.ContentPanel;
52670
52671 var layout = Roo.BorderLayout.create({
52672     north: {
52673         initialSize: 25,
52674         titlebar: false,
52675         panels: [new CP("north", "North")]
52676     },
52677     west: {
52678         split:true,
52679         initialSize: 200,
52680         minSize: 175,
52681         maxSize: 400,
52682         titlebar: true,
52683         collapsible: true,
52684         panels: [new CP("west", {title: "West"})]
52685     },
52686     east: {
52687         split:true,
52688         initialSize: 202,
52689         minSize: 175,
52690         maxSize: 400,
52691         titlebar: true,
52692         collapsible: true,
52693         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
52694     },
52695     south: {
52696         split:true,
52697         initialSize: 100,
52698         minSize: 100,
52699         maxSize: 200,
52700         titlebar: true,
52701         collapsible: true,
52702         panels: [new CP("south", {title: "South", closable: true})]
52703     },
52704     center: {
52705         titlebar: true,
52706         autoScroll:true,
52707         resizeTabs: true,
52708         minTabWidth: 50,
52709         preferredTabWidth: 150,
52710         panels: [
52711             new CP("center1", {title: "Close Me", closable: true}),
52712             new CP("center2", {title: "Center Panel", closable: false})
52713         ]
52714     }
52715 }, document.body);
52716
52717 layout.getRegion("center").showPanel("center1");
52718 </code></pre>
52719  * @param config
52720  * @param targetEl
52721  */
52722 Roo.BorderLayout.create = function(config, targetEl){
52723     var layout = new Roo.BorderLayout(targetEl || document.body, config);
52724     layout.beginUpdate();
52725     var regions = Roo.BorderLayout.RegionFactory.validRegions;
52726     for(var j = 0, jlen = regions.length; j < jlen; j++){
52727         var lr = regions[j];
52728         if(layout.regions[lr] && config[lr].panels){
52729             var r = layout.regions[lr];
52730             var ps = config[lr].panels;
52731             layout.addTypedPanels(r, ps);
52732         }
52733     }
52734     layout.endUpdate();
52735     return layout;
52736 };
52737
52738 // private
52739 Roo.BorderLayout.RegionFactory = {
52740     // private
52741     validRegions : ["north","south","east","west","center"],
52742
52743     // private
52744     create : function(target, mgr, config){
52745         target = target.toLowerCase();
52746         if(config.lightweight || config.basic){
52747             return new Roo.BasicLayoutRegion(mgr, config, target);
52748         }
52749         switch(target){
52750             case "north":
52751                 return new Roo.NorthLayoutRegion(mgr, config);
52752             case "south":
52753                 return new Roo.SouthLayoutRegion(mgr, config);
52754             case "east":
52755                 return new Roo.EastLayoutRegion(mgr, config);
52756             case "west":
52757                 return new Roo.WestLayoutRegion(mgr, config);
52758             case "center":
52759                 return new Roo.CenterLayoutRegion(mgr, config);
52760         }
52761         throw 'Layout region "'+target+'" not supported.';
52762     }
52763 };/*
52764  * Based on:
52765  * Ext JS Library 1.1.1
52766  * Copyright(c) 2006-2007, Ext JS, LLC.
52767  *
52768  * Originally Released Under LGPL - original licence link has changed is not relivant.
52769  *
52770  * Fork - LGPL
52771  * <script type="text/javascript">
52772  */
52773  
52774 /**
52775  * @class Roo.BasicLayoutRegion
52776  * @extends Roo.util.Observable
52777  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
52778  * and does not have a titlebar, tabs or any other features. All it does is size and position 
52779  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
52780  */
52781 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
52782     this.mgr = mgr;
52783     this.position  = pos;
52784     this.events = {
52785         /**
52786          * @scope Roo.BasicLayoutRegion
52787          */
52788         
52789         /**
52790          * @event beforeremove
52791          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
52792          * @param {Roo.LayoutRegion} this
52793          * @param {Roo.ContentPanel} panel The panel
52794          * @param {Object} e The cancel event object
52795          */
52796         "beforeremove" : true,
52797         /**
52798          * @event invalidated
52799          * Fires when the layout for this region is changed.
52800          * @param {Roo.LayoutRegion} this
52801          */
52802         "invalidated" : true,
52803         /**
52804          * @event visibilitychange
52805          * Fires when this region is shown or hidden 
52806          * @param {Roo.LayoutRegion} this
52807          * @param {Boolean} visibility true or false
52808          */
52809         "visibilitychange" : true,
52810         /**
52811          * @event paneladded
52812          * Fires when a panel is added. 
52813          * @param {Roo.LayoutRegion} this
52814          * @param {Roo.ContentPanel} panel The panel
52815          */
52816         "paneladded" : true,
52817         /**
52818          * @event panelremoved
52819          * Fires when a panel is removed. 
52820          * @param {Roo.LayoutRegion} this
52821          * @param {Roo.ContentPanel} panel The panel
52822          */
52823         "panelremoved" : true,
52824         /**
52825          * @event beforecollapse
52826          * Fires when this region before collapse.
52827          * @param {Roo.LayoutRegion} this
52828          */
52829         "beforecollapse" : true,
52830         /**
52831          * @event collapsed
52832          * Fires when this region is collapsed.
52833          * @param {Roo.LayoutRegion} this
52834          */
52835         "collapsed" : true,
52836         /**
52837          * @event expanded
52838          * Fires when this region is expanded.
52839          * @param {Roo.LayoutRegion} this
52840          */
52841         "expanded" : true,
52842         /**
52843          * @event slideshow
52844          * Fires when this region is slid into view.
52845          * @param {Roo.LayoutRegion} this
52846          */
52847         "slideshow" : true,
52848         /**
52849          * @event slidehide
52850          * Fires when this region slides out of view. 
52851          * @param {Roo.LayoutRegion} this
52852          */
52853         "slidehide" : true,
52854         /**
52855          * @event panelactivated
52856          * Fires when a panel is activated. 
52857          * @param {Roo.LayoutRegion} this
52858          * @param {Roo.ContentPanel} panel The activated panel
52859          */
52860         "panelactivated" : true,
52861         /**
52862          * @event resized
52863          * Fires when the user resizes this region. 
52864          * @param {Roo.LayoutRegion} this
52865          * @param {Number} newSize The new size (width for east/west, height for north/south)
52866          */
52867         "resized" : true
52868     };
52869     /** A collection of panels in this region. @type Roo.util.MixedCollection */
52870     this.panels = new Roo.util.MixedCollection();
52871     this.panels.getKey = this.getPanelId.createDelegate(this);
52872     this.box = null;
52873     this.activePanel = null;
52874     // ensure listeners are added...
52875     
52876     if (config.listeners || config.events) {
52877         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
52878             listeners : config.listeners || {},
52879             events : config.events || {}
52880         });
52881     }
52882     
52883     if(skipConfig !== true){
52884         this.applyConfig(config);
52885     }
52886 };
52887
52888 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
52889     getPanelId : function(p){
52890         return p.getId();
52891     },
52892     
52893     applyConfig : function(config){
52894         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52895         this.config = config;
52896         
52897     },
52898     
52899     /**
52900      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
52901      * the width, for horizontal (north, south) the height.
52902      * @param {Number} newSize The new width or height
52903      */
52904     resizeTo : function(newSize){
52905         var el = this.el ? this.el :
52906                  (this.activePanel ? this.activePanel.getEl() : null);
52907         if(el){
52908             switch(this.position){
52909                 case "east":
52910                 case "west":
52911                     el.setWidth(newSize);
52912                     this.fireEvent("resized", this, newSize);
52913                 break;
52914                 case "north":
52915                 case "south":
52916                     el.setHeight(newSize);
52917                     this.fireEvent("resized", this, newSize);
52918                 break;                
52919             }
52920         }
52921     },
52922     
52923     getBox : function(){
52924         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
52925     },
52926     
52927     getMargins : function(){
52928         return this.margins;
52929     },
52930     
52931     updateBox : function(box){
52932         this.box = box;
52933         var el = this.activePanel.getEl();
52934         el.dom.style.left = box.x + "px";
52935         el.dom.style.top = box.y + "px";
52936         this.activePanel.setSize(box.width, box.height);
52937     },
52938     
52939     /**
52940      * Returns the container element for this region.
52941      * @return {Roo.Element}
52942      */
52943     getEl : function(){
52944         return this.activePanel;
52945     },
52946     
52947     /**
52948      * Returns true if this region is currently visible.
52949      * @return {Boolean}
52950      */
52951     isVisible : function(){
52952         return this.activePanel ? true : false;
52953     },
52954     
52955     setActivePanel : function(panel){
52956         panel = this.getPanel(panel);
52957         if(this.activePanel && this.activePanel != panel){
52958             this.activePanel.setActiveState(false);
52959             this.activePanel.getEl().setLeftTop(-10000,-10000);
52960         }
52961         this.activePanel = panel;
52962         panel.setActiveState(true);
52963         if(this.box){
52964             panel.setSize(this.box.width, this.box.height);
52965         }
52966         this.fireEvent("panelactivated", this, panel);
52967         this.fireEvent("invalidated");
52968     },
52969     
52970     /**
52971      * Show the specified panel.
52972      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
52973      * @return {Roo.ContentPanel} The shown panel or null
52974      */
52975     showPanel : function(panel){
52976         if(panel = this.getPanel(panel)){
52977             this.setActivePanel(panel);
52978         }
52979         return panel;
52980     },
52981     
52982     /**
52983      * Get the active panel for this region.
52984      * @return {Roo.ContentPanel} The active panel or null
52985      */
52986     getActivePanel : function(){
52987         return this.activePanel;
52988     },
52989     
52990     /**
52991      * Add the passed ContentPanel(s)
52992      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52993      * @return {Roo.ContentPanel} The panel added (if only one was added)
52994      */
52995     add : function(panel){
52996         if(arguments.length > 1){
52997             for(var i = 0, len = arguments.length; i < len; i++) {
52998                 this.add(arguments[i]);
52999             }
53000             return null;
53001         }
53002         if(this.hasPanel(panel)){
53003             this.showPanel(panel);
53004             return panel;
53005         }
53006         var el = panel.getEl();
53007         if(el.dom.parentNode != this.mgr.el.dom){
53008             this.mgr.el.dom.appendChild(el.dom);
53009         }
53010         if(panel.setRegion){
53011             panel.setRegion(this);
53012         }
53013         this.panels.add(panel);
53014         el.setStyle("position", "absolute");
53015         if(!panel.background){
53016             this.setActivePanel(panel);
53017             if(this.config.initialSize && this.panels.getCount()==1){
53018                 this.resizeTo(this.config.initialSize);
53019             }
53020         }
53021         this.fireEvent("paneladded", this, panel);
53022         return panel;
53023     },
53024     
53025     /**
53026      * Returns true if the panel is in this region.
53027      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53028      * @return {Boolean}
53029      */
53030     hasPanel : function(panel){
53031         if(typeof panel == "object"){ // must be panel obj
53032             panel = panel.getId();
53033         }
53034         return this.getPanel(panel) ? true : false;
53035     },
53036     
53037     /**
53038      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53039      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53040      * @param {Boolean} preservePanel Overrides the config preservePanel option
53041      * @return {Roo.ContentPanel} The panel that was removed
53042      */
53043     remove : function(panel, preservePanel){
53044         panel = this.getPanel(panel);
53045         if(!panel){
53046             return null;
53047         }
53048         var e = {};
53049         this.fireEvent("beforeremove", this, panel, e);
53050         if(e.cancel === true){
53051             return null;
53052         }
53053         var panelId = panel.getId();
53054         this.panels.removeKey(panelId);
53055         return panel;
53056     },
53057     
53058     /**
53059      * Returns the panel specified or null if it's not in this region.
53060      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53061      * @return {Roo.ContentPanel}
53062      */
53063     getPanel : function(id){
53064         if(typeof id == "object"){ // must be panel obj
53065             return id;
53066         }
53067         return this.panels.get(id);
53068     },
53069     
53070     /**
53071      * Returns this regions position (north/south/east/west/center).
53072      * @return {String} 
53073      */
53074     getPosition: function(){
53075         return this.position;    
53076     }
53077 });/*
53078  * Based on:
53079  * Ext JS Library 1.1.1
53080  * Copyright(c) 2006-2007, Ext JS, LLC.
53081  *
53082  * Originally Released Under LGPL - original licence link has changed is not relivant.
53083  *
53084  * Fork - LGPL
53085  * <script type="text/javascript">
53086  */
53087  
53088 /**
53089  * @class Roo.LayoutRegion
53090  * @extends Roo.BasicLayoutRegion
53091  * This class represents a region in a layout manager.
53092  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
53093  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
53094  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
53095  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
53096  * @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})
53097  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
53098  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
53099  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
53100  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
53101  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
53102  * @cfg {String}    title           The title for the region (overrides panel titles)
53103  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
53104  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
53105  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
53106  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
53107  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
53108  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
53109  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
53110  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
53111  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
53112  * @cfg {Boolean}   showPin         True to show a pin button
53113  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
53114  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
53115  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
53116  * @cfg {Number}    width           For East/West panels
53117  * @cfg {Number}    height          For North/South panels
53118  * @cfg {Boolean}   split           To show the splitter
53119  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
53120  */
53121 Roo.LayoutRegion = function(mgr, config, pos){
53122     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
53123     var dh = Roo.DomHelper;
53124     /** This region's container element 
53125     * @type Roo.Element */
53126     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
53127     /** This region's title element 
53128     * @type Roo.Element */
53129
53130     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
53131         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
53132         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
53133     ]}, true);
53134     this.titleEl.enableDisplayMode();
53135     /** This region's title text element 
53136     * @type HTMLElement */
53137     this.titleTextEl = this.titleEl.dom.firstChild;
53138     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
53139     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
53140     this.closeBtn.enableDisplayMode();
53141     this.closeBtn.on("click", this.closeClicked, this);
53142     this.closeBtn.hide();
53143
53144     this.createBody(config);
53145     this.visible = true;
53146     this.collapsed = false;
53147
53148     if(config.hideWhenEmpty){
53149         this.hide();
53150         this.on("paneladded", this.validateVisibility, this);
53151         this.on("panelremoved", this.validateVisibility, this);
53152     }
53153     this.applyConfig(config);
53154 };
53155
53156 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
53157
53158     createBody : function(){
53159         /** This region's body element 
53160         * @type Roo.Element */
53161         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
53162     },
53163
53164     applyConfig : function(c){
53165         if(c.collapsible && this.position != "center" && !this.collapsedEl){
53166             var dh = Roo.DomHelper;
53167             if(c.titlebar !== false){
53168                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
53169                 this.collapseBtn.on("click", this.collapse, this);
53170                 this.collapseBtn.enableDisplayMode();
53171
53172                 if(c.showPin === true || this.showPin){
53173                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
53174                     this.stickBtn.enableDisplayMode();
53175                     this.stickBtn.on("click", this.expand, this);
53176                     this.stickBtn.hide();
53177                 }
53178             }
53179             /** This region's collapsed element
53180             * @type Roo.Element */
53181             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
53182                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
53183             ]}, true);
53184             if(c.floatable !== false){
53185                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
53186                this.collapsedEl.on("click", this.collapseClick, this);
53187             }
53188
53189             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
53190                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
53191                    id: "message", unselectable: "on", style:{"float":"left"}});
53192                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
53193              }
53194             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
53195             this.expandBtn.on("click", this.expand, this);
53196         }
53197         if(this.collapseBtn){
53198             this.collapseBtn.setVisible(c.collapsible == true);
53199         }
53200         this.cmargins = c.cmargins || this.cmargins ||
53201                          (this.position == "west" || this.position == "east" ?
53202                              {top: 0, left: 2, right:2, bottom: 0} :
53203                              {top: 2, left: 0, right:0, bottom: 2});
53204         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
53205         this.bottomTabs = c.tabPosition != "top";
53206         this.autoScroll = c.autoScroll || false;
53207         if(this.autoScroll){
53208             this.bodyEl.setStyle("overflow", "auto");
53209         }else{
53210             this.bodyEl.setStyle("overflow", "hidden");
53211         }
53212         //if(c.titlebar !== false){
53213             if((!c.titlebar && !c.title) || c.titlebar === false){
53214                 this.titleEl.hide();
53215             }else{
53216                 this.titleEl.show();
53217                 if(c.title){
53218                     this.titleTextEl.innerHTML = c.title;
53219                 }
53220             }
53221         //}
53222         this.duration = c.duration || .30;
53223         this.slideDuration = c.slideDuration || .45;
53224         this.config = c;
53225         if(c.collapsed){
53226             this.collapse(true);
53227         }
53228         if(c.hidden){
53229             this.hide();
53230         }
53231     },
53232     /**
53233      * Returns true if this region is currently visible.
53234      * @return {Boolean}
53235      */
53236     isVisible : function(){
53237         return this.visible;
53238     },
53239
53240     /**
53241      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
53242      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
53243      */
53244     setCollapsedTitle : function(title){
53245         title = title || "&#160;";
53246         if(this.collapsedTitleTextEl){
53247             this.collapsedTitleTextEl.innerHTML = title;
53248         }
53249     },
53250
53251     getBox : function(){
53252         var b;
53253         if(!this.collapsed){
53254             b = this.el.getBox(false, true);
53255         }else{
53256             b = this.collapsedEl.getBox(false, true);
53257         }
53258         return b;
53259     },
53260
53261     getMargins : function(){
53262         return this.collapsed ? this.cmargins : this.margins;
53263     },
53264
53265     highlight : function(){
53266         this.el.addClass("x-layout-panel-dragover");
53267     },
53268
53269     unhighlight : function(){
53270         this.el.removeClass("x-layout-panel-dragover");
53271     },
53272
53273     updateBox : function(box){
53274         this.box = box;
53275         if(!this.collapsed){
53276             this.el.dom.style.left = box.x + "px";
53277             this.el.dom.style.top = box.y + "px";
53278             this.updateBody(box.width, box.height);
53279         }else{
53280             this.collapsedEl.dom.style.left = box.x + "px";
53281             this.collapsedEl.dom.style.top = box.y + "px";
53282             this.collapsedEl.setSize(box.width, box.height);
53283         }
53284         if(this.tabs){
53285             this.tabs.autoSizeTabs();
53286         }
53287     },
53288
53289     updateBody : function(w, h){
53290         if(w !== null){
53291             this.el.setWidth(w);
53292             w -= this.el.getBorderWidth("rl");
53293             if(this.config.adjustments){
53294                 w += this.config.adjustments[0];
53295             }
53296         }
53297         if(h !== null){
53298             this.el.setHeight(h);
53299             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
53300             h -= this.el.getBorderWidth("tb");
53301             if(this.config.adjustments){
53302                 h += this.config.adjustments[1];
53303             }
53304             this.bodyEl.setHeight(h);
53305             if(this.tabs){
53306                 h = this.tabs.syncHeight(h);
53307             }
53308         }
53309         if(this.panelSize){
53310             w = w !== null ? w : this.panelSize.width;
53311             h = h !== null ? h : this.panelSize.height;
53312         }
53313         if(this.activePanel){
53314             var el = this.activePanel.getEl();
53315             w = w !== null ? w : el.getWidth();
53316             h = h !== null ? h : el.getHeight();
53317             this.panelSize = {width: w, height: h};
53318             this.activePanel.setSize(w, h);
53319         }
53320         if(Roo.isIE && this.tabs){
53321             this.tabs.el.repaint();
53322         }
53323     },
53324
53325     /**
53326      * Returns the container element for this region.
53327      * @return {Roo.Element}
53328      */
53329     getEl : function(){
53330         return this.el;
53331     },
53332
53333     /**
53334      * Hides this region.
53335      */
53336     hide : function(){
53337         if(!this.collapsed){
53338             this.el.dom.style.left = "-2000px";
53339             this.el.hide();
53340         }else{
53341             this.collapsedEl.dom.style.left = "-2000px";
53342             this.collapsedEl.hide();
53343         }
53344         this.visible = false;
53345         this.fireEvent("visibilitychange", this, false);
53346     },
53347
53348     /**
53349      * Shows this region if it was previously hidden.
53350      */
53351     show : function(){
53352         if(!this.collapsed){
53353             this.el.show();
53354         }else{
53355             this.collapsedEl.show();
53356         }
53357         this.visible = true;
53358         this.fireEvent("visibilitychange", this, true);
53359     },
53360
53361     closeClicked : function(){
53362         if(this.activePanel){
53363             this.remove(this.activePanel);
53364         }
53365     },
53366
53367     collapseClick : function(e){
53368         if(this.isSlid){
53369            e.stopPropagation();
53370            this.slideIn();
53371         }else{
53372            e.stopPropagation();
53373            this.slideOut();
53374         }
53375     },
53376
53377     /**
53378      * Collapses this region.
53379      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
53380      */
53381     collapse : function(skipAnim, skipCheck){
53382         if(this.collapsed) {
53383             return;
53384         }
53385         
53386         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
53387             
53388             this.collapsed = true;
53389             if(this.split){
53390                 this.split.el.hide();
53391             }
53392             if(this.config.animate && skipAnim !== true){
53393                 this.fireEvent("invalidated", this);
53394                 this.animateCollapse();
53395             }else{
53396                 this.el.setLocation(-20000,-20000);
53397                 this.el.hide();
53398                 this.collapsedEl.show();
53399                 this.fireEvent("collapsed", this);
53400                 this.fireEvent("invalidated", this);
53401             }
53402         }
53403         
53404     },
53405
53406     animateCollapse : function(){
53407         // overridden
53408     },
53409
53410     /**
53411      * Expands this region if it was previously collapsed.
53412      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
53413      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
53414      */
53415     expand : function(e, skipAnim){
53416         if(e) {
53417             e.stopPropagation();
53418         }
53419         if(!this.collapsed || this.el.hasActiveFx()) {
53420             return;
53421         }
53422         if(this.isSlid){
53423             this.afterSlideIn();
53424             skipAnim = true;
53425         }
53426         this.collapsed = false;
53427         if(this.config.animate && skipAnim !== true){
53428             this.animateExpand();
53429         }else{
53430             this.el.show();
53431             if(this.split){
53432                 this.split.el.show();
53433             }
53434             this.collapsedEl.setLocation(-2000,-2000);
53435             this.collapsedEl.hide();
53436             this.fireEvent("invalidated", this);
53437             this.fireEvent("expanded", this);
53438         }
53439     },
53440
53441     animateExpand : function(){
53442         // overridden
53443     },
53444
53445     initTabs : function()
53446     {
53447         this.bodyEl.setStyle("overflow", "hidden");
53448         var ts = new Roo.TabPanel(
53449                 this.bodyEl.dom,
53450                 {
53451                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
53452                     disableTooltips: this.config.disableTabTips,
53453                     toolbar : this.config.toolbar
53454                 }
53455         );
53456         if(this.config.hideTabs){
53457             ts.stripWrap.setDisplayed(false);
53458         }
53459         this.tabs = ts;
53460         ts.resizeTabs = this.config.resizeTabs === true;
53461         ts.minTabWidth = this.config.minTabWidth || 40;
53462         ts.maxTabWidth = this.config.maxTabWidth || 250;
53463         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
53464         ts.monitorResize = false;
53465         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53466         ts.bodyEl.addClass('x-layout-tabs-body');
53467         this.panels.each(this.initPanelAsTab, this);
53468     },
53469
53470     initPanelAsTab : function(panel){
53471         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
53472                     this.config.closeOnTab && panel.isClosable());
53473         if(panel.tabTip !== undefined){
53474             ti.setTooltip(panel.tabTip);
53475         }
53476         ti.on("activate", function(){
53477               this.setActivePanel(panel);
53478         }, this);
53479         if(this.config.closeOnTab){
53480             ti.on("beforeclose", function(t, e){
53481                 e.cancel = true;
53482                 this.remove(panel);
53483             }, this);
53484         }
53485         return ti;
53486     },
53487
53488     updatePanelTitle : function(panel, title){
53489         if(this.activePanel == panel){
53490             this.updateTitle(title);
53491         }
53492         if(this.tabs){
53493             var ti = this.tabs.getTab(panel.getEl().id);
53494             ti.setText(title);
53495             if(panel.tabTip !== undefined){
53496                 ti.setTooltip(panel.tabTip);
53497             }
53498         }
53499     },
53500
53501     updateTitle : function(title){
53502         if(this.titleTextEl && !this.config.title){
53503             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
53504         }
53505     },
53506
53507     setActivePanel : function(panel){
53508         panel = this.getPanel(panel);
53509         if(this.activePanel && this.activePanel != panel){
53510             this.activePanel.setActiveState(false);
53511         }
53512         this.activePanel = panel;
53513         panel.setActiveState(true);
53514         if(this.panelSize){
53515             panel.setSize(this.panelSize.width, this.panelSize.height);
53516         }
53517         if(this.closeBtn){
53518             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
53519         }
53520         this.updateTitle(panel.getTitle());
53521         if(this.tabs){
53522             this.fireEvent("invalidated", this);
53523         }
53524         this.fireEvent("panelactivated", this, panel);
53525     },
53526
53527     /**
53528      * Shows the specified panel.
53529      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
53530      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
53531      */
53532     showPanel : function(panel)
53533     {
53534         panel = this.getPanel(panel);
53535         if(panel){
53536             if(this.tabs){
53537                 var tab = this.tabs.getTab(panel.getEl().id);
53538                 if(tab.isHidden()){
53539                     this.tabs.unhideTab(tab.id);
53540                 }
53541                 tab.activate();
53542             }else{
53543                 this.setActivePanel(panel);
53544             }
53545         }
53546         return panel;
53547     },
53548
53549     /**
53550      * Get the active panel for this region.
53551      * @return {Roo.ContentPanel} The active panel or null
53552      */
53553     getActivePanel : function(){
53554         return this.activePanel;
53555     },
53556
53557     validateVisibility : function(){
53558         if(this.panels.getCount() < 1){
53559             this.updateTitle("&#160;");
53560             this.closeBtn.hide();
53561             this.hide();
53562         }else{
53563             if(!this.isVisible()){
53564                 this.show();
53565             }
53566         }
53567     },
53568
53569     /**
53570      * Adds the passed ContentPanel(s) to this region.
53571      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53572      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
53573      */
53574     add : function(panel){
53575         if(arguments.length > 1){
53576             for(var i = 0, len = arguments.length; i < len; i++) {
53577                 this.add(arguments[i]);
53578             }
53579             return null;
53580         }
53581         if(this.hasPanel(panel)){
53582             this.showPanel(panel);
53583             return panel;
53584         }
53585         panel.setRegion(this);
53586         this.panels.add(panel);
53587         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
53588             this.bodyEl.dom.appendChild(panel.getEl().dom);
53589             if(panel.background !== true){
53590                 this.setActivePanel(panel);
53591             }
53592             this.fireEvent("paneladded", this, panel);
53593             return panel;
53594         }
53595         if(!this.tabs){
53596             this.initTabs();
53597         }else{
53598             this.initPanelAsTab(panel);
53599         }
53600         if(panel.background !== true){
53601             this.tabs.activate(panel.getEl().id);
53602         }
53603         this.fireEvent("paneladded", this, panel);
53604         return panel;
53605     },
53606
53607     /**
53608      * Hides the tab for the specified panel.
53609      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53610      */
53611     hidePanel : function(panel){
53612         if(this.tabs && (panel = this.getPanel(panel))){
53613             this.tabs.hideTab(panel.getEl().id);
53614         }
53615     },
53616
53617     /**
53618      * Unhides the tab for a previously hidden panel.
53619      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53620      */
53621     unhidePanel : function(panel){
53622         if(this.tabs && (panel = this.getPanel(panel))){
53623             this.tabs.unhideTab(panel.getEl().id);
53624         }
53625     },
53626
53627     clearPanels : function(){
53628         while(this.panels.getCount() > 0){
53629              this.remove(this.panels.first());
53630         }
53631     },
53632
53633     /**
53634      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53635      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53636      * @param {Boolean} preservePanel Overrides the config preservePanel option
53637      * @return {Roo.ContentPanel} The panel that was removed
53638      */
53639     remove : function(panel, preservePanel){
53640         panel = this.getPanel(panel);
53641         if(!panel){
53642             return null;
53643         }
53644         var e = {};
53645         this.fireEvent("beforeremove", this, panel, e);
53646         if(e.cancel === true){
53647             return null;
53648         }
53649         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
53650         var panelId = panel.getId();
53651         this.panels.removeKey(panelId);
53652         if(preservePanel){
53653             document.body.appendChild(panel.getEl().dom);
53654         }
53655         if(this.tabs){
53656             this.tabs.removeTab(panel.getEl().id);
53657         }else if (!preservePanel){
53658             this.bodyEl.dom.removeChild(panel.getEl().dom);
53659         }
53660         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
53661             var p = this.panels.first();
53662             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
53663             tempEl.appendChild(p.getEl().dom);
53664             this.bodyEl.update("");
53665             this.bodyEl.dom.appendChild(p.getEl().dom);
53666             tempEl = null;
53667             this.updateTitle(p.getTitle());
53668             this.tabs = null;
53669             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53670             this.setActivePanel(p);
53671         }
53672         panel.setRegion(null);
53673         if(this.activePanel == panel){
53674             this.activePanel = null;
53675         }
53676         if(this.config.autoDestroy !== false && preservePanel !== true){
53677             try{panel.destroy();}catch(e){}
53678         }
53679         this.fireEvent("panelremoved", this, panel);
53680         return panel;
53681     },
53682
53683     /**
53684      * Returns the TabPanel component used by this region
53685      * @return {Roo.TabPanel}
53686      */
53687     getTabs : function(){
53688         return this.tabs;
53689     },
53690
53691     createTool : function(parentEl, className){
53692         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
53693             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
53694         btn.addClassOnOver("x-layout-tools-button-over");
53695         return btn;
53696     }
53697 });/*
53698  * Based on:
53699  * Ext JS Library 1.1.1
53700  * Copyright(c) 2006-2007, Ext JS, LLC.
53701  *
53702  * Originally Released Under LGPL - original licence link has changed is not relivant.
53703  *
53704  * Fork - LGPL
53705  * <script type="text/javascript">
53706  */
53707  
53708
53709
53710 /**
53711  * @class Roo.SplitLayoutRegion
53712  * @extends Roo.LayoutRegion
53713  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
53714  */
53715 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
53716     this.cursor = cursor;
53717     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
53718 };
53719
53720 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
53721     splitTip : "Drag to resize.",
53722     collapsibleSplitTip : "Drag to resize. Double click to hide.",
53723     useSplitTips : false,
53724
53725     applyConfig : function(config){
53726         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
53727         if(config.split){
53728             if(!this.split){
53729                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
53730                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
53731                 /** The SplitBar for this region 
53732                 * @type Roo.SplitBar */
53733                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
53734                 this.split.on("moved", this.onSplitMove, this);
53735                 this.split.useShim = config.useShim === true;
53736                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
53737                 if(this.useSplitTips){
53738                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
53739                 }
53740                 if(config.collapsible){
53741                     this.split.el.on("dblclick", this.collapse,  this);
53742                 }
53743             }
53744             if(typeof config.minSize != "undefined"){
53745                 this.split.minSize = config.minSize;
53746             }
53747             if(typeof config.maxSize != "undefined"){
53748                 this.split.maxSize = config.maxSize;
53749             }
53750             if(config.hideWhenEmpty || config.hidden || config.collapsed){
53751                 this.hideSplitter();
53752             }
53753         }
53754     },
53755
53756     getHMaxSize : function(){
53757          var cmax = this.config.maxSize || 10000;
53758          var center = this.mgr.getRegion("center");
53759          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
53760     },
53761
53762     getVMaxSize : function(){
53763          var cmax = this.config.maxSize || 10000;
53764          var center = this.mgr.getRegion("center");
53765          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
53766     },
53767
53768     onSplitMove : function(split, newSize){
53769         this.fireEvent("resized", this, newSize);
53770     },
53771     
53772     /** 
53773      * Returns the {@link Roo.SplitBar} for this region.
53774      * @return {Roo.SplitBar}
53775      */
53776     getSplitBar : function(){
53777         return this.split;
53778     },
53779     
53780     hide : function(){
53781         this.hideSplitter();
53782         Roo.SplitLayoutRegion.superclass.hide.call(this);
53783     },
53784
53785     hideSplitter : function(){
53786         if(this.split){
53787             this.split.el.setLocation(-2000,-2000);
53788             this.split.el.hide();
53789         }
53790     },
53791
53792     show : function(){
53793         if(this.split){
53794             this.split.el.show();
53795         }
53796         Roo.SplitLayoutRegion.superclass.show.call(this);
53797     },
53798     
53799     beforeSlide: function(){
53800         if(Roo.isGecko){// firefox overflow auto bug workaround
53801             this.bodyEl.clip();
53802             if(this.tabs) {
53803                 this.tabs.bodyEl.clip();
53804             }
53805             if(this.activePanel){
53806                 this.activePanel.getEl().clip();
53807                 
53808                 if(this.activePanel.beforeSlide){
53809                     this.activePanel.beforeSlide();
53810                 }
53811             }
53812         }
53813     },
53814     
53815     afterSlide : function(){
53816         if(Roo.isGecko){// firefox overflow auto bug workaround
53817             this.bodyEl.unclip();
53818             if(this.tabs) {
53819                 this.tabs.bodyEl.unclip();
53820             }
53821             if(this.activePanel){
53822                 this.activePanel.getEl().unclip();
53823                 if(this.activePanel.afterSlide){
53824                     this.activePanel.afterSlide();
53825                 }
53826             }
53827         }
53828     },
53829
53830     initAutoHide : function(){
53831         if(this.autoHide !== false){
53832             if(!this.autoHideHd){
53833                 var st = new Roo.util.DelayedTask(this.slideIn, this);
53834                 this.autoHideHd = {
53835                     "mouseout": function(e){
53836                         if(!e.within(this.el, true)){
53837                             st.delay(500);
53838                         }
53839                     },
53840                     "mouseover" : function(e){
53841                         st.cancel();
53842                     },
53843                     scope : this
53844                 };
53845             }
53846             this.el.on(this.autoHideHd);
53847         }
53848     },
53849
53850     clearAutoHide : function(){
53851         if(this.autoHide !== false){
53852             this.el.un("mouseout", this.autoHideHd.mouseout);
53853             this.el.un("mouseover", this.autoHideHd.mouseover);
53854         }
53855     },
53856
53857     clearMonitor : function(){
53858         Roo.get(document).un("click", this.slideInIf, this);
53859     },
53860
53861     // these names are backwards but not changed for compat
53862     slideOut : function(){
53863         if(this.isSlid || this.el.hasActiveFx()){
53864             return;
53865         }
53866         this.isSlid = true;
53867         if(this.collapseBtn){
53868             this.collapseBtn.hide();
53869         }
53870         this.closeBtnState = this.closeBtn.getStyle('display');
53871         this.closeBtn.hide();
53872         if(this.stickBtn){
53873             this.stickBtn.show();
53874         }
53875         this.el.show();
53876         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
53877         this.beforeSlide();
53878         this.el.setStyle("z-index", 10001);
53879         this.el.slideIn(this.getSlideAnchor(), {
53880             callback: function(){
53881                 this.afterSlide();
53882                 this.initAutoHide();
53883                 Roo.get(document).on("click", this.slideInIf, this);
53884                 this.fireEvent("slideshow", this);
53885             },
53886             scope: this,
53887             block: true
53888         });
53889     },
53890
53891     afterSlideIn : function(){
53892         this.clearAutoHide();
53893         this.isSlid = false;
53894         this.clearMonitor();
53895         this.el.setStyle("z-index", "");
53896         if(this.collapseBtn){
53897             this.collapseBtn.show();
53898         }
53899         this.closeBtn.setStyle('display', this.closeBtnState);
53900         if(this.stickBtn){
53901             this.stickBtn.hide();
53902         }
53903         this.fireEvent("slidehide", this);
53904     },
53905
53906     slideIn : function(cb){
53907         if(!this.isSlid || this.el.hasActiveFx()){
53908             Roo.callback(cb);
53909             return;
53910         }
53911         this.isSlid = false;
53912         this.beforeSlide();
53913         this.el.slideOut(this.getSlideAnchor(), {
53914             callback: function(){
53915                 this.el.setLeftTop(-10000, -10000);
53916                 this.afterSlide();
53917                 this.afterSlideIn();
53918                 Roo.callback(cb);
53919             },
53920             scope: this,
53921             block: true
53922         });
53923     },
53924     
53925     slideInIf : function(e){
53926         if(!e.within(this.el)){
53927             this.slideIn();
53928         }
53929     },
53930
53931     animateCollapse : function(){
53932         this.beforeSlide();
53933         this.el.setStyle("z-index", 20000);
53934         var anchor = this.getSlideAnchor();
53935         this.el.slideOut(anchor, {
53936             callback : function(){
53937                 this.el.setStyle("z-index", "");
53938                 this.collapsedEl.slideIn(anchor, {duration:.3});
53939                 this.afterSlide();
53940                 this.el.setLocation(-10000,-10000);
53941                 this.el.hide();
53942                 this.fireEvent("collapsed", this);
53943             },
53944             scope: this,
53945             block: true
53946         });
53947     },
53948
53949     animateExpand : function(){
53950         this.beforeSlide();
53951         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
53952         this.el.setStyle("z-index", 20000);
53953         this.collapsedEl.hide({
53954             duration:.1
53955         });
53956         this.el.slideIn(this.getSlideAnchor(), {
53957             callback : function(){
53958                 this.el.setStyle("z-index", "");
53959                 this.afterSlide();
53960                 if(this.split){
53961                     this.split.el.show();
53962                 }
53963                 this.fireEvent("invalidated", this);
53964                 this.fireEvent("expanded", this);
53965             },
53966             scope: this,
53967             block: true
53968         });
53969     },
53970
53971     anchors : {
53972         "west" : "left",
53973         "east" : "right",
53974         "north" : "top",
53975         "south" : "bottom"
53976     },
53977
53978     sanchors : {
53979         "west" : "l",
53980         "east" : "r",
53981         "north" : "t",
53982         "south" : "b"
53983     },
53984
53985     canchors : {
53986         "west" : "tl-tr",
53987         "east" : "tr-tl",
53988         "north" : "tl-bl",
53989         "south" : "bl-tl"
53990     },
53991
53992     getAnchor : function(){
53993         return this.anchors[this.position];
53994     },
53995
53996     getCollapseAnchor : function(){
53997         return this.canchors[this.position];
53998     },
53999
54000     getSlideAnchor : function(){
54001         return this.sanchors[this.position];
54002     },
54003
54004     getAlignAdj : function(){
54005         var cm = this.cmargins;
54006         switch(this.position){
54007             case "west":
54008                 return [0, 0];
54009             break;
54010             case "east":
54011                 return [0, 0];
54012             break;
54013             case "north":
54014                 return [0, 0];
54015             break;
54016             case "south":
54017                 return [0, 0];
54018             break;
54019         }
54020     },
54021
54022     getExpandAdj : function(){
54023         var c = this.collapsedEl, cm = this.cmargins;
54024         switch(this.position){
54025             case "west":
54026                 return [-(cm.right+c.getWidth()+cm.left), 0];
54027             break;
54028             case "east":
54029                 return [cm.right+c.getWidth()+cm.left, 0];
54030             break;
54031             case "north":
54032                 return [0, -(cm.top+cm.bottom+c.getHeight())];
54033             break;
54034             case "south":
54035                 return [0, cm.top+cm.bottom+c.getHeight()];
54036             break;
54037         }
54038     }
54039 });/*
54040  * Based on:
54041  * Ext JS Library 1.1.1
54042  * Copyright(c) 2006-2007, Ext JS, LLC.
54043  *
54044  * Originally Released Under LGPL - original licence link has changed is not relivant.
54045  *
54046  * Fork - LGPL
54047  * <script type="text/javascript">
54048  */
54049 /*
54050  * These classes are private internal classes
54051  */
54052 Roo.CenterLayoutRegion = function(mgr, config){
54053     Roo.LayoutRegion.call(this, mgr, config, "center");
54054     this.visible = true;
54055     this.minWidth = config.minWidth || 20;
54056     this.minHeight = config.minHeight || 20;
54057 };
54058
54059 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
54060     hide : function(){
54061         // center panel can't be hidden
54062     },
54063     
54064     show : function(){
54065         // center panel can't be hidden
54066     },
54067     
54068     getMinWidth: function(){
54069         return this.minWidth;
54070     },
54071     
54072     getMinHeight: function(){
54073         return this.minHeight;
54074     }
54075 });
54076
54077
54078 Roo.NorthLayoutRegion = function(mgr, config){
54079     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
54080     if(this.split){
54081         this.split.placement = Roo.SplitBar.TOP;
54082         this.split.orientation = Roo.SplitBar.VERTICAL;
54083         this.split.el.addClass("x-layout-split-v");
54084     }
54085     var size = config.initialSize || config.height;
54086     if(typeof size != "undefined"){
54087         this.el.setHeight(size);
54088     }
54089 };
54090 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
54091     orientation: Roo.SplitBar.VERTICAL,
54092     getBox : function(){
54093         if(this.collapsed){
54094             return this.collapsedEl.getBox();
54095         }
54096         var box = this.el.getBox();
54097         if(this.split){
54098             box.height += this.split.el.getHeight();
54099         }
54100         return box;
54101     },
54102     
54103     updateBox : function(box){
54104         if(this.split && !this.collapsed){
54105             box.height -= this.split.el.getHeight();
54106             this.split.el.setLeft(box.x);
54107             this.split.el.setTop(box.y+box.height);
54108             this.split.el.setWidth(box.width);
54109         }
54110         if(this.collapsed){
54111             this.updateBody(box.width, null);
54112         }
54113         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54114     }
54115 });
54116
54117 Roo.SouthLayoutRegion = function(mgr, config){
54118     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
54119     if(this.split){
54120         this.split.placement = Roo.SplitBar.BOTTOM;
54121         this.split.orientation = Roo.SplitBar.VERTICAL;
54122         this.split.el.addClass("x-layout-split-v");
54123     }
54124     var size = config.initialSize || config.height;
54125     if(typeof size != "undefined"){
54126         this.el.setHeight(size);
54127     }
54128 };
54129 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
54130     orientation: Roo.SplitBar.VERTICAL,
54131     getBox : function(){
54132         if(this.collapsed){
54133             return this.collapsedEl.getBox();
54134         }
54135         var box = this.el.getBox();
54136         if(this.split){
54137             var sh = this.split.el.getHeight();
54138             box.height += sh;
54139             box.y -= sh;
54140         }
54141         return box;
54142     },
54143     
54144     updateBox : function(box){
54145         if(this.split && !this.collapsed){
54146             var sh = this.split.el.getHeight();
54147             box.height -= sh;
54148             box.y += sh;
54149             this.split.el.setLeft(box.x);
54150             this.split.el.setTop(box.y-sh);
54151             this.split.el.setWidth(box.width);
54152         }
54153         if(this.collapsed){
54154             this.updateBody(box.width, null);
54155         }
54156         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54157     }
54158 });
54159
54160 Roo.EastLayoutRegion = function(mgr, config){
54161     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
54162     if(this.split){
54163         this.split.placement = Roo.SplitBar.RIGHT;
54164         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54165         this.split.el.addClass("x-layout-split-h");
54166     }
54167     var size = config.initialSize || config.width;
54168     if(typeof size != "undefined"){
54169         this.el.setWidth(size);
54170     }
54171 };
54172 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
54173     orientation: Roo.SplitBar.HORIZONTAL,
54174     getBox : function(){
54175         if(this.collapsed){
54176             return this.collapsedEl.getBox();
54177         }
54178         var box = this.el.getBox();
54179         if(this.split){
54180             var sw = this.split.el.getWidth();
54181             box.width += sw;
54182             box.x -= sw;
54183         }
54184         return box;
54185     },
54186
54187     updateBox : function(box){
54188         if(this.split && !this.collapsed){
54189             var sw = this.split.el.getWidth();
54190             box.width -= sw;
54191             this.split.el.setLeft(box.x);
54192             this.split.el.setTop(box.y);
54193             this.split.el.setHeight(box.height);
54194             box.x += sw;
54195         }
54196         if(this.collapsed){
54197             this.updateBody(null, box.height);
54198         }
54199         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54200     }
54201 });
54202
54203 Roo.WestLayoutRegion = function(mgr, config){
54204     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
54205     if(this.split){
54206         this.split.placement = Roo.SplitBar.LEFT;
54207         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54208         this.split.el.addClass("x-layout-split-h");
54209     }
54210     var size = config.initialSize || config.width;
54211     if(typeof size != "undefined"){
54212         this.el.setWidth(size);
54213     }
54214 };
54215 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
54216     orientation: Roo.SplitBar.HORIZONTAL,
54217     getBox : function(){
54218         if(this.collapsed){
54219             return this.collapsedEl.getBox();
54220         }
54221         var box = this.el.getBox();
54222         if(this.split){
54223             box.width += this.split.el.getWidth();
54224         }
54225         return box;
54226     },
54227     
54228     updateBox : function(box){
54229         if(this.split && !this.collapsed){
54230             var sw = this.split.el.getWidth();
54231             box.width -= sw;
54232             this.split.el.setLeft(box.x+box.width);
54233             this.split.el.setTop(box.y);
54234             this.split.el.setHeight(box.height);
54235         }
54236         if(this.collapsed){
54237             this.updateBody(null, box.height);
54238         }
54239         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54240     }
54241 });
54242 /*
54243  * Based on:
54244  * Ext JS Library 1.1.1
54245  * Copyright(c) 2006-2007, Ext JS, LLC.
54246  *
54247  * Originally Released Under LGPL - original licence link has changed is not relivant.
54248  *
54249  * Fork - LGPL
54250  * <script type="text/javascript">
54251  */
54252  
54253  
54254 /*
54255  * Private internal class for reading and applying state
54256  */
54257 Roo.LayoutStateManager = function(layout){
54258      // default empty state
54259      this.state = {
54260         north: {},
54261         south: {},
54262         east: {},
54263         west: {}       
54264     };
54265 };
54266
54267 Roo.LayoutStateManager.prototype = {
54268     init : function(layout, provider){
54269         this.provider = provider;
54270         var state = provider.get(layout.id+"-layout-state");
54271         if(state){
54272             var wasUpdating = layout.isUpdating();
54273             if(!wasUpdating){
54274                 layout.beginUpdate();
54275             }
54276             for(var key in state){
54277                 if(typeof state[key] != "function"){
54278                     var rstate = state[key];
54279                     var r = layout.getRegion(key);
54280                     if(r && rstate){
54281                         if(rstate.size){
54282                             r.resizeTo(rstate.size);
54283                         }
54284                         if(rstate.collapsed == true){
54285                             r.collapse(true);
54286                         }else{
54287                             r.expand(null, true);
54288                         }
54289                     }
54290                 }
54291             }
54292             if(!wasUpdating){
54293                 layout.endUpdate();
54294             }
54295             this.state = state; 
54296         }
54297         this.layout = layout;
54298         layout.on("regionresized", this.onRegionResized, this);
54299         layout.on("regioncollapsed", this.onRegionCollapsed, this);
54300         layout.on("regionexpanded", this.onRegionExpanded, this);
54301     },
54302     
54303     storeState : function(){
54304         this.provider.set(this.layout.id+"-layout-state", this.state);
54305     },
54306     
54307     onRegionResized : function(region, newSize){
54308         this.state[region.getPosition()].size = newSize;
54309         this.storeState();
54310     },
54311     
54312     onRegionCollapsed : function(region){
54313         this.state[region.getPosition()].collapsed = true;
54314         this.storeState();
54315     },
54316     
54317     onRegionExpanded : function(region){
54318         this.state[region.getPosition()].collapsed = false;
54319         this.storeState();
54320     }
54321 };/*
54322  * Based on:
54323  * Ext JS Library 1.1.1
54324  * Copyright(c) 2006-2007, Ext JS, LLC.
54325  *
54326  * Originally Released Under LGPL - original licence link has changed is not relivant.
54327  *
54328  * Fork - LGPL
54329  * <script type="text/javascript">
54330  */
54331 /**
54332  * @class Roo.ContentPanel
54333  * @extends Roo.util.Observable
54334  * A basic ContentPanel element.
54335  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
54336  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
54337  * @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
54338  * @cfg {Boolean}   closable      True if the panel can be closed/removed
54339  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
54340  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
54341  * @cfg {Toolbar}   toolbar       A toolbar for this panel
54342  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
54343  * @cfg {String} title          The title for this panel
54344  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
54345  * @cfg {String} url            Calls {@link #setUrl} with this value
54346  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
54347  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
54348  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
54349  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
54350  * @cfg {String}    style  Extra style to add to the content panel 
54351
54352  * @constructor
54353  * Create a new ContentPanel.
54354  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
54355  * @param {String/Object} config A string to set only the title or a config object
54356  * @param {String} content (optional) Set the HTML content for this panel
54357  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
54358  */
54359 Roo.ContentPanel = function(el, config, content){
54360     
54361      
54362     /*
54363     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
54364         config = el;
54365         el = Roo.id();
54366     }
54367     if (config && config.parentLayout) { 
54368         el = config.parentLayout.el.createChild(); 
54369     }
54370     */
54371     if(el.autoCreate){ // xtype is available if this is called from factory
54372         config = el;
54373         el = Roo.id();
54374     }
54375     this.el = Roo.get(el);
54376     if(!this.el && config && config.autoCreate){
54377         if(typeof config.autoCreate == "object"){
54378             if(!config.autoCreate.id){
54379                 config.autoCreate.id = config.id||el;
54380             }
54381             this.el = Roo.DomHelper.append(document.body,
54382                         config.autoCreate, true);
54383         }else{
54384             this.el = Roo.DomHelper.append(document.body,
54385                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
54386         }
54387     }
54388     
54389     
54390     this.closable = false;
54391     this.loaded = false;
54392     this.active = false;
54393     if(typeof config == "string"){
54394         this.title = config;
54395     }else{
54396         Roo.apply(this, config);
54397     }
54398     
54399     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
54400         this.wrapEl = this.el.wrap();
54401         this.toolbar.container = this.el.insertSibling(false, 'before');
54402         this.toolbar = new Roo.Toolbar(this.toolbar);
54403     }
54404     
54405     // xtype created footer. - not sure if will work as we normally have to render first..
54406     if (this.footer && !this.footer.el && this.footer.xtype) {
54407         if (!this.wrapEl) {
54408             this.wrapEl = this.el.wrap();
54409         }
54410     
54411         this.footer.container = this.wrapEl.createChild();
54412          
54413         this.footer = Roo.factory(this.footer, Roo);
54414         
54415     }
54416     
54417     if(this.resizeEl){
54418         this.resizeEl = Roo.get(this.resizeEl, true);
54419     }else{
54420         this.resizeEl = this.el;
54421     }
54422     // handle view.xtype
54423     
54424  
54425     
54426     
54427     this.addEvents({
54428         /**
54429          * @event activate
54430          * Fires when this panel is activated. 
54431          * @param {Roo.ContentPanel} this
54432          */
54433         "activate" : true,
54434         /**
54435          * @event deactivate
54436          * Fires when this panel is activated. 
54437          * @param {Roo.ContentPanel} this
54438          */
54439         "deactivate" : true,
54440
54441         /**
54442          * @event resize
54443          * Fires when this panel is resized if fitToFrame is true.
54444          * @param {Roo.ContentPanel} this
54445          * @param {Number} width The width after any component adjustments
54446          * @param {Number} height The height after any component adjustments
54447          */
54448         "resize" : true,
54449         
54450          /**
54451          * @event render
54452          * Fires when this tab is created
54453          * @param {Roo.ContentPanel} this
54454          */
54455         "render" : true
54456          
54457         
54458     });
54459     
54460
54461     
54462     
54463     if(this.autoScroll){
54464         this.resizeEl.setStyle("overflow", "auto");
54465     } else {
54466         // fix randome scrolling
54467         this.el.on('scroll', function() {
54468             Roo.log('fix random scolling');
54469             this.scrollTo('top',0); 
54470         });
54471     }
54472     content = content || this.content;
54473     if(content){
54474         this.setContent(content);
54475     }
54476     if(config && config.url){
54477         this.setUrl(this.url, this.params, this.loadOnce);
54478     }
54479     
54480     
54481     
54482     Roo.ContentPanel.superclass.constructor.call(this);
54483     
54484     if (this.view && typeof(this.view.xtype) != 'undefined') {
54485         this.view.el = this.el.appendChild(document.createElement("div"));
54486         this.view = Roo.factory(this.view); 
54487         this.view.render  &&  this.view.render(false, '');  
54488     }
54489     
54490     
54491     this.fireEvent('render', this);
54492 };
54493
54494 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
54495     tabTip:'',
54496     setRegion : function(region){
54497         this.region = region;
54498         if(region){
54499            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
54500         }else{
54501            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
54502         } 
54503     },
54504     
54505     /**
54506      * Returns the toolbar for this Panel if one was configured. 
54507      * @return {Roo.Toolbar} 
54508      */
54509     getToolbar : function(){
54510         return this.toolbar;
54511     },
54512     
54513     setActiveState : function(active){
54514         this.active = active;
54515         if(!active){
54516             this.fireEvent("deactivate", this);
54517         }else{
54518             this.fireEvent("activate", this);
54519         }
54520     },
54521     /**
54522      * Updates this panel's element
54523      * @param {String} content The new content
54524      * @param {Boolean} loadScripts (optional) true to look for and process scripts
54525     */
54526     setContent : function(content, loadScripts){
54527         this.el.update(content, loadScripts);
54528     },
54529
54530     ignoreResize : function(w, h){
54531         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
54532             return true;
54533         }else{
54534             this.lastSize = {width: w, height: h};
54535             return false;
54536         }
54537     },
54538     /**
54539      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
54540      * @return {Roo.UpdateManager} The UpdateManager
54541      */
54542     getUpdateManager : function(){
54543         return this.el.getUpdateManager();
54544     },
54545      /**
54546      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
54547      * @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:
54548 <pre><code>
54549 panel.load({
54550     url: "your-url.php",
54551     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
54552     callback: yourFunction,
54553     scope: yourObject, //(optional scope)
54554     discardUrl: false,
54555     nocache: false,
54556     text: "Loading...",
54557     timeout: 30,
54558     scripts: false
54559 });
54560 </code></pre>
54561      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
54562      * 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.
54563      * @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}
54564      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
54565      * @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.
54566      * @return {Roo.ContentPanel} this
54567      */
54568     load : function(){
54569         var um = this.el.getUpdateManager();
54570         um.update.apply(um, arguments);
54571         return this;
54572     },
54573
54574
54575     /**
54576      * 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.
54577      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
54578      * @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)
54579      * @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)
54580      * @return {Roo.UpdateManager} The UpdateManager
54581      */
54582     setUrl : function(url, params, loadOnce){
54583         if(this.refreshDelegate){
54584             this.removeListener("activate", this.refreshDelegate);
54585         }
54586         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
54587         this.on("activate", this.refreshDelegate);
54588         return this.el.getUpdateManager();
54589     },
54590     
54591     _handleRefresh : function(url, params, loadOnce){
54592         if(!loadOnce || !this.loaded){
54593             var updater = this.el.getUpdateManager();
54594             updater.update(url, params, this._setLoaded.createDelegate(this));
54595         }
54596     },
54597     
54598     _setLoaded : function(){
54599         this.loaded = true;
54600     }, 
54601     
54602     /**
54603      * Returns this panel's id
54604      * @return {String} 
54605      */
54606     getId : function(){
54607         return this.el.id;
54608     },
54609     
54610     /** 
54611      * Returns this panel's element - used by regiosn to add.
54612      * @return {Roo.Element} 
54613      */
54614     getEl : function(){
54615         return this.wrapEl || this.el;
54616     },
54617     
54618     adjustForComponents : function(width, height)
54619     {
54620         //Roo.log('adjustForComponents ');
54621         if(this.resizeEl != this.el){
54622             width -= this.el.getFrameWidth('lr');
54623             height -= this.el.getFrameWidth('tb');
54624         }
54625         if(this.toolbar){
54626             var te = this.toolbar.getEl();
54627             height -= te.getHeight();
54628             te.setWidth(width);
54629         }
54630         if(this.footer){
54631             var te = this.footer.getEl();
54632             //Roo.log("footer:" + te.getHeight());
54633             
54634             height -= te.getHeight();
54635             te.setWidth(width);
54636         }
54637         
54638         
54639         if(this.adjustments){
54640             width += this.adjustments[0];
54641             height += this.adjustments[1];
54642         }
54643         return {"width": width, "height": height};
54644     },
54645     
54646     setSize : function(width, height){
54647         if(this.fitToFrame && !this.ignoreResize(width, height)){
54648             if(this.fitContainer && this.resizeEl != this.el){
54649                 this.el.setSize(width, height);
54650             }
54651             var size = this.adjustForComponents(width, height);
54652             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
54653             this.fireEvent('resize', this, size.width, size.height);
54654         }
54655     },
54656     
54657     /**
54658      * Returns this panel's title
54659      * @return {String} 
54660      */
54661     getTitle : function(){
54662         return this.title;
54663     },
54664     
54665     /**
54666      * Set this panel's title
54667      * @param {String} title
54668      */
54669     setTitle : function(title){
54670         this.title = title;
54671         if(this.region){
54672             this.region.updatePanelTitle(this, title);
54673         }
54674     },
54675     
54676     /**
54677      * Returns true is this panel was configured to be closable
54678      * @return {Boolean} 
54679      */
54680     isClosable : function(){
54681         return this.closable;
54682     },
54683     
54684     beforeSlide : function(){
54685         this.el.clip();
54686         this.resizeEl.clip();
54687     },
54688     
54689     afterSlide : function(){
54690         this.el.unclip();
54691         this.resizeEl.unclip();
54692     },
54693     
54694     /**
54695      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
54696      *   Will fail silently if the {@link #setUrl} method has not been called.
54697      *   This does not activate the panel, just updates its content.
54698      */
54699     refresh : function(){
54700         if(this.refreshDelegate){
54701            this.loaded = false;
54702            this.refreshDelegate();
54703         }
54704     },
54705     
54706     /**
54707      * Destroys this panel
54708      */
54709     destroy : function(){
54710         this.el.removeAllListeners();
54711         var tempEl = document.createElement("span");
54712         tempEl.appendChild(this.el.dom);
54713         tempEl.innerHTML = "";
54714         this.el.remove();
54715         this.el = null;
54716     },
54717     
54718     /**
54719      * form - if the content panel contains a form - this is a reference to it.
54720      * @type {Roo.form.Form}
54721      */
54722     form : false,
54723     /**
54724      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
54725      *    This contains a reference to it.
54726      * @type {Roo.View}
54727      */
54728     view : false,
54729     
54730       /**
54731      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
54732      * <pre><code>
54733
54734 layout.addxtype({
54735        xtype : 'Form',
54736        items: [ .... ]
54737    }
54738 );
54739
54740 </code></pre>
54741      * @param {Object} cfg Xtype definition of item to add.
54742      */
54743     
54744     addxtype : function(cfg) {
54745         // add form..
54746         if (cfg.xtype.match(/^Form$/)) {
54747             
54748             var el;
54749             //if (this.footer) {
54750             //    el = this.footer.container.insertSibling(false, 'before');
54751             //} else {
54752                 el = this.el.createChild();
54753             //}
54754
54755             this.form = new  Roo.form.Form(cfg);
54756             
54757             
54758             if ( this.form.allItems.length) {
54759                 this.form.render(el.dom);
54760             }
54761             return this.form;
54762         }
54763         // should only have one of theses..
54764         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
54765             // views.. should not be just added - used named prop 'view''
54766             
54767             cfg.el = this.el.appendChild(document.createElement("div"));
54768             // factory?
54769             
54770             var ret = new Roo.factory(cfg);
54771              
54772              ret.render && ret.render(false, ''); // render blank..
54773             this.view = ret;
54774             return ret;
54775         }
54776         return false;
54777     }
54778 });
54779
54780 /**
54781  * @class Roo.GridPanel
54782  * @extends Roo.ContentPanel
54783  * @constructor
54784  * Create a new GridPanel.
54785  * @param {Roo.grid.Grid} grid The grid for this panel
54786  * @param {String/Object} config A string to set only the panel's title, or a config object
54787  */
54788 Roo.GridPanel = function(grid, config){
54789     
54790   
54791     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
54792         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
54793         
54794     this.wrapper.dom.appendChild(grid.getGridEl().dom);
54795     
54796     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
54797     
54798     if(this.toolbar){
54799         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
54800     }
54801     // xtype created footer. - not sure if will work as we normally have to render first..
54802     if (this.footer && !this.footer.el && this.footer.xtype) {
54803         
54804         this.footer.container = this.grid.getView().getFooterPanel(true);
54805         this.footer.dataSource = this.grid.dataSource;
54806         this.footer = Roo.factory(this.footer, Roo);
54807         
54808     }
54809     
54810     grid.monitorWindowResize = false; // turn off autosizing
54811     grid.autoHeight = false;
54812     grid.autoWidth = false;
54813     this.grid = grid;
54814     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
54815 };
54816
54817 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
54818     getId : function(){
54819         return this.grid.id;
54820     },
54821     
54822     /**
54823      * Returns the grid for this panel
54824      * @return {Roo.grid.Grid} 
54825      */
54826     getGrid : function(){
54827         return this.grid;    
54828     },
54829     
54830     setSize : function(width, height){
54831         if(!this.ignoreResize(width, height)){
54832             var grid = this.grid;
54833             var size = this.adjustForComponents(width, height);
54834             grid.getGridEl().setSize(size.width, size.height);
54835             grid.autoSize();
54836         }
54837     },
54838     
54839     beforeSlide : function(){
54840         this.grid.getView().scroller.clip();
54841     },
54842     
54843     afterSlide : function(){
54844         this.grid.getView().scroller.unclip();
54845     },
54846     
54847     destroy : function(){
54848         this.grid.destroy();
54849         delete this.grid;
54850         Roo.GridPanel.superclass.destroy.call(this); 
54851     }
54852 });
54853
54854
54855 /**
54856  * @class Roo.NestedLayoutPanel
54857  * @extends Roo.ContentPanel
54858  * @constructor
54859  * Create a new NestedLayoutPanel.
54860  * 
54861  * 
54862  * @param {Roo.BorderLayout} layout The layout for this panel
54863  * @param {String/Object} config A string to set only the title or a config object
54864  */
54865 Roo.NestedLayoutPanel = function(layout, config)
54866 {
54867     // construct with only one argument..
54868     /* FIXME - implement nicer consturctors
54869     if (layout.layout) {
54870         config = layout;
54871         layout = config.layout;
54872         delete config.layout;
54873     }
54874     if (layout.xtype && !layout.getEl) {
54875         // then layout needs constructing..
54876         layout = Roo.factory(layout, Roo);
54877     }
54878     */
54879     
54880     
54881     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
54882     
54883     layout.monitorWindowResize = false; // turn off autosizing
54884     this.layout = layout;
54885     this.layout.getEl().addClass("x-layout-nested-layout");
54886     
54887     
54888     
54889     
54890 };
54891
54892 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
54893
54894     setSize : function(width, height){
54895         if(!this.ignoreResize(width, height)){
54896             var size = this.adjustForComponents(width, height);
54897             var el = this.layout.getEl();
54898             el.setSize(size.width, size.height);
54899             var touch = el.dom.offsetWidth;
54900             this.layout.layout();
54901             // ie requires a double layout on the first pass
54902             if(Roo.isIE && !this.initialized){
54903                 this.initialized = true;
54904                 this.layout.layout();
54905             }
54906         }
54907     },
54908     
54909     // activate all subpanels if not currently active..
54910     
54911     setActiveState : function(active){
54912         this.active = active;
54913         if(!active){
54914             this.fireEvent("deactivate", this);
54915             return;
54916         }
54917         
54918         this.fireEvent("activate", this);
54919         // not sure if this should happen before or after..
54920         if (!this.layout) {
54921             return; // should not happen..
54922         }
54923         var reg = false;
54924         for (var r in this.layout.regions) {
54925             reg = this.layout.getRegion(r);
54926             if (reg.getActivePanel()) {
54927                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
54928                 reg.setActivePanel(reg.getActivePanel());
54929                 continue;
54930             }
54931             if (!reg.panels.length) {
54932                 continue;
54933             }
54934             reg.showPanel(reg.getPanel(0));
54935         }
54936         
54937         
54938         
54939         
54940     },
54941     
54942     /**
54943      * Returns the nested BorderLayout for this panel
54944      * @return {Roo.BorderLayout} 
54945      */
54946     getLayout : function(){
54947         return this.layout;
54948     },
54949     
54950      /**
54951      * Adds a xtype elements to the layout of the nested panel
54952      * <pre><code>
54953
54954 panel.addxtype({
54955        xtype : 'ContentPanel',
54956        region: 'west',
54957        items: [ .... ]
54958    }
54959 );
54960
54961 panel.addxtype({
54962         xtype : 'NestedLayoutPanel',
54963         region: 'west',
54964         layout: {
54965            center: { },
54966            west: { }   
54967         },
54968         items : [ ... list of content panels or nested layout panels.. ]
54969    }
54970 );
54971 </code></pre>
54972      * @param {Object} cfg Xtype definition of item to add.
54973      */
54974     addxtype : function(cfg) {
54975         return this.layout.addxtype(cfg);
54976     
54977     }
54978 });
54979
54980 Roo.ScrollPanel = function(el, config, content){
54981     config = config || {};
54982     config.fitToFrame = true;
54983     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
54984     
54985     this.el.dom.style.overflow = "hidden";
54986     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
54987     this.el.removeClass("x-layout-inactive-content");
54988     this.el.on("mousewheel", this.onWheel, this);
54989
54990     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
54991     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
54992     up.unselectable(); down.unselectable();
54993     up.on("click", this.scrollUp, this);
54994     down.on("click", this.scrollDown, this);
54995     up.addClassOnOver("x-scroller-btn-over");
54996     down.addClassOnOver("x-scroller-btn-over");
54997     up.addClassOnClick("x-scroller-btn-click");
54998     down.addClassOnClick("x-scroller-btn-click");
54999     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
55000
55001     this.resizeEl = this.el;
55002     this.el = wrap; this.up = up; this.down = down;
55003 };
55004
55005 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
55006     increment : 100,
55007     wheelIncrement : 5,
55008     scrollUp : function(){
55009         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
55010     },
55011
55012     scrollDown : function(){
55013         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
55014     },
55015
55016     afterScroll : function(){
55017         var el = this.resizeEl;
55018         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
55019         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
55020         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
55021     },
55022
55023     setSize : function(){
55024         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
55025         this.afterScroll();
55026     },
55027
55028     onWheel : function(e){
55029         var d = e.getWheelDelta();
55030         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
55031         this.afterScroll();
55032         e.stopEvent();
55033     },
55034
55035     setContent : function(content, loadScripts){
55036         this.resizeEl.update(content, loadScripts);
55037     }
55038
55039 });
55040
55041
55042
55043
55044
55045
55046
55047
55048
55049 /**
55050  * @class Roo.TreePanel
55051  * @extends Roo.ContentPanel
55052  * @constructor
55053  * Create a new TreePanel. - defaults to fit/scoll contents.
55054  * @param {String/Object} config A string to set only the panel's title, or a config object
55055  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
55056  */
55057 Roo.TreePanel = function(config){
55058     var el = config.el;
55059     var tree = config.tree;
55060     delete config.tree; 
55061     delete config.el; // hopefull!
55062     
55063     // wrapper for IE7 strict & safari scroll issue
55064     
55065     var treeEl = el.createChild();
55066     config.resizeEl = treeEl;
55067     
55068     
55069     
55070     Roo.TreePanel.superclass.constructor.call(this, el, config);
55071  
55072  
55073     this.tree = new Roo.tree.TreePanel(treeEl , tree);
55074     //console.log(tree);
55075     this.on('activate', function()
55076     {
55077         if (this.tree.rendered) {
55078             return;
55079         }
55080         //console.log('render tree');
55081         this.tree.render();
55082     });
55083     // this should not be needed.. - it's actually the 'el' that resizes?
55084     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
55085     
55086     //this.on('resize',  function (cp, w, h) {
55087     //        this.tree.innerCt.setWidth(w);
55088     //        this.tree.innerCt.setHeight(h);
55089     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
55090     //});
55091
55092         
55093     
55094 };
55095
55096 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
55097     fitToFrame : true,
55098     autoScroll : true
55099 });
55100
55101
55102
55103
55104
55105
55106
55107
55108
55109
55110
55111 /*
55112  * Based on:
55113  * Ext JS Library 1.1.1
55114  * Copyright(c) 2006-2007, Ext JS, LLC.
55115  *
55116  * Originally Released Under LGPL - original licence link has changed is not relivant.
55117  *
55118  * Fork - LGPL
55119  * <script type="text/javascript">
55120  */
55121  
55122
55123 /**
55124  * @class Roo.ReaderLayout
55125  * @extends Roo.BorderLayout
55126  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
55127  * center region containing two nested regions (a top one for a list view and one for item preview below),
55128  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
55129  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
55130  * expedites the setup of the overall layout and regions for this common application style.
55131  * Example:
55132  <pre><code>
55133 var reader = new Roo.ReaderLayout();
55134 var CP = Roo.ContentPanel;  // shortcut for adding
55135
55136 reader.beginUpdate();
55137 reader.add("north", new CP("north", "North"));
55138 reader.add("west", new CP("west", {title: "West"}));
55139 reader.add("east", new CP("east", {title: "East"}));
55140
55141 reader.regions.listView.add(new CP("listView", "List"));
55142 reader.regions.preview.add(new CP("preview", "Preview"));
55143 reader.endUpdate();
55144 </code></pre>
55145 * @constructor
55146 * Create a new ReaderLayout
55147 * @param {Object} config Configuration options
55148 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
55149 * document.body if omitted)
55150 */
55151 Roo.ReaderLayout = function(config, renderTo){
55152     var c = config || {size:{}};
55153     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
55154         north: c.north !== false ? Roo.apply({
55155             split:false,
55156             initialSize: 32,
55157             titlebar: false
55158         }, c.north) : false,
55159         west: c.west !== false ? Roo.apply({
55160             split:true,
55161             initialSize: 200,
55162             minSize: 175,
55163             maxSize: 400,
55164             titlebar: true,
55165             collapsible: true,
55166             animate: true,
55167             margins:{left:5,right:0,bottom:5,top:5},
55168             cmargins:{left:5,right:5,bottom:5,top:5}
55169         }, c.west) : false,
55170         east: c.east !== false ? Roo.apply({
55171             split:true,
55172             initialSize: 200,
55173             minSize: 175,
55174             maxSize: 400,
55175             titlebar: true,
55176             collapsible: true,
55177             animate: true,
55178             margins:{left:0,right:5,bottom:5,top:5},
55179             cmargins:{left:5,right:5,bottom:5,top:5}
55180         }, c.east) : false,
55181         center: Roo.apply({
55182             tabPosition: 'top',
55183             autoScroll:false,
55184             closeOnTab: true,
55185             titlebar:false,
55186             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
55187         }, c.center)
55188     });
55189
55190     this.el.addClass('x-reader');
55191
55192     this.beginUpdate();
55193
55194     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
55195         south: c.preview !== false ? Roo.apply({
55196             split:true,
55197             initialSize: 200,
55198             minSize: 100,
55199             autoScroll:true,
55200             collapsible:true,
55201             titlebar: true,
55202             cmargins:{top:5,left:0, right:0, bottom:0}
55203         }, c.preview) : false,
55204         center: Roo.apply({
55205             autoScroll:false,
55206             titlebar:false,
55207             minHeight:200
55208         }, c.listView)
55209     });
55210     this.add('center', new Roo.NestedLayoutPanel(inner,
55211             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
55212
55213     this.endUpdate();
55214
55215     this.regions.preview = inner.getRegion('south');
55216     this.regions.listView = inner.getRegion('center');
55217 };
55218
55219 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
55220  * Based on:
55221  * Ext JS Library 1.1.1
55222  * Copyright(c) 2006-2007, Ext JS, LLC.
55223  *
55224  * Originally Released Under LGPL - original licence link has changed is not relivant.
55225  *
55226  * Fork - LGPL
55227  * <script type="text/javascript">
55228  */
55229  
55230 /**
55231  * @class Roo.grid.Grid
55232  * @extends Roo.util.Observable
55233  * This class represents the primary interface of a component based grid control.
55234  * <br><br>Usage:<pre><code>
55235  var grid = new Roo.grid.Grid("my-container-id", {
55236      ds: myDataStore,
55237      cm: myColModel,
55238      selModel: mySelectionModel,
55239      autoSizeColumns: true,
55240      monitorWindowResize: false,
55241      trackMouseOver: true
55242  });
55243  // set any options
55244  grid.render();
55245  * </code></pre>
55246  * <b>Common Problems:</b><br/>
55247  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
55248  * element will correct this<br/>
55249  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
55250  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
55251  * are unpredictable.<br/>
55252  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
55253  * grid to calculate dimensions/offsets.<br/>
55254   * @constructor
55255  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55256  * The container MUST have some type of size defined for the grid to fill. The container will be
55257  * automatically set to position relative if it isn't already.
55258  * @param {Object} config A config object that sets properties on this grid.
55259  */
55260 Roo.grid.Grid = function(container, config){
55261         // initialize the container
55262         this.container = Roo.get(container);
55263         this.container.update("");
55264         this.container.setStyle("overflow", "hidden");
55265     this.container.addClass('x-grid-container');
55266
55267     this.id = this.container.id;
55268
55269     Roo.apply(this, config);
55270     // check and correct shorthanded configs
55271     if(this.ds){
55272         this.dataSource = this.ds;
55273         delete this.ds;
55274     }
55275     if(this.cm){
55276         this.colModel = this.cm;
55277         delete this.cm;
55278     }
55279     if(this.sm){
55280         this.selModel = this.sm;
55281         delete this.sm;
55282     }
55283
55284     if (this.selModel) {
55285         this.selModel = Roo.factory(this.selModel, Roo.grid);
55286         this.sm = this.selModel;
55287         this.sm.xmodule = this.xmodule || false;
55288     }
55289     if (typeof(this.colModel.config) == 'undefined') {
55290         this.colModel = new Roo.grid.ColumnModel(this.colModel);
55291         this.cm = this.colModel;
55292         this.cm.xmodule = this.xmodule || false;
55293     }
55294     if (this.dataSource) {
55295         this.dataSource= Roo.factory(this.dataSource, Roo.data);
55296         this.ds = this.dataSource;
55297         this.ds.xmodule = this.xmodule || false;
55298          
55299     }
55300     
55301     
55302     
55303     if(this.width){
55304         this.container.setWidth(this.width);
55305     }
55306
55307     if(this.height){
55308         this.container.setHeight(this.height);
55309     }
55310     /** @private */
55311         this.addEvents({
55312         // raw events
55313         /**
55314          * @event click
55315          * The raw click event for the entire grid.
55316          * @param {Roo.EventObject} e
55317          */
55318         "click" : true,
55319         /**
55320          * @event dblclick
55321          * The raw dblclick event for the entire grid.
55322          * @param {Roo.EventObject} e
55323          */
55324         "dblclick" : true,
55325         /**
55326          * @event contextmenu
55327          * The raw contextmenu event for the entire grid.
55328          * @param {Roo.EventObject} e
55329          */
55330         "contextmenu" : true,
55331         /**
55332          * @event mousedown
55333          * The raw mousedown event for the entire grid.
55334          * @param {Roo.EventObject} e
55335          */
55336         "mousedown" : true,
55337         /**
55338          * @event mouseup
55339          * The raw mouseup event for the entire grid.
55340          * @param {Roo.EventObject} e
55341          */
55342         "mouseup" : true,
55343         /**
55344          * @event mouseover
55345          * The raw mouseover event for the entire grid.
55346          * @param {Roo.EventObject} e
55347          */
55348         "mouseover" : true,
55349         /**
55350          * @event mouseout
55351          * The raw mouseout event for the entire grid.
55352          * @param {Roo.EventObject} e
55353          */
55354         "mouseout" : true,
55355         /**
55356          * @event keypress
55357          * The raw keypress event for the entire grid.
55358          * @param {Roo.EventObject} e
55359          */
55360         "keypress" : true,
55361         /**
55362          * @event keydown
55363          * The raw keydown event for the entire grid.
55364          * @param {Roo.EventObject} e
55365          */
55366         "keydown" : true,
55367
55368         // custom events
55369
55370         /**
55371          * @event cellclick
55372          * Fires when a cell is clicked
55373          * @param {Grid} this
55374          * @param {Number} rowIndex
55375          * @param {Number} columnIndex
55376          * @param {Roo.EventObject} e
55377          */
55378         "cellclick" : true,
55379         /**
55380          * @event celldblclick
55381          * Fires when a cell is double clicked
55382          * @param {Grid} this
55383          * @param {Number} rowIndex
55384          * @param {Number} columnIndex
55385          * @param {Roo.EventObject} e
55386          */
55387         "celldblclick" : true,
55388         /**
55389          * @event rowclick
55390          * Fires when a row is clicked
55391          * @param {Grid} this
55392          * @param {Number} rowIndex
55393          * @param {Roo.EventObject} e
55394          */
55395         "rowclick" : true,
55396         /**
55397          * @event rowdblclick
55398          * Fires when a row is double clicked
55399          * @param {Grid} this
55400          * @param {Number} rowIndex
55401          * @param {Roo.EventObject} e
55402          */
55403         "rowdblclick" : true,
55404         /**
55405          * @event headerclick
55406          * Fires when a header is clicked
55407          * @param {Grid} this
55408          * @param {Number} columnIndex
55409          * @param {Roo.EventObject} e
55410          */
55411         "headerclick" : true,
55412         /**
55413          * @event headerdblclick
55414          * Fires when a header cell is double clicked
55415          * @param {Grid} this
55416          * @param {Number} columnIndex
55417          * @param {Roo.EventObject} e
55418          */
55419         "headerdblclick" : true,
55420         /**
55421          * @event rowcontextmenu
55422          * Fires when a row is right clicked
55423          * @param {Grid} this
55424          * @param {Number} rowIndex
55425          * @param {Roo.EventObject} e
55426          */
55427         "rowcontextmenu" : true,
55428         /**
55429          * @event cellcontextmenu
55430          * Fires when a cell is right clicked
55431          * @param {Grid} this
55432          * @param {Number} rowIndex
55433          * @param {Number} cellIndex
55434          * @param {Roo.EventObject} e
55435          */
55436          "cellcontextmenu" : true,
55437         /**
55438          * @event headercontextmenu
55439          * Fires when a header is right clicked
55440          * @param {Grid} this
55441          * @param {Number} columnIndex
55442          * @param {Roo.EventObject} e
55443          */
55444         "headercontextmenu" : true,
55445         /**
55446          * @event bodyscroll
55447          * Fires when the body element is scrolled
55448          * @param {Number} scrollLeft
55449          * @param {Number} scrollTop
55450          */
55451         "bodyscroll" : true,
55452         /**
55453          * @event columnresize
55454          * Fires when the user resizes a column
55455          * @param {Number} columnIndex
55456          * @param {Number} newSize
55457          */
55458         "columnresize" : true,
55459         /**
55460          * @event columnmove
55461          * Fires when the user moves a column
55462          * @param {Number} oldIndex
55463          * @param {Number} newIndex
55464          */
55465         "columnmove" : true,
55466         /**
55467          * @event startdrag
55468          * Fires when row(s) start being dragged
55469          * @param {Grid} this
55470          * @param {Roo.GridDD} dd The drag drop object
55471          * @param {event} e The raw browser event
55472          */
55473         "startdrag" : true,
55474         /**
55475          * @event enddrag
55476          * Fires when a drag operation is complete
55477          * @param {Grid} this
55478          * @param {Roo.GridDD} dd The drag drop object
55479          * @param {event} e The raw browser event
55480          */
55481         "enddrag" : true,
55482         /**
55483          * @event dragdrop
55484          * Fires when dragged row(s) are dropped on a valid DD target
55485          * @param {Grid} this
55486          * @param {Roo.GridDD} dd The drag drop object
55487          * @param {String} targetId The target drag drop object
55488          * @param {event} e The raw browser event
55489          */
55490         "dragdrop" : true,
55491         /**
55492          * @event dragover
55493          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
55494          * @param {Grid} this
55495          * @param {Roo.GridDD} dd The drag drop object
55496          * @param {String} targetId The target drag drop object
55497          * @param {event} e The raw browser event
55498          */
55499         "dragover" : true,
55500         /**
55501          * @event dragenter
55502          *  Fires when the dragged row(s) first cross another DD target while being dragged
55503          * @param {Grid} this
55504          * @param {Roo.GridDD} dd The drag drop object
55505          * @param {String} targetId The target drag drop object
55506          * @param {event} e The raw browser event
55507          */
55508         "dragenter" : true,
55509         /**
55510          * @event dragout
55511          * Fires when the dragged row(s) leave another DD target while being dragged
55512          * @param {Grid} this
55513          * @param {Roo.GridDD} dd The drag drop object
55514          * @param {String} targetId The target drag drop object
55515          * @param {event} e The raw browser event
55516          */
55517         "dragout" : true,
55518         /**
55519          * @event rowclass
55520          * Fires when a row is rendered, so you can change add a style to it.
55521          * @param {GridView} gridview   The grid view
55522          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
55523          */
55524         'rowclass' : true,
55525
55526         /**
55527          * @event render
55528          * Fires when the grid is rendered
55529          * @param {Grid} grid
55530          */
55531         'render' : true
55532     });
55533
55534     Roo.grid.Grid.superclass.constructor.call(this);
55535 };
55536 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
55537     
55538     /**
55539      * @cfg {String} ddGroup - drag drop group.
55540      */
55541       /**
55542      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
55543      */
55544
55545     /**
55546      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
55547      */
55548     minColumnWidth : 25,
55549
55550     /**
55551      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
55552      * <b>on initial render.</b> It is more efficient to explicitly size the columns
55553      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
55554      */
55555     autoSizeColumns : false,
55556
55557     /**
55558      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
55559      */
55560     autoSizeHeaders : true,
55561
55562     /**
55563      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
55564      */
55565     monitorWindowResize : true,
55566
55567     /**
55568      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
55569      * rows measured to get a columns size. Default is 0 (all rows).
55570      */
55571     maxRowsToMeasure : 0,
55572
55573     /**
55574      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
55575      */
55576     trackMouseOver : true,
55577
55578     /**
55579     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
55580     */
55581       /**
55582     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
55583     */
55584     
55585     /**
55586     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
55587     */
55588     enableDragDrop : false,
55589     
55590     /**
55591     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
55592     */
55593     enableColumnMove : true,
55594     
55595     /**
55596     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
55597     */
55598     enableColumnHide : true,
55599     
55600     /**
55601     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
55602     */
55603     enableRowHeightSync : false,
55604     
55605     /**
55606     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
55607     */
55608     stripeRows : true,
55609     
55610     /**
55611     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
55612     */
55613     autoHeight : false,
55614
55615     /**
55616      * @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.
55617      */
55618     autoExpandColumn : false,
55619
55620     /**
55621     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
55622     * Default is 50.
55623     */
55624     autoExpandMin : 50,
55625
55626     /**
55627     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
55628     */
55629     autoExpandMax : 1000,
55630
55631     /**
55632     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
55633     */
55634     view : null,
55635
55636     /**
55637     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
55638     */
55639     loadMask : false,
55640     /**
55641     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
55642     */
55643     dropTarget: false,
55644     
55645    
55646     
55647     // private
55648     rendered : false,
55649
55650     /**
55651     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
55652     * of a fixed width. Default is false.
55653     */
55654     /**
55655     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
55656     */
55657     
55658     
55659     /**
55660     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
55661     * %0 is replaced with the number of selected rows.
55662     */
55663     ddText : "{0} selected row{1}",
55664     
55665     
55666     /**
55667      * Called once after all setup has been completed and the grid is ready to be rendered.
55668      * @return {Roo.grid.Grid} this
55669      */
55670     render : function()
55671     {
55672         var c = this.container;
55673         // try to detect autoHeight/width mode
55674         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
55675             this.autoHeight = true;
55676         }
55677         var view = this.getView();
55678         view.init(this);
55679
55680         c.on("click", this.onClick, this);
55681         c.on("dblclick", this.onDblClick, this);
55682         c.on("contextmenu", this.onContextMenu, this);
55683         c.on("keydown", this.onKeyDown, this);
55684         if (Roo.isTouch) {
55685             c.on("touchstart", this.onTouchStart, this);
55686         }
55687
55688         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
55689
55690         this.getSelectionModel().init(this);
55691
55692         view.render();
55693
55694         if(this.loadMask){
55695             this.loadMask = new Roo.LoadMask(this.container,
55696                     Roo.apply({store:this.dataSource}, this.loadMask));
55697         }
55698         
55699         
55700         if (this.toolbar && this.toolbar.xtype) {
55701             this.toolbar.container = this.getView().getHeaderPanel(true);
55702             this.toolbar = new Roo.Toolbar(this.toolbar);
55703         }
55704         if (this.footer && this.footer.xtype) {
55705             this.footer.dataSource = this.getDataSource();
55706             this.footer.container = this.getView().getFooterPanel(true);
55707             this.footer = Roo.factory(this.footer, Roo);
55708         }
55709         if (this.dropTarget && this.dropTarget.xtype) {
55710             delete this.dropTarget.xtype;
55711             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
55712         }
55713         
55714         
55715         this.rendered = true;
55716         this.fireEvent('render', this);
55717         return this;
55718     },
55719
55720     /**
55721      * Reconfigures the grid to use a different Store and Column Model.
55722      * The View will be bound to the new objects and refreshed.
55723      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
55724      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
55725      */
55726     reconfigure : function(dataSource, colModel){
55727         if(this.loadMask){
55728             this.loadMask.destroy();
55729             this.loadMask = new Roo.LoadMask(this.container,
55730                     Roo.apply({store:dataSource}, this.loadMask));
55731         }
55732         this.view.bind(dataSource, colModel);
55733         this.dataSource = dataSource;
55734         this.colModel = colModel;
55735         this.view.refresh(true);
55736     },
55737     /**
55738      * addColumns
55739      * Add's a column, default at the end..
55740      
55741      * @param {int} position to add (default end)
55742      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
55743      */
55744     addColumns : function(pos, ar)
55745     {
55746         
55747         for (var i =0;i< ar.length;i++) {
55748             var cfg = ar[i];
55749             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
55750             this.cm.lookup[cfg.id] = cfg;
55751         }
55752         
55753         
55754         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
55755             pos = this.cm.config.length; //this.cm.config.push(cfg);
55756         } 
55757         pos = Math.max(0,pos);
55758         ar.unshift(0);
55759         ar.unshift(pos);
55760         this.cm.config.splice.apply(this.cm.config, ar);
55761         
55762         
55763         
55764         this.view.generateRules(this.cm);
55765         this.view.refresh(true);
55766         
55767     },
55768     
55769     
55770     
55771     
55772     // private
55773     onKeyDown : function(e){
55774         this.fireEvent("keydown", e);
55775     },
55776
55777     /**
55778      * Destroy this grid.
55779      * @param {Boolean} removeEl True to remove the element
55780      */
55781     destroy : function(removeEl, keepListeners){
55782         if(this.loadMask){
55783             this.loadMask.destroy();
55784         }
55785         var c = this.container;
55786         c.removeAllListeners();
55787         this.view.destroy();
55788         this.colModel.purgeListeners();
55789         if(!keepListeners){
55790             this.purgeListeners();
55791         }
55792         c.update("");
55793         if(removeEl === true){
55794             c.remove();
55795         }
55796     },
55797
55798     // private
55799     processEvent : function(name, e){
55800         // does this fire select???
55801         //Roo.log('grid:processEvent '  + name);
55802         
55803         if (name != 'touchstart' ) {
55804             this.fireEvent(name, e);    
55805         }
55806         
55807         var t = e.getTarget();
55808         var v = this.view;
55809         var header = v.findHeaderIndex(t);
55810         if(header !== false){
55811             var ename = name == 'touchstart' ? 'click' : name;
55812              
55813             this.fireEvent("header" + ename, this, header, e);
55814         }else{
55815             var row = v.findRowIndex(t);
55816             var cell = v.findCellIndex(t);
55817             if (name == 'touchstart') {
55818                 // first touch is always a click.
55819                 // hopefull this happens after selection is updated.?
55820                 name = false;
55821                 
55822                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
55823                     var cs = this.selModel.getSelectedCell();
55824                     if (row == cs[0] && cell == cs[1]){
55825                         name = 'dblclick';
55826                     }
55827                 }
55828                 if (typeof(this.selModel.getSelections) != 'undefined') {
55829                     var cs = this.selModel.getSelections();
55830                     var ds = this.dataSource;
55831                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
55832                         name = 'dblclick';
55833                     }
55834                 }
55835                 if (!name) {
55836                     return;
55837                 }
55838             }
55839             
55840             
55841             if(row !== false){
55842                 this.fireEvent("row" + name, this, row, e);
55843                 if(cell !== false){
55844                     this.fireEvent("cell" + name, this, row, cell, e);
55845                 }
55846             }
55847         }
55848     },
55849
55850     // private
55851     onClick : function(e){
55852         this.processEvent("click", e);
55853     },
55854    // private
55855     onTouchStart : function(e){
55856         this.processEvent("touchstart", e);
55857     },
55858
55859     // private
55860     onContextMenu : function(e, t){
55861         this.processEvent("contextmenu", e);
55862     },
55863
55864     // private
55865     onDblClick : function(e){
55866         this.processEvent("dblclick", e);
55867     },
55868
55869     // private
55870     walkCells : function(row, col, step, fn, scope){
55871         var cm = this.colModel, clen = cm.getColumnCount();
55872         var ds = this.dataSource, rlen = ds.getCount(), first = true;
55873         if(step < 0){
55874             if(col < 0){
55875                 row--;
55876                 first = false;
55877             }
55878             while(row >= 0){
55879                 if(!first){
55880                     col = clen-1;
55881                 }
55882                 first = false;
55883                 while(col >= 0){
55884                     if(fn.call(scope || this, row, col, cm) === true){
55885                         return [row, col];
55886                     }
55887                     col--;
55888                 }
55889                 row--;
55890             }
55891         } else {
55892             if(col >= clen){
55893                 row++;
55894                 first = false;
55895             }
55896             while(row < rlen){
55897                 if(!first){
55898                     col = 0;
55899                 }
55900                 first = false;
55901                 while(col < clen){
55902                     if(fn.call(scope || this, row, col, cm) === true){
55903                         return [row, col];
55904                     }
55905                     col++;
55906                 }
55907                 row++;
55908             }
55909         }
55910         return null;
55911     },
55912
55913     // private
55914     getSelections : function(){
55915         return this.selModel.getSelections();
55916     },
55917
55918     /**
55919      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
55920      * but if manual update is required this method will initiate it.
55921      */
55922     autoSize : function(){
55923         if(this.rendered){
55924             this.view.layout();
55925             if(this.view.adjustForScroll){
55926                 this.view.adjustForScroll();
55927             }
55928         }
55929     },
55930
55931     /**
55932      * Returns the grid's underlying element.
55933      * @return {Element} The element
55934      */
55935     getGridEl : function(){
55936         return this.container;
55937     },
55938
55939     // private for compatibility, overridden by editor grid
55940     stopEditing : function(){},
55941
55942     /**
55943      * Returns the grid's SelectionModel.
55944      * @return {SelectionModel}
55945      */
55946     getSelectionModel : function(){
55947         if(!this.selModel){
55948             this.selModel = new Roo.grid.RowSelectionModel();
55949         }
55950         return this.selModel;
55951     },
55952
55953     /**
55954      * Returns the grid's DataSource.
55955      * @return {DataSource}
55956      */
55957     getDataSource : function(){
55958         return this.dataSource;
55959     },
55960
55961     /**
55962      * Returns the grid's ColumnModel.
55963      * @return {ColumnModel}
55964      */
55965     getColumnModel : function(){
55966         return this.colModel;
55967     },
55968
55969     /**
55970      * Returns the grid's GridView object.
55971      * @return {GridView}
55972      */
55973     getView : function(){
55974         if(!this.view){
55975             this.view = new Roo.grid.GridView(this.viewConfig);
55976             this.relayEvents(this.view, [
55977                 "beforerowremoved", "beforerowsinserted",
55978                 "beforerefresh", "rowremoved",
55979                 "rowsinserted", "rowupdated" ,"refresh"
55980             ]);
55981         }
55982         return this.view;
55983     },
55984     /**
55985      * Called to get grid's drag proxy text, by default returns this.ddText.
55986      * Override this to put something different in the dragged text.
55987      * @return {String}
55988      */
55989     getDragDropText : function(){
55990         var count = this.selModel.getCount();
55991         return String.format(this.ddText, count, count == 1 ? '' : 's');
55992     }
55993 });
55994 /*
55995  * Based on:
55996  * Ext JS Library 1.1.1
55997  * Copyright(c) 2006-2007, Ext JS, LLC.
55998  *
55999  * Originally Released Under LGPL - original licence link has changed is not relivant.
56000  *
56001  * Fork - LGPL
56002  * <script type="text/javascript">
56003  */
56004  
56005 Roo.grid.AbstractGridView = function(){
56006         this.grid = null;
56007         
56008         this.events = {
56009             "beforerowremoved" : true,
56010             "beforerowsinserted" : true,
56011             "beforerefresh" : true,
56012             "rowremoved" : true,
56013             "rowsinserted" : true,
56014             "rowupdated" : true,
56015             "refresh" : true
56016         };
56017     Roo.grid.AbstractGridView.superclass.constructor.call(this);
56018 };
56019
56020 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
56021     rowClass : "x-grid-row",
56022     cellClass : "x-grid-cell",
56023     tdClass : "x-grid-td",
56024     hdClass : "x-grid-hd",
56025     splitClass : "x-grid-hd-split",
56026     
56027     init: function(grid){
56028         this.grid = grid;
56029                 var cid = this.grid.getGridEl().id;
56030         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
56031         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
56032         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
56033         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
56034         },
56035         
56036     getColumnRenderers : function(){
56037         var renderers = [];
56038         var cm = this.grid.colModel;
56039         var colCount = cm.getColumnCount();
56040         for(var i = 0; i < colCount; i++){
56041             renderers[i] = cm.getRenderer(i);
56042         }
56043         return renderers;
56044     },
56045     
56046     getColumnIds : function(){
56047         var ids = [];
56048         var cm = this.grid.colModel;
56049         var colCount = cm.getColumnCount();
56050         for(var i = 0; i < colCount; i++){
56051             ids[i] = cm.getColumnId(i);
56052         }
56053         return ids;
56054     },
56055     
56056     getDataIndexes : function(){
56057         if(!this.indexMap){
56058             this.indexMap = this.buildIndexMap();
56059         }
56060         return this.indexMap.colToData;
56061     },
56062     
56063     getColumnIndexByDataIndex : function(dataIndex){
56064         if(!this.indexMap){
56065             this.indexMap = this.buildIndexMap();
56066         }
56067         return this.indexMap.dataToCol[dataIndex];
56068     },
56069     
56070     /**
56071      * Set a css style for a column dynamically. 
56072      * @param {Number} colIndex The index of the column
56073      * @param {String} name The css property name
56074      * @param {String} value The css value
56075      */
56076     setCSSStyle : function(colIndex, name, value){
56077         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
56078         Roo.util.CSS.updateRule(selector, name, value);
56079     },
56080     
56081     generateRules : function(cm){
56082         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
56083         Roo.util.CSS.removeStyleSheet(rulesId);
56084         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56085             var cid = cm.getColumnId(i);
56086             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
56087                          this.tdSelector, cid, " {\n}\n",
56088                          this.hdSelector, cid, " {\n}\n",
56089                          this.splitSelector, cid, " {\n}\n");
56090         }
56091         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
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 // private
56105 // This is a support class used internally by the Grid components
56106 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
56107     this.grid = grid;
56108     this.view = grid.getView();
56109     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56110     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
56111     if(hd2){
56112         this.setHandleElId(Roo.id(hd));
56113         this.setOuterHandleElId(Roo.id(hd2));
56114     }
56115     this.scroll = false;
56116 };
56117 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
56118     maxDragWidth: 120,
56119     getDragData : function(e){
56120         var t = Roo.lib.Event.getTarget(e);
56121         var h = this.view.findHeaderCell(t);
56122         if(h){
56123             return {ddel: h.firstChild, header:h};
56124         }
56125         return false;
56126     },
56127
56128     onInitDrag : function(e){
56129         this.view.headersDisabled = true;
56130         var clone = this.dragData.ddel.cloneNode(true);
56131         clone.id = Roo.id();
56132         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
56133         this.proxy.update(clone);
56134         return true;
56135     },
56136
56137     afterValidDrop : function(){
56138         var v = this.view;
56139         setTimeout(function(){
56140             v.headersDisabled = false;
56141         }, 50);
56142     },
56143
56144     afterInvalidDrop : function(){
56145         var v = this.view;
56146         setTimeout(function(){
56147             v.headersDisabled = false;
56148         }, 50);
56149     }
56150 });
56151 /*
56152  * Based on:
56153  * Ext JS Library 1.1.1
56154  * Copyright(c) 2006-2007, Ext JS, LLC.
56155  *
56156  * Originally Released Under LGPL - original licence link has changed is not relivant.
56157  *
56158  * Fork - LGPL
56159  * <script type="text/javascript">
56160  */
56161 // private
56162 // This is a support class used internally by the Grid components
56163 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
56164     this.grid = grid;
56165     this.view = grid.getView();
56166     // split the proxies so they don't interfere with mouse events
56167     this.proxyTop = Roo.DomHelper.append(document.body, {
56168         cls:"col-move-top", html:"&#160;"
56169     }, true);
56170     this.proxyBottom = Roo.DomHelper.append(document.body, {
56171         cls:"col-move-bottom", html:"&#160;"
56172     }, true);
56173     this.proxyTop.hide = this.proxyBottom.hide = function(){
56174         this.setLeftTop(-100,-100);
56175         this.setStyle("visibility", "hidden");
56176     };
56177     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56178     // temporarily disabled
56179     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
56180     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
56181 };
56182 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
56183     proxyOffsets : [-4, -9],
56184     fly: Roo.Element.fly,
56185
56186     getTargetFromEvent : function(e){
56187         var t = Roo.lib.Event.getTarget(e);
56188         var cindex = this.view.findCellIndex(t);
56189         if(cindex !== false){
56190             return this.view.getHeaderCell(cindex);
56191         }
56192         return null;
56193     },
56194
56195     nextVisible : function(h){
56196         var v = this.view, cm = this.grid.colModel;
56197         h = h.nextSibling;
56198         while(h){
56199             if(!cm.isHidden(v.getCellIndex(h))){
56200                 return h;
56201             }
56202             h = h.nextSibling;
56203         }
56204         return null;
56205     },
56206
56207     prevVisible : function(h){
56208         var v = this.view, cm = this.grid.colModel;
56209         h = h.prevSibling;
56210         while(h){
56211             if(!cm.isHidden(v.getCellIndex(h))){
56212                 return h;
56213             }
56214             h = h.prevSibling;
56215         }
56216         return null;
56217     },
56218
56219     positionIndicator : function(h, n, e){
56220         var x = Roo.lib.Event.getPageX(e);
56221         var r = Roo.lib.Dom.getRegion(n.firstChild);
56222         var px, pt, py = r.top + this.proxyOffsets[1];
56223         if((r.right - x) <= (r.right-r.left)/2){
56224             px = r.right+this.view.borderWidth;
56225             pt = "after";
56226         }else{
56227             px = r.left;
56228             pt = "before";
56229         }
56230         var oldIndex = this.view.getCellIndex(h);
56231         var newIndex = this.view.getCellIndex(n);
56232
56233         if(this.grid.colModel.isFixed(newIndex)){
56234             return false;
56235         }
56236
56237         var locked = this.grid.colModel.isLocked(newIndex);
56238
56239         if(pt == "after"){
56240             newIndex++;
56241         }
56242         if(oldIndex < newIndex){
56243             newIndex--;
56244         }
56245         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
56246             return false;
56247         }
56248         px +=  this.proxyOffsets[0];
56249         this.proxyTop.setLeftTop(px, py);
56250         this.proxyTop.show();
56251         if(!this.bottomOffset){
56252             this.bottomOffset = this.view.mainHd.getHeight();
56253         }
56254         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
56255         this.proxyBottom.show();
56256         return pt;
56257     },
56258
56259     onNodeEnter : function(n, dd, e, data){
56260         if(data.header != n){
56261             this.positionIndicator(data.header, n, e);
56262         }
56263     },
56264
56265     onNodeOver : function(n, dd, e, data){
56266         var result = false;
56267         if(data.header != n){
56268             result = this.positionIndicator(data.header, n, e);
56269         }
56270         if(!result){
56271             this.proxyTop.hide();
56272             this.proxyBottom.hide();
56273         }
56274         return result ? this.dropAllowed : this.dropNotAllowed;
56275     },
56276
56277     onNodeOut : function(n, dd, e, data){
56278         this.proxyTop.hide();
56279         this.proxyBottom.hide();
56280     },
56281
56282     onNodeDrop : function(n, dd, e, data){
56283         var h = data.header;
56284         if(h != n){
56285             var cm = this.grid.colModel;
56286             var x = Roo.lib.Event.getPageX(e);
56287             var r = Roo.lib.Dom.getRegion(n.firstChild);
56288             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
56289             var oldIndex = this.view.getCellIndex(h);
56290             var newIndex = this.view.getCellIndex(n);
56291             var locked = cm.isLocked(newIndex);
56292             if(pt == "after"){
56293                 newIndex++;
56294             }
56295             if(oldIndex < newIndex){
56296                 newIndex--;
56297             }
56298             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
56299                 return false;
56300             }
56301             cm.setLocked(oldIndex, locked, true);
56302             cm.moveColumn(oldIndex, newIndex);
56303             this.grid.fireEvent("columnmove", oldIndex, newIndex);
56304             return true;
56305         }
56306         return false;
56307     }
56308 });
56309 /*
56310  * Based on:
56311  * Ext JS Library 1.1.1
56312  * Copyright(c) 2006-2007, Ext JS, LLC.
56313  *
56314  * Originally Released Under LGPL - original licence link has changed is not relivant.
56315  *
56316  * Fork - LGPL
56317  * <script type="text/javascript">
56318  */
56319   
56320 /**
56321  * @class Roo.grid.GridView
56322  * @extends Roo.util.Observable
56323  *
56324  * @constructor
56325  * @param {Object} config
56326  */
56327 Roo.grid.GridView = function(config){
56328     Roo.grid.GridView.superclass.constructor.call(this);
56329     this.el = null;
56330
56331     Roo.apply(this, config);
56332 };
56333
56334 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
56335
56336     unselectable :  'unselectable="on"',
56337     unselectableCls :  'x-unselectable',
56338     
56339     
56340     rowClass : "x-grid-row",
56341
56342     cellClass : "x-grid-col",
56343
56344     tdClass : "x-grid-td",
56345
56346     hdClass : "x-grid-hd",
56347
56348     splitClass : "x-grid-split",
56349
56350     sortClasses : ["sort-asc", "sort-desc"],
56351
56352     enableMoveAnim : false,
56353
56354     hlColor: "C3DAF9",
56355
56356     dh : Roo.DomHelper,
56357
56358     fly : Roo.Element.fly,
56359
56360     css : Roo.util.CSS,
56361
56362     borderWidth: 1,
56363
56364     splitOffset: 3,
56365
56366     scrollIncrement : 22,
56367
56368     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
56369
56370     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
56371
56372     bind : function(ds, cm){
56373         if(this.ds){
56374             this.ds.un("load", this.onLoad, this);
56375             this.ds.un("datachanged", this.onDataChange, this);
56376             this.ds.un("add", this.onAdd, this);
56377             this.ds.un("remove", this.onRemove, this);
56378             this.ds.un("update", this.onUpdate, this);
56379             this.ds.un("clear", this.onClear, this);
56380         }
56381         if(ds){
56382             ds.on("load", this.onLoad, this);
56383             ds.on("datachanged", this.onDataChange, this);
56384             ds.on("add", this.onAdd, this);
56385             ds.on("remove", this.onRemove, this);
56386             ds.on("update", this.onUpdate, this);
56387             ds.on("clear", this.onClear, this);
56388         }
56389         this.ds = ds;
56390
56391         if(this.cm){
56392             this.cm.un("widthchange", this.onColWidthChange, this);
56393             this.cm.un("headerchange", this.onHeaderChange, this);
56394             this.cm.un("hiddenchange", this.onHiddenChange, this);
56395             this.cm.un("columnmoved", this.onColumnMove, this);
56396             this.cm.un("columnlockchange", this.onColumnLock, this);
56397         }
56398         if(cm){
56399             this.generateRules(cm);
56400             cm.on("widthchange", this.onColWidthChange, this);
56401             cm.on("headerchange", this.onHeaderChange, this);
56402             cm.on("hiddenchange", this.onHiddenChange, this);
56403             cm.on("columnmoved", this.onColumnMove, this);
56404             cm.on("columnlockchange", this.onColumnLock, this);
56405         }
56406         this.cm = cm;
56407     },
56408
56409     init: function(grid){
56410         Roo.grid.GridView.superclass.init.call(this, grid);
56411
56412         this.bind(grid.dataSource, grid.colModel);
56413
56414         grid.on("headerclick", this.handleHeaderClick, this);
56415
56416         if(grid.trackMouseOver){
56417             grid.on("mouseover", this.onRowOver, this);
56418             grid.on("mouseout", this.onRowOut, this);
56419         }
56420         grid.cancelTextSelection = function(){};
56421         this.gridId = grid.id;
56422
56423         var tpls = this.templates || {};
56424
56425         if(!tpls.master){
56426             tpls.master = new Roo.Template(
56427                '<div class="x-grid" hidefocus="true">',
56428                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
56429                   '<div class="x-grid-topbar"></div>',
56430                   '<div class="x-grid-scroller"><div></div></div>',
56431                   '<div class="x-grid-locked">',
56432                       '<div class="x-grid-header">{lockedHeader}</div>',
56433                       '<div class="x-grid-body">{lockedBody}</div>',
56434                   "</div>",
56435                   '<div class="x-grid-viewport">',
56436                       '<div class="x-grid-header">{header}</div>',
56437                       '<div class="x-grid-body">{body}</div>',
56438                   "</div>",
56439                   '<div class="x-grid-bottombar"></div>',
56440                  
56441                   '<div class="x-grid-resize-proxy">&#160;</div>',
56442                "</div>"
56443             );
56444             tpls.master.disableformats = true;
56445         }
56446
56447         if(!tpls.header){
56448             tpls.header = new Roo.Template(
56449                '<table border="0" cellspacing="0" cellpadding="0">',
56450                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
56451                "</table>{splits}"
56452             );
56453             tpls.header.disableformats = true;
56454         }
56455         tpls.header.compile();
56456
56457         if(!tpls.hcell){
56458             tpls.hcell = new Roo.Template(
56459                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
56460                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
56461                 "</div></td>"
56462              );
56463              tpls.hcell.disableFormats = true;
56464         }
56465         tpls.hcell.compile();
56466
56467         if(!tpls.hsplit){
56468             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
56469                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
56470             tpls.hsplit.disableFormats = true;
56471         }
56472         tpls.hsplit.compile();
56473
56474         if(!tpls.body){
56475             tpls.body = new Roo.Template(
56476                '<table border="0" cellspacing="0" cellpadding="0">',
56477                "<tbody>{rows}</tbody>",
56478                "</table>"
56479             );
56480             tpls.body.disableFormats = true;
56481         }
56482         tpls.body.compile();
56483
56484         if(!tpls.row){
56485             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
56486             tpls.row.disableFormats = true;
56487         }
56488         tpls.row.compile();
56489
56490         if(!tpls.cell){
56491             tpls.cell = new Roo.Template(
56492                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
56493                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
56494                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
56495                 "</td>"
56496             );
56497             tpls.cell.disableFormats = true;
56498         }
56499         tpls.cell.compile();
56500
56501         this.templates = tpls;
56502     },
56503
56504     // remap these for backwards compat
56505     onColWidthChange : function(){
56506         this.updateColumns.apply(this, arguments);
56507     },
56508     onHeaderChange : function(){
56509         this.updateHeaders.apply(this, arguments);
56510     }, 
56511     onHiddenChange : function(){
56512         this.handleHiddenChange.apply(this, arguments);
56513     },
56514     onColumnMove : function(){
56515         this.handleColumnMove.apply(this, arguments);
56516     },
56517     onColumnLock : function(){
56518         this.handleLockChange.apply(this, arguments);
56519     },
56520
56521     onDataChange : function(){
56522         this.refresh();
56523         this.updateHeaderSortState();
56524     },
56525
56526     onClear : function(){
56527         this.refresh();
56528     },
56529
56530     onUpdate : function(ds, record){
56531         this.refreshRow(record);
56532     },
56533
56534     refreshRow : function(record){
56535         var ds = this.ds, index;
56536         if(typeof record == 'number'){
56537             index = record;
56538             record = ds.getAt(index);
56539         }else{
56540             index = ds.indexOf(record);
56541         }
56542         this.insertRows(ds, index, index, true);
56543         this.onRemove(ds, record, index+1, true);
56544         this.syncRowHeights(index, index);
56545         this.layout();
56546         this.fireEvent("rowupdated", this, index, record);
56547     },
56548
56549     onAdd : function(ds, records, index){
56550         this.insertRows(ds, index, index + (records.length-1));
56551     },
56552
56553     onRemove : function(ds, record, index, isUpdate){
56554         if(isUpdate !== true){
56555             this.fireEvent("beforerowremoved", this, index, record);
56556         }
56557         var bt = this.getBodyTable(), lt = this.getLockedTable();
56558         if(bt.rows[index]){
56559             bt.firstChild.removeChild(bt.rows[index]);
56560         }
56561         if(lt.rows[index]){
56562             lt.firstChild.removeChild(lt.rows[index]);
56563         }
56564         if(isUpdate !== true){
56565             this.stripeRows(index);
56566             this.syncRowHeights(index, index);
56567             this.layout();
56568             this.fireEvent("rowremoved", this, index, record);
56569         }
56570     },
56571
56572     onLoad : function(){
56573         this.scrollToTop();
56574     },
56575
56576     /**
56577      * Scrolls the grid to the top
56578      */
56579     scrollToTop : function(){
56580         if(this.scroller){
56581             this.scroller.dom.scrollTop = 0;
56582             this.syncScroll();
56583         }
56584     },
56585
56586     /**
56587      * Gets a panel in the header of the grid that can be used for toolbars etc.
56588      * After modifying the contents of this panel a call to grid.autoSize() may be
56589      * required to register any changes in size.
56590      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
56591      * @return Roo.Element
56592      */
56593     getHeaderPanel : function(doShow){
56594         if(doShow){
56595             this.headerPanel.show();
56596         }
56597         return this.headerPanel;
56598     },
56599
56600     /**
56601      * Gets a panel in the footer of the grid that can be used for toolbars etc.
56602      * After modifying the contents of this panel a call to grid.autoSize() may be
56603      * required to register any changes in size.
56604      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
56605      * @return Roo.Element
56606      */
56607     getFooterPanel : function(doShow){
56608         if(doShow){
56609             this.footerPanel.show();
56610         }
56611         return this.footerPanel;
56612     },
56613
56614     initElements : function(){
56615         var E = Roo.Element;
56616         var el = this.grid.getGridEl().dom.firstChild;
56617         var cs = el.childNodes;
56618
56619         this.el = new E(el);
56620         
56621          this.focusEl = new E(el.firstChild);
56622         this.focusEl.swallowEvent("click", true);
56623         
56624         this.headerPanel = new E(cs[1]);
56625         this.headerPanel.enableDisplayMode("block");
56626
56627         this.scroller = new E(cs[2]);
56628         this.scrollSizer = new E(this.scroller.dom.firstChild);
56629
56630         this.lockedWrap = new E(cs[3]);
56631         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
56632         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
56633
56634         this.mainWrap = new E(cs[4]);
56635         this.mainHd = new E(this.mainWrap.dom.firstChild);
56636         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
56637
56638         this.footerPanel = new E(cs[5]);
56639         this.footerPanel.enableDisplayMode("block");
56640
56641         this.resizeProxy = new E(cs[6]);
56642
56643         this.headerSelector = String.format(
56644            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
56645            this.lockedHd.id, this.mainHd.id
56646         );
56647
56648         this.splitterSelector = String.format(
56649            '#{0} div.x-grid-split, #{1} div.x-grid-split',
56650            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
56651         );
56652     },
56653     idToCssName : function(s)
56654     {
56655         return s.replace(/[^a-z0-9]+/ig, '-');
56656     },
56657
56658     getHeaderCell : function(index){
56659         return Roo.DomQuery.select(this.headerSelector)[index];
56660     },
56661
56662     getHeaderCellMeasure : function(index){
56663         return this.getHeaderCell(index).firstChild;
56664     },
56665
56666     getHeaderCellText : function(index){
56667         return this.getHeaderCell(index).firstChild.firstChild;
56668     },
56669
56670     getLockedTable : function(){
56671         return this.lockedBody.dom.firstChild;
56672     },
56673
56674     getBodyTable : function(){
56675         return this.mainBody.dom.firstChild;
56676     },
56677
56678     getLockedRow : function(index){
56679         return this.getLockedTable().rows[index];
56680     },
56681
56682     getRow : function(index){
56683         return this.getBodyTable().rows[index];
56684     },
56685
56686     getRowComposite : function(index){
56687         if(!this.rowEl){
56688             this.rowEl = new Roo.CompositeElementLite();
56689         }
56690         var els = [], lrow, mrow;
56691         if(lrow = this.getLockedRow(index)){
56692             els.push(lrow);
56693         }
56694         if(mrow = this.getRow(index)){
56695             els.push(mrow);
56696         }
56697         this.rowEl.elements = els;
56698         return this.rowEl;
56699     },
56700     /**
56701      * Gets the 'td' of the cell
56702      * 
56703      * @param {Integer} rowIndex row to select
56704      * @param {Integer} colIndex column to select
56705      * 
56706      * @return {Object} 
56707      */
56708     getCell : function(rowIndex, colIndex){
56709         var locked = this.cm.getLockedCount();
56710         var source;
56711         if(colIndex < locked){
56712             source = this.lockedBody.dom.firstChild;
56713         }else{
56714             source = this.mainBody.dom.firstChild;
56715             colIndex -= locked;
56716         }
56717         return source.rows[rowIndex].childNodes[colIndex];
56718     },
56719
56720     getCellText : function(rowIndex, colIndex){
56721         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
56722     },
56723
56724     getCellBox : function(cell){
56725         var b = this.fly(cell).getBox();
56726         if(Roo.isOpera){ // opera fails to report the Y
56727             b.y = cell.offsetTop + this.mainBody.getY();
56728         }
56729         return b;
56730     },
56731
56732     getCellIndex : function(cell){
56733         var id = String(cell.className).match(this.cellRE);
56734         if(id){
56735             return parseInt(id[1], 10);
56736         }
56737         return 0;
56738     },
56739
56740     findHeaderIndex : function(n){
56741         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56742         return r ? this.getCellIndex(r) : false;
56743     },
56744
56745     findHeaderCell : function(n){
56746         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56747         return r ? r : false;
56748     },
56749
56750     findRowIndex : function(n){
56751         if(!n){
56752             return false;
56753         }
56754         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
56755         return r ? r.rowIndex : false;
56756     },
56757
56758     findCellIndex : function(node){
56759         var stop = this.el.dom;
56760         while(node && node != stop){
56761             if(this.findRE.test(node.className)){
56762                 return this.getCellIndex(node);
56763             }
56764             node = node.parentNode;
56765         }
56766         return false;
56767     },
56768
56769     getColumnId : function(index){
56770         return this.cm.getColumnId(index);
56771     },
56772
56773     getSplitters : function()
56774     {
56775         if(this.splitterSelector){
56776            return Roo.DomQuery.select(this.splitterSelector);
56777         }else{
56778             return null;
56779       }
56780     },
56781
56782     getSplitter : function(index){
56783         return this.getSplitters()[index];
56784     },
56785
56786     onRowOver : function(e, t){
56787         var row;
56788         if((row = this.findRowIndex(t)) !== false){
56789             this.getRowComposite(row).addClass("x-grid-row-over");
56790         }
56791     },
56792
56793     onRowOut : function(e, t){
56794         var row;
56795         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
56796             this.getRowComposite(row).removeClass("x-grid-row-over");
56797         }
56798     },
56799
56800     renderHeaders : function(){
56801         var cm = this.cm;
56802         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
56803         var cb = [], lb = [], sb = [], lsb = [], p = {};
56804         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56805             p.cellId = "x-grid-hd-0-" + i;
56806             p.splitId = "x-grid-csplit-0-" + i;
56807             p.id = cm.getColumnId(i);
56808             p.value = cm.getColumnHeader(i) || "";
56809             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
56810             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
56811             if(!cm.isLocked(i)){
56812                 cb[cb.length] = ct.apply(p);
56813                 sb[sb.length] = st.apply(p);
56814             }else{
56815                 lb[lb.length] = ct.apply(p);
56816                 lsb[lsb.length] = st.apply(p);
56817             }
56818         }
56819         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
56820                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
56821     },
56822
56823     updateHeaders : function(){
56824         var html = this.renderHeaders();
56825         this.lockedHd.update(html[0]);
56826         this.mainHd.update(html[1]);
56827     },
56828
56829     /**
56830      * Focuses the specified row.
56831      * @param {Number} row The row index
56832      */
56833     focusRow : function(row)
56834     {
56835         //Roo.log('GridView.focusRow');
56836         var x = this.scroller.dom.scrollLeft;
56837         this.focusCell(row, 0, false);
56838         this.scroller.dom.scrollLeft = x;
56839     },
56840
56841     /**
56842      * Focuses the specified cell.
56843      * @param {Number} row The row index
56844      * @param {Number} col The column index
56845      * @param {Boolean} hscroll false to disable horizontal scrolling
56846      */
56847     focusCell : function(row, col, hscroll)
56848     {
56849         //Roo.log('GridView.focusCell');
56850         var el = this.ensureVisible(row, col, hscroll);
56851         this.focusEl.alignTo(el, "tl-tl");
56852         if(Roo.isGecko){
56853             this.focusEl.focus();
56854         }else{
56855             this.focusEl.focus.defer(1, this.focusEl);
56856         }
56857     },
56858
56859     /**
56860      * Scrolls the specified cell into view
56861      * @param {Number} row The row index
56862      * @param {Number} col The column index
56863      * @param {Boolean} hscroll false to disable horizontal scrolling
56864      */
56865     ensureVisible : function(row, col, hscroll)
56866     {
56867         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
56868         //return null; //disable for testing.
56869         if(typeof row != "number"){
56870             row = row.rowIndex;
56871         }
56872         if(row < 0 && row >= this.ds.getCount()){
56873             return  null;
56874         }
56875         col = (col !== undefined ? col : 0);
56876         var cm = this.grid.colModel;
56877         while(cm.isHidden(col)){
56878             col++;
56879         }
56880
56881         var el = this.getCell(row, col);
56882         if(!el){
56883             return null;
56884         }
56885         var c = this.scroller.dom;
56886
56887         var ctop = parseInt(el.offsetTop, 10);
56888         var cleft = parseInt(el.offsetLeft, 10);
56889         var cbot = ctop + el.offsetHeight;
56890         var cright = cleft + el.offsetWidth;
56891         
56892         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
56893         var stop = parseInt(c.scrollTop, 10);
56894         var sleft = parseInt(c.scrollLeft, 10);
56895         var sbot = stop + ch;
56896         var sright = sleft + c.clientWidth;
56897         /*
56898         Roo.log('GridView.ensureVisible:' +
56899                 ' ctop:' + ctop +
56900                 ' c.clientHeight:' + c.clientHeight +
56901                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
56902                 ' stop:' + stop +
56903                 ' cbot:' + cbot +
56904                 ' sbot:' + sbot +
56905                 ' ch:' + ch  
56906                 );
56907         */
56908         if(ctop < stop){
56909             c.scrollTop = ctop;
56910             //Roo.log("set scrolltop to ctop DISABLE?");
56911         }else if(cbot > sbot){
56912             //Roo.log("set scrolltop to cbot-ch");
56913             c.scrollTop = cbot-ch;
56914         }
56915         
56916         if(hscroll !== false){
56917             if(cleft < sleft){
56918                 c.scrollLeft = cleft;
56919             }else if(cright > sright){
56920                 c.scrollLeft = cright-c.clientWidth;
56921             }
56922         }
56923          
56924         return el;
56925     },
56926
56927     updateColumns : function(){
56928         this.grid.stopEditing();
56929         var cm = this.grid.colModel, colIds = this.getColumnIds();
56930         //var totalWidth = cm.getTotalWidth();
56931         var pos = 0;
56932         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56933             //if(cm.isHidden(i)) continue;
56934             var w = cm.getColumnWidth(i);
56935             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56936             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56937         }
56938         this.updateSplitters();
56939     },
56940
56941     generateRules : function(cm){
56942         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
56943         Roo.util.CSS.removeStyleSheet(rulesId);
56944         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56945             var cid = cm.getColumnId(i);
56946             var align = '';
56947             if(cm.config[i].align){
56948                 align = 'text-align:'+cm.config[i].align+';';
56949             }
56950             var hidden = '';
56951             if(cm.isHidden(i)){
56952                 hidden = 'display:none;';
56953             }
56954             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
56955             ruleBuf.push(
56956                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
56957                     this.hdSelector, cid, " {\n", align, width, "}\n",
56958                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
56959                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
56960         }
56961         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56962     },
56963
56964     updateSplitters : function(){
56965         var cm = this.cm, s = this.getSplitters();
56966         if(s){ // splitters not created yet
56967             var pos = 0, locked = true;
56968             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56969                 if(cm.isHidden(i)) {
56970                     continue;
56971                 }
56972                 var w = cm.getColumnWidth(i); // make sure it's a number
56973                 if(!cm.isLocked(i) && locked){
56974                     pos = 0;
56975                     locked = false;
56976                 }
56977                 pos += w;
56978                 s[i].style.left = (pos-this.splitOffset) + "px";
56979             }
56980         }
56981     },
56982
56983     handleHiddenChange : function(colModel, colIndex, hidden){
56984         if(hidden){
56985             this.hideColumn(colIndex);
56986         }else{
56987             this.unhideColumn(colIndex);
56988         }
56989     },
56990
56991     hideColumn : function(colIndex){
56992         var cid = this.getColumnId(colIndex);
56993         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
56994         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
56995         if(Roo.isSafari){
56996             this.updateHeaders();
56997         }
56998         this.updateSplitters();
56999         this.layout();
57000     },
57001
57002     unhideColumn : function(colIndex){
57003         var cid = this.getColumnId(colIndex);
57004         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
57005         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
57006
57007         if(Roo.isSafari){
57008             this.updateHeaders();
57009         }
57010         this.updateSplitters();
57011         this.layout();
57012     },
57013
57014     insertRows : function(dm, firstRow, lastRow, isUpdate){
57015         if(firstRow == 0 && lastRow == dm.getCount()-1){
57016             this.refresh();
57017         }else{
57018             if(!isUpdate){
57019                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
57020             }
57021             var s = this.getScrollState();
57022             var markup = this.renderRows(firstRow, lastRow);
57023             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
57024             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
57025             this.restoreScroll(s);
57026             if(!isUpdate){
57027                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
57028                 this.syncRowHeights(firstRow, lastRow);
57029                 this.stripeRows(firstRow);
57030                 this.layout();
57031             }
57032         }
57033     },
57034
57035     bufferRows : function(markup, target, index){
57036         var before = null, trows = target.rows, tbody = target.tBodies[0];
57037         if(index < trows.length){
57038             before = trows[index];
57039         }
57040         var b = document.createElement("div");
57041         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
57042         var rows = b.firstChild.rows;
57043         for(var i = 0, len = rows.length; i < len; i++){
57044             if(before){
57045                 tbody.insertBefore(rows[0], before);
57046             }else{
57047                 tbody.appendChild(rows[0]);
57048             }
57049         }
57050         b.innerHTML = "";
57051         b = null;
57052     },
57053
57054     deleteRows : function(dm, firstRow, lastRow){
57055         if(dm.getRowCount()<1){
57056             this.fireEvent("beforerefresh", this);
57057             this.mainBody.update("");
57058             this.lockedBody.update("");
57059             this.fireEvent("refresh", this);
57060         }else{
57061             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
57062             var bt = this.getBodyTable();
57063             var tbody = bt.firstChild;
57064             var rows = bt.rows;
57065             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
57066                 tbody.removeChild(rows[firstRow]);
57067             }
57068             this.stripeRows(firstRow);
57069             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
57070         }
57071     },
57072
57073     updateRows : function(dataSource, firstRow, lastRow){
57074         var s = this.getScrollState();
57075         this.refresh();
57076         this.restoreScroll(s);
57077     },
57078
57079     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
57080         if(!noRefresh){
57081            this.refresh();
57082         }
57083         this.updateHeaderSortState();
57084     },
57085
57086     getScrollState : function(){
57087         
57088         var sb = this.scroller.dom;
57089         return {left: sb.scrollLeft, top: sb.scrollTop};
57090     },
57091
57092     stripeRows : function(startRow){
57093         if(!this.grid.stripeRows || this.ds.getCount() < 1){
57094             return;
57095         }
57096         startRow = startRow || 0;
57097         var rows = this.getBodyTable().rows;
57098         var lrows = this.getLockedTable().rows;
57099         var cls = ' x-grid-row-alt ';
57100         for(var i = startRow, len = rows.length; i < len; i++){
57101             var row = rows[i], lrow = lrows[i];
57102             var isAlt = ((i+1) % 2 == 0);
57103             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
57104             if(isAlt == hasAlt){
57105                 continue;
57106             }
57107             if(isAlt){
57108                 row.className += " x-grid-row-alt";
57109             }else{
57110                 row.className = row.className.replace("x-grid-row-alt", "");
57111             }
57112             if(lrow){
57113                 lrow.className = row.className;
57114             }
57115         }
57116     },
57117
57118     restoreScroll : function(state){
57119         //Roo.log('GridView.restoreScroll');
57120         var sb = this.scroller.dom;
57121         sb.scrollLeft = state.left;
57122         sb.scrollTop = state.top;
57123         this.syncScroll();
57124     },
57125
57126     syncScroll : function(){
57127         //Roo.log('GridView.syncScroll');
57128         var sb = this.scroller.dom;
57129         var sh = this.mainHd.dom;
57130         var bs = this.mainBody.dom;
57131         var lv = this.lockedBody.dom;
57132         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
57133         lv.scrollTop = bs.scrollTop = sb.scrollTop;
57134     },
57135
57136     handleScroll : function(e){
57137         this.syncScroll();
57138         var sb = this.scroller.dom;
57139         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
57140         e.stopEvent();
57141     },
57142
57143     handleWheel : function(e){
57144         var d = e.getWheelDelta();
57145         this.scroller.dom.scrollTop -= d*22;
57146         // set this here to prevent jumpy scrolling on large tables
57147         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
57148         e.stopEvent();
57149     },
57150
57151     renderRows : function(startRow, endRow){
57152         // pull in all the crap needed to render rows
57153         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
57154         var colCount = cm.getColumnCount();
57155
57156         if(ds.getCount() < 1){
57157             return ["", ""];
57158         }
57159
57160         // build a map for all the columns
57161         var cs = [];
57162         for(var i = 0; i < colCount; i++){
57163             var name = cm.getDataIndex(i);
57164             cs[i] = {
57165                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
57166                 renderer : cm.getRenderer(i),
57167                 id : cm.getColumnId(i),
57168                 locked : cm.isLocked(i),
57169                 has_editor : cm.isCellEditable(i)
57170             };
57171         }
57172
57173         startRow = startRow || 0;
57174         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
57175
57176         // records to render
57177         var rs = ds.getRange(startRow, endRow);
57178
57179         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
57180     },
57181
57182     // As much as I hate to duplicate code, this was branched because FireFox really hates
57183     // [].join("") on strings. The performance difference was substantial enough to
57184     // branch this function
57185     doRender : Roo.isGecko ?
57186             function(cs, rs, ds, startRow, colCount, stripe){
57187                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57188                 // buffers
57189                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57190                 
57191                 var hasListener = this.grid.hasListener('rowclass');
57192                 var rowcfg = {};
57193                 for(var j = 0, len = rs.length; j < len; j++){
57194                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
57195                     for(var i = 0; i < colCount; i++){
57196                         c = cs[i];
57197                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57198                         p.id = c.id;
57199                         p.css = p.attr = "";
57200                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57201                         if(p.value == undefined || p.value === "") {
57202                             p.value = "&#160;";
57203                         }
57204                         if(c.has_editor){
57205                             p.css += ' x-grid-editable-cell';
57206                         }
57207                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
57208                             p.css +=  ' x-grid-dirty-cell';
57209                         }
57210                         var markup = ct.apply(p);
57211                         if(!c.locked){
57212                             cb+= markup;
57213                         }else{
57214                             lcb+= markup;
57215                         }
57216                     }
57217                     var alt = [];
57218                     if(stripe && ((rowIndex+1) % 2 == 0)){
57219                         alt.push("x-grid-row-alt")
57220                     }
57221                     if(r.dirty){
57222                         alt.push(  " x-grid-dirty-row");
57223                     }
57224                     rp.cells = lcb;
57225                     if(this.getRowClass){
57226                         alt.push(this.getRowClass(r, rowIndex));
57227                     }
57228                     if (hasListener) {
57229                         rowcfg = {
57230                              
57231                             record: r,
57232                             rowIndex : rowIndex,
57233                             rowClass : ''
57234                         };
57235                         this.grid.fireEvent('rowclass', this, rowcfg);
57236                         alt.push(rowcfg.rowClass);
57237                     }
57238                     rp.alt = alt.join(" ");
57239                     lbuf+= rt.apply(rp);
57240                     rp.cells = cb;
57241                     buf+=  rt.apply(rp);
57242                 }
57243                 return [lbuf, buf];
57244             } :
57245             function(cs, rs, ds, startRow, colCount, stripe){
57246                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57247                 // buffers
57248                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57249                 var hasListener = this.grid.hasListener('rowclass');
57250  
57251                 var rowcfg = {};
57252                 for(var j = 0, len = rs.length; j < len; j++){
57253                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
57254                     for(var i = 0; i < colCount; i++){
57255                         c = cs[i];
57256                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57257                         p.id = c.id;
57258                         p.css = p.attr = "";
57259                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57260                         if(p.value == undefined || p.value === "") {
57261                             p.value = "&#160;";
57262                         }
57263                         //Roo.log(c);
57264                          if(c.has_editor){
57265                             p.css += ' x-grid-editable-cell';
57266                         }
57267                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
57268                             p.css += ' x-grid-dirty-cell' 
57269                         }
57270                         
57271                         var markup = ct.apply(p);
57272                         if(!c.locked){
57273                             cb[cb.length] = markup;
57274                         }else{
57275                             lcb[lcb.length] = markup;
57276                         }
57277                     }
57278                     var alt = [];
57279                     if(stripe && ((rowIndex+1) % 2 == 0)){
57280                         alt.push( "x-grid-row-alt");
57281                     }
57282                     if(r.dirty){
57283                         alt.push(" x-grid-dirty-row");
57284                     }
57285                     rp.cells = lcb;
57286                     if(this.getRowClass){
57287                         alt.push( this.getRowClass(r, rowIndex));
57288                     }
57289                     if (hasListener) {
57290                         rowcfg = {
57291                              
57292                             record: r,
57293                             rowIndex : rowIndex,
57294                             rowClass : ''
57295                         };
57296                         this.grid.fireEvent('rowclass', this, rowcfg);
57297                         alt.push(rowcfg.rowClass);
57298                     }
57299                     
57300                     rp.alt = alt.join(" ");
57301                     rp.cells = lcb.join("");
57302                     lbuf[lbuf.length] = rt.apply(rp);
57303                     rp.cells = cb.join("");
57304                     buf[buf.length] =  rt.apply(rp);
57305                 }
57306                 return [lbuf.join(""), buf.join("")];
57307             },
57308
57309     renderBody : function(){
57310         var markup = this.renderRows();
57311         var bt = this.templates.body;
57312         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
57313     },
57314
57315     /**
57316      * Refreshes the grid
57317      * @param {Boolean} headersToo
57318      */
57319     refresh : function(headersToo){
57320         this.fireEvent("beforerefresh", this);
57321         this.grid.stopEditing();
57322         var result = this.renderBody();
57323         this.lockedBody.update(result[0]);
57324         this.mainBody.update(result[1]);
57325         if(headersToo === true){
57326             this.updateHeaders();
57327             this.updateColumns();
57328             this.updateSplitters();
57329             this.updateHeaderSortState();
57330         }
57331         this.syncRowHeights();
57332         this.layout();
57333         this.fireEvent("refresh", this);
57334     },
57335
57336     handleColumnMove : function(cm, oldIndex, newIndex){
57337         this.indexMap = null;
57338         var s = this.getScrollState();
57339         this.refresh(true);
57340         this.restoreScroll(s);
57341         this.afterMove(newIndex);
57342     },
57343
57344     afterMove : function(colIndex){
57345         if(this.enableMoveAnim && Roo.enableFx){
57346             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
57347         }
57348         // if multisort - fix sortOrder, and reload..
57349         if (this.grid.dataSource.multiSort) {
57350             // the we can call sort again..
57351             var dm = this.grid.dataSource;
57352             var cm = this.grid.colModel;
57353             var so = [];
57354             for(var i = 0; i < cm.config.length; i++ ) {
57355                 
57356                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
57357                     continue; // dont' bother, it's not in sort list or being set.
57358                 }
57359                 
57360                 so.push(cm.config[i].dataIndex);
57361             };
57362             dm.sortOrder = so;
57363             dm.load(dm.lastOptions);
57364             
57365             
57366         }
57367         
57368     },
57369
57370     updateCell : function(dm, rowIndex, dataIndex){
57371         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
57372         if(typeof colIndex == "undefined"){ // not present in grid
57373             return;
57374         }
57375         var cm = this.grid.colModel;
57376         var cell = this.getCell(rowIndex, colIndex);
57377         var cellText = this.getCellText(rowIndex, colIndex);
57378
57379         var p = {
57380             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
57381             id : cm.getColumnId(colIndex),
57382             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
57383         };
57384         var renderer = cm.getRenderer(colIndex);
57385         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
57386         if(typeof val == "undefined" || val === "") {
57387             val = "&#160;";
57388         }
57389         cellText.innerHTML = val;
57390         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
57391         this.syncRowHeights(rowIndex, rowIndex);
57392     },
57393
57394     calcColumnWidth : function(colIndex, maxRowsToMeasure){
57395         var maxWidth = 0;
57396         if(this.grid.autoSizeHeaders){
57397             var h = this.getHeaderCellMeasure(colIndex);
57398             maxWidth = Math.max(maxWidth, h.scrollWidth);
57399         }
57400         var tb, index;
57401         if(this.cm.isLocked(colIndex)){
57402             tb = this.getLockedTable();
57403             index = colIndex;
57404         }else{
57405             tb = this.getBodyTable();
57406             index = colIndex - this.cm.getLockedCount();
57407         }
57408         if(tb && tb.rows){
57409             var rows = tb.rows;
57410             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
57411             for(var i = 0; i < stopIndex; i++){
57412                 var cell = rows[i].childNodes[index].firstChild;
57413                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
57414             }
57415         }
57416         return maxWidth + /*margin for error in IE*/ 5;
57417     },
57418     /**
57419      * Autofit a column to its content.
57420      * @param {Number} colIndex
57421      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
57422      */
57423      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
57424          if(this.cm.isHidden(colIndex)){
57425              return; // can't calc a hidden column
57426          }
57427         if(forceMinSize){
57428             var cid = this.cm.getColumnId(colIndex);
57429             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
57430            if(this.grid.autoSizeHeaders){
57431                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
57432            }
57433         }
57434         var newWidth = this.calcColumnWidth(colIndex);
57435         this.cm.setColumnWidth(colIndex,
57436             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
57437         if(!suppressEvent){
57438             this.grid.fireEvent("columnresize", colIndex, newWidth);
57439         }
57440     },
57441
57442     /**
57443      * Autofits all columns to their content and then expands to fit any extra space in the grid
57444      */
57445      autoSizeColumns : function(){
57446         var cm = this.grid.colModel;
57447         var colCount = cm.getColumnCount();
57448         for(var i = 0; i < colCount; i++){
57449             this.autoSizeColumn(i, true, true);
57450         }
57451         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
57452             this.fitColumns();
57453         }else{
57454             this.updateColumns();
57455             this.layout();
57456         }
57457     },
57458
57459     /**
57460      * Autofits all columns to the grid's width proportionate with their current size
57461      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
57462      */
57463     fitColumns : function(reserveScrollSpace){
57464         var cm = this.grid.colModel;
57465         var colCount = cm.getColumnCount();
57466         var cols = [];
57467         var width = 0;
57468         var i, w;
57469         for (i = 0; i < colCount; i++){
57470             if(!cm.isHidden(i) && !cm.isFixed(i)){
57471                 w = cm.getColumnWidth(i);
57472                 cols.push(i);
57473                 cols.push(w);
57474                 width += w;
57475             }
57476         }
57477         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
57478         if(reserveScrollSpace){
57479             avail -= 17;
57480         }
57481         var frac = (avail - cm.getTotalWidth())/width;
57482         while (cols.length){
57483             w = cols.pop();
57484             i = cols.pop();
57485             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
57486         }
57487         this.updateColumns();
57488         this.layout();
57489     },
57490
57491     onRowSelect : function(rowIndex){
57492         var row = this.getRowComposite(rowIndex);
57493         row.addClass("x-grid-row-selected");
57494     },
57495
57496     onRowDeselect : function(rowIndex){
57497         var row = this.getRowComposite(rowIndex);
57498         row.removeClass("x-grid-row-selected");
57499     },
57500
57501     onCellSelect : function(row, col){
57502         var cell = this.getCell(row, col);
57503         if(cell){
57504             Roo.fly(cell).addClass("x-grid-cell-selected");
57505         }
57506     },
57507
57508     onCellDeselect : function(row, col){
57509         var cell = this.getCell(row, col);
57510         if(cell){
57511             Roo.fly(cell).removeClass("x-grid-cell-selected");
57512         }
57513     },
57514
57515     updateHeaderSortState : function(){
57516         
57517         // sort state can be single { field: xxx, direction : yyy}
57518         // or   { xxx=>ASC , yyy : DESC ..... }
57519         
57520         var mstate = {};
57521         if (!this.ds.multiSort) { 
57522             var state = this.ds.getSortState();
57523             if(!state){
57524                 return;
57525             }
57526             mstate[state.field] = state.direction;
57527             // FIXME... - this is not used here.. but might be elsewhere..
57528             this.sortState = state;
57529             
57530         } else {
57531             mstate = this.ds.sortToggle;
57532         }
57533         //remove existing sort classes..
57534         
57535         var sc = this.sortClasses;
57536         var hds = this.el.select(this.headerSelector).removeClass(sc);
57537         
57538         for(var f in mstate) {
57539         
57540             var sortColumn = this.cm.findColumnIndex(f);
57541             
57542             if(sortColumn != -1){
57543                 var sortDir = mstate[f];        
57544                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
57545             }
57546         }
57547         
57548          
57549         
57550     },
57551
57552
57553     handleHeaderClick : function(g, index,e){
57554         
57555         Roo.log("header click");
57556         
57557         if (Roo.isTouch) {
57558             // touch events on header are handled by context
57559             this.handleHdCtx(g,index,e);
57560             return;
57561         }
57562         
57563         
57564         if(this.headersDisabled){
57565             return;
57566         }
57567         var dm = g.dataSource, cm = g.colModel;
57568         if(!cm.isSortable(index)){
57569             return;
57570         }
57571         g.stopEditing();
57572         
57573         if (dm.multiSort) {
57574             // update the sortOrder
57575             var so = [];
57576             for(var i = 0; i < cm.config.length; i++ ) {
57577                 
57578                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
57579                     continue; // dont' bother, it's not in sort list or being set.
57580                 }
57581                 
57582                 so.push(cm.config[i].dataIndex);
57583             };
57584             dm.sortOrder = so;
57585         }
57586         
57587         
57588         dm.sort(cm.getDataIndex(index));
57589     },
57590
57591
57592     destroy : function(){
57593         if(this.colMenu){
57594             this.colMenu.removeAll();
57595             Roo.menu.MenuMgr.unregister(this.colMenu);
57596             this.colMenu.getEl().remove();
57597             delete this.colMenu;
57598         }
57599         if(this.hmenu){
57600             this.hmenu.removeAll();
57601             Roo.menu.MenuMgr.unregister(this.hmenu);
57602             this.hmenu.getEl().remove();
57603             delete this.hmenu;
57604         }
57605         if(this.grid.enableColumnMove){
57606             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57607             if(dds){
57608                 for(var dd in dds){
57609                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
57610                         var elid = dds[dd].dragElId;
57611                         dds[dd].unreg();
57612                         Roo.get(elid).remove();
57613                     } else if(dds[dd].config.isTarget){
57614                         dds[dd].proxyTop.remove();
57615                         dds[dd].proxyBottom.remove();
57616                         dds[dd].unreg();
57617                     }
57618                     if(Roo.dd.DDM.locationCache[dd]){
57619                         delete Roo.dd.DDM.locationCache[dd];
57620                     }
57621                 }
57622                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57623             }
57624         }
57625         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
57626         this.bind(null, null);
57627         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
57628     },
57629
57630     handleLockChange : function(){
57631         this.refresh(true);
57632     },
57633
57634     onDenyColumnLock : function(){
57635
57636     },
57637
57638     onDenyColumnHide : function(){
57639
57640     },
57641
57642     handleHdMenuClick : function(item){
57643         var index = this.hdCtxIndex;
57644         var cm = this.cm, ds = this.ds;
57645         switch(item.id){
57646             case "asc":
57647                 ds.sort(cm.getDataIndex(index), "ASC");
57648                 break;
57649             case "desc":
57650                 ds.sort(cm.getDataIndex(index), "DESC");
57651                 break;
57652             case "lock":
57653                 var lc = cm.getLockedCount();
57654                 if(cm.getColumnCount(true) <= lc+1){
57655                     this.onDenyColumnLock();
57656                     return;
57657                 }
57658                 if(lc != index){
57659                     cm.setLocked(index, true, true);
57660                     cm.moveColumn(index, lc);
57661                     this.grid.fireEvent("columnmove", index, lc);
57662                 }else{
57663                     cm.setLocked(index, true);
57664                 }
57665             break;
57666             case "unlock":
57667                 var lc = cm.getLockedCount();
57668                 if((lc-1) != index){
57669                     cm.setLocked(index, false, true);
57670                     cm.moveColumn(index, lc-1);
57671                     this.grid.fireEvent("columnmove", index, lc-1);
57672                 }else{
57673                     cm.setLocked(index, false);
57674                 }
57675             break;
57676             case 'wider': // used to expand cols on touch..
57677             case 'narrow':
57678                 var cw = cm.getColumnWidth(index);
57679                 cw += (item.id == 'wider' ? 1 : -1) * 50;
57680                 cw = Math.max(0, cw);
57681                 cw = Math.min(cw,4000);
57682                 cm.setColumnWidth(index, cw);
57683                 break;
57684                 
57685             default:
57686                 index = cm.getIndexById(item.id.substr(4));
57687                 if(index != -1){
57688                     if(item.checked && cm.getColumnCount(true) <= 1){
57689                         this.onDenyColumnHide();
57690                         return false;
57691                     }
57692                     cm.setHidden(index, item.checked);
57693                 }
57694         }
57695         return true;
57696     },
57697
57698     beforeColMenuShow : function(){
57699         var cm = this.cm,  colCount = cm.getColumnCount();
57700         this.colMenu.removeAll();
57701         for(var i = 0; i < colCount; i++){
57702             this.colMenu.add(new Roo.menu.CheckItem({
57703                 id: "col-"+cm.getColumnId(i),
57704                 text: cm.getColumnHeader(i),
57705                 checked: !cm.isHidden(i),
57706                 hideOnClick:false
57707             }));
57708         }
57709     },
57710
57711     handleHdCtx : function(g, index, e){
57712         e.stopEvent();
57713         var hd = this.getHeaderCell(index);
57714         this.hdCtxIndex = index;
57715         var ms = this.hmenu.items, cm = this.cm;
57716         ms.get("asc").setDisabled(!cm.isSortable(index));
57717         ms.get("desc").setDisabled(!cm.isSortable(index));
57718         if(this.grid.enableColLock !== false){
57719             ms.get("lock").setDisabled(cm.isLocked(index));
57720             ms.get("unlock").setDisabled(!cm.isLocked(index));
57721         }
57722         this.hmenu.show(hd, "tl-bl");
57723     },
57724
57725     handleHdOver : function(e){
57726         var hd = this.findHeaderCell(e.getTarget());
57727         if(hd && !this.headersDisabled){
57728             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
57729                this.fly(hd).addClass("x-grid-hd-over");
57730             }
57731         }
57732     },
57733
57734     handleHdOut : function(e){
57735         var hd = this.findHeaderCell(e.getTarget());
57736         if(hd){
57737             this.fly(hd).removeClass("x-grid-hd-over");
57738         }
57739     },
57740
57741     handleSplitDblClick : function(e, t){
57742         var i = this.getCellIndex(t);
57743         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
57744             this.autoSizeColumn(i, true);
57745             this.layout();
57746         }
57747     },
57748
57749     render : function(){
57750
57751         var cm = this.cm;
57752         var colCount = cm.getColumnCount();
57753
57754         if(this.grid.monitorWindowResize === true){
57755             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
57756         }
57757         var header = this.renderHeaders();
57758         var body = this.templates.body.apply({rows:""});
57759         var html = this.templates.master.apply({
57760             lockedBody: body,
57761             body: body,
57762             lockedHeader: header[0],
57763             header: header[1]
57764         });
57765
57766         //this.updateColumns();
57767
57768         this.grid.getGridEl().dom.innerHTML = html;
57769
57770         this.initElements();
57771         
57772         // a kludge to fix the random scolling effect in webkit
57773         this.el.on("scroll", function() {
57774             this.el.dom.scrollTop=0; // hopefully not recursive..
57775         },this);
57776
57777         this.scroller.on("scroll", this.handleScroll, this);
57778         this.lockedBody.on("mousewheel", this.handleWheel, this);
57779         this.mainBody.on("mousewheel", this.handleWheel, this);
57780
57781         this.mainHd.on("mouseover", this.handleHdOver, this);
57782         this.mainHd.on("mouseout", this.handleHdOut, this);
57783         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
57784                 {delegate: "."+this.splitClass});
57785
57786         this.lockedHd.on("mouseover", this.handleHdOver, this);
57787         this.lockedHd.on("mouseout", this.handleHdOut, this);
57788         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
57789                 {delegate: "."+this.splitClass});
57790
57791         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
57792             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57793         }
57794
57795         this.updateSplitters();
57796
57797         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
57798             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57799             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57800         }
57801
57802         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
57803             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
57804             this.hmenu.add(
57805                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
57806                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
57807             );
57808             if(this.grid.enableColLock !== false){
57809                 this.hmenu.add('-',
57810                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
57811                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
57812                 );
57813             }
57814             if (Roo.isTouch) {
57815                  this.hmenu.add('-',
57816                     {id:"wider", text: this.columnsWiderText},
57817                     {id:"narrow", text: this.columnsNarrowText }
57818                 );
57819                 
57820                  
57821             }
57822             
57823             if(this.grid.enableColumnHide !== false){
57824
57825                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
57826                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
57827                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
57828
57829                 this.hmenu.add('-',
57830                     {id:"columns", text: this.columnsText, menu: this.colMenu}
57831                 );
57832             }
57833             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
57834
57835             this.grid.on("headercontextmenu", this.handleHdCtx, this);
57836         }
57837
57838         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
57839             this.dd = new Roo.grid.GridDragZone(this.grid, {
57840                 ddGroup : this.grid.ddGroup || 'GridDD'
57841             });
57842             
57843         }
57844
57845         /*
57846         for(var i = 0; i < colCount; i++){
57847             if(cm.isHidden(i)){
57848                 this.hideColumn(i);
57849             }
57850             if(cm.config[i].align){
57851                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
57852                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
57853             }
57854         }*/
57855         
57856         this.updateHeaderSortState();
57857
57858         this.beforeInitialResize();
57859         this.layout(true);
57860
57861         // two part rendering gives faster view to the user
57862         this.renderPhase2.defer(1, this);
57863     },
57864
57865     renderPhase2 : function(){
57866         // render the rows now
57867         this.refresh();
57868         if(this.grid.autoSizeColumns){
57869             this.autoSizeColumns();
57870         }
57871     },
57872
57873     beforeInitialResize : function(){
57874
57875     },
57876
57877     onColumnSplitterMoved : function(i, w){
57878         this.userResized = true;
57879         var cm = this.grid.colModel;
57880         cm.setColumnWidth(i, w, true);
57881         var cid = cm.getColumnId(i);
57882         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57883         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57884         this.updateSplitters();
57885         this.layout();
57886         this.grid.fireEvent("columnresize", i, w);
57887     },
57888
57889     syncRowHeights : function(startIndex, endIndex){
57890         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
57891             startIndex = startIndex || 0;
57892             var mrows = this.getBodyTable().rows;
57893             var lrows = this.getLockedTable().rows;
57894             var len = mrows.length-1;
57895             endIndex = Math.min(endIndex || len, len);
57896             for(var i = startIndex; i <= endIndex; i++){
57897                 var m = mrows[i], l = lrows[i];
57898                 var h = Math.max(m.offsetHeight, l.offsetHeight);
57899                 m.style.height = l.style.height = h + "px";
57900             }
57901         }
57902     },
57903
57904     layout : function(initialRender, is2ndPass)
57905     {
57906         var g = this.grid;
57907         var auto = g.autoHeight;
57908         var scrollOffset = 16;
57909         var c = g.getGridEl(), cm = this.cm,
57910                 expandCol = g.autoExpandColumn,
57911                 gv = this;
57912         //c.beginMeasure();
57913
57914         if(!c.dom.offsetWidth){ // display:none?
57915             if(initialRender){
57916                 this.lockedWrap.show();
57917                 this.mainWrap.show();
57918             }
57919             return;
57920         }
57921
57922         var hasLock = this.cm.isLocked(0);
57923
57924         var tbh = this.headerPanel.getHeight();
57925         var bbh = this.footerPanel.getHeight();
57926
57927         if(auto){
57928             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
57929             var newHeight = ch + c.getBorderWidth("tb");
57930             if(g.maxHeight){
57931                 newHeight = Math.min(g.maxHeight, newHeight);
57932             }
57933             c.setHeight(newHeight);
57934         }
57935
57936         if(g.autoWidth){
57937             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
57938         }
57939
57940         var s = this.scroller;
57941
57942         var csize = c.getSize(true);
57943
57944         this.el.setSize(csize.width, csize.height);
57945
57946         this.headerPanel.setWidth(csize.width);
57947         this.footerPanel.setWidth(csize.width);
57948
57949         var hdHeight = this.mainHd.getHeight();
57950         var vw = csize.width;
57951         var vh = csize.height - (tbh + bbh);
57952
57953         s.setSize(vw, vh);
57954
57955         var bt = this.getBodyTable();
57956         
57957         if(cm.getLockedCount() == cm.config.length){
57958             bt = this.getLockedTable();
57959         }
57960         
57961         var ltWidth = hasLock ?
57962                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
57963
57964         var scrollHeight = bt.offsetHeight;
57965         var scrollWidth = ltWidth + bt.offsetWidth;
57966         var vscroll = false, hscroll = false;
57967
57968         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
57969
57970         var lw = this.lockedWrap, mw = this.mainWrap;
57971         var lb = this.lockedBody, mb = this.mainBody;
57972
57973         setTimeout(function(){
57974             var t = s.dom.offsetTop;
57975             var w = s.dom.clientWidth,
57976                 h = s.dom.clientHeight;
57977
57978             lw.setTop(t);
57979             lw.setSize(ltWidth, h);
57980
57981             mw.setLeftTop(ltWidth, t);
57982             mw.setSize(w-ltWidth, h);
57983
57984             lb.setHeight(h-hdHeight);
57985             mb.setHeight(h-hdHeight);
57986
57987             if(is2ndPass !== true && !gv.userResized && expandCol){
57988                 // high speed resize without full column calculation
57989                 
57990                 var ci = cm.getIndexById(expandCol);
57991                 if (ci < 0) {
57992                     ci = cm.findColumnIndex(expandCol);
57993                 }
57994                 ci = Math.max(0, ci); // make sure it's got at least the first col.
57995                 var expandId = cm.getColumnId(ci);
57996                 var  tw = cm.getTotalWidth(false);
57997                 var currentWidth = cm.getColumnWidth(ci);
57998                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
57999                 if(currentWidth != cw){
58000                     cm.setColumnWidth(ci, cw, true);
58001                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
58002                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
58003                     gv.updateSplitters();
58004                     gv.layout(false, true);
58005                 }
58006             }
58007
58008             if(initialRender){
58009                 lw.show();
58010                 mw.show();
58011             }
58012             //c.endMeasure();
58013         }, 10);
58014     },
58015
58016     onWindowResize : function(){
58017         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
58018             return;
58019         }
58020         this.layout();
58021     },
58022
58023     appendFooter : function(parentEl){
58024         return null;
58025     },
58026
58027     sortAscText : "Sort Ascending",
58028     sortDescText : "Sort Descending",
58029     lockText : "Lock Column",
58030     unlockText : "Unlock Column",
58031     columnsText : "Columns",
58032  
58033     columnsWiderText : "Wider",
58034     columnsNarrowText : "Thinner"
58035 });
58036
58037
58038 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
58039     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
58040     this.proxy.el.addClass('x-grid3-col-dd');
58041 };
58042
58043 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
58044     handleMouseDown : function(e){
58045
58046     },
58047
58048     callHandleMouseDown : function(e){
58049         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
58050     }
58051 });
58052 /*
58053  * Based on:
58054  * Ext JS Library 1.1.1
58055  * Copyright(c) 2006-2007, Ext JS, LLC.
58056  *
58057  * Originally Released Under LGPL - original licence link has changed is not relivant.
58058  *
58059  * Fork - LGPL
58060  * <script type="text/javascript">
58061  */
58062  
58063 // private
58064 // This is a support class used internally by the Grid components
58065 Roo.grid.SplitDragZone = function(grid, hd, hd2){
58066     this.grid = grid;
58067     this.view = grid.getView();
58068     this.proxy = this.view.resizeProxy;
58069     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
58070         "gridSplitters" + this.grid.getGridEl().id, {
58071         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
58072     });
58073     this.setHandleElId(Roo.id(hd));
58074     this.setOuterHandleElId(Roo.id(hd2));
58075     this.scroll = false;
58076 };
58077 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
58078     fly: Roo.Element.fly,
58079
58080     b4StartDrag : function(x, y){
58081         this.view.headersDisabled = true;
58082         this.proxy.setHeight(this.view.mainWrap.getHeight());
58083         var w = this.cm.getColumnWidth(this.cellIndex);
58084         var minw = Math.max(w-this.grid.minColumnWidth, 0);
58085         this.resetConstraints();
58086         this.setXConstraint(minw, 1000);
58087         this.setYConstraint(0, 0);
58088         this.minX = x - minw;
58089         this.maxX = x + 1000;
58090         this.startPos = x;
58091         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
58092     },
58093
58094
58095     handleMouseDown : function(e){
58096         ev = Roo.EventObject.setEvent(e);
58097         var t = this.fly(ev.getTarget());
58098         if(t.hasClass("x-grid-split")){
58099             this.cellIndex = this.view.getCellIndex(t.dom);
58100             this.split = t.dom;
58101             this.cm = this.grid.colModel;
58102             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
58103                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
58104             }
58105         }
58106     },
58107
58108     endDrag : function(e){
58109         this.view.headersDisabled = false;
58110         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
58111         var diff = endX - this.startPos;
58112         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
58113     },
58114
58115     autoOffset : function(){
58116         this.setDelta(0,0);
58117     }
58118 });/*
58119  * Based on:
58120  * Ext JS Library 1.1.1
58121  * Copyright(c) 2006-2007, Ext JS, LLC.
58122  *
58123  * Originally Released Under LGPL - original licence link has changed is not relivant.
58124  *
58125  * Fork - LGPL
58126  * <script type="text/javascript">
58127  */
58128  
58129 // private
58130 // This is a support class used internally by the Grid components
58131 Roo.grid.GridDragZone = function(grid, config){
58132     this.view = grid.getView();
58133     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
58134     if(this.view.lockedBody){
58135         this.setHandleElId(Roo.id(this.view.mainBody.dom));
58136         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
58137     }
58138     this.scroll = false;
58139     this.grid = grid;
58140     this.ddel = document.createElement('div');
58141     this.ddel.className = 'x-grid-dd-wrap';
58142 };
58143
58144 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
58145     ddGroup : "GridDD",
58146
58147     getDragData : function(e){
58148         var t = Roo.lib.Event.getTarget(e);
58149         var rowIndex = this.view.findRowIndex(t);
58150         var sm = this.grid.selModel;
58151             
58152         //Roo.log(rowIndex);
58153         
58154         if (sm.getSelectedCell) {
58155             // cell selection..
58156             if (!sm.getSelectedCell()) {
58157                 return false;
58158             }
58159             if (rowIndex != sm.getSelectedCell()[0]) {
58160                 return false;
58161             }
58162         
58163         }
58164         if (sm.getSelections && sm.getSelections().length < 1) {
58165             return false;
58166         }
58167         
58168         
58169         // before it used to all dragging of unseleted... - now we dont do that.
58170         if(rowIndex !== false){
58171             
58172             // if editorgrid.. 
58173             
58174             
58175             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
58176                
58177             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
58178               //  
58179             //}
58180             if (e.hasModifier()){
58181                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
58182             }
58183             
58184             Roo.log("getDragData");
58185             
58186             return {
58187                 grid: this.grid,
58188                 ddel: this.ddel,
58189                 rowIndex: rowIndex,
58190                 selections: sm.getSelections ? sm.getSelections() : (
58191                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
58192             };
58193         }
58194         return false;
58195     },
58196     
58197     
58198     onInitDrag : function(e){
58199         var data = this.dragData;
58200         this.ddel.innerHTML = this.grid.getDragDropText();
58201         this.proxy.update(this.ddel);
58202         // fire start drag?
58203     },
58204
58205     afterRepair : function(){
58206         this.dragging = false;
58207     },
58208
58209     getRepairXY : function(e, data){
58210         return false;
58211     },
58212
58213     onEndDrag : function(data, e){
58214         // fire end drag?
58215     },
58216
58217     onValidDrop : function(dd, e, id){
58218         // fire drag drop?
58219         this.hideProxy();
58220     },
58221
58222     beforeInvalidDrop : function(e, id){
58223
58224     }
58225 });/*
58226  * Based on:
58227  * Ext JS Library 1.1.1
58228  * Copyright(c) 2006-2007, Ext JS, LLC.
58229  *
58230  * Originally Released Under LGPL - original licence link has changed is not relivant.
58231  *
58232  * Fork - LGPL
58233  * <script type="text/javascript">
58234  */
58235  
58236
58237 /**
58238  * @class Roo.grid.ColumnModel
58239  * @extends Roo.util.Observable
58240  * This is the default implementation of a ColumnModel used by the Grid. It defines
58241  * the columns in the grid.
58242  * <br>Usage:<br>
58243  <pre><code>
58244  var colModel = new Roo.grid.ColumnModel([
58245         {header: "Ticker", width: 60, sortable: true, locked: true},
58246         {header: "Company Name", width: 150, sortable: true},
58247         {header: "Market Cap.", width: 100, sortable: true},
58248         {header: "$ Sales", width: 100, sortable: true, renderer: money},
58249         {header: "Employees", width: 100, sortable: true, resizable: false}
58250  ]);
58251  </code></pre>
58252  * <p>
58253  
58254  * The config options listed for this class are options which may appear in each
58255  * individual column definition.
58256  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
58257  * @constructor
58258  * @param {Object} config An Array of column config objects. See this class's
58259  * config objects for details.
58260 */
58261 Roo.grid.ColumnModel = function(config){
58262         /**
58263      * The config passed into the constructor
58264      */
58265     this.config = config;
58266     this.lookup = {};
58267
58268     // if no id, create one
58269     // if the column does not have a dataIndex mapping,
58270     // map it to the order it is in the config
58271     for(var i = 0, len = config.length; i < len; i++){
58272         var c = config[i];
58273         if(typeof c.dataIndex == "undefined"){
58274             c.dataIndex = i;
58275         }
58276         if(typeof c.renderer == "string"){
58277             c.renderer = Roo.util.Format[c.renderer];
58278         }
58279         if(typeof c.id == "undefined"){
58280             c.id = Roo.id();
58281         }
58282         if(c.editor && c.editor.xtype){
58283             c.editor  = Roo.factory(c.editor, Roo.grid);
58284         }
58285         if(c.editor && c.editor.isFormField){
58286             c.editor = new Roo.grid.GridEditor(c.editor);
58287         }
58288         this.lookup[c.id] = c;
58289     }
58290
58291     /**
58292      * The width of columns which have no width specified (defaults to 100)
58293      * @type Number
58294      */
58295     this.defaultWidth = 100;
58296
58297     /**
58298      * Default sortable of columns which have no sortable specified (defaults to false)
58299      * @type Boolean
58300      */
58301     this.defaultSortable = false;
58302
58303     this.addEvents({
58304         /**
58305              * @event widthchange
58306              * Fires when the width of a column changes.
58307              * @param {ColumnModel} this
58308              * @param {Number} columnIndex The column index
58309              * @param {Number} newWidth The new width
58310              */
58311             "widthchange": true,
58312         /**
58313              * @event headerchange
58314              * Fires when the text of a header changes.
58315              * @param {ColumnModel} this
58316              * @param {Number} columnIndex The column index
58317              * @param {Number} newText The new header text
58318              */
58319             "headerchange": true,
58320         /**
58321              * @event hiddenchange
58322              * Fires when a column is hidden or "unhidden".
58323              * @param {ColumnModel} this
58324              * @param {Number} columnIndex The column index
58325              * @param {Boolean} hidden true if hidden, false otherwise
58326              */
58327             "hiddenchange": true,
58328             /**
58329          * @event columnmoved
58330          * Fires when a column is moved.
58331          * @param {ColumnModel} this
58332          * @param {Number} oldIndex
58333          * @param {Number} newIndex
58334          */
58335         "columnmoved" : true,
58336         /**
58337          * @event columlockchange
58338          * Fires when a column's locked state is changed
58339          * @param {ColumnModel} this
58340          * @param {Number} colIndex
58341          * @param {Boolean} locked true if locked
58342          */
58343         "columnlockchange" : true
58344     });
58345     Roo.grid.ColumnModel.superclass.constructor.call(this);
58346 };
58347 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
58348     /**
58349      * @cfg {String} header The header text to display in the Grid view.
58350      */
58351     /**
58352      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
58353      * {@link Roo.data.Record} definition from which to draw the column's value. If not
58354      * specified, the column's index is used as an index into the Record's data Array.
58355      */
58356     /**
58357      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
58358      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
58359      */
58360     /**
58361      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
58362      * Defaults to the value of the {@link #defaultSortable} property.
58363      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
58364      */
58365     /**
58366      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
58367      */
58368     /**
58369      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
58370      */
58371     /**
58372      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
58373      */
58374     /**
58375      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
58376      */
58377     /**
58378      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
58379      * given the cell's data value. See {@link #setRenderer}. If not specified, the
58380      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
58381      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
58382      */
58383        /**
58384      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
58385      */
58386     /**
58387      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
58388      */
58389     /**
58390      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
58391      */
58392     /**
58393      * @cfg {String} cursor (Optional)
58394      */
58395     /**
58396      * @cfg {String} tooltip (Optional)
58397      */
58398     /**
58399      * @cfg {Number} xs (Optional)
58400      */
58401     /**
58402      * @cfg {Number} sm (Optional)
58403      */
58404     /**
58405      * @cfg {Number} md (Optional)
58406      */
58407     /**
58408      * @cfg {Number} lg (Optional)
58409      */
58410     /**
58411      * Returns the id of the column at the specified index.
58412      * @param {Number} index The column index
58413      * @return {String} the id
58414      */
58415     getColumnId : function(index){
58416         return this.config[index].id;
58417     },
58418
58419     /**
58420      * Returns the column for a specified id.
58421      * @param {String} id The column id
58422      * @return {Object} the column
58423      */
58424     getColumnById : function(id){
58425         return this.lookup[id];
58426     },
58427
58428     
58429     /**
58430      * Returns the column for a specified dataIndex.
58431      * @param {String} dataIndex The column dataIndex
58432      * @return {Object|Boolean} the column or false if not found
58433      */
58434     getColumnByDataIndex: function(dataIndex){
58435         var index = this.findColumnIndex(dataIndex);
58436         return index > -1 ? this.config[index] : false;
58437     },
58438     
58439     /**
58440      * Returns the index for a specified column id.
58441      * @param {String} id The column id
58442      * @return {Number} the index, or -1 if not found
58443      */
58444     getIndexById : function(id){
58445         for(var i = 0, len = this.config.length; i < len; i++){
58446             if(this.config[i].id == id){
58447                 return i;
58448             }
58449         }
58450         return -1;
58451     },
58452     
58453     /**
58454      * Returns the index for a specified column dataIndex.
58455      * @param {String} dataIndex The column dataIndex
58456      * @return {Number} the index, or -1 if not found
58457      */
58458     
58459     findColumnIndex : function(dataIndex){
58460         for(var i = 0, len = this.config.length; i < len; i++){
58461             if(this.config[i].dataIndex == dataIndex){
58462                 return i;
58463             }
58464         }
58465         return -1;
58466     },
58467     
58468     
58469     moveColumn : function(oldIndex, newIndex){
58470         var c = this.config[oldIndex];
58471         this.config.splice(oldIndex, 1);
58472         this.config.splice(newIndex, 0, c);
58473         this.dataMap = null;
58474         this.fireEvent("columnmoved", this, oldIndex, newIndex);
58475     },
58476
58477     isLocked : function(colIndex){
58478         return this.config[colIndex].locked === true;
58479     },
58480
58481     setLocked : function(colIndex, value, suppressEvent){
58482         if(this.isLocked(colIndex) == value){
58483             return;
58484         }
58485         this.config[colIndex].locked = value;
58486         if(!suppressEvent){
58487             this.fireEvent("columnlockchange", this, colIndex, value);
58488         }
58489     },
58490
58491     getTotalLockedWidth : function(){
58492         var totalWidth = 0;
58493         for(var i = 0; i < this.config.length; i++){
58494             if(this.isLocked(i) && !this.isHidden(i)){
58495                 this.totalWidth += this.getColumnWidth(i);
58496             }
58497         }
58498         return totalWidth;
58499     },
58500
58501     getLockedCount : function(){
58502         for(var i = 0, len = this.config.length; i < len; i++){
58503             if(!this.isLocked(i)){
58504                 return i;
58505             }
58506         }
58507         
58508         return this.config.length;
58509     },
58510
58511     /**
58512      * Returns the number of columns.
58513      * @return {Number}
58514      */
58515     getColumnCount : function(visibleOnly){
58516         if(visibleOnly === true){
58517             var c = 0;
58518             for(var i = 0, len = this.config.length; i < len; i++){
58519                 if(!this.isHidden(i)){
58520                     c++;
58521                 }
58522             }
58523             return c;
58524         }
58525         return this.config.length;
58526     },
58527
58528     /**
58529      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
58530      * @param {Function} fn
58531      * @param {Object} scope (optional)
58532      * @return {Array} result
58533      */
58534     getColumnsBy : function(fn, scope){
58535         var r = [];
58536         for(var i = 0, len = this.config.length; i < len; i++){
58537             var c = this.config[i];
58538             if(fn.call(scope||this, c, i) === true){
58539                 r[r.length] = c;
58540             }
58541         }
58542         return r;
58543     },
58544
58545     /**
58546      * Returns true if the specified column is sortable.
58547      * @param {Number} col The column index
58548      * @return {Boolean}
58549      */
58550     isSortable : function(col){
58551         if(typeof this.config[col].sortable == "undefined"){
58552             return this.defaultSortable;
58553         }
58554         return this.config[col].sortable;
58555     },
58556
58557     /**
58558      * Returns the rendering (formatting) function defined for the column.
58559      * @param {Number} col The column index.
58560      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
58561      */
58562     getRenderer : function(col){
58563         if(!this.config[col].renderer){
58564             return Roo.grid.ColumnModel.defaultRenderer;
58565         }
58566         return this.config[col].renderer;
58567     },
58568
58569     /**
58570      * Sets the rendering (formatting) function for a column.
58571      * @param {Number} col The column index
58572      * @param {Function} fn The function to use to process the cell's raw data
58573      * to return HTML markup for the grid view. The render function is called with
58574      * the following parameters:<ul>
58575      * <li>Data value.</li>
58576      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
58577      * <li>css A CSS style string to apply to the table cell.</li>
58578      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
58579      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
58580      * <li>Row index</li>
58581      * <li>Column index</li>
58582      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
58583      */
58584     setRenderer : function(col, fn){
58585         this.config[col].renderer = fn;
58586     },
58587
58588     /**
58589      * Returns the width for the specified column.
58590      * @param {Number} col The column index
58591      * @return {Number}
58592      */
58593     getColumnWidth : function(col){
58594         return this.config[col].width * 1 || this.defaultWidth;
58595     },
58596
58597     /**
58598      * Sets the width for a column.
58599      * @param {Number} col The column index
58600      * @param {Number} width The new width
58601      */
58602     setColumnWidth : function(col, width, suppressEvent){
58603         this.config[col].width = width;
58604         this.totalWidth = null;
58605         if(!suppressEvent){
58606              this.fireEvent("widthchange", this, col, width);
58607         }
58608     },
58609
58610     /**
58611      * Returns the total width of all columns.
58612      * @param {Boolean} includeHidden True to include hidden column widths
58613      * @return {Number}
58614      */
58615     getTotalWidth : function(includeHidden){
58616         if(!this.totalWidth){
58617             this.totalWidth = 0;
58618             for(var i = 0, len = this.config.length; i < len; i++){
58619                 if(includeHidden || !this.isHidden(i)){
58620                     this.totalWidth += this.getColumnWidth(i);
58621                 }
58622             }
58623         }
58624         return this.totalWidth;
58625     },
58626
58627     /**
58628      * Returns the header for the specified column.
58629      * @param {Number} col The column index
58630      * @return {String}
58631      */
58632     getColumnHeader : function(col){
58633         return this.config[col].header;
58634     },
58635
58636     /**
58637      * Sets the header for a column.
58638      * @param {Number} col The column index
58639      * @param {String} header The new header
58640      */
58641     setColumnHeader : function(col, header){
58642         this.config[col].header = header;
58643         this.fireEvent("headerchange", this, col, header);
58644     },
58645
58646     /**
58647      * Returns the tooltip for the specified column.
58648      * @param {Number} col The column index
58649      * @return {String}
58650      */
58651     getColumnTooltip : function(col){
58652             return this.config[col].tooltip;
58653     },
58654     /**
58655      * Sets the tooltip for a column.
58656      * @param {Number} col The column index
58657      * @param {String} tooltip The new tooltip
58658      */
58659     setColumnTooltip : function(col, tooltip){
58660             this.config[col].tooltip = tooltip;
58661     },
58662
58663     /**
58664      * Returns the dataIndex for the specified column.
58665      * @param {Number} col The column index
58666      * @return {Number}
58667      */
58668     getDataIndex : function(col){
58669         return this.config[col].dataIndex;
58670     },
58671
58672     /**
58673      * Sets the dataIndex for a column.
58674      * @param {Number} col The column index
58675      * @param {Number} dataIndex The new dataIndex
58676      */
58677     setDataIndex : function(col, dataIndex){
58678         this.config[col].dataIndex = dataIndex;
58679     },
58680
58681     
58682     
58683     /**
58684      * Returns true if the cell is editable.
58685      * @param {Number} colIndex The column index
58686      * @param {Number} rowIndex The row index - this is nto actually used..?
58687      * @return {Boolean}
58688      */
58689     isCellEditable : function(colIndex, rowIndex){
58690         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
58691     },
58692
58693     /**
58694      * Returns the editor defined for the cell/column.
58695      * return false or null to disable editing.
58696      * @param {Number} colIndex The column index
58697      * @param {Number} rowIndex The row index
58698      * @return {Object}
58699      */
58700     getCellEditor : function(colIndex, rowIndex){
58701         return this.config[colIndex].editor;
58702     },
58703
58704     /**
58705      * Sets if a column is editable.
58706      * @param {Number} col The column index
58707      * @param {Boolean} editable True if the column is editable
58708      */
58709     setEditable : function(col, editable){
58710         this.config[col].editable = editable;
58711     },
58712
58713
58714     /**
58715      * Returns true if the column is hidden.
58716      * @param {Number} colIndex The column index
58717      * @return {Boolean}
58718      */
58719     isHidden : function(colIndex){
58720         return this.config[colIndex].hidden;
58721     },
58722
58723
58724     /**
58725      * Returns true if the column width cannot be changed
58726      */
58727     isFixed : function(colIndex){
58728         return this.config[colIndex].fixed;
58729     },
58730
58731     /**
58732      * Returns true if the column can be resized
58733      * @return {Boolean}
58734      */
58735     isResizable : function(colIndex){
58736         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
58737     },
58738     /**
58739      * Sets if a column is hidden.
58740      * @param {Number} colIndex The column index
58741      * @param {Boolean} hidden True if the column is hidden
58742      */
58743     setHidden : function(colIndex, hidden){
58744         this.config[colIndex].hidden = hidden;
58745         this.totalWidth = null;
58746         this.fireEvent("hiddenchange", this, colIndex, hidden);
58747     },
58748
58749     /**
58750      * Sets the editor for a column.
58751      * @param {Number} col The column index
58752      * @param {Object} editor The editor object
58753      */
58754     setEditor : function(col, editor){
58755         this.config[col].editor = editor;
58756     }
58757 });
58758
58759 Roo.grid.ColumnModel.defaultRenderer = function(value)
58760 {
58761     if(typeof value == "object") {
58762         return value;
58763     }
58764         if(typeof value == "string" && value.length < 1){
58765             return "&#160;";
58766         }
58767     
58768         return String.format("{0}", value);
58769 };
58770
58771 // Alias for backwards compatibility
58772 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
58773 /*
58774  * Based on:
58775  * Ext JS Library 1.1.1
58776  * Copyright(c) 2006-2007, Ext JS, LLC.
58777  *
58778  * Originally Released Under LGPL - original licence link has changed is not relivant.
58779  *
58780  * Fork - LGPL
58781  * <script type="text/javascript">
58782  */
58783
58784 /**
58785  * @class Roo.grid.AbstractSelectionModel
58786  * @extends Roo.util.Observable
58787  * Abstract base class for grid SelectionModels.  It provides the interface that should be
58788  * implemented by descendant classes.  This class should not be directly instantiated.
58789  * @constructor
58790  */
58791 Roo.grid.AbstractSelectionModel = function(){
58792     this.locked = false;
58793     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
58794 };
58795
58796 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
58797     /** @ignore Called by the grid automatically. Do not call directly. */
58798     init : function(grid){
58799         this.grid = grid;
58800         this.initEvents();
58801     },
58802
58803     /**
58804      * Locks the selections.
58805      */
58806     lock : function(){
58807         this.locked = true;
58808     },
58809
58810     /**
58811      * Unlocks the selections.
58812      */
58813     unlock : function(){
58814         this.locked = false;
58815     },
58816
58817     /**
58818      * Returns true if the selections are locked.
58819      * @return {Boolean}
58820      */
58821     isLocked : function(){
58822         return this.locked;
58823     }
58824 });/*
58825  * Based on:
58826  * Ext JS Library 1.1.1
58827  * Copyright(c) 2006-2007, Ext JS, LLC.
58828  *
58829  * Originally Released Under LGPL - original licence link has changed is not relivant.
58830  *
58831  * Fork - LGPL
58832  * <script type="text/javascript">
58833  */
58834 /**
58835  * @extends Roo.grid.AbstractSelectionModel
58836  * @class Roo.grid.RowSelectionModel
58837  * The default SelectionModel used by {@link Roo.grid.Grid}.
58838  * It supports multiple selections and keyboard selection/navigation. 
58839  * @constructor
58840  * @param {Object} config
58841  */
58842 Roo.grid.RowSelectionModel = function(config){
58843     Roo.apply(this, config);
58844     this.selections = new Roo.util.MixedCollection(false, function(o){
58845         return o.id;
58846     });
58847
58848     this.last = false;
58849     this.lastActive = false;
58850
58851     this.addEvents({
58852         /**
58853              * @event selectionchange
58854              * Fires when the selection changes
58855              * @param {SelectionModel} this
58856              */
58857             "selectionchange" : true,
58858         /**
58859              * @event afterselectionchange
58860              * Fires after the selection changes (eg. by key press or clicking)
58861              * @param {SelectionModel} this
58862              */
58863             "afterselectionchange" : true,
58864         /**
58865              * @event beforerowselect
58866              * Fires when a row is selected being selected, return false to cancel.
58867              * @param {SelectionModel} this
58868              * @param {Number} rowIndex The selected index
58869              * @param {Boolean} keepExisting False if other selections will be cleared
58870              */
58871             "beforerowselect" : true,
58872         /**
58873              * @event rowselect
58874              * Fires when a row is selected.
58875              * @param {SelectionModel} this
58876              * @param {Number} rowIndex The selected index
58877              * @param {Roo.data.Record} r The record
58878              */
58879             "rowselect" : true,
58880         /**
58881              * @event rowdeselect
58882              * Fires when a row is deselected.
58883              * @param {SelectionModel} this
58884              * @param {Number} rowIndex The selected index
58885              */
58886         "rowdeselect" : true
58887     });
58888     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
58889     this.locked = false;
58890 };
58891
58892 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
58893     /**
58894      * @cfg {Boolean} singleSelect
58895      * True to allow selection of only one row at a time (defaults to false)
58896      */
58897     singleSelect : false,
58898
58899     // private
58900     initEvents : function(){
58901
58902         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
58903             this.grid.on("mousedown", this.handleMouseDown, this);
58904         }else{ // allow click to work like normal
58905             this.grid.on("rowclick", this.handleDragableRowClick, this);
58906         }
58907
58908         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
58909             "up" : function(e){
58910                 if(!e.shiftKey){
58911                     this.selectPrevious(e.shiftKey);
58912                 }else if(this.last !== false && this.lastActive !== false){
58913                     var last = this.last;
58914                     this.selectRange(this.last,  this.lastActive-1);
58915                     this.grid.getView().focusRow(this.lastActive);
58916                     if(last !== false){
58917                         this.last = last;
58918                     }
58919                 }else{
58920                     this.selectFirstRow();
58921                 }
58922                 this.fireEvent("afterselectionchange", this);
58923             },
58924             "down" : function(e){
58925                 if(!e.shiftKey){
58926                     this.selectNext(e.shiftKey);
58927                 }else if(this.last !== false && this.lastActive !== false){
58928                     var last = this.last;
58929                     this.selectRange(this.last,  this.lastActive+1);
58930                     this.grid.getView().focusRow(this.lastActive);
58931                     if(last !== false){
58932                         this.last = last;
58933                     }
58934                 }else{
58935                     this.selectFirstRow();
58936                 }
58937                 this.fireEvent("afterselectionchange", this);
58938             },
58939             scope: this
58940         });
58941
58942         var view = this.grid.view;
58943         view.on("refresh", this.onRefresh, this);
58944         view.on("rowupdated", this.onRowUpdated, this);
58945         view.on("rowremoved", this.onRemove, this);
58946     },
58947
58948     // private
58949     onRefresh : function(){
58950         var ds = this.grid.dataSource, i, v = this.grid.view;
58951         var s = this.selections;
58952         s.each(function(r){
58953             if((i = ds.indexOfId(r.id)) != -1){
58954                 v.onRowSelect(i);
58955                 s.add(ds.getAt(i)); // updating the selection relate data
58956             }else{
58957                 s.remove(r);
58958             }
58959         });
58960     },
58961
58962     // private
58963     onRemove : function(v, index, r){
58964         this.selections.remove(r);
58965     },
58966
58967     // private
58968     onRowUpdated : function(v, index, r){
58969         if(this.isSelected(r)){
58970             v.onRowSelect(index);
58971         }
58972     },
58973
58974     /**
58975      * Select records.
58976      * @param {Array} records The records to select
58977      * @param {Boolean} keepExisting (optional) True to keep existing selections
58978      */
58979     selectRecords : function(records, keepExisting){
58980         if(!keepExisting){
58981             this.clearSelections();
58982         }
58983         var ds = this.grid.dataSource;
58984         for(var i = 0, len = records.length; i < len; i++){
58985             this.selectRow(ds.indexOf(records[i]), true);
58986         }
58987     },
58988
58989     /**
58990      * Gets the number of selected rows.
58991      * @return {Number}
58992      */
58993     getCount : function(){
58994         return this.selections.length;
58995     },
58996
58997     /**
58998      * Selects the first row in the grid.
58999      */
59000     selectFirstRow : function(){
59001         this.selectRow(0);
59002     },
59003
59004     /**
59005      * Select the last row.
59006      * @param {Boolean} keepExisting (optional) True to keep existing selections
59007      */
59008     selectLastRow : function(keepExisting){
59009         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
59010     },
59011
59012     /**
59013      * Selects the row immediately following the last selected row.
59014      * @param {Boolean} keepExisting (optional) True to keep existing selections
59015      */
59016     selectNext : function(keepExisting){
59017         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
59018             this.selectRow(this.last+1, keepExisting);
59019             this.grid.getView().focusRow(this.last);
59020         }
59021     },
59022
59023     /**
59024      * Selects the row that precedes the last selected row.
59025      * @param {Boolean} keepExisting (optional) True to keep existing selections
59026      */
59027     selectPrevious : function(keepExisting){
59028         if(this.last){
59029             this.selectRow(this.last-1, keepExisting);
59030             this.grid.getView().focusRow(this.last);
59031         }
59032     },
59033
59034     /**
59035      * Returns the selected records
59036      * @return {Array} Array of selected records
59037      */
59038     getSelections : function(){
59039         return [].concat(this.selections.items);
59040     },
59041
59042     /**
59043      * Returns the first selected record.
59044      * @return {Record}
59045      */
59046     getSelected : function(){
59047         return this.selections.itemAt(0);
59048     },
59049
59050
59051     /**
59052      * Clears all selections.
59053      */
59054     clearSelections : function(fast){
59055         if(this.locked) {
59056             return;
59057         }
59058         if(fast !== true){
59059             var ds = this.grid.dataSource;
59060             var s = this.selections;
59061             s.each(function(r){
59062                 this.deselectRow(ds.indexOfId(r.id));
59063             }, this);
59064             s.clear();
59065         }else{
59066             this.selections.clear();
59067         }
59068         this.last = false;
59069     },
59070
59071
59072     /**
59073      * Selects all rows.
59074      */
59075     selectAll : function(){
59076         if(this.locked) {
59077             return;
59078         }
59079         this.selections.clear();
59080         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
59081             this.selectRow(i, true);
59082         }
59083     },
59084
59085     /**
59086      * Returns True if there is a selection.
59087      * @return {Boolean}
59088      */
59089     hasSelection : function(){
59090         return this.selections.length > 0;
59091     },
59092
59093     /**
59094      * Returns True if the specified row is selected.
59095      * @param {Number/Record} record The record or index of the record to check
59096      * @return {Boolean}
59097      */
59098     isSelected : function(index){
59099         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
59100         return (r && this.selections.key(r.id) ? true : false);
59101     },
59102
59103     /**
59104      * Returns True if the specified record id is selected.
59105      * @param {String} id The id of record to check
59106      * @return {Boolean}
59107      */
59108     isIdSelected : function(id){
59109         return (this.selections.key(id) ? true : false);
59110     },
59111
59112     // private
59113     handleMouseDown : function(e, t){
59114         var view = this.grid.getView(), rowIndex;
59115         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
59116             return;
59117         };
59118         if(e.shiftKey && this.last !== false){
59119             var last = this.last;
59120             this.selectRange(last, rowIndex, e.ctrlKey);
59121             this.last = last; // reset the last
59122             view.focusRow(rowIndex);
59123         }else{
59124             var isSelected = this.isSelected(rowIndex);
59125             if(e.button !== 0 && isSelected){
59126                 view.focusRow(rowIndex);
59127             }else if(e.ctrlKey && isSelected){
59128                 this.deselectRow(rowIndex);
59129             }else if(!isSelected){
59130                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
59131                 view.focusRow(rowIndex);
59132             }
59133         }
59134         this.fireEvent("afterselectionchange", this);
59135     },
59136     // private
59137     handleDragableRowClick :  function(grid, rowIndex, e) 
59138     {
59139         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
59140             this.selectRow(rowIndex, false);
59141             grid.view.focusRow(rowIndex);
59142              this.fireEvent("afterselectionchange", this);
59143         }
59144     },
59145     
59146     /**
59147      * Selects multiple rows.
59148      * @param {Array} rows Array of the indexes of the row to select
59149      * @param {Boolean} keepExisting (optional) True to keep existing selections
59150      */
59151     selectRows : function(rows, keepExisting){
59152         if(!keepExisting){
59153             this.clearSelections();
59154         }
59155         for(var i = 0, len = rows.length; i < len; i++){
59156             this.selectRow(rows[i], true);
59157         }
59158     },
59159
59160     /**
59161      * Selects a range of rows. All rows in between startRow and endRow are also selected.
59162      * @param {Number} startRow The index of the first row in the range
59163      * @param {Number} endRow The index of the last row in the range
59164      * @param {Boolean} keepExisting (optional) True to retain existing selections
59165      */
59166     selectRange : function(startRow, endRow, keepExisting){
59167         if(this.locked) {
59168             return;
59169         }
59170         if(!keepExisting){
59171             this.clearSelections();
59172         }
59173         if(startRow <= endRow){
59174             for(var i = startRow; i <= endRow; i++){
59175                 this.selectRow(i, true);
59176             }
59177         }else{
59178             for(var i = startRow; i >= endRow; i--){
59179                 this.selectRow(i, true);
59180             }
59181         }
59182     },
59183
59184     /**
59185      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
59186      * @param {Number} startRow The index of the first row in the range
59187      * @param {Number} endRow The index of the last row in the range
59188      */
59189     deselectRange : function(startRow, endRow, preventViewNotify){
59190         if(this.locked) {
59191             return;
59192         }
59193         for(var i = startRow; i <= endRow; i++){
59194             this.deselectRow(i, preventViewNotify);
59195         }
59196     },
59197
59198     /**
59199      * Selects a row.
59200      * @param {Number} row The index of the row to select
59201      * @param {Boolean} keepExisting (optional) True to keep existing selections
59202      */
59203     selectRow : function(index, keepExisting, preventViewNotify){
59204         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
59205             return;
59206         }
59207         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
59208             if(!keepExisting || this.singleSelect){
59209                 this.clearSelections();
59210             }
59211             var r = this.grid.dataSource.getAt(index);
59212             this.selections.add(r);
59213             this.last = this.lastActive = index;
59214             if(!preventViewNotify){
59215                 this.grid.getView().onRowSelect(index);
59216             }
59217             this.fireEvent("rowselect", this, index, r);
59218             this.fireEvent("selectionchange", this);
59219         }
59220     },
59221
59222     /**
59223      * Deselects a row.
59224      * @param {Number} row The index of the row to deselect
59225      */
59226     deselectRow : function(index, preventViewNotify){
59227         if(this.locked) {
59228             return;
59229         }
59230         if(this.last == index){
59231             this.last = false;
59232         }
59233         if(this.lastActive == index){
59234             this.lastActive = false;
59235         }
59236         var r = this.grid.dataSource.getAt(index);
59237         this.selections.remove(r);
59238         if(!preventViewNotify){
59239             this.grid.getView().onRowDeselect(index);
59240         }
59241         this.fireEvent("rowdeselect", this, index);
59242         this.fireEvent("selectionchange", this);
59243     },
59244
59245     // private
59246     restoreLast : function(){
59247         if(this._last){
59248             this.last = this._last;
59249         }
59250     },
59251
59252     // private
59253     acceptsNav : function(row, col, cm){
59254         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59255     },
59256
59257     // private
59258     onEditorKey : function(field, e){
59259         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
59260         if(k == e.TAB){
59261             e.stopEvent();
59262             ed.completeEdit();
59263             if(e.shiftKey){
59264                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59265             }else{
59266                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59267             }
59268         }else if(k == e.ENTER && !e.ctrlKey){
59269             e.stopEvent();
59270             ed.completeEdit();
59271             if(e.shiftKey){
59272                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
59273             }else{
59274                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
59275             }
59276         }else if(k == e.ESC){
59277             ed.cancelEdit();
59278         }
59279         if(newCell){
59280             g.startEditing(newCell[0], newCell[1]);
59281         }
59282     }
59283 });/*
59284  * Based on:
59285  * Ext JS Library 1.1.1
59286  * Copyright(c) 2006-2007, Ext JS, LLC.
59287  *
59288  * Originally Released Under LGPL - original licence link has changed is not relivant.
59289  *
59290  * Fork - LGPL
59291  * <script type="text/javascript">
59292  */
59293 /**
59294  * @class Roo.grid.CellSelectionModel
59295  * @extends Roo.grid.AbstractSelectionModel
59296  * This class provides the basic implementation for cell selection in a grid.
59297  * @constructor
59298  * @param {Object} config The object containing the configuration of this model.
59299  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
59300  */
59301 Roo.grid.CellSelectionModel = function(config){
59302     Roo.apply(this, config);
59303
59304     this.selection = null;
59305
59306     this.addEvents({
59307         /**
59308              * @event beforerowselect
59309              * Fires before a cell is selected.
59310              * @param {SelectionModel} this
59311              * @param {Number} rowIndex The selected row index
59312              * @param {Number} colIndex The selected cell index
59313              */
59314             "beforecellselect" : true,
59315         /**
59316              * @event cellselect
59317              * Fires when a cell is selected.
59318              * @param {SelectionModel} this
59319              * @param {Number} rowIndex The selected row index
59320              * @param {Number} colIndex The selected cell index
59321              */
59322             "cellselect" : true,
59323         /**
59324              * @event selectionchange
59325              * Fires when the active selection changes.
59326              * @param {SelectionModel} this
59327              * @param {Object} selection null for no selection or an object (o) with two properties
59328                 <ul>
59329                 <li>o.record: the record object for the row the selection is in</li>
59330                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
59331                 </ul>
59332              */
59333             "selectionchange" : true,
59334         /**
59335              * @event tabend
59336              * Fires when the tab (or enter) was pressed on the last editable cell
59337              * You can use this to trigger add new row.
59338              * @param {SelectionModel} this
59339              */
59340             "tabend" : true,
59341          /**
59342              * @event beforeeditnext
59343              * Fires before the next editable sell is made active
59344              * You can use this to skip to another cell or fire the tabend
59345              *    if you set cell to false
59346              * @param {Object} eventdata object : { cell : [ row, col ] } 
59347              */
59348             "beforeeditnext" : true
59349     });
59350     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
59351 };
59352
59353 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
59354     
59355     enter_is_tab: false,
59356
59357     /** @ignore */
59358     initEvents : function(){
59359         this.grid.on("mousedown", this.handleMouseDown, this);
59360         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
59361         var view = this.grid.view;
59362         view.on("refresh", this.onViewChange, this);
59363         view.on("rowupdated", this.onRowUpdated, this);
59364         view.on("beforerowremoved", this.clearSelections, this);
59365         view.on("beforerowsinserted", this.clearSelections, this);
59366         if(this.grid.isEditor){
59367             this.grid.on("beforeedit", this.beforeEdit,  this);
59368         }
59369     },
59370
59371         //private
59372     beforeEdit : function(e){
59373         this.select(e.row, e.column, false, true, e.record);
59374     },
59375
59376         //private
59377     onRowUpdated : function(v, index, r){
59378         if(this.selection && this.selection.record == r){
59379             v.onCellSelect(index, this.selection.cell[1]);
59380         }
59381     },
59382
59383         //private
59384     onViewChange : function(){
59385         this.clearSelections(true);
59386     },
59387
59388         /**
59389          * Returns the currently selected cell,.
59390          * @return {Array} The selected cell (row, column) or null if none selected.
59391          */
59392     getSelectedCell : function(){
59393         return this.selection ? this.selection.cell : null;
59394     },
59395
59396     /**
59397      * Clears all selections.
59398      * @param {Boolean} true to prevent the gridview from being notified about the change.
59399      */
59400     clearSelections : function(preventNotify){
59401         var s = this.selection;
59402         if(s){
59403             if(preventNotify !== true){
59404                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
59405             }
59406             this.selection = null;
59407             this.fireEvent("selectionchange", this, null);
59408         }
59409     },
59410
59411     /**
59412      * Returns true if there is a selection.
59413      * @return {Boolean}
59414      */
59415     hasSelection : function(){
59416         return this.selection ? true : false;
59417     },
59418
59419     /** @ignore */
59420     handleMouseDown : function(e, t){
59421         var v = this.grid.getView();
59422         if(this.isLocked()){
59423             return;
59424         };
59425         var row = v.findRowIndex(t);
59426         var cell = v.findCellIndex(t);
59427         if(row !== false && cell !== false){
59428             this.select(row, cell);
59429         }
59430     },
59431
59432     /**
59433      * Selects a cell.
59434      * @param {Number} rowIndex
59435      * @param {Number} collIndex
59436      */
59437     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
59438         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
59439             this.clearSelections();
59440             r = r || this.grid.dataSource.getAt(rowIndex);
59441             this.selection = {
59442                 record : r,
59443                 cell : [rowIndex, colIndex]
59444             };
59445             if(!preventViewNotify){
59446                 var v = this.grid.getView();
59447                 v.onCellSelect(rowIndex, colIndex);
59448                 if(preventFocus !== true){
59449                     v.focusCell(rowIndex, colIndex);
59450                 }
59451             }
59452             this.fireEvent("cellselect", this, rowIndex, colIndex);
59453             this.fireEvent("selectionchange", this, this.selection);
59454         }
59455     },
59456
59457         //private
59458     isSelectable : function(rowIndex, colIndex, cm){
59459         return !cm.isHidden(colIndex);
59460     },
59461
59462     /** @ignore */
59463     handleKeyDown : function(e){
59464         //Roo.log('Cell Sel Model handleKeyDown');
59465         if(!e.isNavKeyPress()){
59466             return;
59467         }
59468         var g = this.grid, s = this.selection;
59469         if(!s){
59470             e.stopEvent();
59471             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
59472             if(cell){
59473                 this.select(cell[0], cell[1]);
59474             }
59475             return;
59476         }
59477         var sm = this;
59478         var walk = function(row, col, step){
59479             return g.walkCells(row, col, step, sm.isSelectable,  sm);
59480         };
59481         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
59482         var newCell;
59483
59484       
59485
59486         switch(k){
59487             case e.TAB:
59488                 // handled by onEditorKey
59489                 if (g.isEditor && g.editing) {
59490                     return;
59491                 }
59492                 if(e.shiftKey) {
59493                     newCell = walk(r, c-1, -1);
59494                 } else {
59495                     newCell = walk(r, c+1, 1);
59496                 }
59497                 break;
59498             
59499             case e.DOWN:
59500                newCell = walk(r+1, c, 1);
59501                 break;
59502             
59503             case e.UP:
59504                 newCell = walk(r-1, c, -1);
59505                 break;
59506             
59507             case e.RIGHT:
59508                 newCell = walk(r, c+1, 1);
59509                 break;
59510             
59511             case e.LEFT:
59512                 newCell = walk(r, c-1, -1);
59513                 break;
59514             
59515             case e.ENTER:
59516                 
59517                 if(g.isEditor && !g.editing){
59518                    g.startEditing(r, c);
59519                    e.stopEvent();
59520                    return;
59521                 }
59522                 
59523                 
59524              break;
59525         };
59526         if(newCell){
59527             this.select(newCell[0], newCell[1]);
59528             e.stopEvent();
59529             
59530         }
59531     },
59532
59533     acceptsNav : function(row, col, cm){
59534         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59535     },
59536     /**
59537      * Selects a cell.
59538      * @param {Number} field (not used) - as it's normally used as a listener
59539      * @param {Number} e - event - fake it by using
59540      *
59541      * var e = Roo.EventObjectImpl.prototype;
59542      * e.keyCode = e.TAB
59543      *
59544      * 
59545      */
59546     onEditorKey : function(field, e){
59547         
59548         var k = e.getKey(),
59549             newCell,
59550             g = this.grid,
59551             ed = g.activeEditor,
59552             forward = false;
59553         ///Roo.log('onEditorKey' + k);
59554         
59555         
59556         if (this.enter_is_tab && k == e.ENTER) {
59557             k = e.TAB;
59558         }
59559         
59560         if(k == e.TAB){
59561             if(e.shiftKey){
59562                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59563             }else{
59564                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59565                 forward = true;
59566             }
59567             
59568             e.stopEvent();
59569             
59570         } else if(k == e.ENTER &&  !e.ctrlKey){
59571             ed.completeEdit();
59572             e.stopEvent();
59573             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59574         
59575                 } else if(k == e.ESC){
59576             ed.cancelEdit();
59577         }
59578                 
59579         if (newCell) {
59580             var ecall = { cell : newCell, forward : forward };
59581             this.fireEvent('beforeeditnext', ecall );
59582             newCell = ecall.cell;
59583                         forward = ecall.forward;
59584         }
59585                 
59586         if(newCell){
59587             //Roo.log('next cell after edit');
59588             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
59589         } else if (forward) {
59590             // tabbed past last
59591             this.fireEvent.defer(100, this, ['tabend',this]);
59592         }
59593     }
59594 });/*
59595  * Based on:
59596  * Ext JS Library 1.1.1
59597  * Copyright(c) 2006-2007, Ext JS, LLC.
59598  *
59599  * Originally Released Under LGPL - original licence link has changed is not relivant.
59600  *
59601  * Fork - LGPL
59602  * <script type="text/javascript">
59603  */
59604  
59605 /**
59606  * @class Roo.grid.EditorGrid
59607  * @extends Roo.grid.Grid
59608  * Class for creating and editable grid.
59609  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
59610  * The container MUST have some type of size defined for the grid to fill. The container will be 
59611  * automatically set to position relative if it isn't already.
59612  * @param {Object} dataSource The data model to bind to
59613  * @param {Object} colModel The column model with info about this grid's columns
59614  */
59615 Roo.grid.EditorGrid = function(container, config){
59616     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
59617     this.getGridEl().addClass("xedit-grid");
59618
59619     if(!this.selModel){
59620         this.selModel = new Roo.grid.CellSelectionModel();
59621     }
59622
59623     this.activeEditor = null;
59624
59625         this.addEvents({
59626             /**
59627              * @event beforeedit
59628              * Fires before cell editing is triggered. The edit event object has the following properties <br />
59629              * <ul style="padding:5px;padding-left:16px;">
59630              * <li>grid - This grid</li>
59631              * <li>record - The record being edited</li>
59632              * <li>field - The field name being edited</li>
59633              * <li>value - The value for the field being edited.</li>
59634              * <li>row - The grid row index</li>
59635              * <li>column - The grid column index</li>
59636              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59637              * </ul>
59638              * @param {Object} e An edit event (see above for description)
59639              */
59640             "beforeedit" : true,
59641             /**
59642              * @event afteredit
59643              * Fires after a cell is edited. <br />
59644              * <ul style="padding:5px;padding-left:16px;">
59645              * <li>grid - This grid</li>
59646              * <li>record - The record being edited</li>
59647              * <li>field - The field name being edited</li>
59648              * <li>value - The value being set</li>
59649              * <li>originalValue - The original value for the field, before the edit.</li>
59650              * <li>row - The grid row index</li>
59651              * <li>column - The grid column index</li>
59652              * </ul>
59653              * @param {Object} e An edit event (see above for description)
59654              */
59655             "afteredit" : true,
59656             /**
59657              * @event validateedit
59658              * Fires after a cell is edited, but before the value is set in the record. 
59659          * You can use this to modify the value being set in the field, Return false
59660              * to cancel the change. The edit event object has the following properties <br />
59661              * <ul style="padding:5px;padding-left:16px;">
59662          * <li>editor - This editor</li>
59663              * <li>grid - This grid</li>
59664              * <li>record - The record being edited</li>
59665              * <li>field - The field name being edited</li>
59666              * <li>value - The value being set</li>
59667              * <li>originalValue - The original value for the field, before the edit.</li>
59668              * <li>row - The grid row index</li>
59669              * <li>column - The grid column index</li>
59670              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59671              * </ul>
59672              * @param {Object} e An edit event (see above for description)
59673              */
59674             "validateedit" : true
59675         });
59676     this.on("bodyscroll", this.stopEditing,  this);
59677     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
59678 };
59679
59680 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
59681     /**
59682      * @cfg {Number} clicksToEdit
59683      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
59684      */
59685     clicksToEdit: 2,
59686
59687     // private
59688     isEditor : true,
59689     // private
59690     trackMouseOver: false, // causes very odd FF errors
59691
59692     onCellDblClick : function(g, row, col){
59693         this.startEditing(row, col);
59694     },
59695
59696     onEditComplete : function(ed, value, startValue){
59697         this.editing = false;
59698         this.activeEditor = null;
59699         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
59700         var r = ed.record;
59701         var field = this.colModel.getDataIndex(ed.col);
59702         var e = {
59703             grid: this,
59704             record: r,
59705             field: field,
59706             originalValue: startValue,
59707             value: value,
59708             row: ed.row,
59709             column: ed.col,
59710             cancel:false,
59711             editor: ed
59712         };
59713         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
59714         cell.show();
59715           
59716         if(String(value) !== String(startValue)){
59717             
59718             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
59719                 r.set(field, e.value);
59720                 // if we are dealing with a combo box..
59721                 // then we also set the 'name' colum to be the displayField
59722                 if (ed.field.displayField && ed.field.name) {
59723                     r.set(ed.field.name, ed.field.el.dom.value);
59724                 }
59725                 
59726                 delete e.cancel; //?? why!!!
59727                 this.fireEvent("afteredit", e);
59728             }
59729         } else {
59730             this.fireEvent("afteredit", e); // always fire it!
59731         }
59732         this.view.focusCell(ed.row, ed.col);
59733     },
59734
59735     /**
59736      * Starts editing the specified for the specified row/column
59737      * @param {Number} rowIndex
59738      * @param {Number} colIndex
59739      */
59740     startEditing : function(row, col){
59741         this.stopEditing();
59742         if(this.colModel.isCellEditable(col, row)){
59743             this.view.ensureVisible(row, col, true);
59744           
59745             var r = this.dataSource.getAt(row);
59746             var field = this.colModel.getDataIndex(col);
59747             var cell = Roo.get(this.view.getCell(row,col));
59748             var e = {
59749                 grid: this,
59750                 record: r,
59751                 field: field,
59752                 value: r.data[field],
59753                 row: row,
59754                 column: col,
59755                 cancel:false 
59756             };
59757             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
59758                 this.editing = true;
59759                 var ed = this.colModel.getCellEditor(col, row);
59760                 
59761                 if (!ed) {
59762                     return;
59763                 }
59764                 if(!ed.rendered){
59765                     ed.render(ed.parentEl || document.body);
59766                 }
59767                 ed.field.reset();
59768                
59769                 cell.hide();
59770                 
59771                 (function(){ // complex but required for focus issues in safari, ie and opera
59772                     ed.row = row;
59773                     ed.col = col;
59774                     ed.record = r;
59775                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
59776                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
59777                     this.activeEditor = ed;
59778                     var v = r.data[field];
59779                     ed.startEdit(this.view.getCell(row, col), v);
59780                     // combo's with 'displayField and name set
59781                     if (ed.field.displayField && ed.field.name) {
59782                         ed.field.el.dom.value = r.data[ed.field.name];
59783                     }
59784                     
59785                     
59786                 }).defer(50, this);
59787             }
59788         }
59789     },
59790         
59791     /**
59792      * Stops any active editing
59793      */
59794     stopEditing : function(){
59795         if(this.activeEditor){
59796             this.activeEditor.completeEdit();
59797         }
59798         this.activeEditor = null;
59799     },
59800         
59801          /**
59802      * Called to get grid's drag proxy text, by default returns this.ddText.
59803      * @return {String}
59804      */
59805     getDragDropText : function(){
59806         var count = this.selModel.getSelectedCell() ? 1 : 0;
59807         return String.format(this.ddText, count, count == 1 ? '' : 's');
59808     }
59809         
59810 });/*
59811  * Based on:
59812  * Ext JS Library 1.1.1
59813  * Copyright(c) 2006-2007, Ext JS, LLC.
59814  *
59815  * Originally Released Under LGPL - original licence link has changed is not relivant.
59816  *
59817  * Fork - LGPL
59818  * <script type="text/javascript">
59819  */
59820
59821 // private - not really -- you end up using it !
59822 // This is a support class used internally by the Grid components
59823
59824 /**
59825  * @class Roo.grid.GridEditor
59826  * @extends Roo.Editor
59827  * Class for creating and editable grid elements.
59828  * @param {Object} config any settings (must include field)
59829  */
59830 Roo.grid.GridEditor = function(field, config){
59831     if (!config && field.field) {
59832         config = field;
59833         field = Roo.factory(config.field, Roo.form);
59834     }
59835     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
59836     field.monitorTab = false;
59837 };
59838
59839 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
59840     
59841     /**
59842      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
59843      */
59844     
59845     alignment: "tl-tl",
59846     autoSize: "width",
59847     hideEl : false,
59848     cls: "x-small-editor x-grid-editor",
59849     shim:false,
59850     shadow:"frame"
59851 });/*
59852  * Based on:
59853  * Ext JS Library 1.1.1
59854  * Copyright(c) 2006-2007, Ext JS, LLC.
59855  *
59856  * Originally Released Under LGPL - original licence link has changed is not relivant.
59857  *
59858  * Fork - LGPL
59859  * <script type="text/javascript">
59860  */
59861   
59862
59863   
59864 Roo.grid.PropertyRecord = Roo.data.Record.create([
59865     {name:'name',type:'string'},  'value'
59866 ]);
59867
59868
59869 Roo.grid.PropertyStore = function(grid, source){
59870     this.grid = grid;
59871     this.store = new Roo.data.Store({
59872         recordType : Roo.grid.PropertyRecord
59873     });
59874     this.store.on('update', this.onUpdate,  this);
59875     if(source){
59876         this.setSource(source);
59877     }
59878     Roo.grid.PropertyStore.superclass.constructor.call(this);
59879 };
59880
59881
59882
59883 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
59884     setSource : function(o){
59885         this.source = o;
59886         this.store.removeAll();
59887         var data = [];
59888         for(var k in o){
59889             if(this.isEditableValue(o[k])){
59890                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
59891             }
59892         }
59893         this.store.loadRecords({records: data}, {}, true);
59894     },
59895
59896     onUpdate : function(ds, record, type){
59897         if(type == Roo.data.Record.EDIT){
59898             var v = record.data['value'];
59899             var oldValue = record.modified['value'];
59900             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
59901                 this.source[record.id] = v;
59902                 record.commit();
59903                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
59904             }else{
59905                 record.reject();
59906             }
59907         }
59908     },
59909
59910     getProperty : function(row){
59911        return this.store.getAt(row);
59912     },
59913
59914     isEditableValue: function(val){
59915         if(val && val instanceof Date){
59916             return true;
59917         }else if(typeof val == 'object' || typeof val == 'function'){
59918             return false;
59919         }
59920         return true;
59921     },
59922
59923     setValue : function(prop, value){
59924         this.source[prop] = value;
59925         this.store.getById(prop).set('value', value);
59926     },
59927
59928     getSource : function(){
59929         return this.source;
59930     }
59931 });
59932
59933 Roo.grid.PropertyColumnModel = function(grid, store){
59934     this.grid = grid;
59935     var g = Roo.grid;
59936     g.PropertyColumnModel.superclass.constructor.call(this, [
59937         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
59938         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
59939     ]);
59940     this.store = store;
59941     this.bselect = Roo.DomHelper.append(document.body, {
59942         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
59943             {tag: 'option', value: 'true', html: 'true'},
59944             {tag: 'option', value: 'false', html: 'false'}
59945         ]
59946     });
59947     Roo.id(this.bselect);
59948     var f = Roo.form;
59949     this.editors = {
59950         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
59951         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
59952         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
59953         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
59954         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
59955     };
59956     this.renderCellDelegate = this.renderCell.createDelegate(this);
59957     this.renderPropDelegate = this.renderProp.createDelegate(this);
59958 };
59959
59960 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
59961     
59962     
59963     nameText : 'Name',
59964     valueText : 'Value',
59965     
59966     dateFormat : 'm/j/Y',
59967     
59968     
59969     renderDate : function(dateVal){
59970         return dateVal.dateFormat(this.dateFormat);
59971     },
59972
59973     renderBool : function(bVal){
59974         return bVal ? 'true' : 'false';
59975     },
59976
59977     isCellEditable : function(colIndex, rowIndex){
59978         return colIndex == 1;
59979     },
59980
59981     getRenderer : function(col){
59982         return col == 1 ?
59983             this.renderCellDelegate : this.renderPropDelegate;
59984     },
59985
59986     renderProp : function(v){
59987         return this.getPropertyName(v);
59988     },
59989
59990     renderCell : function(val){
59991         var rv = val;
59992         if(val instanceof Date){
59993             rv = this.renderDate(val);
59994         }else if(typeof val == 'boolean'){
59995             rv = this.renderBool(val);
59996         }
59997         return Roo.util.Format.htmlEncode(rv);
59998     },
59999
60000     getPropertyName : function(name){
60001         var pn = this.grid.propertyNames;
60002         return pn && pn[name] ? pn[name] : name;
60003     },
60004
60005     getCellEditor : function(colIndex, rowIndex){
60006         var p = this.store.getProperty(rowIndex);
60007         var n = p.data['name'], val = p.data['value'];
60008         
60009         if(typeof(this.grid.customEditors[n]) == 'string'){
60010             return this.editors[this.grid.customEditors[n]];
60011         }
60012         if(typeof(this.grid.customEditors[n]) != 'undefined'){
60013             return this.grid.customEditors[n];
60014         }
60015         if(val instanceof Date){
60016             return this.editors['date'];
60017         }else if(typeof val == 'number'){
60018             return this.editors['number'];
60019         }else if(typeof val == 'boolean'){
60020             return this.editors['boolean'];
60021         }else{
60022             return this.editors['string'];
60023         }
60024     }
60025 });
60026
60027 /**
60028  * @class Roo.grid.PropertyGrid
60029  * @extends Roo.grid.EditorGrid
60030  * This class represents the  interface of a component based property grid control.
60031  * <br><br>Usage:<pre><code>
60032  var grid = new Roo.grid.PropertyGrid("my-container-id", {
60033       
60034  });
60035  // set any options
60036  grid.render();
60037  * </code></pre>
60038   
60039  * @constructor
60040  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
60041  * The container MUST have some type of size defined for the grid to fill. The container will be
60042  * automatically set to position relative if it isn't already.
60043  * @param {Object} config A config object that sets properties on this grid.
60044  */
60045 Roo.grid.PropertyGrid = function(container, config){
60046     config = config || {};
60047     var store = new Roo.grid.PropertyStore(this);
60048     this.store = store;
60049     var cm = new Roo.grid.PropertyColumnModel(this, store);
60050     store.store.sort('name', 'ASC');
60051     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
60052         ds: store.store,
60053         cm: cm,
60054         enableColLock:false,
60055         enableColumnMove:false,
60056         stripeRows:false,
60057         trackMouseOver: false,
60058         clicksToEdit:1
60059     }, config));
60060     this.getGridEl().addClass('x-props-grid');
60061     this.lastEditRow = null;
60062     this.on('columnresize', this.onColumnResize, this);
60063     this.addEvents({
60064          /**
60065              * @event beforepropertychange
60066              * Fires before a property changes (return false to stop?)
60067              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
60068              * @param {String} id Record Id
60069              * @param {String} newval New Value
60070          * @param {String} oldval Old Value
60071              */
60072         "beforepropertychange": true,
60073         /**
60074              * @event propertychange
60075              * Fires after a property changes
60076              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
60077              * @param {String} id Record Id
60078              * @param {String} newval New Value
60079          * @param {String} oldval Old Value
60080              */
60081         "propertychange": true
60082     });
60083     this.customEditors = this.customEditors || {};
60084 };
60085 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
60086     
60087      /**
60088      * @cfg {Object} customEditors map of colnames=> custom editors.
60089      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
60090      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
60091      * false disables editing of the field.
60092          */
60093     
60094       /**
60095      * @cfg {Object} propertyNames map of property Names to their displayed value
60096          */
60097     
60098     render : function(){
60099         Roo.grid.PropertyGrid.superclass.render.call(this);
60100         this.autoSize.defer(100, this);
60101     },
60102
60103     autoSize : function(){
60104         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
60105         if(this.view){
60106             this.view.fitColumns();
60107         }
60108     },
60109
60110     onColumnResize : function(){
60111         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
60112         this.autoSize();
60113     },
60114     /**
60115      * Sets the data for the Grid
60116      * accepts a Key => Value object of all the elements avaiable.
60117      * @param {Object} data  to appear in grid.
60118      */
60119     setSource : function(source){
60120         this.store.setSource(source);
60121         //this.autoSize();
60122     },
60123     /**
60124      * Gets all the data from the grid.
60125      * @return {Object} data  data stored in grid
60126      */
60127     getSource : function(){
60128         return this.store.getSource();
60129     }
60130 });/*
60131   
60132  * Licence LGPL
60133  
60134  */
60135  
60136 /**
60137  * @class Roo.grid.Calendar
60138  * @extends Roo.util.Grid
60139  * This class extends the Grid to provide a calendar widget
60140  * <br><br>Usage:<pre><code>
60141  var grid = new Roo.grid.Calendar("my-container-id", {
60142      ds: myDataStore,
60143      cm: myColModel,
60144      selModel: mySelectionModel,
60145      autoSizeColumns: true,
60146      monitorWindowResize: false,
60147      trackMouseOver: true
60148      eventstore : real data store..
60149  });
60150  // set any options
60151  grid.render();
60152   
60153   * @constructor
60154  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
60155  * The container MUST have some type of size defined for the grid to fill. The container will be
60156  * automatically set to position relative if it isn't already.
60157  * @param {Object} config A config object that sets properties on this grid.
60158  */
60159 Roo.grid.Calendar = function(container, config){
60160         // initialize the container
60161         this.container = Roo.get(container);
60162         this.container.update("");
60163         this.container.setStyle("overflow", "hidden");
60164     this.container.addClass('x-grid-container');
60165
60166     this.id = this.container.id;
60167
60168     Roo.apply(this, config);
60169     // check and correct shorthanded configs
60170     
60171     var rows = [];
60172     var d =1;
60173     for (var r = 0;r < 6;r++) {
60174         
60175         rows[r]=[];
60176         for (var c =0;c < 7;c++) {
60177             rows[r][c]= '';
60178         }
60179     }
60180     if (this.eventStore) {
60181         this.eventStore= Roo.factory(this.eventStore, Roo.data);
60182         this.eventStore.on('load',this.onLoad, this);
60183         this.eventStore.on('beforeload',this.clearEvents, this);
60184          
60185     }
60186     
60187     this.dataSource = new Roo.data.Store({
60188             proxy: new Roo.data.MemoryProxy(rows),
60189             reader: new Roo.data.ArrayReader({}, [
60190                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
60191     });
60192
60193     this.dataSource.load();
60194     this.ds = this.dataSource;
60195     this.ds.xmodule = this.xmodule || false;
60196     
60197     
60198     var cellRender = function(v,x,r)
60199     {
60200         return String.format(
60201             '<div class="fc-day  fc-widget-content"><div>' +
60202                 '<div class="fc-event-container"></div>' +
60203                 '<div class="fc-day-number">{0}</div>'+
60204                 
60205                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
60206             '</div></div>', v);
60207     
60208     }
60209     
60210     
60211     this.colModel = new Roo.grid.ColumnModel( [
60212         {
60213             xtype: 'ColumnModel',
60214             xns: Roo.grid,
60215             dataIndex : 'weekday0',
60216             header : 'Sunday',
60217             renderer : cellRender
60218         },
60219         {
60220             xtype: 'ColumnModel',
60221             xns: Roo.grid,
60222             dataIndex : 'weekday1',
60223             header : 'Monday',
60224             renderer : cellRender
60225         },
60226         {
60227             xtype: 'ColumnModel',
60228             xns: Roo.grid,
60229             dataIndex : 'weekday2',
60230             header : 'Tuesday',
60231             renderer : cellRender
60232         },
60233         {
60234             xtype: 'ColumnModel',
60235             xns: Roo.grid,
60236             dataIndex : 'weekday3',
60237             header : 'Wednesday',
60238             renderer : cellRender
60239         },
60240         {
60241             xtype: 'ColumnModel',
60242             xns: Roo.grid,
60243             dataIndex : 'weekday4',
60244             header : 'Thursday',
60245             renderer : cellRender
60246         },
60247         {
60248             xtype: 'ColumnModel',
60249             xns: Roo.grid,
60250             dataIndex : 'weekday5',
60251             header : 'Friday',
60252             renderer : cellRender
60253         },
60254         {
60255             xtype: 'ColumnModel',
60256             xns: Roo.grid,
60257             dataIndex : 'weekday6',
60258             header : 'Saturday',
60259             renderer : cellRender
60260         }
60261     ]);
60262     this.cm = this.colModel;
60263     this.cm.xmodule = this.xmodule || false;
60264  
60265         
60266           
60267     //this.selModel = new Roo.grid.CellSelectionModel();
60268     //this.sm = this.selModel;
60269     //this.selModel.init(this);
60270     
60271     
60272     if(this.width){
60273         this.container.setWidth(this.width);
60274     }
60275
60276     if(this.height){
60277         this.container.setHeight(this.height);
60278     }
60279     /** @private */
60280         this.addEvents({
60281         // raw events
60282         /**
60283          * @event click
60284          * The raw click event for the entire grid.
60285          * @param {Roo.EventObject} e
60286          */
60287         "click" : true,
60288         /**
60289          * @event dblclick
60290          * The raw dblclick event for the entire grid.
60291          * @param {Roo.EventObject} e
60292          */
60293         "dblclick" : true,
60294         /**
60295          * @event contextmenu
60296          * The raw contextmenu event for the entire grid.
60297          * @param {Roo.EventObject} e
60298          */
60299         "contextmenu" : true,
60300         /**
60301          * @event mousedown
60302          * The raw mousedown event for the entire grid.
60303          * @param {Roo.EventObject} e
60304          */
60305         "mousedown" : true,
60306         /**
60307          * @event mouseup
60308          * The raw mouseup event for the entire grid.
60309          * @param {Roo.EventObject} e
60310          */
60311         "mouseup" : true,
60312         /**
60313          * @event mouseover
60314          * The raw mouseover event for the entire grid.
60315          * @param {Roo.EventObject} e
60316          */
60317         "mouseover" : true,
60318         /**
60319          * @event mouseout
60320          * The raw mouseout event for the entire grid.
60321          * @param {Roo.EventObject} e
60322          */
60323         "mouseout" : true,
60324         /**
60325          * @event keypress
60326          * The raw keypress event for the entire grid.
60327          * @param {Roo.EventObject} e
60328          */
60329         "keypress" : true,
60330         /**
60331          * @event keydown
60332          * The raw keydown event for the entire grid.
60333          * @param {Roo.EventObject} e
60334          */
60335         "keydown" : true,
60336
60337         // custom events
60338
60339         /**
60340          * @event cellclick
60341          * Fires when a cell is clicked
60342          * @param {Grid} this
60343          * @param {Number} rowIndex
60344          * @param {Number} columnIndex
60345          * @param {Roo.EventObject} e
60346          */
60347         "cellclick" : true,
60348         /**
60349          * @event celldblclick
60350          * Fires when a cell is double clicked
60351          * @param {Grid} this
60352          * @param {Number} rowIndex
60353          * @param {Number} columnIndex
60354          * @param {Roo.EventObject} e
60355          */
60356         "celldblclick" : true,
60357         /**
60358          * @event rowclick
60359          * Fires when a row is clicked
60360          * @param {Grid} this
60361          * @param {Number} rowIndex
60362          * @param {Roo.EventObject} e
60363          */
60364         "rowclick" : true,
60365         /**
60366          * @event rowdblclick
60367          * Fires when a row is double clicked
60368          * @param {Grid} this
60369          * @param {Number} rowIndex
60370          * @param {Roo.EventObject} e
60371          */
60372         "rowdblclick" : true,
60373         /**
60374          * @event headerclick
60375          * Fires when a header is clicked
60376          * @param {Grid} this
60377          * @param {Number} columnIndex
60378          * @param {Roo.EventObject} e
60379          */
60380         "headerclick" : true,
60381         /**
60382          * @event headerdblclick
60383          * Fires when a header cell is double clicked
60384          * @param {Grid} this
60385          * @param {Number} columnIndex
60386          * @param {Roo.EventObject} e
60387          */
60388         "headerdblclick" : true,
60389         /**
60390          * @event rowcontextmenu
60391          * Fires when a row is right clicked
60392          * @param {Grid} this
60393          * @param {Number} rowIndex
60394          * @param {Roo.EventObject} e
60395          */
60396         "rowcontextmenu" : true,
60397         /**
60398          * @event cellcontextmenu
60399          * Fires when a cell is right clicked
60400          * @param {Grid} this
60401          * @param {Number} rowIndex
60402          * @param {Number} cellIndex
60403          * @param {Roo.EventObject} e
60404          */
60405          "cellcontextmenu" : true,
60406         /**
60407          * @event headercontextmenu
60408          * Fires when a header is right clicked
60409          * @param {Grid} this
60410          * @param {Number} columnIndex
60411          * @param {Roo.EventObject} e
60412          */
60413         "headercontextmenu" : true,
60414         /**
60415          * @event bodyscroll
60416          * Fires when the body element is scrolled
60417          * @param {Number} scrollLeft
60418          * @param {Number} scrollTop
60419          */
60420         "bodyscroll" : true,
60421         /**
60422          * @event columnresize
60423          * Fires when the user resizes a column
60424          * @param {Number} columnIndex
60425          * @param {Number} newSize
60426          */
60427         "columnresize" : true,
60428         /**
60429          * @event columnmove
60430          * Fires when the user moves a column
60431          * @param {Number} oldIndex
60432          * @param {Number} newIndex
60433          */
60434         "columnmove" : true,
60435         /**
60436          * @event startdrag
60437          * Fires when row(s) start being dragged
60438          * @param {Grid} this
60439          * @param {Roo.GridDD} dd The drag drop object
60440          * @param {event} e The raw browser event
60441          */
60442         "startdrag" : true,
60443         /**
60444          * @event enddrag
60445          * Fires when a drag operation is complete
60446          * @param {Grid} this
60447          * @param {Roo.GridDD} dd The drag drop object
60448          * @param {event} e The raw browser event
60449          */
60450         "enddrag" : true,
60451         /**
60452          * @event dragdrop
60453          * Fires when dragged row(s) are dropped on a valid DD target
60454          * @param {Grid} this
60455          * @param {Roo.GridDD} dd The drag drop object
60456          * @param {String} targetId The target drag drop object
60457          * @param {event} e The raw browser event
60458          */
60459         "dragdrop" : true,
60460         /**
60461          * @event dragover
60462          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
60463          * @param {Grid} this
60464          * @param {Roo.GridDD} dd The drag drop object
60465          * @param {String} targetId The target drag drop object
60466          * @param {event} e The raw browser event
60467          */
60468         "dragover" : true,
60469         /**
60470          * @event dragenter
60471          *  Fires when the dragged row(s) first cross another DD target while being dragged
60472          * @param {Grid} this
60473          * @param {Roo.GridDD} dd The drag drop object
60474          * @param {String} targetId The target drag drop object
60475          * @param {event} e The raw browser event
60476          */
60477         "dragenter" : true,
60478         /**
60479          * @event dragout
60480          * Fires when the dragged row(s) leave another DD target while being dragged
60481          * @param {Grid} this
60482          * @param {Roo.GridDD} dd The drag drop object
60483          * @param {String} targetId The target drag drop object
60484          * @param {event} e The raw browser event
60485          */
60486         "dragout" : true,
60487         /**
60488          * @event rowclass
60489          * Fires when a row is rendered, so you can change add a style to it.
60490          * @param {GridView} gridview   The grid view
60491          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
60492          */
60493         'rowclass' : true,
60494
60495         /**
60496          * @event render
60497          * Fires when the grid is rendered
60498          * @param {Grid} grid
60499          */
60500         'render' : true,
60501             /**
60502              * @event select
60503              * Fires when a date is selected
60504              * @param {DatePicker} this
60505              * @param {Date} date The selected date
60506              */
60507         'select': true,
60508         /**
60509              * @event monthchange
60510              * Fires when the displayed month changes 
60511              * @param {DatePicker} this
60512              * @param {Date} date The selected month
60513              */
60514         'monthchange': true,
60515         /**
60516              * @event evententer
60517              * Fires when mouse over an event
60518              * @param {Calendar} this
60519              * @param {event} Event
60520              */
60521         'evententer': true,
60522         /**
60523              * @event eventleave
60524              * Fires when the mouse leaves an
60525              * @param {Calendar} this
60526              * @param {event}
60527              */
60528         'eventleave': true,
60529         /**
60530              * @event eventclick
60531              * Fires when the mouse click an
60532              * @param {Calendar} this
60533              * @param {event}
60534              */
60535         'eventclick': true,
60536         /**
60537              * @event eventrender
60538              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
60539              * @param {Calendar} this
60540              * @param {data} data to be modified
60541              */
60542         'eventrender': true
60543         
60544     });
60545
60546     Roo.grid.Grid.superclass.constructor.call(this);
60547     this.on('render', function() {
60548         this.view.el.addClass('x-grid-cal'); 
60549         
60550         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
60551
60552     },this);
60553     
60554     if (!Roo.grid.Calendar.style) {
60555         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
60556             
60557             
60558             '.x-grid-cal .x-grid-col' :  {
60559                 height: 'auto !important',
60560                 'vertical-align': 'top'
60561             },
60562             '.x-grid-cal  .fc-event-hori' : {
60563                 height: '14px'
60564             }
60565              
60566             
60567         }, Roo.id());
60568     }
60569
60570     
60571     
60572 };
60573 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
60574     /**
60575      * @cfg {Store} eventStore The store that loads events.
60576      */
60577     eventStore : 25,
60578
60579      
60580     activeDate : false,
60581     startDay : 0,
60582     autoWidth : true,
60583     monitorWindowResize : false,
60584
60585     
60586     resizeColumns : function() {
60587         var col = (this.view.el.getWidth() / 7) - 3;
60588         // loop through cols, and setWidth
60589         for(var i =0 ; i < 7 ; i++){
60590             this.cm.setColumnWidth(i, col);
60591         }
60592     },
60593      setDate :function(date) {
60594         
60595         Roo.log('setDate?');
60596         
60597         this.resizeColumns();
60598         var vd = this.activeDate;
60599         this.activeDate = date;
60600 //        if(vd && this.el){
60601 //            var t = date.getTime();
60602 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
60603 //                Roo.log('using add remove');
60604 //                
60605 //                this.fireEvent('monthchange', this, date);
60606 //                
60607 //                this.cells.removeClass("fc-state-highlight");
60608 //                this.cells.each(function(c){
60609 //                   if(c.dateValue == t){
60610 //                       c.addClass("fc-state-highlight");
60611 //                       setTimeout(function(){
60612 //                            try{c.dom.firstChild.focus();}catch(e){}
60613 //                       }, 50);
60614 //                       return false;
60615 //                   }
60616 //                   return true;
60617 //                });
60618 //                return;
60619 //            }
60620 //        }
60621         
60622         var days = date.getDaysInMonth();
60623         
60624         var firstOfMonth = date.getFirstDateOfMonth();
60625         var startingPos = firstOfMonth.getDay()-this.startDay;
60626         
60627         if(startingPos < this.startDay){
60628             startingPos += 7;
60629         }
60630         
60631         var pm = date.add(Date.MONTH, -1);
60632         var prevStart = pm.getDaysInMonth()-startingPos;
60633 //        
60634         
60635         
60636         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60637         
60638         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
60639         //this.cells.addClassOnOver('fc-state-hover');
60640         
60641         var cells = this.cells.elements;
60642         var textEls = this.textNodes;
60643         
60644         //Roo.each(cells, function(cell){
60645         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
60646         //});
60647         
60648         days += startingPos;
60649
60650         // convert everything to numbers so it's fast
60651         var day = 86400000;
60652         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
60653         //Roo.log(d);
60654         //Roo.log(pm);
60655         //Roo.log(prevStart);
60656         
60657         var today = new Date().clearTime().getTime();
60658         var sel = date.clearTime().getTime();
60659         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
60660         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
60661         var ddMatch = this.disabledDatesRE;
60662         var ddText = this.disabledDatesText;
60663         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
60664         var ddaysText = this.disabledDaysText;
60665         var format = this.format;
60666         
60667         var setCellClass = function(cal, cell){
60668             
60669             //Roo.log('set Cell Class');
60670             cell.title = "";
60671             var t = d.getTime();
60672             
60673             //Roo.log(d);
60674             
60675             
60676             cell.dateValue = t;
60677             if(t == today){
60678                 cell.className += " fc-today";
60679                 cell.className += " fc-state-highlight";
60680                 cell.title = cal.todayText;
60681             }
60682             if(t == sel){
60683                 // disable highlight in other month..
60684                 cell.className += " fc-state-highlight";
60685                 
60686             }
60687             // disabling
60688             if(t < min) {
60689                 //cell.className = " fc-state-disabled";
60690                 cell.title = cal.minText;
60691                 return;
60692             }
60693             if(t > max) {
60694                 //cell.className = " fc-state-disabled";
60695                 cell.title = cal.maxText;
60696                 return;
60697             }
60698             if(ddays){
60699                 if(ddays.indexOf(d.getDay()) != -1){
60700                     // cell.title = ddaysText;
60701                    // cell.className = " fc-state-disabled";
60702                 }
60703             }
60704             if(ddMatch && format){
60705                 var fvalue = d.dateFormat(format);
60706                 if(ddMatch.test(fvalue)){
60707                     cell.title = ddText.replace("%0", fvalue);
60708                    cell.className = " fc-state-disabled";
60709                 }
60710             }
60711             
60712             if (!cell.initialClassName) {
60713                 cell.initialClassName = cell.dom.className;
60714             }
60715             
60716             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
60717         };
60718
60719         var i = 0;
60720         
60721         for(; i < startingPos; i++) {
60722             cells[i].dayName =  (++prevStart);
60723             Roo.log(textEls[i]);
60724             d.setDate(d.getDate()+1);
60725             
60726             //cells[i].className = "fc-past fc-other-month";
60727             setCellClass(this, cells[i]);
60728         }
60729         
60730         var intDay = 0;
60731         
60732         for(; i < days; i++){
60733             intDay = i - startingPos + 1;
60734             cells[i].dayName =  (intDay);
60735             d.setDate(d.getDate()+1);
60736             
60737             cells[i].className = ''; // "x-date-active";
60738             setCellClass(this, cells[i]);
60739         }
60740         var extraDays = 0;
60741         
60742         for(; i < 42; i++) {
60743             //textEls[i].innerHTML = (++extraDays);
60744             
60745             d.setDate(d.getDate()+1);
60746             cells[i].dayName = (++extraDays);
60747             cells[i].className = "fc-future fc-other-month";
60748             setCellClass(this, cells[i]);
60749         }
60750         
60751         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
60752         
60753         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
60754         
60755         // this will cause all the cells to mis
60756         var rows= [];
60757         var i =0;
60758         for (var r = 0;r < 6;r++) {
60759             for (var c =0;c < 7;c++) {
60760                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
60761             }    
60762         }
60763         
60764         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60765         for(i=0;i<cells.length;i++) {
60766             
60767             this.cells.elements[i].dayName = cells[i].dayName ;
60768             this.cells.elements[i].className = cells[i].className;
60769             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
60770             this.cells.elements[i].title = cells[i].title ;
60771             this.cells.elements[i].dateValue = cells[i].dateValue ;
60772         }
60773         
60774         
60775         
60776         
60777         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
60778         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
60779         
60780         ////if(totalRows != 6){
60781             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
60782            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
60783        // }
60784         
60785         this.fireEvent('monthchange', this, date);
60786         
60787         
60788     },
60789  /**
60790      * Returns the grid's SelectionModel.
60791      * @return {SelectionModel}
60792      */
60793     getSelectionModel : function(){
60794         if(!this.selModel){
60795             this.selModel = new Roo.grid.CellSelectionModel();
60796         }
60797         return this.selModel;
60798     },
60799
60800     load: function() {
60801         this.eventStore.load()
60802         
60803         
60804         
60805     },
60806     
60807     findCell : function(dt) {
60808         dt = dt.clearTime().getTime();
60809         var ret = false;
60810         this.cells.each(function(c){
60811             //Roo.log("check " +c.dateValue + '?=' + dt);
60812             if(c.dateValue == dt){
60813                 ret = c;
60814                 return false;
60815             }
60816             return true;
60817         });
60818         
60819         return ret;
60820     },
60821     
60822     findCells : function(rec) {
60823         var s = rec.data.start_dt.clone().clearTime().getTime();
60824        // Roo.log(s);
60825         var e= rec.data.end_dt.clone().clearTime().getTime();
60826        // Roo.log(e);
60827         var ret = [];
60828         this.cells.each(function(c){
60829              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
60830             
60831             if(c.dateValue > e){
60832                 return ;
60833             }
60834             if(c.dateValue < s){
60835                 return ;
60836             }
60837             ret.push(c);
60838         });
60839         
60840         return ret;    
60841     },
60842     
60843     findBestRow: function(cells)
60844     {
60845         var ret = 0;
60846         
60847         for (var i =0 ; i < cells.length;i++) {
60848             ret  = Math.max(cells[i].rows || 0,ret);
60849         }
60850         return ret;
60851         
60852     },
60853     
60854     
60855     addItem : function(rec)
60856     {
60857         // look for vertical location slot in
60858         var cells = this.findCells(rec);
60859         
60860         rec.row = this.findBestRow(cells);
60861         
60862         // work out the location.
60863         
60864         var crow = false;
60865         var rows = [];
60866         for(var i =0; i < cells.length; i++) {
60867             if (!crow) {
60868                 crow = {
60869                     start : cells[i],
60870                     end :  cells[i]
60871                 };
60872                 continue;
60873             }
60874             if (crow.start.getY() == cells[i].getY()) {
60875                 // on same row.
60876                 crow.end = cells[i];
60877                 continue;
60878             }
60879             // different row.
60880             rows.push(crow);
60881             crow = {
60882                 start: cells[i],
60883                 end : cells[i]
60884             };
60885             
60886         }
60887         
60888         rows.push(crow);
60889         rec.els = [];
60890         rec.rows = rows;
60891         rec.cells = cells;
60892         for (var i = 0; i < cells.length;i++) {
60893             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
60894             
60895         }
60896         
60897         
60898     },
60899     
60900     clearEvents: function() {
60901         
60902         if (!this.eventStore.getCount()) {
60903             return;
60904         }
60905         // reset number of rows in cells.
60906         Roo.each(this.cells.elements, function(c){
60907             c.rows = 0;
60908         });
60909         
60910         this.eventStore.each(function(e) {
60911             this.clearEvent(e);
60912         },this);
60913         
60914     },
60915     
60916     clearEvent : function(ev)
60917     {
60918         if (ev.els) {
60919             Roo.each(ev.els, function(el) {
60920                 el.un('mouseenter' ,this.onEventEnter, this);
60921                 el.un('mouseleave' ,this.onEventLeave, this);
60922                 el.remove();
60923             },this);
60924             ev.els = [];
60925         }
60926     },
60927     
60928     
60929     renderEvent : function(ev,ctr) {
60930         if (!ctr) {
60931              ctr = this.view.el.select('.fc-event-container',true).first();
60932         }
60933         
60934          
60935         this.clearEvent(ev);
60936             //code
60937        
60938         
60939         
60940         ev.els = [];
60941         var cells = ev.cells;
60942         var rows = ev.rows;
60943         this.fireEvent('eventrender', this, ev);
60944         
60945         for(var i =0; i < rows.length; i++) {
60946             
60947             cls = '';
60948             if (i == 0) {
60949                 cls += ' fc-event-start';
60950             }
60951             if ((i+1) == rows.length) {
60952                 cls += ' fc-event-end';
60953             }
60954             
60955             //Roo.log(ev.data);
60956             // how many rows should it span..
60957             var cg = this.eventTmpl.append(ctr,Roo.apply({
60958                 fccls : cls
60959                 
60960             }, ev.data) , true);
60961             
60962             
60963             cg.on('mouseenter' ,this.onEventEnter, this, ev);
60964             cg.on('mouseleave' ,this.onEventLeave, this, ev);
60965             cg.on('click', this.onEventClick, this, ev);
60966             
60967             ev.els.push(cg);
60968             
60969             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
60970             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
60971             //Roo.log(cg);
60972              
60973             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
60974             cg.setWidth(ebox.right - sbox.x -2);
60975         }
60976     },
60977     
60978     renderEvents: function()
60979     {   
60980         // first make sure there is enough space..
60981         
60982         if (!this.eventTmpl) {
60983             this.eventTmpl = new Roo.Template(
60984                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
60985                     '<div class="fc-event-inner">' +
60986                         '<span class="fc-event-time">{time}</span>' +
60987                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
60988                     '</div>' +
60989                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
60990                 '</div>'
60991             );
60992                 
60993         }
60994                
60995         
60996         
60997         this.cells.each(function(c) {
60998             //Roo.log(c.select('.fc-day-content div',true).first());
60999             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
61000         });
61001         
61002         var ctr = this.view.el.select('.fc-event-container',true).first();
61003         
61004         var cls;
61005         this.eventStore.each(function(ev){
61006             
61007             this.renderEvent(ev);
61008              
61009              
61010         }, this);
61011         this.view.layout();
61012         
61013     },
61014     
61015     onEventEnter: function (e, el,event,d) {
61016         this.fireEvent('evententer', this, el, event);
61017     },
61018     
61019     onEventLeave: function (e, el,event,d) {
61020         this.fireEvent('eventleave', this, el, event);
61021     },
61022     
61023     onEventClick: function (e, el,event,d) {
61024         this.fireEvent('eventclick', this, el, event);
61025     },
61026     
61027     onMonthChange: function () {
61028         this.store.load();
61029     },
61030     
61031     onLoad: function () {
61032         
61033         //Roo.log('calendar onload');
61034 //         
61035         if(this.eventStore.getCount() > 0){
61036             
61037            
61038             
61039             this.eventStore.each(function(d){
61040                 
61041                 
61042                 // FIXME..
61043                 var add =   d.data;
61044                 if (typeof(add.end_dt) == 'undefined')  {
61045                     Roo.log("Missing End time in calendar data: ");
61046                     Roo.log(d);
61047                     return;
61048                 }
61049                 if (typeof(add.start_dt) == 'undefined')  {
61050                     Roo.log("Missing Start time in calendar data: ");
61051                     Roo.log(d);
61052                     return;
61053                 }
61054                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
61055                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
61056                 add.id = add.id || d.id;
61057                 add.title = add.title || '??';
61058                 
61059                 this.addItem(d);
61060                 
61061              
61062             },this);
61063         }
61064         
61065         this.renderEvents();
61066     }
61067     
61068
61069 });
61070 /*
61071  grid : {
61072                 xtype: 'Grid',
61073                 xns: Roo.grid,
61074                 listeners : {
61075                     render : function ()
61076                     {
61077                         _this.grid = this;
61078                         
61079                         if (!this.view.el.hasClass('course-timesheet')) {
61080                             this.view.el.addClass('course-timesheet');
61081                         }
61082                         if (this.tsStyle) {
61083                             this.ds.load({});
61084                             return; 
61085                         }
61086                         Roo.log('width');
61087                         Roo.log(_this.grid.view.el.getWidth());
61088                         
61089                         
61090                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
61091                             '.course-timesheet .x-grid-row' : {
61092                                 height: '80px'
61093                             },
61094                             '.x-grid-row td' : {
61095                                 'vertical-align' : 0
61096                             },
61097                             '.course-edit-link' : {
61098                                 'color' : 'blue',
61099                                 'text-overflow' : 'ellipsis',
61100                                 'overflow' : 'hidden',
61101                                 'white-space' : 'nowrap',
61102                                 'cursor' : 'pointer'
61103                             },
61104                             '.sub-link' : {
61105                                 'color' : 'green'
61106                             },
61107                             '.de-act-sup-link' : {
61108                                 'color' : 'purple',
61109                                 'text-decoration' : 'line-through'
61110                             },
61111                             '.de-act-link' : {
61112                                 'color' : 'red',
61113                                 'text-decoration' : 'line-through'
61114                             },
61115                             '.course-timesheet .course-highlight' : {
61116                                 'border-top-style': 'dashed !important',
61117                                 'border-bottom-bottom': 'dashed !important'
61118                             },
61119                             '.course-timesheet .course-item' : {
61120                                 'font-family'   : 'tahoma, arial, helvetica',
61121                                 'font-size'     : '11px',
61122                                 'overflow'      : 'hidden',
61123                                 'padding-left'  : '10px',
61124                                 'padding-right' : '10px',
61125                                 'padding-top' : '10px' 
61126                             }
61127                             
61128                         }, Roo.id());
61129                                 this.ds.load({});
61130                     }
61131                 },
61132                 autoWidth : true,
61133                 monitorWindowResize : false,
61134                 cellrenderer : function(v,x,r)
61135                 {
61136                     return v;
61137                 },
61138                 sm : {
61139                     xtype: 'CellSelectionModel',
61140                     xns: Roo.grid
61141                 },
61142                 dataSource : {
61143                     xtype: 'Store',
61144                     xns: Roo.data,
61145                     listeners : {
61146                         beforeload : function (_self, options)
61147                         {
61148                             options.params = options.params || {};
61149                             options.params._month = _this.monthField.getValue();
61150                             options.params.limit = 9999;
61151                             options.params['sort'] = 'when_dt';    
61152                             options.params['dir'] = 'ASC';    
61153                             this.proxy.loadResponse = this.loadResponse;
61154                             Roo.log("load?");
61155                             //this.addColumns();
61156                         },
61157                         load : function (_self, records, options)
61158                         {
61159                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
61160                                 // if you click on the translation.. you can edit it...
61161                                 var el = Roo.get(this);
61162                                 var id = el.dom.getAttribute('data-id');
61163                                 var d = el.dom.getAttribute('data-date');
61164                                 var t = el.dom.getAttribute('data-time');
61165                                 //var id = this.child('span').dom.textContent;
61166                                 
61167                                 //Roo.log(this);
61168                                 Pman.Dialog.CourseCalendar.show({
61169                                     id : id,
61170                                     when_d : d,
61171                                     when_t : t,
61172                                     productitem_active : id ? 1 : 0
61173                                 }, function() {
61174                                     _this.grid.ds.load({});
61175                                 });
61176                            
61177                            });
61178                            
61179                            _this.panel.fireEvent('resize', [ '', '' ]);
61180                         }
61181                     },
61182                     loadResponse : function(o, success, response){
61183                             // this is overridden on before load..
61184                             
61185                             Roo.log("our code?");       
61186                             //Roo.log(success);
61187                             //Roo.log(response)
61188                             delete this.activeRequest;
61189                             if(!success){
61190                                 this.fireEvent("loadexception", this, o, response);
61191                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61192                                 return;
61193                             }
61194                             var result;
61195                             try {
61196                                 result = o.reader.read(response);
61197                             }catch(e){
61198                                 Roo.log("load exception?");
61199                                 this.fireEvent("loadexception", this, o, response, e);
61200                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61201                                 return;
61202                             }
61203                             Roo.log("ready...");        
61204                             // loop through result.records;
61205                             // and set this.tdate[date] = [] << array of records..
61206                             _this.tdata  = {};
61207                             Roo.each(result.records, function(r){
61208                                 //Roo.log(r.data);
61209                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
61210                                     _this.tdata[r.data.when_dt.format('j')] = [];
61211                                 }
61212                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
61213                             });
61214                             
61215                             //Roo.log(_this.tdata);
61216                             
61217                             result.records = [];
61218                             result.totalRecords = 6;
61219                     
61220                             // let's generate some duumy records for the rows.
61221                             //var st = _this.dateField.getValue();
61222                             
61223                             // work out monday..
61224                             //st = st.add(Date.DAY, -1 * st.format('w'));
61225                             
61226                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61227                             
61228                             var firstOfMonth = date.getFirstDayOfMonth();
61229                             var days = date.getDaysInMonth();
61230                             var d = 1;
61231                             var firstAdded = false;
61232                             for (var i = 0; i < result.totalRecords ; i++) {
61233                                 //var d= st.add(Date.DAY, i);
61234                                 var row = {};
61235                                 var added = 0;
61236                                 for(var w = 0 ; w < 7 ; w++){
61237                                     if(!firstAdded && firstOfMonth != w){
61238                                         continue;
61239                                     }
61240                                     if(d > days){
61241                                         continue;
61242                                     }
61243                                     firstAdded = true;
61244                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
61245                                     row['weekday'+w] = String.format(
61246                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
61247                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
61248                                                     d,
61249                                                     date.format('Y-m-')+dd
61250                                                 );
61251                                     added++;
61252                                     if(typeof(_this.tdata[d]) != 'undefined'){
61253                                         Roo.each(_this.tdata[d], function(r){
61254                                             var is_sub = '';
61255                                             var deactive = '';
61256                                             var id = r.id;
61257                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
61258                                             if(r.parent_id*1>0){
61259                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
61260                                                 id = r.parent_id;
61261                                             }
61262                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
61263                                                 deactive = 'de-act-link';
61264                                             }
61265                                             
61266                                             row['weekday'+w] += String.format(
61267                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
61268                                                     id, //0
61269                                                     r.product_id_name, //1
61270                                                     r.when_dt.format('h:ia'), //2
61271                                                     is_sub, //3
61272                                                     deactive, //4
61273                                                     desc // 5
61274                                             );
61275                                         });
61276                                     }
61277                                     d++;
61278                                 }
61279                                 
61280                                 // only do this if something added..
61281                                 if(added > 0){ 
61282                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
61283                                 }
61284                                 
61285                                 
61286                                 // push it twice. (second one with an hour..
61287                                 
61288                             }
61289                             //Roo.log(result);
61290                             this.fireEvent("load", this, o, o.request.arg);
61291                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
61292                         },
61293                     sortInfo : {field: 'when_dt', direction : 'ASC' },
61294                     proxy : {
61295                         xtype: 'HttpProxy',
61296                         xns: Roo.data,
61297                         method : 'GET',
61298                         url : baseURL + '/Roo/Shop_course.php'
61299                     },
61300                     reader : {
61301                         xtype: 'JsonReader',
61302                         xns: Roo.data,
61303                         id : 'id',
61304                         fields : [
61305                             {
61306                                 'name': 'id',
61307                                 'type': 'int'
61308                             },
61309                             {
61310                                 'name': 'when_dt',
61311                                 'type': 'string'
61312                             },
61313                             {
61314                                 'name': 'end_dt',
61315                                 'type': 'string'
61316                             },
61317                             {
61318                                 'name': 'parent_id',
61319                                 'type': 'int'
61320                             },
61321                             {
61322                                 'name': 'product_id',
61323                                 'type': 'int'
61324                             },
61325                             {
61326                                 'name': 'productitem_id',
61327                                 'type': 'int'
61328                             },
61329                             {
61330                                 'name': 'guid',
61331                                 'type': 'int'
61332                             }
61333                         ]
61334                     }
61335                 },
61336                 toolbar : {
61337                     xtype: 'Toolbar',
61338                     xns: Roo,
61339                     items : [
61340                         {
61341                             xtype: 'Button',
61342                             xns: Roo.Toolbar,
61343                             listeners : {
61344                                 click : function (_self, e)
61345                                 {
61346                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61347                                     sd.setMonth(sd.getMonth()-1);
61348                                     _this.monthField.setValue(sd.format('Y-m-d'));
61349                                     _this.grid.ds.load({});
61350                                 }
61351                             },
61352                             text : "Back"
61353                         },
61354                         {
61355                             xtype: 'Separator',
61356                             xns: Roo.Toolbar
61357                         },
61358                         {
61359                             xtype: 'MonthField',
61360                             xns: Roo.form,
61361                             listeners : {
61362                                 render : function (_self)
61363                                 {
61364                                     _this.monthField = _self;
61365                                    // _this.monthField.set  today
61366                                 },
61367                                 select : function (combo, date)
61368                                 {
61369                                     _this.grid.ds.load({});
61370                                 }
61371                             },
61372                             value : (function() { return new Date(); })()
61373                         },
61374                         {
61375                             xtype: 'Separator',
61376                             xns: Roo.Toolbar
61377                         },
61378                         {
61379                             xtype: 'TextItem',
61380                             xns: Roo.Toolbar,
61381                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
61382                         },
61383                         {
61384                             xtype: 'Fill',
61385                             xns: Roo.Toolbar
61386                         },
61387                         {
61388                             xtype: 'Button',
61389                             xns: Roo.Toolbar,
61390                             listeners : {
61391                                 click : function (_self, e)
61392                                 {
61393                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61394                                     sd.setMonth(sd.getMonth()+1);
61395                                     _this.monthField.setValue(sd.format('Y-m-d'));
61396                                     _this.grid.ds.load({});
61397                                 }
61398                             },
61399                             text : "Next"
61400                         }
61401                     ]
61402                 },
61403                  
61404             }
61405         };
61406         
61407         *//*
61408  * Based on:
61409  * Ext JS Library 1.1.1
61410  * Copyright(c) 2006-2007, Ext JS, LLC.
61411  *
61412  * Originally Released Under LGPL - original licence link has changed is not relivant.
61413  *
61414  * Fork - LGPL
61415  * <script type="text/javascript">
61416  */
61417  
61418 /**
61419  * @class Roo.LoadMask
61420  * A simple utility class for generically masking elements while loading data.  If the element being masked has
61421  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
61422  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
61423  * element's UpdateManager load indicator and will be destroyed after the initial load.
61424  * @constructor
61425  * Create a new LoadMask
61426  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
61427  * @param {Object} config The config object
61428  */
61429 Roo.LoadMask = function(el, config){
61430     this.el = Roo.get(el);
61431     Roo.apply(this, config);
61432     if(this.store){
61433         this.store.on('beforeload', this.onBeforeLoad, this);
61434         this.store.on('load', this.onLoad, this);
61435         this.store.on('loadexception', this.onLoadException, this);
61436         this.removeMask = false;
61437     }else{
61438         var um = this.el.getUpdateManager();
61439         um.showLoadIndicator = false; // disable the default indicator
61440         um.on('beforeupdate', this.onBeforeLoad, this);
61441         um.on('update', this.onLoad, this);
61442         um.on('failure', this.onLoad, this);
61443         this.removeMask = true;
61444     }
61445 };
61446
61447 Roo.LoadMask.prototype = {
61448     /**
61449      * @cfg {Boolean} removeMask
61450      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
61451      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
61452      */
61453     /**
61454      * @cfg {String} msg
61455      * The text to display in a centered loading message box (defaults to 'Loading...')
61456      */
61457     msg : 'Loading...',
61458     /**
61459      * @cfg {String} msgCls
61460      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
61461      */
61462     msgCls : 'x-mask-loading',
61463
61464     /**
61465      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
61466      * @type Boolean
61467      */
61468     disabled: false,
61469
61470     /**
61471      * Disables the mask to prevent it from being displayed
61472      */
61473     disable : function(){
61474        this.disabled = true;
61475     },
61476
61477     /**
61478      * Enables the mask so that it can be displayed
61479      */
61480     enable : function(){
61481         this.disabled = false;
61482     },
61483     
61484     onLoadException : function()
61485     {
61486         Roo.log(arguments);
61487         
61488         if (typeof(arguments[3]) != 'undefined') {
61489             Roo.MessageBox.alert("Error loading",arguments[3]);
61490         } 
61491         /*
61492         try {
61493             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
61494                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
61495             }   
61496         } catch(e) {
61497             
61498         }
61499         */
61500     
61501         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61502     },
61503     // private
61504     onLoad : function()
61505     {
61506         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61507     },
61508
61509     // private
61510     onBeforeLoad : function(){
61511         if(!this.disabled){
61512             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
61513         }
61514     },
61515
61516     // private
61517     destroy : function(){
61518         if(this.store){
61519             this.store.un('beforeload', this.onBeforeLoad, this);
61520             this.store.un('load', this.onLoad, this);
61521             this.store.un('loadexception', this.onLoadException, this);
61522         }else{
61523             var um = this.el.getUpdateManager();
61524             um.un('beforeupdate', this.onBeforeLoad, this);
61525             um.un('update', this.onLoad, this);
61526             um.un('failure', this.onLoad, this);
61527         }
61528     }
61529 };/*
61530  * Based on:
61531  * Ext JS Library 1.1.1
61532  * Copyright(c) 2006-2007, Ext JS, LLC.
61533  *
61534  * Originally Released Under LGPL - original licence link has changed is not relivant.
61535  *
61536  * Fork - LGPL
61537  * <script type="text/javascript">
61538  */
61539
61540
61541 /**
61542  * @class Roo.XTemplate
61543  * @extends Roo.Template
61544  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
61545 <pre><code>
61546 var t = new Roo.XTemplate(
61547         '&lt;select name="{name}"&gt;',
61548                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
61549         '&lt;/select&gt;'
61550 );
61551  
61552 // then append, applying the master template values
61553  </code></pre>
61554  *
61555  * Supported features:
61556  *
61557  *  Tags:
61558
61559 <pre><code>
61560       {a_variable} - output encoded.
61561       {a_variable.format:("Y-m-d")} - call a method on the variable
61562       {a_variable:raw} - unencoded output
61563       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
61564       {a_variable:this.method_on_template(...)} - call a method on the template object.
61565  
61566 </code></pre>
61567  *  The tpl tag:
61568 <pre><code>
61569         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
61570         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
61571         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
61572         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
61573   
61574         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
61575         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
61576 </code></pre>
61577  *      
61578  */
61579 Roo.XTemplate = function()
61580 {
61581     Roo.XTemplate.superclass.constructor.apply(this, arguments);
61582     if (this.html) {
61583         this.compile();
61584     }
61585 };
61586
61587
61588 Roo.extend(Roo.XTemplate, Roo.Template, {
61589
61590     /**
61591      * The various sub templates
61592      */
61593     tpls : false,
61594     /**
61595      *
61596      * basic tag replacing syntax
61597      * WORD:WORD()
61598      *
61599      * // you can fake an object call by doing this
61600      *  x.t:(test,tesT) 
61601      * 
61602      */
61603     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
61604
61605     /**
61606      * compile the template
61607      *
61608      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
61609      *
61610      */
61611     compile: function()
61612     {
61613         var s = this.html;
61614      
61615         s = ['<tpl>', s, '</tpl>'].join('');
61616     
61617         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
61618             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
61619             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
61620             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
61621             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
61622             m,
61623             id     = 0,
61624             tpls   = [];
61625     
61626         while(true == !!(m = s.match(re))){
61627             var forMatch   = m[0].match(nameRe),
61628                 ifMatch   = m[0].match(ifRe),
61629                 execMatch   = m[0].match(execRe),
61630                 namedMatch   = m[0].match(namedRe),
61631                 
61632                 exp  = null, 
61633                 fn   = null,
61634                 exec = null,
61635                 name = forMatch && forMatch[1] ? forMatch[1] : '';
61636                 
61637             if (ifMatch) {
61638                 // if - puts fn into test..
61639                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
61640                 if(exp){
61641                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
61642                 }
61643             }
61644             
61645             if (execMatch) {
61646                 // exec - calls a function... returns empty if true is  returned.
61647                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
61648                 if(exp){
61649                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
61650                 }
61651             }
61652             
61653             
61654             if (name) {
61655                 // for = 
61656                 switch(name){
61657                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
61658                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
61659                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
61660                 }
61661             }
61662             var uid = namedMatch ? namedMatch[1] : id;
61663             
61664             
61665             tpls.push({
61666                 id:     namedMatch ? namedMatch[1] : id,
61667                 target: name,
61668                 exec:   exec,
61669                 test:   fn,
61670                 body:   m[1] || ''
61671             });
61672             if (namedMatch) {
61673                 s = s.replace(m[0], '');
61674             } else { 
61675                 s = s.replace(m[0], '{xtpl'+ id + '}');
61676             }
61677             ++id;
61678         }
61679         this.tpls = [];
61680         for(var i = tpls.length-1; i >= 0; --i){
61681             this.compileTpl(tpls[i]);
61682             this.tpls[tpls[i].id] = tpls[i];
61683         }
61684         this.master = tpls[tpls.length-1];
61685         return this;
61686     },
61687     /**
61688      * same as applyTemplate, except it's done to one of the subTemplates
61689      * when using named templates, you can do:
61690      *
61691      * var str = pl.applySubTemplate('your-name', values);
61692      *
61693      * 
61694      * @param {Number} id of the template
61695      * @param {Object} values to apply to template
61696      * @param {Object} parent (normaly the instance of this object)
61697      */
61698     applySubTemplate : function(id, values, parent)
61699     {
61700         
61701         
61702         var t = this.tpls[id];
61703         
61704         
61705         try { 
61706             if(t.test && !t.test.call(this, values, parent)){
61707                 return '';
61708             }
61709         } catch(e) {
61710             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
61711             Roo.log(e.toString());
61712             Roo.log(t.test);
61713             return ''
61714         }
61715         try { 
61716             
61717             if(t.exec && t.exec.call(this, values, parent)){
61718                 return '';
61719             }
61720         } catch(e) {
61721             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
61722             Roo.log(e.toString());
61723             Roo.log(t.exec);
61724             return ''
61725         }
61726         try {
61727             var vs = t.target ? t.target.call(this, values, parent) : values;
61728             parent = t.target ? values : parent;
61729             if(t.target && vs instanceof Array){
61730                 var buf = [];
61731                 for(var i = 0, len = vs.length; i < len; i++){
61732                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
61733                 }
61734                 return buf.join('');
61735             }
61736             return t.compiled.call(this, vs, parent);
61737         } catch (e) {
61738             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
61739             Roo.log(e.toString());
61740             Roo.log(t.compiled);
61741             return '';
61742         }
61743     },
61744
61745     compileTpl : function(tpl)
61746     {
61747         var fm = Roo.util.Format;
61748         var useF = this.disableFormats !== true;
61749         var sep = Roo.isGecko ? "+" : ",";
61750         var undef = function(str) {
61751             Roo.log("Property not found :"  + str);
61752             return '';
61753         };
61754         
61755         var fn = function(m, name, format, args)
61756         {
61757             //Roo.log(arguments);
61758             args = args ? args.replace(/\\'/g,"'") : args;
61759             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
61760             if (typeof(format) == 'undefined') {
61761                 format= 'htmlEncode';
61762             }
61763             if (format == 'raw' ) {
61764                 format = false;
61765             }
61766             
61767             if(name.substr(0, 4) == 'xtpl'){
61768                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
61769             }
61770             
61771             // build an array of options to determine if value is undefined..
61772             
61773             // basically get 'xxxx.yyyy' then do
61774             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
61775             //    (function () { Roo.log("Property not found"); return ''; })() :
61776             //    ......
61777             
61778             var udef_ar = [];
61779             var lookfor = '';
61780             Roo.each(name.split('.'), function(st) {
61781                 lookfor += (lookfor.length ? '.': '') + st;
61782                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
61783             });
61784             
61785             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
61786             
61787             
61788             if(format && useF){
61789                 
61790                 args = args ? ',' + args : "";
61791                  
61792                 if(format.substr(0, 5) != "this."){
61793                     format = "fm." + format + '(';
61794                 }else{
61795                     format = 'this.call("'+ format.substr(5) + '", ';
61796                     args = ", values";
61797                 }
61798                 
61799                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
61800             }
61801              
61802             if (args.length) {
61803                 // called with xxyx.yuu:(test,test)
61804                 // change to ()
61805                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
61806             }
61807             // raw.. - :raw modifier..
61808             return "'"+ sep + udef_st  + name + ")"+sep+"'";
61809             
61810         };
61811         var body;
61812         // branched to use + in gecko and [].join() in others
61813         if(Roo.isGecko){
61814             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
61815                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
61816                     "';};};";
61817         }else{
61818             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
61819             body.push(tpl.body.replace(/(\r\n|\n)/g,
61820                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
61821             body.push("'].join('');};};");
61822             body = body.join('');
61823         }
61824         
61825         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
61826        
61827         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
61828         eval(body);
61829         
61830         return this;
61831     },
61832
61833     applyTemplate : function(values){
61834         return this.master.compiled.call(this, values, {});
61835         //var s = this.subs;
61836     },
61837
61838     apply : function(){
61839         return this.applyTemplate.apply(this, arguments);
61840     }
61841
61842  });
61843
61844 Roo.XTemplate.from = function(el){
61845     el = Roo.getDom(el);
61846     return new Roo.XTemplate(el.value || el.innerHTML);
61847 };