roojs-all.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         }
672         
673     });
674
675
676 })();
677
678 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
679                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
680                 "Roo.app", "Roo.ux",
681                 "Roo.bootstrap",
682                 "Roo.bootstrap.dash");
683 /*
684  * Based on:
685  * Ext JS Library 1.1.1
686  * Copyright(c) 2006-2007, Ext JS, LLC.
687  *
688  * Originally Released Under LGPL - original licence link has changed is not relivant.
689  *
690  * Fork - LGPL
691  * <script type="text/javascript">
692  */
693
694 (function() {    
695     // wrappedn so fnCleanup is not in global scope...
696     if(Roo.isIE) {
697         function fnCleanUp() {
698             var p = Function.prototype;
699             delete p.createSequence;
700             delete p.defer;
701             delete p.createDelegate;
702             delete p.createCallback;
703             delete p.createInterceptor;
704
705             window.detachEvent("onunload", fnCleanUp);
706         }
707         window.attachEvent("onunload", fnCleanUp);
708     }
709 })();
710
711
712 /**
713  * @class Function
714  * These functions are available on every Function object (any JavaScript function).
715  */
716 Roo.apply(Function.prototype, {
717      /**
718      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
719      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
720      * Will create a function that is bound to those 2 args.
721      * @return {Function} The new function
722     */
723     createCallback : function(/*args...*/){
724         // make args available, in function below
725         var args = arguments;
726         var method = this;
727         return function() {
728             return method.apply(window, args);
729         };
730     },
731
732     /**
733      * Creates a delegate (callback) that sets the scope to obj.
734      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
735      * Will create a function that is automatically scoped to this.
736      * @param {Object} obj (optional) The object for which the scope is set
737      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
738      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
739      *                                             if a number the args are inserted at the specified position
740      * @return {Function} The new function
741      */
742     createDelegate : function(obj, args, appendArgs){
743         var method = this;
744         return function() {
745             var callArgs = args || arguments;
746             if(appendArgs === true){
747                 callArgs = Array.prototype.slice.call(arguments, 0);
748                 callArgs = callArgs.concat(args);
749             }else if(typeof appendArgs == "number"){
750                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
751                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
752                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
753             }
754             return method.apply(obj || window, callArgs);
755         };
756     },
757
758     /**
759      * Calls this function after the number of millseconds specified.
760      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
761      * @param {Object} obj (optional) The object for which the scope is set
762      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
763      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
764      *                                             if a number the args are inserted at the specified position
765      * @return {Number} The timeout id that can be used with clearTimeout
766      */
767     defer : function(millis, obj, args, appendArgs){
768         var fn = this.createDelegate(obj, args, appendArgs);
769         if(millis){
770             return setTimeout(fn, millis);
771         }
772         fn();
773         return 0;
774     },
775     /**
776      * Create a combined function call sequence of the original function + the passed function.
777      * The resulting function returns the results of the original function.
778      * The passed fcn is called with the parameters of the original function
779      * @param {Function} fcn The function to sequence
780      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
781      * @return {Function} The new function
782      */
783     createSequence : function(fcn, scope){
784         if(typeof fcn != "function"){
785             return this;
786         }
787         var method = this;
788         return function() {
789             var retval = method.apply(this || window, arguments);
790             fcn.apply(scope || this || window, arguments);
791             return retval;
792         };
793     },
794
795     /**
796      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
797      * The resulting function returns the results of the original function.
798      * The passed fcn is called with the parameters of the original function.
799      * @addon
800      * @param {Function} fcn The function to call before the original
801      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
802      * @return {Function} The new function
803      */
804     createInterceptor : function(fcn, scope){
805         if(typeof fcn != "function"){
806             return this;
807         }
808         var method = this;
809         return function() {
810             fcn.target = this;
811             fcn.method = method;
812             if(fcn.apply(scope || this || window, arguments) === false){
813                 return;
814             }
815             return method.apply(this || window, arguments);
816         };
817     }
818 });
819 /*
820  * Based on:
821  * Ext JS Library 1.1.1
822  * Copyright(c) 2006-2007, Ext JS, LLC.
823  *
824  * Originally Released Under LGPL - original licence link has changed is not relivant.
825  *
826  * Fork - LGPL
827  * <script type="text/javascript">
828  */
829
830 Roo.applyIf(String, {
831     
832     /** @scope String */
833     
834     /**
835      * Escapes the passed string for ' and \
836      * @param {String} string The string to escape
837      * @return {String} The escaped string
838      * @static
839      */
840     escape : function(string) {
841         return string.replace(/('|\\)/g, "\\$1");
842     },
843
844     /**
845      * Pads the left side of a string with a specified character.  This is especially useful
846      * for normalizing number and date strings.  Example usage:
847      * <pre><code>
848 var s = String.leftPad('123', 5, '0');
849 // s now contains the string: '00123'
850 </code></pre>
851      * @param {String} string The original string
852      * @param {Number} size The total length of the output string
853      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
854      * @return {String} The padded string
855      * @static
856      */
857     leftPad : function (val, size, ch) {
858         var result = new String(val);
859         if(ch === null || ch === undefined || ch === '') {
860             ch = " ";
861         }
862         while (result.length < size) {
863             result = ch + result;
864         }
865         return result;
866     },
867
868     /**
869      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
870      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
871      * <pre><code>
872 var cls = 'my-class', text = 'Some text';
873 var s = String.format('<div class="{0}">{1}</div>', cls, text);
874 // s now contains the string: '<div class="my-class">Some text</div>'
875 </code></pre>
876      * @param {String} string The tokenized string to be formatted
877      * @param {String} value1 The value to replace token {0}
878      * @param {String} value2 Etc...
879      * @return {String} The formatted string
880      * @static
881      */
882     format : function(format){
883         var args = Array.prototype.slice.call(arguments, 1);
884         return format.replace(/\{(\d+)\}/g, function(m, i){
885             return Roo.util.Format.htmlEncode(args[i]);
886         });
887     }
888   
889     
890 });
891
892 /**
893  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
894  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
895  * they are already different, the first value passed in is returned.  Note that this method returns the new value
896  * but does not change the current string.
897  * <pre><code>
898 // alternate sort directions
899 sort = sort.toggle('ASC', 'DESC');
900
901 // instead of conditional logic:
902 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
903 </code></pre>
904  * @param {String} value The value to compare to the current string
905  * @param {String} other The new value to use if the string already equals the first value passed in
906  * @return {String} The new value
907  */
908  
909 String.prototype.toggle = function(value, other){
910     return this == value ? other : value;
911 };
912
913
914 /**
915   * Remove invalid unicode characters from a string 
916   *
917   * @return {String} The clean string
918   */
919 String.prototype.unicodeClean = function () {
920     return this.replace(/[\s\S]/g,
921         function(character) {
922             if (character.charCodeAt()< 256) {
923               return character;
924            }
925            try {
926                 encodeURIComponent(character);
927            } catch(e) { 
928               return '';
929            }
930            return character;
931         }
932     );
933 };
934   
935 /*
936  * Based on:
937  * Ext JS Library 1.1.1
938  * Copyright(c) 2006-2007, Ext JS, LLC.
939  *
940  * Originally Released Under LGPL - original licence link has changed is not relivant.
941  *
942  * Fork - LGPL
943  * <script type="text/javascript">
944  */
945
946  /**
947  * @class Number
948  */
949 Roo.applyIf(Number.prototype, {
950     /**
951      * Checks whether or not the current number is within a desired range.  If the number is already within the
952      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
953      * exceeded.  Note that this method returns the constrained value but does not change the current number.
954      * @param {Number} min The minimum number in the range
955      * @param {Number} max The maximum number in the range
956      * @return {Number} The constrained value if outside the range, otherwise the current value
957      */
958     constrain : function(min, max){
959         return Math.min(Math.max(this, min), max);
960     }
961 });/*
962  * Based on:
963  * Ext JS Library 1.1.1
964  * Copyright(c) 2006-2007, Ext JS, LLC.
965  *
966  * Originally Released Under LGPL - original licence link has changed is not relivant.
967  *
968  * Fork - LGPL
969  * <script type="text/javascript">
970  */
971  /**
972  * @class Array
973  */
974 Roo.applyIf(Array.prototype, {
975     /**
976      * 
977      * Checks whether or not the specified object exists in the array.
978      * @param {Object} o The object to check for
979      * @return {Number} The index of o in the array (or -1 if it is not found)
980      */
981     indexOf : function(o){
982        for (var i = 0, len = this.length; i < len; i++){
983               if(this[i] == o) { return i; }
984        }
985            return -1;
986     },
987
988     /**
989      * Removes the specified object from the array.  If the object is not found nothing happens.
990      * @param {Object} o The object to remove
991      */
992     remove : function(o){
993        var index = this.indexOf(o);
994        if(index != -1){
995            this.splice(index, 1);
996        }
997     },
998     /**
999      * Map (JS 1.6 compatibility)
1000      * @param {Function} function  to call
1001      */
1002     map : function(fun )
1003     {
1004         var len = this.length >>> 0;
1005         if (typeof fun != "function") {
1006             throw new TypeError();
1007         }
1008         var res = new Array(len);
1009         var thisp = arguments[1];
1010         for (var i = 0; i < len; i++)
1011         {
1012             if (i in this) {
1013                 res[i] = fun.call(thisp, this[i], i, this);
1014             }
1015         }
1016
1017         return res;
1018     }
1019     
1020 });
1021
1022
1023  
1024 /*
1025  * Based on:
1026  * Ext JS Library 1.1.1
1027  * Copyright(c) 2006-2007, Ext JS, LLC.
1028  *
1029  * Originally Released Under LGPL - original licence link has changed is not relivant.
1030  *
1031  * Fork - LGPL
1032  * <script type="text/javascript">
1033  */
1034
1035 /**
1036  * @class Date
1037  *
1038  * The date parsing and format syntax is a subset of
1039  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1040  * supported will provide results equivalent to their PHP versions.
1041  *
1042  * Following is the list of all currently supported formats:
1043  *<pre>
1044 Sample date:
1045 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1046
1047 Format  Output      Description
1048 ------  ----------  --------------------------------------------------------------
1049   d      10         Day of the month, 2 digits with leading zeros
1050   D      Wed        A textual representation of a day, three letters
1051   j      10         Day of the month without leading zeros
1052   l      Wednesday  A full textual representation of the day of the week
1053   S      th         English ordinal day of month suffix, 2 chars (use with j)
1054   w      3          Numeric representation of the day of the week
1055   z      9          The julian date, or day of the year (0-365)
1056   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1057   F      January    A full textual representation of the month
1058   m      01         Numeric representation of a month, with leading zeros
1059   M      Jan        Month name abbreviation, three letters
1060   n      1          Numeric representation of a month, without leading zeros
1061   t      31         Number of days in the given month
1062   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1063   Y      2007       A full numeric representation of a year, 4 digits
1064   y      07         A two digit representation of a year
1065   a      pm         Lowercase Ante meridiem and Post meridiem
1066   A      PM         Uppercase Ante meridiem and Post meridiem
1067   g      3          12-hour format of an hour without leading zeros
1068   G      15         24-hour format of an hour without leading zeros
1069   h      03         12-hour format of an hour with leading zeros
1070   H      15         24-hour format of an hour with leading zeros
1071   i      05         Minutes with leading zeros
1072   s      01         Seconds, with leading zeros
1073   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1074   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1075   T      CST        Timezone setting of the machine running the code
1076   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1077 </pre>
1078  *
1079  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1080  * <pre><code>
1081 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1082 document.write(dt.format('Y-m-d'));                         //2007-01-10
1083 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1084 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1085  </code></pre>
1086  *
1087  * Here are some standard date/time patterns that you might find helpful.  They
1088  * are not part of the source of Date.js, but to use them you can simply copy this
1089  * block of code into any script that is included after Date.js and they will also become
1090  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1091  * <pre><code>
1092 Date.patterns = {
1093     ISO8601Long:"Y-m-d H:i:s",
1094     ISO8601Short:"Y-m-d",
1095     ShortDate: "n/j/Y",
1096     LongDate: "l, F d, Y",
1097     FullDateTime: "l, F d, Y g:i:s A",
1098     MonthDay: "F d",
1099     ShortTime: "g:i A",
1100     LongTime: "g:i:s A",
1101     SortableDateTime: "Y-m-d\\TH:i:s",
1102     UniversalSortableDateTime: "Y-m-d H:i:sO",
1103     YearMonth: "F, Y"
1104 };
1105 </code></pre>
1106  *
1107  * Example usage:
1108  * <pre><code>
1109 var dt = new Date();
1110 document.write(dt.format(Date.patterns.ShortDate));
1111  </code></pre>
1112  */
1113
1114 /*
1115  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1116  * They generate precompiled functions from date formats instead of parsing and
1117  * processing the pattern every time you format a date.  These functions are available
1118  * on every Date object (any javascript function).
1119  *
1120  * The original article and download are here:
1121  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1122  *
1123  */
1124  
1125  
1126  // was in core
1127 /**
1128  Returns the number of milliseconds between this date and date
1129  @param {Date} date (optional) Defaults to now
1130  @return {Number} The diff in milliseconds
1131  @member Date getElapsed
1132  */
1133 Date.prototype.getElapsed = function(date) {
1134         return Math.abs((date || new Date()).getTime()-this.getTime());
1135 };
1136 // was in date file..
1137
1138
1139 // private
1140 Date.parseFunctions = {count:0};
1141 // private
1142 Date.parseRegexes = [];
1143 // private
1144 Date.formatFunctions = {count:0};
1145
1146 // private
1147 Date.prototype.dateFormat = function(format) {
1148     if (Date.formatFunctions[format] == null) {
1149         Date.createNewFormat(format);
1150     }
1151     var func = Date.formatFunctions[format];
1152     return this[func]();
1153 };
1154
1155
1156 /**
1157  * Formats a date given the supplied format string
1158  * @param {String} format The format string
1159  * @return {String} The formatted date
1160  * @method
1161  */
1162 Date.prototype.format = Date.prototype.dateFormat;
1163
1164 // private
1165 Date.createNewFormat = function(format) {
1166     var funcName = "format" + Date.formatFunctions.count++;
1167     Date.formatFunctions[format] = funcName;
1168     var code = "Date.prototype." + funcName + " = function(){return ";
1169     var special = false;
1170     var ch = '';
1171     for (var i = 0; i < format.length; ++i) {
1172         ch = format.charAt(i);
1173         if (!special && ch == "\\") {
1174             special = true;
1175         }
1176         else if (special) {
1177             special = false;
1178             code += "'" + String.escape(ch) + "' + ";
1179         }
1180         else {
1181             code += Date.getFormatCode(ch);
1182         }
1183     }
1184     /** eval:var:zzzzzzzzzzzzz */
1185     eval(code.substring(0, code.length - 3) + ";}");
1186 };
1187
1188 // private
1189 Date.getFormatCode = function(character) {
1190     switch (character) {
1191     case "d":
1192         return "String.leftPad(this.getDate(), 2, '0') + ";
1193     case "D":
1194         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1195     case "j":
1196         return "this.getDate() + ";
1197     case "l":
1198         return "Date.dayNames[this.getDay()] + ";
1199     case "S":
1200         return "this.getSuffix() + ";
1201     case "w":
1202         return "this.getDay() + ";
1203     case "z":
1204         return "this.getDayOfYear() + ";
1205     case "W":
1206         return "this.getWeekOfYear() + ";
1207     case "F":
1208         return "Date.monthNames[this.getMonth()] + ";
1209     case "m":
1210         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1211     case "M":
1212         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1213     case "n":
1214         return "(this.getMonth() + 1) + ";
1215     case "t":
1216         return "this.getDaysInMonth() + ";
1217     case "L":
1218         return "(this.isLeapYear() ? 1 : 0) + ";
1219     case "Y":
1220         return "this.getFullYear() + ";
1221     case "y":
1222         return "('' + this.getFullYear()).substring(2, 4) + ";
1223     case "a":
1224         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1225     case "A":
1226         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1227     case "g":
1228         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1229     case "G":
1230         return "this.getHours() + ";
1231     case "h":
1232         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1233     case "H":
1234         return "String.leftPad(this.getHours(), 2, '0') + ";
1235     case "i":
1236         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1237     case "s":
1238         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1239     case "O":
1240         return "this.getGMTOffset() + ";
1241     case "P":
1242         return "this.getGMTColonOffset() + ";
1243     case "T":
1244         return "this.getTimezone() + ";
1245     case "Z":
1246         return "(this.getTimezoneOffset() * -60) + ";
1247     default:
1248         return "'" + String.escape(character) + "' + ";
1249     }
1250 };
1251
1252 /**
1253  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1254  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1255  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1256  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1257  * string or the parse operation will fail.
1258  * Example Usage:
1259 <pre><code>
1260 //dt = Fri May 25 2007 (current date)
1261 var dt = new Date();
1262
1263 //dt = Thu May 25 2006 (today's month/day in 2006)
1264 dt = Date.parseDate("2006", "Y");
1265
1266 //dt = Sun Jan 15 2006 (all date parts specified)
1267 dt = Date.parseDate("2006-1-15", "Y-m-d");
1268
1269 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1270 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1271 </code></pre>
1272  * @param {String} input The unparsed date as a string
1273  * @param {String} format The format the date is in
1274  * @return {Date} The parsed date
1275  * @static
1276  */
1277 Date.parseDate = function(input, format) {
1278     if (Date.parseFunctions[format] == null) {
1279         Date.createParser(format);
1280     }
1281     var func = Date.parseFunctions[format];
1282     return Date[func](input);
1283 };
1284 /**
1285  * @private
1286  */
1287
1288 Date.createParser = function(format) {
1289     var funcName = "parse" + Date.parseFunctions.count++;
1290     var regexNum = Date.parseRegexes.length;
1291     var currentGroup = 1;
1292     Date.parseFunctions[format] = funcName;
1293
1294     var code = "Date." + funcName + " = function(input){\n"
1295         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1296         + "var d = new Date();\n"
1297         + "y = d.getFullYear();\n"
1298         + "m = d.getMonth();\n"
1299         + "d = d.getDate();\n"
1300         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1301         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1302         + "if (results && results.length > 0) {";
1303     var regex = "";
1304
1305     var special = false;
1306     var ch = '';
1307     for (var i = 0; i < format.length; ++i) {
1308         ch = format.charAt(i);
1309         if (!special && ch == "\\") {
1310             special = true;
1311         }
1312         else if (special) {
1313             special = false;
1314             regex += String.escape(ch);
1315         }
1316         else {
1317             var obj = Date.formatCodeToRegex(ch, currentGroup);
1318             currentGroup += obj.g;
1319             regex += obj.s;
1320             if (obj.g && obj.c) {
1321                 code += obj.c;
1322             }
1323         }
1324     }
1325
1326     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1327         + "{v = new Date(y, m, d, h, i, s);}\n"
1328         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1329         + "{v = new Date(y, m, d, h, i);}\n"
1330         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1331         + "{v = new Date(y, m, d, h);}\n"
1332         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1333         + "{v = new Date(y, m, d);}\n"
1334         + "else if (y >= 0 && m >= 0)\n"
1335         + "{v = new Date(y, m);}\n"
1336         + "else if (y >= 0)\n"
1337         + "{v = new Date(y);}\n"
1338         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1339         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1340         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1341         + ";}";
1342
1343     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1344     /** eval:var:zzzzzzzzzzzzz */
1345     eval(code);
1346 };
1347
1348 // private
1349 Date.formatCodeToRegex = function(character, currentGroup) {
1350     switch (character) {
1351     case "D":
1352         return {g:0,
1353         c:null,
1354         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1355     case "j":
1356         return {g:1,
1357             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1358             s:"(\\d{1,2})"}; // day of month without leading zeroes
1359     case "d":
1360         return {g:1,
1361             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1362             s:"(\\d{2})"}; // day of month with leading zeroes
1363     case "l":
1364         return {g:0,
1365             c:null,
1366             s:"(?:" + Date.dayNames.join("|") + ")"};
1367     case "S":
1368         return {g:0,
1369             c:null,
1370             s:"(?:st|nd|rd|th)"};
1371     case "w":
1372         return {g:0,
1373             c:null,
1374             s:"\\d"};
1375     case "z":
1376         return {g:0,
1377             c:null,
1378             s:"(?:\\d{1,3})"};
1379     case "W":
1380         return {g:0,
1381             c:null,
1382             s:"(?:\\d{2})"};
1383     case "F":
1384         return {g:1,
1385             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1386             s:"(" + Date.monthNames.join("|") + ")"};
1387     case "M":
1388         return {g:1,
1389             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1390             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1391     case "n":
1392         return {g:1,
1393             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1394             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1395     case "m":
1396         return {g:1,
1397             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1398             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1399     case "t":
1400         return {g:0,
1401             c:null,
1402             s:"\\d{1,2}"};
1403     case "L":
1404         return {g:0,
1405             c:null,
1406             s:"(?:1|0)"};
1407     case "Y":
1408         return {g:1,
1409             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1410             s:"(\\d{4})"};
1411     case "y":
1412         return {g:1,
1413             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1414                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1415             s:"(\\d{1,2})"};
1416     case "a":
1417         return {g:1,
1418             c:"if (results[" + currentGroup + "] == 'am') {\n"
1419                 + "if (h == 12) { h = 0; }\n"
1420                 + "} else { if (h < 12) { h += 12; }}",
1421             s:"(am|pm)"};
1422     case "A":
1423         return {g:1,
1424             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1425                 + "if (h == 12) { h = 0; }\n"
1426                 + "} else { if (h < 12) { h += 12; }}",
1427             s:"(AM|PM)"};
1428     case "g":
1429     case "G":
1430         return {g:1,
1431             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1432             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1433     case "h":
1434     case "H":
1435         return {g:1,
1436             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1437             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1438     case "i":
1439         return {g:1,
1440             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1441             s:"(\\d{2})"};
1442     case "s":
1443         return {g:1,
1444             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1445             s:"(\\d{2})"};
1446     case "O":
1447         return {g:1,
1448             c:[
1449                 "o = results[", currentGroup, "];\n",
1450                 "var sn = o.substring(0,1);\n", // get + / - sign
1451                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1452                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1453                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1454                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1455             ].join(""),
1456             s:"([+\-]\\d{2,4})"};
1457     
1458     
1459     case "P":
1460         return {g:1,
1461                 c:[
1462                    "o = results[", currentGroup, "];\n",
1463                    "var sn = o.substring(0,1);\n",
1464                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1465                    "var mn = o.substring(4,6) % 60;\n",
1466                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1467                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1468             ].join(""),
1469             s:"([+\-]\\d{4})"};
1470     case "T":
1471         return {g:0,
1472             c:null,
1473             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1474     case "Z":
1475         return {g:1,
1476             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1477                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1478             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1479     default:
1480         return {g:0,
1481             c:null,
1482             s:String.escape(character)};
1483     }
1484 };
1485
1486 /**
1487  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1488  * @return {String} The abbreviated timezone name (e.g. 'CST')
1489  */
1490 Date.prototype.getTimezone = function() {
1491     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1492 };
1493
1494 /**
1495  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1496  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1497  */
1498 Date.prototype.getGMTOffset = function() {
1499     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1500         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1501         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1502 };
1503
1504 /**
1505  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1506  * @return {String} 2-characters representing hours and 2-characters representing minutes
1507  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1508  */
1509 Date.prototype.getGMTColonOffset = function() {
1510         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1511                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1512                 + ":"
1513                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1514 }
1515
1516 /**
1517  * Get the numeric day number of the year, adjusted for leap year.
1518  * @return {Number} 0 through 364 (365 in leap years)
1519  */
1520 Date.prototype.getDayOfYear = function() {
1521     var num = 0;
1522     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1523     for (var i = 0; i < this.getMonth(); ++i) {
1524         num += Date.daysInMonth[i];
1525     }
1526     return num + this.getDate() - 1;
1527 };
1528
1529 /**
1530  * Get the string representation of the numeric week number of the year
1531  * (equivalent to the format specifier 'W').
1532  * @return {String} '00' through '52'
1533  */
1534 Date.prototype.getWeekOfYear = function() {
1535     // Skip to Thursday of this week
1536     var now = this.getDayOfYear() + (4 - this.getDay());
1537     // Find the first Thursday of the year
1538     var jan1 = new Date(this.getFullYear(), 0, 1);
1539     var then = (7 - jan1.getDay() + 4);
1540     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1541 };
1542
1543 /**
1544  * Whether or not the current date is in a leap year.
1545  * @return {Boolean} True if the current date is in a leap year, else false
1546  */
1547 Date.prototype.isLeapYear = function() {
1548     var year = this.getFullYear();
1549     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1550 };
1551
1552 /**
1553  * Get the first day of the current month, adjusted for leap year.  The returned value
1554  * is the numeric day index within the week (0-6) which can be used in conjunction with
1555  * the {@link #monthNames} array to retrieve the textual day name.
1556  * Example:
1557  *<pre><code>
1558 var dt = new Date('1/10/2007');
1559 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1560 </code></pre>
1561  * @return {Number} The day number (0-6)
1562  */
1563 Date.prototype.getFirstDayOfMonth = function() {
1564     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1565     return (day < 0) ? (day + 7) : day;
1566 };
1567
1568 /**
1569  * Get the last day of the current month, adjusted for leap year.  The returned value
1570  * is the numeric day index within the week (0-6) which can be used in conjunction with
1571  * the {@link #monthNames} array to retrieve the textual day name.
1572  * Example:
1573  *<pre><code>
1574 var dt = new Date('1/10/2007');
1575 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1576 </code></pre>
1577  * @return {Number} The day number (0-6)
1578  */
1579 Date.prototype.getLastDayOfMonth = function() {
1580     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1581     return (day < 0) ? (day + 7) : day;
1582 };
1583
1584
1585 /**
1586  * Get the first date of this date's month
1587  * @return {Date}
1588  */
1589 Date.prototype.getFirstDateOfMonth = function() {
1590     return new Date(this.getFullYear(), this.getMonth(), 1);
1591 };
1592
1593 /**
1594  * Get the last date of this date's month
1595  * @return {Date}
1596  */
1597 Date.prototype.getLastDateOfMonth = function() {
1598     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1599 };
1600 /**
1601  * Get the number of days in the current month, adjusted for leap year.
1602  * @return {Number} The number of days in the month
1603  */
1604 Date.prototype.getDaysInMonth = function() {
1605     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1606     return Date.daysInMonth[this.getMonth()];
1607 };
1608
1609 /**
1610  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1611  * @return {String} 'st, 'nd', 'rd' or 'th'
1612  */
1613 Date.prototype.getSuffix = function() {
1614     switch (this.getDate()) {
1615         case 1:
1616         case 21:
1617         case 31:
1618             return "st";
1619         case 2:
1620         case 22:
1621             return "nd";
1622         case 3:
1623         case 23:
1624             return "rd";
1625         default:
1626             return "th";
1627     }
1628 };
1629
1630 // private
1631 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1632
1633 /**
1634  * An array of textual month names.
1635  * Override these values for international dates, for example...
1636  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1637  * @type Array
1638  * @static
1639  */
1640 Date.monthNames =
1641    ["January",
1642     "February",
1643     "March",
1644     "April",
1645     "May",
1646     "June",
1647     "July",
1648     "August",
1649     "September",
1650     "October",
1651     "November",
1652     "December"];
1653
1654 /**
1655  * An array of textual day names.
1656  * Override these values for international dates, for example...
1657  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1658  * @type Array
1659  * @static
1660  */
1661 Date.dayNames =
1662    ["Sunday",
1663     "Monday",
1664     "Tuesday",
1665     "Wednesday",
1666     "Thursday",
1667     "Friday",
1668     "Saturday"];
1669
1670 // private
1671 Date.y2kYear = 50;
1672 // private
1673 Date.monthNumbers = {
1674     Jan:0,
1675     Feb:1,
1676     Mar:2,
1677     Apr:3,
1678     May:4,
1679     Jun:5,
1680     Jul:6,
1681     Aug:7,
1682     Sep:8,
1683     Oct:9,
1684     Nov:10,
1685     Dec:11};
1686
1687 /**
1688  * Creates and returns a new Date instance with the exact same date value as the called instance.
1689  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1690  * variable will also be changed.  When the intention is to create a new variable that will not
1691  * modify the original instance, you should create a clone.
1692  *
1693  * Example of correctly cloning a date:
1694  * <pre><code>
1695 //wrong way:
1696 var orig = new Date('10/1/2006');
1697 var copy = orig;
1698 copy.setDate(5);
1699 document.write(orig);  //returns 'Thu Oct 05 2006'!
1700
1701 //correct way:
1702 var orig = new Date('10/1/2006');
1703 var copy = orig.clone();
1704 copy.setDate(5);
1705 document.write(orig);  //returns 'Thu Oct 01 2006'
1706 </code></pre>
1707  * @return {Date} The new Date instance
1708  */
1709 Date.prototype.clone = function() {
1710         return new Date(this.getTime());
1711 };
1712
1713 /**
1714  * Clears any time information from this date
1715  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1716  @return {Date} this or the clone
1717  */
1718 Date.prototype.clearTime = function(clone){
1719     if(clone){
1720         return this.clone().clearTime();
1721     }
1722     this.setHours(0);
1723     this.setMinutes(0);
1724     this.setSeconds(0);
1725     this.setMilliseconds(0);
1726     return this;
1727 };
1728
1729 // private
1730 // safari setMonth is broken -- check that this is only donw once...
1731 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1732     Date.brokenSetMonth = Date.prototype.setMonth;
1733         Date.prototype.setMonth = function(num){
1734                 if(num <= -1){
1735                         var n = Math.ceil(-num);
1736                         var back_year = Math.ceil(n/12);
1737                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1738                         this.setFullYear(this.getFullYear() - back_year);
1739                         return Date.brokenSetMonth.call(this, month);
1740                 } else {
1741                         return Date.brokenSetMonth.apply(this, arguments);
1742                 }
1743         };
1744 }
1745
1746 /** Date interval constant 
1747 * @static 
1748 * @type String */
1749 Date.MILLI = "ms";
1750 /** Date interval constant 
1751 * @static 
1752 * @type String */
1753 Date.SECOND = "s";
1754 /** Date interval constant 
1755 * @static 
1756 * @type String */
1757 Date.MINUTE = "mi";
1758 /** Date interval constant 
1759 * @static 
1760 * @type String */
1761 Date.HOUR = "h";
1762 /** Date interval constant 
1763 * @static 
1764 * @type String */
1765 Date.DAY = "d";
1766 /** Date interval constant 
1767 * @static 
1768 * @type String */
1769 Date.MONTH = "mo";
1770 /** Date interval constant 
1771 * @static 
1772 * @type String */
1773 Date.YEAR = "y";
1774
1775 /**
1776  * Provides a convenient method of performing basic date arithmetic.  This method
1777  * does not modify the Date instance being called - it creates and returns
1778  * a new Date instance containing the resulting date value.
1779  *
1780  * Examples:
1781  * <pre><code>
1782 //Basic usage:
1783 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1784 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1785
1786 //Negative values will subtract correctly:
1787 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1788 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1789
1790 //You can even chain several calls together in one line!
1791 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1792 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1793  </code></pre>
1794  *
1795  * @param {String} interval   A valid date interval enum value
1796  * @param {Number} value      The amount to add to the current date
1797  * @return {Date} The new Date instance
1798  */
1799 Date.prototype.add = function(interval, value){
1800   var d = this.clone();
1801   if (!interval || value === 0) { return d; }
1802   switch(interval.toLowerCase()){
1803     case Date.MILLI:
1804       d.setMilliseconds(this.getMilliseconds() + value);
1805       break;
1806     case Date.SECOND:
1807       d.setSeconds(this.getSeconds() + value);
1808       break;
1809     case Date.MINUTE:
1810       d.setMinutes(this.getMinutes() + value);
1811       break;
1812     case Date.HOUR:
1813       d.setHours(this.getHours() + value);
1814       break;
1815     case Date.DAY:
1816       d.setDate(this.getDate() + value);
1817       break;
1818     case Date.MONTH:
1819       var day = this.getDate();
1820       if(day > 28){
1821           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1822       }
1823       d.setDate(day);
1824       d.setMonth(this.getMonth() + value);
1825       break;
1826     case Date.YEAR:
1827       d.setFullYear(this.getFullYear() + value);
1828       break;
1829   }
1830   return d;
1831 };
1832 /*
1833  * Based on:
1834  * Ext JS Library 1.1.1
1835  * Copyright(c) 2006-2007, Ext JS, LLC.
1836  *
1837  * Originally Released Under LGPL - original licence link has changed is not relivant.
1838  *
1839  * Fork - LGPL
1840  * <script type="text/javascript">
1841  */
1842
1843 /**
1844  * @class Roo.lib.Dom
1845  * @static
1846  * 
1847  * Dom utils (from YIU afaik)
1848  * 
1849  **/
1850 Roo.lib.Dom = {
1851     /**
1852      * Get the view width
1853      * @param {Boolean} full True will get the full document, otherwise it's the view width
1854      * @return {Number} The width
1855      */
1856      
1857     getViewWidth : function(full) {
1858         return full ? this.getDocumentWidth() : this.getViewportWidth();
1859     },
1860     /**
1861      * Get the view height
1862      * @param {Boolean} full True will get the full document, otherwise it's the view height
1863      * @return {Number} The height
1864      */
1865     getViewHeight : function(full) {
1866         return full ? this.getDocumentHeight() : this.getViewportHeight();
1867     },
1868
1869     getDocumentHeight: function() {
1870         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1871         return Math.max(scrollHeight, this.getViewportHeight());
1872     },
1873
1874     getDocumentWidth: function() {
1875         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1876         return Math.max(scrollWidth, this.getViewportWidth());
1877     },
1878
1879     getViewportHeight: function() {
1880         var height = self.innerHeight;
1881         var mode = document.compatMode;
1882
1883         if ((mode || Roo.isIE) && !Roo.isOpera) {
1884             height = (mode == "CSS1Compat") ?
1885                      document.documentElement.clientHeight :
1886                      document.body.clientHeight;
1887         }
1888
1889         return height;
1890     },
1891
1892     getViewportWidth: function() {
1893         var width = self.innerWidth;
1894         var mode = document.compatMode;
1895
1896         if (mode || Roo.isIE) {
1897             width = (mode == "CSS1Compat") ?
1898                     document.documentElement.clientWidth :
1899                     document.body.clientWidth;
1900         }
1901         return width;
1902     },
1903
1904     isAncestor : function(p, c) {
1905         p = Roo.getDom(p);
1906         c = Roo.getDom(c);
1907         if (!p || !c) {
1908             return false;
1909         }
1910
1911         if (p.contains && !Roo.isSafari) {
1912             return p.contains(c);
1913         } else if (p.compareDocumentPosition) {
1914             return !!(p.compareDocumentPosition(c) & 16);
1915         } else {
1916             var parent = c.parentNode;
1917             while (parent) {
1918                 if (parent == p) {
1919                     return true;
1920                 }
1921                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1922                     return false;
1923                 }
1924                 parent = parent.parentNode;
1925             }
1926             return false;
1927         }
1928     },
1929
1930     getRegion : function(el) {
1931         return Roo.lib.Region.getRegion(el);
1932     },
1933
1934     getY : function(el) {
1935         return this.getXY(el)[1];
1936     },
1937
1938     getX : function(el) {
1939         return this.getXY(el)[0];
1940     },
1941
1942     getXY : function(el) {
1943         var p, pe, b, scroll, bd = document.body;
1944         el = Roo.getDom(el);
1945         var fly = Roo.lib.AnimBase.fly;
1946         if (el.getBoundingClientRect) {
1947             b = el.getBoundingClientRect();
1948             scroll = fly(document).getScroll();
1949             return [b.left + scroll.left, b.top + scroll.top];
1950         }
1951         var x = 0, y = 0;
1952
1953         p = el;
1954
1955         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1956
1957         while (p) {
1958
1959             x += p.offsetLeft;
1960             y += p.offsetTop;
1961
1962             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1963                 hasAbsolute = true;
1964             }
1965
1966             if (Roo.isGecko) {
1967                 pe = fly(p);
1968
1969                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1970                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1971
1972
1973                 x += bl;
1974                 y += bt;
1975
1976
1977                 if (p != el && pe.getStyle('overflow') != 'visible') {
1978                     x += bl;
1979                     y += bt;
1980                 }
1981             }
1982             p = p.offsetParent;
1983         }
1984
1985         if (Roo.isSafari && hasAbsolute) {
1986             x -= bd.offsetLeft;
1987             y -= bd.offsetTop;
1988         }
1989
1990         if (Roo.isGecko && !hasAbsolute) {
1991             var dbd = fly(bd);
1992             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1993             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1994         }
1995
1996         p = el.parentNode;
1997         while (p && p != bd) {
1998             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1999                 x -= p.scrollLeft;
2000                 y -= p.scrollTop;
2001             }
2002             p = p.parentNode;
2003         }
2004         return [x, y];
2005     },
2006  
2007   
2008
2009
2010     setXY : function(el, xy) {
2011         el = Roo.fly(el, '_setXY');
2012         el.position();
2013         var pts = el.translatePoints(xy);
2014         if (xy[0] !== false) {
2015             el.dom.style.left = pts.left + "px";
2016         }
2017         if (xy[1] !== false) {
2018             el.dom.style.top = pts.top + "px";
2019         }
2020     },
2021
2022     setX : function(el, x) {
2023         this.setXY(el, [x, false]);
2024     },
2025
2026     setY : function(el, y) {
2027         this.setXY(el, [false, y]);
2028     }
2029 };
2030 /*
2031  * Portions of this file are based on pieces of Yahoo User Interface Library
2032  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2033  * YUI licensed under the BSD License:
2034  * http://developer.yahoo.net/yui/license.txt
2035  * <script type="text/javascript">
2036  *
2037  */
2038
2039 Roo.lib.Event = function() {
2040     var loadComplete = false;
2041     var listeners = [];
2042     var unloadListeners = [];
2043     var retryCount = 0;
2044     var onAvailStack = [];
2045     var counter = 0;
2046     var lastError = null;
2047
2048     return {
2049         POLL_RETRYS: 200,
2050         POLL_INTERVAL: 20,
2051         EL: 0,
2052         TYPE: 1,
2053         FN: 2,
2054         WFN: 3,
2055         OBJ: 3,
2056         ADJ_SCOPE: 4,
2057         _interval: null,
2058
2059         startInterval: function() {
2060             if (!this._interval) {
2061                 var self = this;
2062                 var callback = function() {
2063                     self._tryPreloadAttach();
2064                 };
2065                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2066
2067             }
2068         },
2069
2070         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2071             onAvailStack.push({ id:         p_id,
2072                 fn:         p_fn,
2073                 obj:        p_obj,
2074                 override:   p_override,
2075                 checkReady: false    });
2076
2077             retryCount = this.POLL_RETRYS;
2078             this.startInterval();
2079         },
2080
2081
2082         addListener: function(el, eventName, fn) {
2083             el = Roo.getDom(el);
2084             if (!el || !fn) {
2085                 return false;
2086             }
2087
2088             if ("unload" == eventName) {
2089                 unloadListeners[unloadListeners.length] =
2090                 [el, eventName, fn];
2091                 return true;
2092             }
2093
2094             var wrappedFn = function(e) {
2095                 return fn(Roo.lib.Event.getEvent(e));
2096             };
2097
2098             var li = [el, eventName, fn, wrappedFn];
2099
2100             var index = listeners.length;
2101             listeners[index] = li;
2102
2103             this.doAdd(el, eventName, wrappedFn, false);
2104             return true;
2105
2106         },
2107
2108
2109         removeListener: function(el, eventName, fn) {
2110             var i, len;
2111
2112             el = Roo.getDom(el);
2113
2114             if(!fn) {
2115                 return this.purgeElement(el, false, eventName);
2116             }
2117
2118
2119             if ("unload" == eventName) {
2120
2121                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2122                     var li = unloadListeners[i];
2123                     if (li &&
2124                         li[0] == el &&
2125                         li[1] == eventName &&
2126                         li[2] == fn) {
2127                         unloadListeners.splice(i, 1);
2128                         return true;
2129                     }
2130                 }
2131
2132                 return false;
2133             }
2134
2135             var cacheItem = null;
2136
2137
2138             var index = arguments[3];
2139
2140             if ("undefined" == typeof index) {
2141                 index = this._getCacheIndex(el, eventName, fn);
2142             }
2143
2144             if (index >= 0) {
2145                 cacheItem = listeners[index];
2146             }
2147
2148             if (!el || !cacheItem) {
2149                 return false;
2150             }
2151
2152             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2153
2154             delete listeners[index][this.WFN];
2155             delete listeners[index][this.FN];
2156             listeners.splice(index, 1);
2157
2158             return true;
2159
2160         },
2161
2162
2163         getTarget: function(ev, resolveTextNode) {
2164             ev = ev.browserEvent || ev;
2165             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2166             var t = ev.target || ev.srcElement;
2167             return this.resolveTextNode(t);
2168         },
2169
2170
2171         resolveTextNode: function(node) {
2172             if (Roo.isSafari && node && 3 == node.nodeType) {
2173                 return node.parentNode;
2174             } else {
2175                 return node;
2176             }
2177         },
2178
2179
2180         getPageX: function(ev) {
2181             ev = ev.browserEvent || ev;
2182             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2183             var x = ev.pageX;
2184             if (!x && 0 !== x) {
2185                 x = ev.clientX || 0;
2186
2187                 if (Roo.isIE) {
2188                     x += this.getScroll()[1];
2189                 }
2190             }
2191
2192             return x;
2193         },
2194
2195
2196         getPageY: function(ev) {
2197             ev = ev.browserEvent || ev;
2198             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2199             var y = ev.pageY;
2200             if (!y && 0 !== y) {
2201                 y = ev.clientY || 0;
2202
2203                 if (Roo.isIE) {
2204                     y += this.getScroll()[0];
2205                 }
2206             }
2207
2208
2209             return y;
2210         },
2211
2212
2213         getXY: function(ev) {
2214             ev = ev.browserEvent || ev;
2215             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2216             return [this.getPageX(ev), this.getPageY(ev)];
2217         },
2218
2219
2220         getRelatedTarget: function(ev) {
2221             ev = ev.browserEvent || ev;
2222             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2223             var t = ev.relatedTarget;
2224             if (!t) {
2225                 if (ev.type == "mouseout") {
2226                     t = ev.toElement;
2227                 } else if (ev.type == "mouseover") {
2228                     t = ev.fromElement;
2229                 }
2230             }
2231
2232             return this.resolveTextNode(t);
2233         },
2234
2235
2236         getTime: function(ev) {
2237             ev = ev.browserEvent || ev;
2238             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2239             if (!ev.time) {
2240                 var t = new Date().getTime();
2241                 try {
2242                     ev.time = t;
2243                 } catch(ex) {
2244                     this.lastError = ex;
2245                     return t;
2246                 }
2247             }
2248
2249             return ev.time;
2250         },
2251
2252
2253         stopEvent: function(ev) {
2254             this.stopPropagation(ev);
2255             this.preventDefault(ev);
2256         },
2257
2258
2259         stopPropagation: function(ev) {
2260             ev = ev.browserEvent || ev;
2261             if (ev.stopPropagation) {
2262                 ev.stopPropagation();
2263             } else {
2264                 ev.cancelBubble = true;
2265             }
2266         },
2267
2268
2269         preventDefault: function(ev) {
2270             ev = ev.browserEvent || ev;
2271             if(ev.preventDefault) {
2272                 ev.preventDefault();
2273             } else {
2274                 ev.returnValue = false;
2275             }
2276         },
2277
2278
2279         getEvent: function(e) {
2280             var ev = e || window.event;
2281             if (!ev) {
2282                 var c = this.getEvent.caller;
2283                 while (c) {
2284                     ev = c.arguments[0];
2285                     if (ev && Event == ev.constructor) {
2286                         break;
2287                     }
2288                     c = c.caller;
2289                 }
2290             }
2291             return ev;
2292         },
2293
2294
2295         getCharCode: function(ev) {
2296             ev = ev.browserEvent || ev;
2297             return ev.charCode || ev.keyCode || 0;
2298         },
2299
2300
2301         _getCacheIndex: function(el, eventName, fn) {
2302             for (var i = 0,len = listeners.length; i < len; ++i) {
2303                 var li = listeners[i];
2304                 if (li &&
2305                     li[this.FN] == fn &&
2306                     li[this.EL] == el &&
2307                     li[this.TYPE] == eventName) {
2308                     return i;
2309                 }
2310             }
2311
2312             return -1;
2313         },
2314
2315
2316         elCache: {},
2317
2318
2319         getEl: function(id) {
2320             return document.getElementById(id);
2321         },
2322
2323
2324         clearCache: function() {
2325         },
2326
2327
2328         _load: function(e) {
2329             loadComplete = true;
2330             var EU = Roo.lib.Event;
2331
2332
2333             if (Roo.isIE) {
2334                 EU.doRemove(window, "load", EU._load);
2335             }
2336         },
2337
2338
2339         _tryPreloadAttach: function() {
2340
2341             if (this.locked) {
2342                 return false;
2343             }
2344
2345             this.locked = true;
2346
2347
2348             var tryAgain = !loadComplete;
2349             if (!tryAgain) {
2350                 tryAgain = (retryCount > 0);
2351             }
2352
2353
2354             var notAvail = [];
2355             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2356                 var item = onAvailStack[i];
2357                 if (item) {
2358                     var el = this.getEl(item.id);
2359
2360                     if (el) {
2361                         if (!item.checkReady ||
2362                             loadComplete ||
2363                             el.nextSibling ||
2364                             (document && document.body)) {
2365
2366                             var scope = el;
2367                             if (item.override) {
2368                                 if (item.override === true) {
2369                                     scope = item.obj;
2370                                 } else {
2371                                     scope = item.override;
2372                                 }
2373                             }
2374                             item.fn.call(scope, item.obj);
2375                             onAvailStack[i] = null;
2376                         }
2377                     } else {
2378                         notAvail.push(item);
2379                     }
2380                 }
2381             }
2382
2383             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2384
2385             if (tryAgain) {
2386
2387                 this.startInterval();
2388             } else {
2389                 clearInterval(this._interval);
2390                 this._interval = null;
2391             }
2392
2393             this.locked = false;
2394
2395             return true;
2396
2397         },
2398
2399
2400         purgeElement: function(el, recurse, eventName) {
2401             var elListeners = this.getListeners(el, eventName);
2402             if (elListeners) {
2403                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2404                     var l = elListeners[i];
2405                     this.removeListener(el, l.type, l.fn);
2406                 }
2407             }
2408
2409             if (recurse && el && el.childNodes) {
2410                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2411                     this.purgeElement(el.childNodes[i], recurse, eventName);
2412                 }
2413             }
2414         },
2415
2416
2417         getListeners: function(el, eventName) {
2418             var results = [], searchLists;
2419             if (!eventName) {
2420                 searchLists = [listeners, unloadListeners];
2421             } else if (eventName == "unload") {
2422                 searchLists = [unloadListeners];
2423             } else {
2424                 searchLists = [listeners];
2425             }
2426
2427             for (var j = 0; j < searchLists.length; ++j) {
2428                 var searchList = searchLists[j];
2429                 if (searchList && searchList.length > 0) {
2430                     for (var i = 0,len = searchList.length; i < len; ++i) {
2431                         var l = searchList[i];
2432                         if (l && l[this.EL] === el &&
2433                             (!eventName || eventName === l[this.TYPE])) {
2434                             results.push({
2435                                 type:   l[this.TYPE],
2436                                 fn:     l[this.FN],
2437                                 obj:    l[this.OBJ],
2438                                 adjust: l[this.ADJ_SCOPE],
2439                                 index:  i
2440                             });
2441                         }
2442                     }
2443                 }
2444             }
2445
2446             return (results.length) ? results : null;
2447         },
2448
2449
2450         _unload: function(e) {
2451
2452             var EU = Roo.lib.Event, i, j, l, len, index;
2453
2454             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2455                 l = unloadListeners[i];
2456                 if (l) {
2457                     var scope = window;
2458                     if (l[EU.ADJ_SCOPE]) {
2459                         if (l[EU.ADJ_SCOPE] === true) {
2460                             scope = l[EU.OBJ];
2461                         } else {
2462                             scope = l[EU.ADJ_SCOPE];
2463                         }
2464                     }
2465                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2466                     unloadListeners[i] = null;
2467                     l = null;
2468                     scope = null;
2469                 }
2470             }
2471
2472             unloadListeners = null;
2473
2474             if (listeners && listeners.length > 0) {
2475                 j = listeners.length;
2476                 while (j) {
2477                     index = j - 1;
2478                     l = listeners[index];
2479                     if (l) {
2480                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2481                                 l[EU.FN], index);
2482                     }
2483                     j = j - 1;
2484                 }
2485                 l = null;
2486
2487                 EU.clearCache();
2488             }
2489
2490             EU.doRemove(window, "unload", EU._unload);
2491
2492         },
2493
2494
2495         getScroll: function() {
2496             var dd = document.documentElement, db = document.body;
2497             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2498                 return [dd.scrollTop, dd.scrollLeft];
2499             } else if (db) {
2500                 return [db.scrollTop, db.scrollLeft];
2501             } else {
2502                 return [0, 0];
2503             }
2504         },
2505
2506
2507         doAdd: function () {
2508             if (window.addEventListener) {
2509                 return function(el, eventName, fn, capture) {
2510                     el.addEventListener(eventName, fn, (capture));
2511                 };
2512             } else if (window.attachEvent) {
2513                 return function(el, eventName, fn, capture) {
2514                     el.attachEvent("on" + eventName, fn);
2515                 };
2516             } else {
2517                 return function() {
2518                 };
2519             }
2520         }(),
2521
2522
2523         doRemove: function() {
2524             if (window.removeEventListener) {
2525                 return function (el, eventName, fn, capture) {
2526                     el.removeEventListener(eventName, fn, (capture));
2527                 };
2528             } else if (window.detachEvent) {
2529                 return function (el, eventName, fn) {
2530                     el.detachEvent("on" + eventName, fn);
2531                 };
2532             } else {
2533                 return function() {
2534                 };
2535             }
2536         }()
2537     };
2538     
2539 }();
2540 (function() {     
2541    
2542     var E = Roo.lib.Event;
2543     E.on = E.addListener;
2544     E.un = E.removeListener;
2545
2546     if (document && document.body) {
2547         E._load();
2548     } else {
2549         E.doAdd(window, "load", E._load);
2550     }
2551     E.doAdd(window, "unload", E._unload);
2552     E._tryPreloadAttach();
2553 })();
2554
2555 /*
2556  * Portions of this file are based on pieces of Yahoo User Interface Library
2557  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2558  * YUI licensed under the BSD License:
2559  * http://developer.yahoo.net/yui/license.txt
2560  * <script type="text/javascript">
2561  *
2562  */
2563
2564 (function() {
2565     /**
2566      * @class Roo.lib.Ajax
2567      *
2568      */
2569     Roo.lib.Ajax = {
2570         /**
2571          * @static 
2572          */
2573         request : function(method, uri, cb, data, options) {
2574             if(options){
2575                 var hs = options.headers;
2576                 if(hs){
2577                     for(var h in hs){
2578                         if(hs.hasOwnProperty(h)){
2579                             this.initHeader(h, hs[h], false);
2580                         }
2581                     }
2582                 }
2583                 if(options.xmlData){
2584                     this.initHeader('Content-Type', 'text/xml', false);
2585                     method = 'POST';
2586                     data = options.xmlData;
2587                 }
2588             }
2589
2590             return this.asyncRequest(method, uri, cb, data);
2591         },
2592
2593         serializeForm : function(form) {
2594             if(typeof form == 'string') {
2595                 form = (document.getElementById(form) || document.forms[form]);
2596             }
2597
2598             var el, name, val, disabled, data = '', hasSubmit = false;
2599             for (var i = 0; i < form.elements.length; i++) {
2600                 el = form.elements[i];
2601                 disabled = form.elements[i].disabled;
2602                 name = form.elements[i].name;
2603                 val = form.elements[i].value;
2604
2605                 if (!disabled && name){
2606                     switch (el.type)
2607                             {
2608                         case 'select-one':
2609                         case 'select-multiple':
2610                             for (var j = 0; j < el.options.length; j++) {
2611                                 if (el.options[j].selected) {
2612                                     if (Roo.isIE) {
2613                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2614                                     }
2615                                     else {
2616                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2617                                     }
2618                                 }
2619                             }
2620                             break;
2621                         case 'radio':
2622                         case 'checkbox':
2623                             if (el.checked) {
2624                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2625                             }
2626                             break;
2627                         case 'file':
2628
2629                         case undefined:
2630
2631                         case 'reset':
2632
2633                         case 'button':
2634
2635                             break;
2636                         case 'submit':
2637                             if(hasSubmit == false) {
2638                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2639                                 hasSubmit = true;
2640                             }
2641                             break;
2642                         default:
2643                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2644                             break;
2645                     }
2646                 }
2647             }
2648             data = data.substr(0, data.length - 1);
2649             return data;
2650         },
2651
2652         headers:{},
2653
2654         hasHeaders:false,
2655
2656         useDefaultHeader:true,
2657
2658         defaultPostHeader:'application/x-www-form-urlencoded',
2659
2660         useDefaultXhrHeader:true,
2661
2662         defaultXhrHeader:'XMLHttpRequest',
2663
2664         hasDefaultHeaders:true,
2665
2666         defaultHeaders:{},
2667
2668         poll:{},
2669
2670         timeout:{},
2671
2672         pollInterval:50,
2673
2674         transactionId:0,
2675
2676         setProgId:function(id)
2677         {
2678             this.activeX.unshift(id);
2679         },
2680
2681         setDefaultPostHeader:function(b)
2682         {
2683             this.useDefaultHeader = b;
2684         },
2685
2686         setDefaultXhrHeader:function(b)
2687         {
2688             this.useDefaultXhrHeader = b;
2689         },
2690
2691         setPollingInterval:function(i)
2692         {
2693             if (typeof i == 'number' && isFinite(i)) {
2694                 this.pollInterval = i;
2695             }
2696         },
2697
2698         createXhrObject:function(transactionId)
2699         {
2700             var obj,http;
2701             try
2702             {
2703
2704                 http = new XMLHttpRequest();
2705
2706                 obj = { conn:http, tId:transactionId };
2707             }
2708             catch(e)
2709             {
2710                 for (var i = 0; i < this.activeX.length; ++i) {
2711                     try
2712                     {
2713
2714                         http = new ActiveXObject(this.activeX[i]);
2715
2716                         obj = { conn:http, tId:transactionId };
2717                         break;
2718                     }
2719                     catch(e) {
2720                     }
2721                 }
2722             }
2723             finally
2724             {
2725                 return obj;
2726             }
2727         },
2728
2729         getConnectionObject:function()
2730         {
2731             var o;
2732             var tId = this.transactionId;
2733
2734             try
2735             {
2736                 o = this.createXhrObject(tId);
2737                 if (o) {
2738                     this.transactionId++;
2739                 }
2740             }
2741             catch(e) {
2742             }
2743             finally
2744             {
2745                 return o;
2746             }
2747         },
2748
2749         asyncRequest:function(method, uri, callback, postData)
2750         {
2751             var o = this.getConnectionObject();
2752
2753             if (!o) {
2754                 return null;
2755             }
2756             else {
2757                 o.conn.open(method, uri, true);
2758
2759                 if (this.useDefaultXhrHeader) {
2760                     if (!this.defaultHeaders['X-Requested-With']) {
2761                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2762                     }
2763                 }
2764
2765                 if(postData && this.useDefaultHeader){
2766                     this.initHeader('Content-Type', this.defaultPostHeader);
2767                 }
2768
2769                  if (this.hasDefaultHeaders || this.hasHeaders) {
2770                     this.setHeader(o);
2771                 }
2772
2773                 this.handleReadyState(o, callback);
2774                 o.conn.send(postData || null);
2775
2776                 return o;
2777             }
2778         },
2779
2780         handleReadyState:function(o, callback)
2781         {
2782             var oConn = this;
2783
2784             if (callback && callback.timeout) {
2785                 
2786                 this.timeout[o.tId] = window.setTimeout(function() {
2787                     oConn.abort(o, callback, true);
2788                 }, callback.timeout);
2789             }
2790
2791             this.poll[o.tId] = window.setInterval(
2792                     function() {
2793                         if (o.conn && o.conn.readyState == 4) {
2794                             window.clearInterval(oConn.poll[o.tId]);
2795                             delete oConn.poll[o.tId];
2796
2797                             if(callback && callback.timeout) {
2798                                 window.clearTimeout(oConn.timeout[o.tId]);
2799                                 delete oConn.timeout[o.tId];
2800                             }
2801
2802                             oConn.handleTransactionResponse(o, callback);
2803                         }
2804                     }
2805                     , this.pollInterval);
2806         },
2807
2808         handleTransactionResponse:function(o, callback, isAbort)
2809         {
2810
2811             if (!callback) {
2812                 this.releaseObject(o);
2813                 return;
2814             }
2815
2816             var httpStatus, responseObject;
2817
2818             try
2819             {
2820                 if (o.conn.status !== undefined && o.conn.status != 0) {
2821                     httpStatus = o.conn.status;
2822                 }
2823                 else {
2824                     httpStatus = 13030;
2825                 }
2826             }
2827             catch(e) {
2828
2829
2830                 httpStatus = 13030;
2831             }
2832
2833             if (httpStatus >= 200 && httpStatus < 300) {
2834                 responseObject = this.createResponseObject(o, callback.argument);
2835                 if (callback.success) {
2836                     if (!callback.scope) {
2837                         callback.success(responseObject);
2838                     }
2839                     else {
2840
2841
2842                         callback.success.apply(callback.scope, [responseObject]);
2843                     }
2844                 }
2845             }
2846             else {
2847                 switch (httpStatus) {
2848
2849                     case 12002:
2850                     case 12029:
2851                     case 12030:
2852                     case 12031:
2853                     case 12152:
2854                     case 13030:
2855                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2856                         if (callback.failure) {
2857                             if (!callback.scope) {
2858                                 callback.failure(responseObject);
2859                             }
2860                             else {
2861                                 callback.failure.apply(callback.scope, [responseObject]);
2862                             }
2863                         }
2864                         break;
2865                     default:
2866                         responseObject = this.createResponseObject(o, callback.argument);
2867                         if (callback.failure) {
2868                             if (!callback.scope) {
2869                                 callback.failure(responseObject);
2870                             }
2871                             else {
2872                                 callback.failure.apply(callback.scope, [responseObject]);
2873                             }
2874                         }
2875                 }
2876             }
2877
2878             this.releaseObject(o);
2879             responseObject = null;
2880         },
2881
2882         createResponseObject:function(o, callbackArg)
2883         {
2884             var obj = {};
2885             var headerObj = {};
2886
2887             try
2888             {
2889                 var headerStr = o.conn.getAllResponseHeaders();
2890                 var header = headerStr.split('\n');
2891                 for (var i = 0; i < header.length; i++) {
2892                     var delimitPos = header[i].indexOf(':');
2893                     if (delimitPos != -1) {
2894                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2895                     }
2896                 }
2897             }
2898             catch(e) {
2899             }
2900
2901             obj.tId = o.tId;
2902             obj.status = o.conn.status;
2903             obj.statusText = o.conn.statusText;
2904             obj.getResponseHeader = headerObj;
2905             obj.getAllResponseHeaders = headerStr;
2906             obj.responseText = o.conn.responseText;
2907             obj.responseXML = o.conn.responseXML;
2908
2909             if (typeof callbackArg !== undefined) {
2910                 obj.argument = callbackArg;
2911             }
2912
2913             return obj;
2914         },
2915
2916         createExceptionObject:function(tId, callbackArg, isAbort)
2917         {
2918             var COMM_CODE = 0;
2919             var COMM_ERROR = 'communication failure';
2920             var ABORT_CODE = -1;
2921             var ABORT_ERROR = 'transaction aborted';
2922
2923             var obj = {};
2924
2925             obj.tId = tId;
2926             if (isAbort) {
2927                 obj.status = ABORT_CODE;
2928                 obj.statusText = ABORT_ERROR;
2929             }
2930             else {
2931                 obj.status = COMM_CODE;
2932                 obj.statusText = COMM_ERROR;
2933             }
2934
2935             if (callbackArg) {
2936                 obj.argument = callbackArg;
2937             }
2938
2939             return obj;
2940         },
2941
2942         initHeader:function(label, value, isDefault)
2943         {
2944             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2945
2946             if (headerObj[label] === undefined) {
2947                 headerObj[label] = value;
2948             }
2949             else {
2950
2951
2952                 headerObj[label] = value + "," + headerObj[label];
2953             }
2954
2955             if (isDefault) {
2956                 this.hasDefaultHeaders = true;
2957             }
2958             else {
2959                 this.hasHeaders = true;
2960             }
2961         },
2962
2963
2964         setHeader:function(o)
2965         {
2966             if (this.hasDefaultHeaders) {
2967                 for (var prop in this.defaultHeaders) {
2968                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2969                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2970                     }
2971                 }
2972             }
2973
2974             if (this.hasHeaders) {
2975                 for (var prop in this.headers) {
2976                     if (this.headers.hasOwnProperty(prop)) {
2977                         o.conn.setRequestHeader(prop, this.headers[prop]);
2978                     }
2979                 }
2980                 this.headers = {};
2981                 this.hasHeaders = false;
2982             }
2983         },
2984
2985         resetDefaultHeaders:function() {
2986             delete this.defaultHeaders;
2987             this.defaultHeaders = {};
2988             this.hasDefaultHeaders = false;
2989         },
2990
2991         abort:function(o, callback, isTimeout)
2992         {
2993             if(this.isCallInProgress(o)) {
2994                 o.conn.abort();
2995                 window.clearInterval(this.poll[o.tId]);
2996                 delete this.poll[o.tId];
2997                 if (isTimeout) {
2998                     delete this.timeout[o.tId];
2999                 }
3000
3001                 this.handleTransactionResponse(o, callback, true);
3002
3003                 return true;
3004             }
3005             else {
3006                 return false;
3007             }
3008         },
3009
3010
3011         isCallInProgress:function(o)
3012         {
3013             if (o && o.conn) {
3014                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3015             }
3016             else {
3017
3018                 return false;
3019             }
3020         },
3021
3022
3023         releaseObject:function(o)
3024         {
3025
3026             o.conn = null;
3027
3028             o = null;
3029         },
3030
3031         activeX:[
3032         'MSXML2.XMLHTTP.3.0',
3033         'MSXML2.XMLHTTP',
3034         'Microsoft.XMLHTTP'
3035         ]
3036
3037
3038     };
3039 })();/*
3040  * Portions of this file are based on pieces of Yahoo User Interface Library
3041  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3042  * YUI licensed under the BSD License:
3043  * http://developer.yahoo.net/yui/license.txt
3044  * <script type="text/javascript">
3045  *
3046  */
3047
3048 Roo.lib.Region = function(t, r, b, l) {
3049     this.top = t;
3050     this[1] = t;
3051     this.right = r;
3052     this.bottom = b;
3053     this.left = l;
3054     this[0] = l;
3055 };
3056
3057
3058 Roo.lib.Region.prototype = {
3059     contains : function(region) {
3060         return ( region.left >= this.left &&
3061                  region.right <= this.right &&
3062                  region.top >= this.top &&
3063                  region.bottom <= this.bottom    );
3064
3065     },
3066
3067     getArea : function() {
3068         return ( (this.bottom - this.top) * (this.right - this.left) );
3069     },
3070
3071     intersect : function(region) {
3072         var t = Math.max(this.top, region.top);
3073         var r = Math.min(this.right, region.right);
3074         var b = Math.min(this.bottom, region.bottom);
3075         var l = Math.max(this.left, region.left);
3076
3077         if (b >= t && r >= l) {
3078             return new Roo.lib.Region(t, r, b, l);
3079         } else {
3080             return null;
3081         }
3082     },
3083     union : function(region) {
3084         var t = Math.min(this.top, region.top);
3085         var r = Math.max(this.right, region.right);
3086         var b = Math.max(this.bottom, region.bottom);
3087         var l = Math.min(this.left, region.left);
3088
3089         return new Roo.lib.Region(t, r, b, l);
3090     },
3091
3092     adjust : function(t, l, b, r) {
3093         this.top += t;
3094         this.left += l;
3095         this.right += r;
3096         this.bottom += b;
3097         return this;
3098     }
3099 };
3100
3101 Roo.lib.Region.getRegion = function(el) {
3102     var p = Roo.lib.Dom.getXY(el);
3103
3104     var t = p[1];
3105     var r = p[0] + el.offsetWidth;
3106     var b = p[1] + el.offsetHeight;
3107     var l = p[0];
3108
3109     return new Roo.lib.Region(t, r, b, l);
3110 };
3111 /*
3112  * Portions of this file are based on pieces of Yahoo User Interface Library
3113  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3114  * YUI licensed under the BSD License:
3115  * http://developer.yahoo.net/yui/license.txt
3116  * <script type="text/javascript">
3117  *
3118  */
3119 //@@dep Roo.lib.Region
3120
3121
3122 Roo.lib.Point = function(x, y) {
3123     if (x instanceof Array) {
3124         y = x[1];
3125         x = x[0];
3126     }
3127     this.x = this.right = this.left = this[0] = x;
3128     this.y = this.top = this.bottom = this[1] = y;
3129 };
3130
3131 Roo.lib.Point.prototype = new Roo.lib.Region();
3132 /*
3133  * Portions of this file are based on pieces of Yahoo User Interface Library
3134  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3135  * YUI licensed under the BSD License:
3136  * http://developer.yahoo.net/yui/license.txt
3137  * <script type="text/javascript">
3138  *
3139  */
3140  
3141 (function() {   
3142
3143     Roo.lib.Anim = {
3144         scroll : function(el, args, duration, easing, cb, scope) {
3145             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3146         },
3147
3148         motion : function(el, args, duration, easing, cb, scope) {
3149             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3150         },
3151
3152         color : function(el, args, duration, easing, cb, scope) {
3153             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3154         },
3155
3156         run : function(el, args, duration, easing, cb, scope, type) {
3157             type = type || Roo.lib.AnimBase;
3158             if (typeof easing == "string") {
3159                 easing = Roo.lib.Easing[easing];
3160             }
3161             var anim = new type(el, args, duration, easing);
3162             anim.animateX(function() {
3163                 Roo.callback(cb, scope);
3164             });
3165             return anim;
3166         }
3167     };
3168 })();/*
3169  * Portions of this file are based on pieces of Yahoo User Interface Library
3170  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3171  * YUI licensed under the BSD License:
3172  * http://developer.yahoo.net/yui/license.txt
3173  * <script type="text/javascript">
3174  *
3175  */
3176
3177 (function() {    
3178     var libFlyweight;
3179     
3180     function fly(el) {
3181         if (!libFlyweight) {
3182             libFlyweight = new Roo.Element.Flyweight();
3183         }
3184         libFlyweight.dom = el;
3185         return libFlyweight;
3186     }
3187
3188     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3189     
3190    
3191     
3192     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3193         if (el) {
3194             this.init(el, attributes, duration, method);
3195         }
3196     };
3197
3198     Roo.lib.AnimBase.fly = fly;
3199     
3200     
3201     
3202     Roo.lib.AnimBase.prototype = {
3203
3204         toString: function() {
3205             var el = this.getEl();
3206             var id = el.id || el.tagName;
3207             return ("Anim " + id);
3208         },
3209
3210         patterns: {
3211             noNegatives:        /width|height|opacity|padding/i,
3212             offsetAttribute:  /^((width|height)|(top|left))$/,
3213             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3214             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3215         },
3216
3217
3218         doMethod: function(attr, start, end) {
3219             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3220         },
3221
3222
3223         setAttribute: function(attr, val, unit) {
3224             if (this.patterns.noNegatives.test(attr)) {
3225                 val = (val > 0) ? val : 0;
3226             }
3227
3228             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3229         },
3230
3231
3232         getAttribute: function(attr) {
3233             var el = this.getEl();
3234             var val = fly(el).getStyle(attr);
3235
3236             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3237                 return parseFloat(val);
3238             }
3239
3240             var a = this.patterns.offsetAttribute.exec(attr) || [];
3241             var pos = !!( a[3] );
3242             var box = !!( a[2] );
3243
3244
3245             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3246                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3247             } else {
3248                 val = 0;
3249             }
3250
3251             return val;
3252         },
3253
3254
3255         getDefaultUnit: function(attr) {
3256             if (this.patterns.defaultUnit.test(attr)) {
3257                 return 'px';
3258             }
3259
3260             return '';
3261         },
3262
3263         animateX : function(callback, scope) {
3264             var f = function() {
3265                 this.onComplete.removeListener(f);
3266                 if (typeof callback == "function") {
3267                     callback.call(scope || this, this);
3268                 }
3269             };
3270             this.onComplete.addListener(f, this);
3271             this.animate();
3272         },
3273
3274
3275         setRuntimeAttribute: function(attr) {
3276             var start;
3277             var end;
3278             var attributes = this.attributes;
3279
3280             this.runtimeAttributes[attr] = {};
3281
3282             var isset = function(prop) {
3283                 return (typeof prop !== 'undefined');
3284             };
3285
3286             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3287                 return false;
3288             }
3289
3290             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3291
3292
3293             if (isset(attributes[attr]['to'])) {
3294                 end = attributes[attr]['to'];
3295             } else if (isset(attributes[attr]['by'])) {
3296                 if (start.constructor == Array) {
3297                     end = [];
3298                     for (var i = 0, len = start.length; i < len; ++i) {
3299                         end[i] = start[i] + attributes[attr]['by'][i];
3300                     }
3301                 } else {
3302                     end = start + attributes[attr]['by'];
3303                 }
3304             }
3305
3306             this.runtimeAttributes[attr].start = start;
3307             this.runtimeAttributes[attr].end = end;
3308
3309
3310             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3311         },
3312
3313
3314         init: function(el, attributes, duration, method) {
3315
3316             var isAnimated = false;
3317
3318
3319             var startTime = null;
3320
3321
3322             var actualFrames = 0;
3323
3324
3325             el = Roo.getDom(el);
3326
3327
3328             this.attributes = attributes || {};
3329
3330
3331             this.duration = duration || 1;
3332
3333
3334             this.method = method || Roo.lib.Easing.easeNone;
3335
3336
3337             this.useSeconds = true;
3338
3339
3340             this.currentFrame = 0;
3341
3342
3343             this.totalFrames = Roo.lib.AnimMgr.fps;
3344
3345
3346             this.getEl = function() {
3347                 return el;
3348             };
3349
3350
3351             this.isAnimated = function() {
3352                 return isAnimated;
3353             };
3354
3355
3356             this.getStartTime = function() {
3357                 return startTime;
3358             };
3359
3360             this.runtimeAttributes = {};
3361
3362
3363             this.animate = function() {
3364                 if (this.isAnimated()) {
3365                     return false;
3366                 }
3367
3368                 this.currentFrame = 0;
3369
3370                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3371
3372                 Roo.lib.AnimMgr.registerElement(this);
3373             };
3374
3375
3376             this.stop = function(finish) {
3377                 if (finish) {
3378                     this.currentFrame = this.totalFrames;
3379                     this._onTween.fire();
3380                 }
3381                 Roo.lib.AnimMgr.stop(this);
3382             };
3383
3384             var onStart = function() {
3385                 this.onStart.fire();
3386
3387                 this.runtimeAttributes = {};
3388                 for (var attr in this.attributes) {
3389                     this.setRuntimeAttribute(attr);
3390                 }
3391
3392                 isAnimated = true;
3393                 actualFrames = 0;
3394                 startTime = new Date();
3395             };
3396
3397
3398             var onTween = function() {
3399                 var data = {
3400                     duration: new Date() - this.getStartTime(),
3401                     currentFrame: this.currentFrame
3402                 };
3403
3404                 data.toString = function() {
3405                     return (
3406                             'duration: ' + data.duration +
3407                             ', currentFrame: ' + data.currentFrame
3408                             );
3409                 };
3410
3411                 this.onTween.fire(data);
3412
3413                 var runtimeAttributes = this.runtimeAttributes;
3414
3415                 for (var attr in runtimeAttributes) {
3416                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3417                 }
3418
3419                 actualFrames += 1;
3420             };
3421
3422             var onComplete = function() {
3423                 var actual_duration = (new Date() - startTime) / 1000 ;
3424
3425                 var data = {
3426                     duration: actual_duration,
3427                     frames: actualFrames,
3428                     fps: actualFrames / actual_duration
3429                 };
3430
3431                 data.toString = function() {
3432                     return (
3433                             'duration: ' + data.duration +
3434                             ', frames: ' + data.frames +
3435                             ', fps: ' + data.fps
3436                             );
3437                 };
3438
3439                 isAnimated = false;
3440                 actualFrames = 0;
3441                 this.onComplete.fire(data);
3442             };
3443
3444
3445             this._onStart = new Roo.util.Event(this);
3446             this.onStart = new Roo.util.Event(this);
3447             this.onTween = new Roo.util.Event(this);
3448             this._onTween = new Roo.util.Event(this);
3449             this.onComplete = new Roo.util.Event(this);
3450             this._onComplete = new Roo.util.Event(this);
3451             this._onStart.addListener(onStart);
3452             this._onTween.addListener(onTween);
3453             this._onComplete.addListener(onComplete);
3454         }
3455     };
3456 })();
3457 /*
3458  * Portions of this file are based on pieces of Yahoo User Interface Library
3459  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3460  * YUI licensed under the BSD License:
3461  * http://developer.yahoo.net/yui/license.txt
3462  * <script type="text/javascript">
3463  *
3464  */
3465
3466 Roo.lib.AnimMgr = new function() {
3467
3468     var thread = null;
3469
3470
3471     var queue = [];
3472
3473
3474     var tweenCount = 0;
3475
3476
3477     this.fps = 1000;
3478
3479
3480     this.delay = 1;
3481
3482
3483     this.registerElement = function(tween) {
3484         queue[queue.length] = tween;
3485         tweenCount += 1;
3486         tween._onStart.fire();
3487         this.start();
3488     };
3489
3490
3491     this.unRegister = function(tween, index) {
3492         tween._onComplete.fire();
3493         index = index || getIndex(tween);
3494         if (index != -1) {
3495             queue.splice(index, 1);
3496         }
3497
3498         tweenCount -= 1;
3499         if (tweenCount <= 0) {
3500             this.stop();
3501         }
3502     };
3503
3504
3505     this.start = function() {
3506         if (thread === null) {
3507             thread = setInterval(this.run, this.delay);
3508         }
3509     };
3510
3511
3512     this.stop = function(tween) {
3513         if (!tween) {
3514             clearInterval(thread);
3515
3516             for (var i = 0, len = queue.length; i < len; ++i) {
3517                 if (queue[0].isAnimated()) {
3518                     this.unRegister(queue[0], 0);
3519                 }
3520             }
3521
3522             queue = [];
3523             thread = null;
3524             tweenCount = 0;
3525         }
3526         else {
3527             this.unRegister(tween);
3528         }
3529     };
3530
3531
3532     this.run = function() {
3533         for (var i = 0, len = queue.length; i < len; ++i) {
3534             var tween = queue[i];
3535             if (!tween || !tween.isAnimated()) {
3536                 continue;
3537             }
3538
3539             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3540             {
3541                 tween.currentFrame += 1;
3542
3543                 if (tween.useSeconds) {
3544                     correctFrame(tween);
3545                 }
3546                 tween._onTween.fire();
3547             }
3548             else {
3549                 Roo.lib.AnimMgr.stop(tween, i);
3550             }
3551         }
3552     };
3553
3554     var getIndex = function(anim) {
3555         for (var i = 0, len = queue.length; i < len; ++i) {
3556             if (queue[i] == anim) {
3557                 return i;
3558             }
3559         }
3560         return -1;
3561     };
3562
3563
3564     var correctFrame = function(tween) {
3565         var frames = tween.totalFrames;
3566         var frame = tween.currentFrame;
3567         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3568         var elapsed = (new Date() - tween.getStartTime());
3569         var tweak = 0;
3570
3571         if (elapsed < tween.duration * 1000) {
3572             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3573         } else {
3574             tweak = frames - (frame + 1);
3575         }
3576         if (tweak > 0 && isFinite(tweak)) {
3577             if (tween.currentFrame + tweak >= frames) {
3578                 tweak = frames - (frame + 1);
3579             }
3580
3581             tween.currentFrame += tweak;
3582         }
3583     };
3584 };
3585
3586     /*
3587  * Portions of this file are based on pieces of Yahoo User Interface Library
3588  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3589  * YUI licensed under the BSD License:
3590  * http://developer.yahoo.net/yui/license.txt
3591  * <script type="text/javascript">
3592  *
3593  */
3594 Roo.lib.Bezier = new function() {
3595
3596         this.getPosition = function(points, t) {
3597             var n = points.length;
3598             var tmp = [];
3599
3600             for (var i = 0; i < n; ++i) {
3601                 tmp[i] = [points[i][0], points[i][1]];
3602             }
3603
3604             for (var j = 1; j < n; ++j) {
3605                 for (i = 0; i < n - j; ++i) {
3606                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3607                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3608                 }
3609             }
3610
3611             return [ tmp[0][0], tmp[0][1] ];
3612
3613         };
3614     };/*
3615  * Portions of this file are based on pieces of Yahoo User Interface Library
3616  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3617  * YUI licensed under the BSD License:
3618  * http://developer.yahoo.net/yui/license.txt
3619  * <script type="text/javascript">
3620  *
3621  */
3622 (function() {
3623
3624     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3625         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3626     };
3627
3628     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3629
3630     var fly = Roo.lib.AnimBase.fly;
3631     var Y = Roo.lib;
3632     var superclass = Y.ColorAnim.superclass;
3633     var proto = Y.ColorAnim.prototype;
3634
3635     proto.toString = function() {
3636         var el = this.getEl();
3637         var id = el.id || el.tagName;
3638         return ("ColorAnim " + id);
3639     };
3640
3641     proto.patterns.color = /color$/i;
3642     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3643     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3644     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3645     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3646
3647
3648     proto.parseColor = function(s) {
3649         if (s.length == 3) {
3650             return s;
3651         }
3652
3653         var c = this.patterns.hex.exec(s);
3654         if (c && c.length == 4) {
3655             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3656         }
3657
3658         c = this.patterns.rgb.exec(s);
3659         if (c && c.length == 4) {
3660             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3661         }
3662
3663         c = this.patterns.hex3.exec(s);
3664         if (c && c.length == 4) {
3665             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3666         }
3667
3668         return null;
3669     };
3670     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3671     proto.getAttribute = function(attr) {
3672         var el = this.getEl();
3673         if (this.patterns.color.test(attr)) {
3674             var val = fly(el).getStyle(attr);
3675
3676             if (this.patterns.transparent.test(val)) {
3677                 var parent = el.parentNode;
3678                 val = fly(parent).getStyle(attr);
3679
3680                 while (parent && this.patterns.transparent.test(val)) {
3681                     parent = parent.parentNode;
3682                     val = fly(parent).getStyle(attr);
3683                     if (parent.tagName.toUpperCase() == 'HTML') {
3684                         val = '#fff';
3685                     }
3686                 }
3687             }
3688         } else {
3689             val = superclass.getAttribute.call(this, attr);
3690         }
3691
3692         return val;
3693     };
3694     proto.getAttribute = function(attr) {
3695         var el = this.getEl();
3696         if (this.patterns.color.test(attr)) {
3697             var val = fly(el).getStyle(attr);
3698
3699             if (this.patterns.transparent.test(val)) {
3700                 var parent = el.parentNode;
3701                 val = fly(parent).getStyle(attr);
3702
3703                 while (parent && this.patterns.transparent.test(val)) {
3704                     parent = parent.parentNode;
3705                     val = fly(parent).getStyle(attr);
3706                     if (parent.tagName.toUpperCase() == 'HTML') {
3707                         val = '#fff';
3708                     }
3709                 }
3710             }
3711         } else {
3712             val = superclass.getAttribute.call(this, attr);
3713         }
3714
3715         return val;
3716     };
3717
3718     proto.doMethod = function(attr, start, end) {
3719         var val;
3720
3721         if (this.patterns.color.test(attr)) {
3722             val = [];
3723             for (var i = 0, len = start.length; i < len; ++i) {
3724                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3725             }
3726
3727             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3728         }
3729         else {
3730             val = superclass.doMethod.call(this, attr, start, end);
3731         }
3732
3733         return val;
3734     };
3735
3736     proto.setRuntimeAttribute = function(attr) {
3737         superclass.setRuntimeAttribute.call(this, attr);
3738
3739         if (this.patterns.color.test(attr)) {
3740             var attributes = this.attributes;
3741             var start = this.parseColor(this.runtimeAttributes[attr].start);
3742             var end = this.parseColor(this.runtimeAttributes[attr].end);
3743
3744             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3745                 end = this.parseColor(attributes[attr].by);
3746
3747                 for (var i = 0, len = start.length; i < len; ++i) {
3748                     end[i] = start[i] + end[i];
3749                 }
3750             }
3751
3752             this.runtimeAttributes[attr].start = start;
3753             this.runtimeAttributes[attr].end = end;
3754         }
3755     };
3756 })();
3757
3758 /*
3759  * Portions of this file are based on pieces of Yahoo User Interface Library
3760  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3761  * YUI licensed under the BSD License:
3762  * http://developer.yahoo.net/yui/license.txt
3763  * <script type="text/javascript">
3764  *
3765  */
3766 Roo.lib.Easing = {
3767
3768
3769     easeNone: function (t, b, c, d) {
3770         return c * t / d + b;
3771     },
3772
3773
3774     easeIn: function (t, b, c, d) {
3775         return c * (t /= d) * t + b;
3776     },
3777
3778
3779     easeOut: function (t, b, c, d) {
3780         return -c * (t /= d) * (t - 2) + b;
3781     },
3782
3783
3784     easeBoth: function (t, b, c, d) {
3785         if ((t /= d / 2) < 1) {
3786             return c / 2 * t * t + b;
3787         }
3788
3789         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3790     },
3791
3792
3793     easeInStrong: function (t, b, c, d) {
3794         return c * (t /= d) * t * t * t + b;
3795     },
3796
3797
3798     easeOutStrong: function (t, b, c, d) {
3799         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3800     },
3801
3802
3803     easeBothStrong: function (t, b, c, d) {
3804         if ((t /= d / 2) < 1) {
3805             return c / 2 * t * t * t * t + b;
3806         }
3807
3808         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3809     },
3810
3811
3812
3813     elasticIn: function (t, b, c, d, a, p) {
3814         if (t == 0) {
3815             return b;
3816         }
3817         if ((t /= d) == 1) {
3818             return b + c;
3819         }
3820         if (!p) {
3821             p = d * .3;
3822         }
3823
3824         if (!a || a < Math.abs(c)) {
3825             a = c;
3826             var s = p / 4;
3827         }
3828         else {
3829             var s = p / (2 * Math.PI) * Math.asin(c / a);
3830         }
3831
3832         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3833     },
3834
3835
3836     elasticOut: function (t, b, c, d, a, p) {
3837         if (t == 0) {
3838             return b;
3839         }
3840         if ((t /= d) == 1) {
3841             return b + c;
3842         }
3843         if (!p) {
3844             p = d * .3;
3845         }
3846
3847         if (!a || a < Math.abs(c)) {
3848             a = c;
3849             var s = p / 4;
3850         }
3851         else {
3852             var s = p / (2 * Math.PI) * Math.asin(c / a);
3853         }
3854
3855         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3856     },
3857
3858
3859     elasticBoth: function (t, b, c, d, a, p) {
3860         if (t == 0) {
3861             return b;
3862         }
3863
3864         if ((t /= d / 2) == 2) {
3865             return b + c;
3866         }
3867
3868         if (!p) {
3869             p = d * (.3 * 1.5);
3870         }
3871
3872         if (!a || a < Math.abs(c)) {
3873             a = c;
3874             var s = p / 4;
3875         }
3876         else {
3877             var s = p / (2 * Math.PI) * Math.asin(c / a);
3878         }
3879
3880         if (t < 1) {
3881             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3882                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3883         }
3884         return a * Math.pow(2, -10 * (t -= 1)) *
3885                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3886     },
3887
3888
3889
3890     backIn: function (t, b, c, d, s) {
3891         if (typeof s == 'undefined') {
3892             s = 1.70158;
3893         }
3894         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3895     },
3896
3897
3898     backOut: function (t, b, c, d, s) {
3899         if (typeof s == 'undefined') {
3900             s = 1.70158;
3901         }
3902         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3903     },
3904
3905
3906     backBoth: function (t, b, c, d, s) {
3907         if (typeof s == 'undefined') {
3908             s = 1.70158;
3909         }
3910
3911         if ((t /= d / 2 ) < 1) {
3912             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3913         }
3914         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3915     },
3916
3917
3918     bounceIn: function (t, b, c, d) {
3919         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3920     },
3921
3922
3923     bounceOut: function (t, b, c, d) {
3924         if ((t /= d) < (1 / 2.75)) {
3925             return c * (7.5625 * t * t) + b;
3926         } else if (t < (2 / 2.75)) {
3927             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3928         } else if (t < (2.5 / 2.75)) {
3929             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3930         }
3931         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3932     },
3933
3934
3935     bounceBoth: function (t, b, c, d) {
3936         if (t < d / 2) {
3937             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3938         }
3939         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3940     }
3941 };/*
3942  * Portions of this file are based on pieces of Yahoo User Interface Library
3943  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3944  * YUI licensed under the BSD License:
3945  * http://developer.yahoo.net/yui/license.txt
3946  * <script type="text/javascript">
3947  *
3948  */
3949     (function() {
3950         Roo.lib.Motion = function(el, attributes, duration, method) {
3951             if (el) {
3952                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3953             }
3954         };
3955
3956         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3957
3958
3959         var Y = Roo.lib;
3960         var superclass = Y.Motion.superclass;
3961         var proto = Y.Motion.prototype;
3962
3963         proto.toString = function() {
3964             var el = this.getEl();
3965             var id = el.id || el.tagName;
3966             return ("Motion " + id);
3967         };
3968
3969         proto.patterns.points = /^points$/i;
3970
3971         proto.setAttribute = function(attr, val, unit) {
3972             if (this.patterns.points.test(attr)) {
3973                 unit = unit || 'px';
3974                 superclass.setAttribute.call(this, 'left', val[0], unit);
3975                 superclass.setAttribute.call(this, 'top', val[1], unit);
3976             } else {
3977                 superclass.setAttribute.call(this, attr, val, unit);
3978             }
3979         };
3980
3981         proto.getAttribute = function(attr) {
3982             if (this.patterns.points.test(attr)) {
3983                 var val = [
3984                         superclass.getAttribute.call(this, 'left'),
3985                         superclass.getAttribute.call(this, 'top')
3986                         ];
3987             } else {
3988                 val = superclass.getAttribute.call(this, attr);
3989             }
3990
3991             return val;
3992         };
3993
3994         proto.doMethod = function(attr, start, end) {
3995             var val = null;
3996
3997             if (this.patterns.points.test(attr)) {
3998                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3999                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4000             } else {
4001                 val = superclass.doMethod.call(this, attr, start, end);
4002             }
4003             return val;
4004         };
4005
4006         proto.setRuntimeAttribute = function(attr) {
4007             if (this.patterns.points.test(attr)) {
4008                 var el = this.getEl();
4009                 var attributes = this.attributes;
4010                 var start;
4011                 var control = attributes['points']['control'] || [];
4012                 var end;
4013                 var i, len;
4014
4015                 if (control.length > 0 && !(control[0] instanceof Array)) {
4016                     control = [control];
4017                 } else {
4018                     var tmp = [];
4019                     for (i = 0,len = control.length; i < len; ++i) {
4020                         tmp[i] = control[i];
4021                     }
4022                     control = tmp;
4023                 }
4024
4025                 Roo.fly(el).position();
4026
4027                 if (isset(attributes['points']['from'])) {
4028                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4029                 }
4030                 else {
4031                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4032                 }
4033
4034                 start = this.getAttribute('points');
4035
4036
4037                 if (isset(attributes['points']['to'])) {
4038                     end = translateValues.call(this, attributes['points']['to'], start);
4039
4040                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4041                     for (i = 0,len = control.length; i < len; ++i) {
4042                         control[i] = translateValues.call(this, control[i], start);
4043                     }
4044
4045
4046                 } else if (isset(attributes['points']['by'])) {
4047                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4048
4049                     for (i = 0,len = control.length; i < len; ++i) {
4050                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4051                     }
4052                 }
4053
4054                 this.runtimeAttributes[attr] = [start];
4055
4056                 if (control.length > 0) {
4057                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4058                 }
4059
4060                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4061             }
4062             else {
4063                 superclass.setRuntimeAttribute.call(this, attr);
4064             }
4065         };
4066
4067         var translateValues = function(val, start) {
4068             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4069             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4070
4071             return val;
4072         };
4073
4074         var isset = function(prop) {
4075             return (typeof prop !== 'undefined');
4076         };
4077     })();
4078 /*
4079  * Portions of this file are based on pieces of Yahoo User Interface Library
4080  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4081  * YUI licensed under the BSD License:
4082  * http://developer.yahoo.net/yui/license.txt
4083  * <script type="text/javascript">
4084  *
4085  */
4086     (function() {
4087         Roo.lib.Scroll = function(el, attributes, duration, method) {
4088             if (el) {
4089                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4090             }
4091         };
4092
4093         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4094
4095
4096         var Y = Roo.lib;
4097         var superclass = Y.Scroll.superclass;
4098         var proto = Y.Scroll.prototype;
4099
4100         proto.toString = function() {
4101             var el = this.getEl();
4102             var id = el.id || el.tagName;
4103             return ("Scroll " + id);
4104         };
4105
4106         proto.doMethod = function(attr, start, end) {
4107             var val = null;
4108
4109             if (attr == 'scroll') {
4110                 val = [
4111                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4112                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4113                         ];
4114
4115             } else {
4116                 val = superclass.doMethod.call(this, attr, start, end);
4117             }
4118             return val;
4119         };
4120
4121         proto.getAttribute = function(attr) {
4122             var val = null;
4123             var el = this.getEl();
4124
4125             if (attr == 'scroll') {
4126                 val = [ el.scrollLeft, el.scrollTop ];
4127             } else {
4128                 val = superclass.getAttribute.call(this, attr);
4129             }
4130
4131             return val;
4132         };
4133
4134         proto.setAttribute = function(attr, val, unit) {
4135             var el = this.getEl();
4136
4137             if (attr == 'scroll') {
4138                 el.scrollLeft = val[0];
4139                 el.scrollTop = val[1];
4140             } else {
4141                 superclass.setAttribute.call(this, attr, val, unit);
4142             }
4143         };
4144     })();
4145 /*
4146  * Based on:
4147  * Ext JS Library 1.1.1
4148  * Copyright(c) 2006-2007, Ext JS, LLC.
4149  *
4150  * Originally Released Under LGPL - original licence link has changed is not relivant.
4151  *
4152  * Fork - LGPL
4153  * <script type="text/javascript">
4154  */
4155
4156
4157 // nasty IE9 hack - what a pile of crap that is..
4158
4159  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4160     Range.prototype.createContextualFragment = function (html) {
4161         var doc = window.document;
4162         var container = doc.createElement("div");
4163         container.innerHTML = html;
4164         var frag = doc.createDocumentFragment(), n;
4165         while ((n = container.firstChild)) {
4166             frag.appendChild(n);
4167         }
4168         return frag;
4169     };
4170 }
4171
4172 /**
4173  * @class Roo.DomHelper
4174  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4175  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4176  * @singleton
4177  */
4178 Roo.DomHelper = function(){
4179     var tempTableEl = null;
4180     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4181     var tableRe = /^table|tbody|tr|td$/i;
4182     var xmlns = {};
4183     // build as innerHTML where available
4184     /** @ignore */
4185     var createHtml = function(o){
4186         if(typeof o == 'string'){
4187             return o;
4188         }
4189         var b = "";
4190         if(!o.tag){
4191             o.tag = "div";
4192         }
4193         b += "<" + o.tag;
4194         for(var attr in o){
4195             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4196             if(attr == "style"){
4197                 var s = o["style"];
4198                 if(typeof s == "function"){
4199                     s = s.call();
4200                 }
4201                 if(typeof s == "string"){
4202                     b += ' style="' + s + '"';
4203                 }else if(typeof s == "object"){
4204                     b += ' style="';
4205                     for(var key in s){
4206                         if(typeof s[key] != "function"){
4207                             b += key + ":" + s[key] + ";";
4208                         }
4209                     }
4210                     b += '"';
4211                 }
4212             }else{
4213                 if(attr == "cls"){
4214                     b += ' class="' + o["cls"] + '"';
4215                 }else if(attr == "htmlFor"){
4216                     b += ' for="' + o["htmlFor"] + '"';
4217                 }else{
4218                     b += " " + attr + '="' + o[attr] + '"';
4219                 }
4220             }
4221         }
4222         if(emptyTags.test(o.tag)){
4223             b += "/>";
4224         }else{
4225             b += ">";
4226             var cn = o.children || o.cn;
4227             if(cn){
4228                 //http://bugs.kde.org/show_bug.cgi?id=71506
4229                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4230                     for(var i = 0, len = cn.length; i < len; i++) {
4231                         b += createHtml(cn[i], b);
4232                     }
4233                 }else{
4234                     b += createHtml(cn, b);
4235                 }
4236             }
4237             if(o.html){
4238                 b += o.html;
4239             }
4240             b += "</" + o.tag + ">";
4241         }
4242         return b;
4243     };
4244
4245     // build as dom
4246     /** @ignore */
4247     var createDom = function(o, parentNode){
4248          
4249         // defininition craeted..
4250         var ns = false;
4251         if (o.ns && o.ns != 'html') {
4252                
4253             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4254                 xmlns[o.ns] = o.xmlns;
4255                 ns = o.xmlns;
4256             }
4257             if (typeof(xmlns[o.ns]) == 'undefined') {
4258                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4259             }
4260             ns = xmlns[o.ns];
4261         }
4262         
4263         
4264         if (typeof(o) == 'string') {
4265             return parentNode.appendChild(document.createTextNode(o));
4266         }
4267         o.tag = o.tag || div;
4268         if (o.ns && Roo.isIE) {
4269             ns = false;
4270             o.tag = o.ns + ':' + o.tag;
4271             
4272         }
4273         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4274         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4275         for(var attr in o){
4276             
4277             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4278                     attr == "style" || typeof o[attr] == "function") { continue; }
4279                     
4280             if(attr=="cls" && Roo.isIE){
4281                 el.className = o["cls"];
4282             }else{
4283                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4284                 else { 
4285                     el[attr] = o[attr];
4286                 }
4287             }
4288         }
4289         Roo.DomHelper.applyStyles(el, o.style);
4290         var cn = o.children || o.cn;
4291         if(cn){
4292             //http://bugs.kde.org/show_bug.cgi?id=71506
4293              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4294                 for(var i = 0, len = cn.length; i < len; i++) {
4295                     createDom(cn[i], el);
4296                 }
4297             }else{
4298                 createDom(cn, el);
4299             }
4300         }
4301         if(o.html){
4302             el.innerHTML = o.html;
4303         }
4304         if(parentNode){
4305            parentNode.appendChild(el);
4306         }
4307         return el;
4308     };
4309
4310     var ieTable = function(depth, s, h, e){
4311         tempTableEl.innerHTML = [s, h, e].join('');
4312         var i = -1, el = tempTableEl;
4313         while(++i < depth){
4314             el = el.firstChild;
4315         }
4316         return el;
4317     };
4318
4319     // kill repeat to save bytes
4320     var ts = '<table>',
4321         te = '</table>',
4322         tbs = ts+'<tbody>',
4323         tbe = '</tbody>'+te,
4324         trs = tbs + '<tr>',
4325         tre = '</tr>'+tbe;
4326
4327     /**
4328      * @ignore
4329      * Nasty code for IE's broken table implementation
4330      */
4331     var insertIntoTable = function(tag, where, el, html){
4332         if(!tempTableEl){
4333             tempTableEl = document.createElement('div');
4334         }
4335         var node;
4336         var before = null;
4337         if(tag == 'td'){
4338             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4339                 return;
4340             }
4341             if(where == 'beforebegin'){
4342                 before = el;
4343                 el = el.parentNode;
4344             } else{
4345                 before = el.nextSibling;
4346                 el = el.parentNode;
4347             }
4348             node = ieTable(4, trs, html, tre);
4349         }
4350         else if(tag == 'tr'){
4351             if(where == 'beforebegin'){
4352                 before = el;
4353                 el = el.parentNode;
4354                 node = ieTable(3, tbs, html, tbe);
4355             } else if(where == 'afterend'){
4356                 before = el.nextSibling;
4357                 el = el.parentNode;
4358                 node = ieTable(3, tbs, html, tbe);
4359             } else{ // INTO a TR
4360                 if(where == 'afterbegin'){
4361                     before = el.firstChild;
4362                 }
4363                 node = ieTable(4, trs, html, tre);
4364             }
4365         } else if(tag == 'tbody'){
4366             if(where == 'beforebegin'){
4367                 before = el;
4368                 el = el.parentNode;
4369                 node = ieTable(2, ts, html, te);
4370             } else if(where == 'afterend'){
4371                 before = el.nextSibling;
4372                 el = el.parentNode;
4373                 node = ieTable(2, ts, html, te);
4374             } else{
4375                 if(where == 'afterbegin'){
4376                     before = el.firstChild;
4377                 }
4378                 node = ieTable(3, tbs, html, tbe);
4379             }
4380         } else{ // TABLE
4381             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4382                 return;
4383             }
4384             if(where == 'afterbegin'){
4385                 before = el.firstChild;
4386             }
4387             node = ieTable(2, ts, html, te);
4388         }
4389         el.insertBefore(node, before);
4390         return node;
4391     };
4392
4393     return {
4394     /** True to force the use of DOM instead of html fragments @type Boolean */
4395     useDom : false,
4396
4397     /**
4398      * Returns the markup for the passed Element(s) config
4399      * @param {Object} o The Dom object spec (and children)
4400      * @return {String}
4401      */
4402     markup : function(o){
4403         return createHtml(o);
4404     },
4405
4406     /**
4407      * Applies a style specification to an element
4408      * @param {String/HTMLElement} el The element to apply styles to
4409      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4410      * a function which returns such a specification.
4411      */
4412     applyStyles : function(el, styles){
4413         if(styles){
4414            el = Roo.fly(el);
4415            if(typeof styles == "string"){
4416                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4417                var matches;
4418                while ((matches = re.exec(styles)) != null){
4419                    el.setStyle(matches[1], matches[2]);
4420                }
4421            }else if (typeof styles == "object"){
4422                for (var style in styles){
4423                   el.setStyle(style, styles[style]);
4424                }
4425            }else if (typeof styles == "function"){
4426                 Roo.DomHelper.applyStyles(el, styles.call());
4427            }
4428         }
4429     },
4430
4431     /**
4432      * Inserts an HTML fragment into the Dom
4433      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4434      * @param {HTMLElement} el The context element
4435      * @param {String} html The HTML fragmenet
4436      * @return {HTMLElement} The new node
4437      */
4438     insertHtml : function(where, el, html){
4439         where = where.toLowerCase();
4440         if(el.insertAdjacentHTML){
4441             if(tableRe.test(el.tagName)){
4442                 var rs;
4443                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4444                     return rs;
4445                 }
4446             }
4447             switch(where){
4448                 case "beforebegin":
4449                     el.insertAdjacentHTML('BeforeBegin', html);
4450                     return el.previousSibling;
4451                 case "afterbegin":
4452                     el.insertAdjacentHTML('AfterBegin', html);
4453                     return el.firstChild;
4454                 case "beforeend":
4455                     el.insertAdjacentHTML('BeforeEnd', html);
4456                     return el.lastChild;
4457                 case "afterend":
4458                     el.insertAdjacentHTML('AfterEnd', html);
4459                     return el.nextSibling;
4460             }
4461             throw 'Illegal insertion point -> "' + where + '"';
4462         }
4463         var range = el.ownerDocument.createRange();
4464         var frag;
4465         switch(where){
4466              case "beforebegin":
4467                 range.setStartBefore(el);
4468                 frag = range.createContextualFragment(html);
4469                 el.parentNode.insertBefore(frag, el);
4470                 return el.previousSibling;
4471              case "afterbegin":
4472                 if(el.firstChild){
4473                     range.setStartBefore(el.firstChild);
4474                     frag = range.createContextualFragment(html);
4475                     el.insertBefore(frag, el.firstChild);
4476                     return el.firstChild;
4477                 }else{
4478                     el.innerHTML = html;
4479                     return el.firstChild;
4480                 }
4481             case "beforeend":
4482                 if(el.lastChild){
4483                     range.setStartAfter(el.lastChild);
4484                     frag = range.createContextualFragment(html);
4485                     el.appendChild(frag);
4486                     return el.lastChild;
4487                 }else{
4488                     el.innerHTML = html;
4489                     return el.lastChild;
4490                 }
4491             case "afterend":
4492                 range.setStartAfter(el);
4493                 frag = range.createContextualFragment(html);
4494                 el.parentNode.insertBefore(frag, el.nextSibling);
4495                 return el.nextSibling;
4496             }
4497             throw 'Illegal insertion point -> "' + where + '"';
4498     },
4499
4500     /**
4501      * Creates new Dom element(s) and inserts them before el
4502      * @param {String/HTMLElement/Element} el The context element
4503      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4504      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4505      * @return {HTMLElement/Roo.Element} The new node
4506      */
4507     insertBefore : function(el, o, returnElement){
4508         return this.doInsert(el, o, returnElement, "beforeBegin");
4509     },
4510
4511     /**
4512      * Creates new Dom element(s) and inserts them after el
4513      * @param {String/HTMLElement/Element} el The context element
4514      * @param {Object} o The Dom object spec (and children)
4515      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4516      * @return {HTMLElement/Roo.Element} The new node
4517      */
4518     insertAfter : function(el, o, returnElement){
4519         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4520     },
4521
4522     /**
4523      * Creates new Dom element(s) and inserts them as the first child of el
4524      * @param {String/HTMLElement/Element} el The context element
4525      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4526      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4527      * @return {HTMLElement/Roo.Element} The new node
4528      */
4529     insertFirst : function(el, o, returnElement){
4530         return this.doInsert(el, o, returnElement, "afterBegin");
4531     },
4532
4533     // private
4534     doInsert : function(el, o, returnElement, pos, sibling){
4535         el = Roo.getDom(el);
4536         var newNode;
4537         if(this.useDom || o.ns){
4538             newNode = createDom(o, null);
4539             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4540         }else{
4541             var html = createHtml(o);
4542             newNode = this.insertHtml(pos, el, html);
4543         }
4544         return returnElement ? Roo.get(newNode, true) : newNode;
4545     },
4546
4547     /**
4548      * Creates new Dom element(s) and appends them to el
4549      * @param {String/HTMLElement/Element} el The context element
4550      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4551      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4552      * @return {HTMLElement/Roo.Element} The new node
4553      */
4554     append : function(el, o, returnElement){
4555         el = Roo.getDom(el);
4556         var newNode;
4557         if(this.useDom || o.ns){
4558             newNode = createDom(o, null);
4559             el.appendChild(newNode);
4560         }else{
4561             var html = createHtml(o);
4562             newNode = this.insertHtml("beforeEnd", el, html);
4563         }
4564         return returnElement ? Roo.get(newNode, true) : newNode;
4565     },
4566
4567     /**
4568      * Creates new Dom element(s) and overwrites the contents of el with them
4569      * @param {String/HTMLElement/Element} el The context element
4570      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4571      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4572      * @return {HTMLElement/Roo.Element} The new node
4573      */
4574     overwrite : function(el, o, returnElement){
4575         el = Roo.getDom(el);
4576         if (o.ns) {
4577           
4578             while (el.childNodes.length) {
4579                 el.removeChild(el.firstChild);
4580             }
4581             createDom(o, el);
4582         } else {
4583             el.innerHTML = createHtml(o);   
4584         }
4585         
4586         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4587     },
4588
4589     /**
4590      * Creates a new Roo.DomHelper.Template from the Dom object spec
4591      * @param {Object} o The Dom object spec (and children)
4592      * @return {Roo.DomHelper.Template} The new template
4593      */
4594     createTemplate : function(o){
4595         var html = createHtml(o);
4596         return new Roo.Template(html);
4597     }
4598     };
4599 }();
4600 /*
4601  * Based on:
4602  * Ext JS Library 1.1.1
4603  * Copyright(c) 2006-2007, Ext JS, LLC.
4604  *
4605  * Originally Released Under LGPL - original licence link has changed is not relivant.
4606  *
4607  * Fork - LGPL
4608  * <script type="text/javascript">
4609  */
4610  
4611 /**
4612 * @class Roo.Template
4613 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4614 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4615 * Usage:
4616 <pre><code>
4617 var t = new Roo.Template({
4618     html :  '&lt;div name="{id}"&gt;' + 
4619         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4620         '&lt;/div&gt;',
4621     myformat: function (value, allValues) {
4622         return 'XX' + value;
4623     }
4624 });
4625 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4626 </code></pre>
4627 * For more information see this blog post with examples:
4628 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4629      - Create Elements using DOM, HTML fragments and Templates</a>. 
4630 * @constructor
4631 * @param {Object} cfg - Configuration object.
4632 */
4633 Roo.Template = function(cfg){
4634     // BC!
4635     if(cfg instanceof Array){
4636         cfg = cfg.join("");
4637     }else if(arguments.length > 1){
4638         cfg = Array.prototype.join.call(arguments, "");
4639     }
4640     
4641     
4642     if (typeof(cfg) == 'object') {
4643         Roo.apply(this,cfg)
4644     } else {
4645         // bc
4646         this.html = cfg;
4647     }
4648     if (this.url) {
4649         this.load();
4650     }
4651     
4652 };
4653 Roo.Template.prototype = {
4654     
4655     /**
4656      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4657      *                    it should be fixed so that template is observable...
4658      */
4659     url : false,
4660     /**
4661      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4662      */
4663     html : '',
4664     /**
4665      * Returns an HTML fragment of this template with the specified values applied.
4666      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4667      * @return {String} The HTML fragment
4668      */
4669     applyTemplate : function(values){
4670         try {
4671            
4672             if(this.compiled){
4673                 return this.compiled(values);
4674             }
4675             var useF = this.disableFormats !== true;
4676             var fm = Roo.util.Format, tpl = this;
4677             var fn = function(m, name, format, args){
4678                 if(format && useF){
4679                     if(format.substr(0, 5) == "this."){
4680                         return tpl.call(format.substr(5), values[name], values);
4681                     }else{
4682                         if(args){
4683                             // quoted values are required for strings in compiled templates, 
4684                             // but for non compiled we need to strip them
4685                             // quoted reversed for jsmin
4686                             var re = /^\s*['"](.*)["']\s*$/;
4687                             args = args.split(',');
4688                             for(var i = 0, len = args.length; i < len; i++){
4689                                 args[i] = args[i].replace(re, "$1");
4690                             }
4691                             args = [values[name]].concat(args);
4692                         }else{
4693                             args = [values[name]];
4694                         }
4695                         return fm[format].apply(fm, args);
4696                     }
4697                 }else{
4698                     return values[name] !== undefined ? values[name] : "";
4699                 }
4700             };
4701             return this.html.replace(this.re, fn);
4702         } catch (e) {
4703             Roo.log(e);
4704             throw e;
4705         }
4706          
4707     },
4708     
4709     loading : false,
4710       
4711     load : function ()
4712     {
4713          
4714         if (this.loading) {
4715             return;
4716         }
4717         var _t = this;
4718         
4719         this.loading = true;
4720         this.compiled = false;
4721         
4722         var cx = new Roo.data.Connection();
4723         cx.request({
4724             url : this.url,
4725             method : 'GET',
4726             success : function (response) {
4727                 _t.loading = false;
4728                 _t.html = response.responseText;
4729                 _t.url = false;
4730                 _t.compile();
4731              },
4732             failure : function(response) {
4733                 Roo.log("Template failed to load from " + _t.url);
4734                 _t.loading = false;
4735             }
4736         });
4737     },
4738
4739     /**
4740      * Sets the HTML used as the template and optionally compiles it.
4741      * @param {String} html
4742      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4743      * @return {Roo.Template} this
4744      */
4745     set : function(html, compile){
4746         this.html = html;
4747         this.compiled = null;
4748         if(compile){
4749             this.compile();
4750         }
4751         return this;
4752     },
4753     
4754     /**
4755      * True to disable format functions (defaults to false)
4756      * @type Boolean
4757      */
4758     disableFormats : false,
4759     
4760     /**
4761     * The regular expression used to match template variables 
4762     * @type RegExp
4763     * @property 
4764     */
4765     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4766     
4767     /**
4768      * Compiles the template into an internal function, eliminating the RegEx overhead.
4769      * @return {Roo.Template} this
4770      */
4771     compile : function(){
4772         var fm = Roo.util.Format;
4773         var useF = this.disableFormats !== true;
4774         var sep = Roo.isGecko ? "+" : ",";
4775         var fn = function(m, name, format, args){
4776             if(format && useF){
4777                 args = args ? ',' + args : "";
4778                 if(format.substr(0, 5) != "this."){
4779                     format = "fm." + format + '(';
4780                 }else{
4781                     format = 'this.call("'+ format.substr(5) + '", ';
4782                     args = ", values";
4783                 }
4784             }else{
4785                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4786             }
4787             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4788         };
4789         var body;
4790         // branched to use + in gecko and [].join() in others
4791         if(Roo.isGecko){
4792             body = "this.compiled = function(values){ return '" +
4793                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4794                     "';};";
4795         }else{
4796             body = ["this.compiled = function(values){ return ['"];
4797             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4798             body.push("'].join('');};");
4799             body = body.join('');
4800         }
4801         /**
4802          * eval:var:values
4803          * eval:var:fm
4804          */
4805         eval(body);
4806         return this;
4807     },
4808     
4809     // private function used to call members
4810     call : function(fnName, value, allValues){
4811         return this[fnName](value, allValues);
4812     },
4813     
4814     /**
4815      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4816      * @param {String/HTMLElement/Roo.Element} el The context element
4817      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4818      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4819      * @return {HTMLElement/Roo.Element} The new node or Element
4820      */
4821     insertFirst: function(el, values, returnElement){
4822         return this.doInsert('afterBegin', el, values, returnElement);
4823     },
4824
4825     /**
4826      * Applies the supplied values to the template and inserts the new node(s) before el.
4827      * @param {String/HTMLElement/Roo.Element} el The context element
4828      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4829      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4830      * @return {HTMLElement/Roo.Element} The new node or Element
4831      */
4832     insertBefore: function(el, values, returnElement){
4833         return this.doInsert('beforeBegin', el, values, returnElement);
4834     },
4835
4836     /**
4837      * Applies the supplied values to the template and inserts the new node(s) after el.
4838      * @param {String/HTMLElement/Roo.Element} el The context element
4839      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4840      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4841      * @return {HTMLElement/Roo.Element} The new node or Element
4842      */
4843     insertAfter : function(el, values, returnElement){
4844         return this.doInsert('afterEnd', el, values, returnElement);
4845     },
4846     
4847     /**
4848      * Applies the supplied values to the template and appends the new node(s) to el.
4849      * @param {String/HTMLElement/Roo.Element} el The context element
4850      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4851      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4852      * @return {HTMLElement/Roo.Element} The new node or Element
4853      */
4854     append : function(el, values, returnElement){
4855         return this.doInsert('beforeEnd', el, values, returnElement);
4856     },
4857
4858     doInsert : function(where, el, values, returnEl){
4859         el = Roo.getDom(el);
4860         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4861         return returnEl ? Roo.get(newNode, true) : newNode;
4862     },
4863
4864     /**
4865      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4866      * @param {String/HTMLElement/Roo.Element} el The context element
4867      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4868      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4869      * @return {HTMLElement/Roo.Element} The new node or Element
4870      */
4871     overwrite : function(el, values, returnElement){
4872         el = Roo.getDom(el);
4873         el.innerHTML = this.applyTemplate(values);
4874         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4875     }
4876 };
4877 /**
4878  * Alias for {@link #applyTemplate}
4879  * @method
4880  */
4881 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4882
4883 // backwards compat
4884 Roo.DomHelper.Template = Roo.Template;
4885
4886 /**
4887  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4888  * @param {String/HTMLElement} el A DOM element or its id
4889  * @returns {Roo.Template} The created template
4890  * @static
4891  */
4892 Roo.Template.from = function(el){
4893     el = Roo.getDom(el);
4894     return new Roo.Template(el.value || el.innerHTML);
4895 };/*
4896  * Based on:
4897  * Ext JS Library 1.1.1
4898  * Copyright(c) 2006-2007, Ext JS, LLC.
4899  *
4900  * Originally Released Under LGPL - original licence link has changed is not relivant.
4901  *
4902  * Fork - LGPL
4903  * <script type="text/javascript">
4904  */
4905  
4906
4907 /*
4908  * This is code is also distributed under MIT license for use
4909  * with jQuery and prototype JavaScript libraries.
4910  */
4911 /**
4912  * @class Roo.DomQuery
4913 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4914 <p>
4915 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4916
4917 <p>
4918 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4919 </p>
4920 <h4>Element Selectors:</h4>
4921 <ul class="list">
4922     <li> <b>*</b> any element</li>
4923     <li> <b>E</b> an element with the tag E</li>
4924     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4925     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4926     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4927     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4928 </ul>
4929 <h4>Attribute Selectors:</h4>
4930 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4931 <ul class="list">
4932     <li> <b>E[foo]</b> has an attribute "foo"</li>
4933     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4934     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4935     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4936     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4937     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4938     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4939 </ul>
4940 <h4>Pseudo Classes:</h4>
4941 <ul class="list">
4942     <li> <b>E:first-child</b> E is the first child of its parent</li>
4943     <li> <b>E:last-child</b> E is the last child of its parent</li>
4944     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4945     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4946     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4947     <li> <b>E:only-child</b> E is the only child of its parent</li>
4948     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4949     <li> <b>E:first</b> the first E in the resultset</li>
4950     <li> <b>E:last</b> the last E in the resultset</li>
4951     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4952     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4953     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4954     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4955     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4956     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4957     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4958     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4959     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4960 </ul>
4961 <h4>CSS Value Selectors:</h4>
4962 <ul class="list">
4963     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4964     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4965     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4966     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4967     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4968     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4969 </ul>
4970  * @singleton
4971  */
4972 Roo.DomQuery = function(){
4973     var cache = {}, simpleCache = {}, valueCache = {};
4974     var nonSpace = /\S/;
4975     var trimRe = /^\s+|\s+$/g;
4976     var tplRe = /\{(\d+)\}/g;
4977     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4978     var tagTokenRe = /^(#)?([\w-\*]+)/;
4979     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4980
4981     function child(p, index){
4982         var i = 0;
4983         var n = p.firstChild;
4984         while(n){
4985             if(n.nodeType == 1){
4986                if(++i == index){
4987                    return n;
4988                }
4989             }
4990             n = n.nextSibling;
4991         }
4992         return null;
4993     };
4994
4995     function next(n){
4996         while((n = n.nextSibling) && n.nodeType != 1);
4997         return n;
4998     };
4999
5000     function prev(n){
5001         while((n = n.previousSibling) && n.nodeType != 1);
5002         return n;
5003     };
5004
5005     function children(d){
5006         var n = d.firstChild, ni = -1;
5007             while(n){
5008                 var nx = n.nextSibling;
5009                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5010                     d.removeChild(n);
5011                 }else{
5012                     n.nodeIndex = ++ni;
5013                 }
5014                 n = nx;
5015             }
5016             return this;
5017         };
5018
5019     function byClassName(c, a, v){
5020         if(!v){
5021             return c;
5022         }
5023         var r = [], ri = -1, cn;
5024         for(var i = 0, ci; ci = c[i]; i++){
5025             if((' '+ci.className+' ').indexOf(v) != -1){
5026                 r[++ri] = ci;
5027             }
5028         }
5029         return r;
5030     };
5031
5032     function attrValue(n, attr){
5033         if(!n.tagName && typeof n.length != "undefined"){
5034             n = n[0];
5035         }
5036         if(!n){
5037             return null;
5038         }
5039         if(attr == "for"){
5040             return n.htmlFor;
5041         }
5042         if(attr == "class" || attr == "className"){
5043             return n.className;
5044         }
5045         return n.getAttribute(attr) || n[attr];
5046
5047     };
5048
5049     function getNodes(ns, mode, tagName){
5050         var result = [], ri = -1, cs;
5051         if(!ns){
5052             return result;
5053         }
5054         tagName = tagName || "*";
5055         if(typeof ns.getElementsByTagName != "undefined"){
5056             ns = [ns];
5057         }
5058         if(!mode){
5059             for(var i = 0, ni; ni = ns[i]; i++){
5060                 cs = ni.getElementsByTagName(tagName);
5061                 for(var j = 0, ci; ci = cs[j]; j++){
5062                     result[++ri] = ci;
5063                 }
5064             }
5065         }else if(mode == "/" || mode == ">"){
5066             var utag = tagName.toUpperCase();
5067             for(var i = 0, ni, cn; ni = ns[i]; i++){
5068                 cn = ni.children || ni.childNodes;
5069                 for(var j = 0, cj; cj = cn[j]; j++){
5070                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5071                         result[++ri] = cj;
5072                     }
5073                 }
5074             }
5075         }else if(mode == "+"){
5076             var utag = tagName.toUpperCase();
5077             for(var i = 0, n; n = ns[i]; i++){
5078                 while((n = n.nextSibling) && n.nodeType != 1);
5079                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5080                     result[++ri] = n;
5081                 }
5082             }
5083         }else if(mode == "~"){
5084             for(var i = 0, n; n = ns[i]; i++){
5085                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5086                 if(n){
5087                     result[++ri] = n;
5088                 }
5089             }
5090         }
5091         return result;
5092     };
5093
5094     function concat(a, b){
5095         if(b.slice){
5096             return a.concat(b);
5097         }
5098         for(var i = 0, l = b.length; i < l; i++){
5099             a[a.length] = b[i];
5100         }
5101         return a;
5102     }
5103
5104     function byTag(cs, tagName){
5105         if(cs.tagName || cs == document){
5106             cs = [cs];
5107         }
5108         if(!tagName){
5109             return cs;
5110         }
5111         var r = [], ri = -1;
5112         tagName = tagName.toLowerCase();
5113         for(var i = 0, ci; ci = cs[i]; i++){
5114             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5115                 r[++ri] = ci;
5116             }
5117         }
5118         return r;
5119     };
5120
5121     function byId(cs, attr, id){
5122         if(cs.tagName || cs == document){
5123             cs = [cs];
5124         }
5125         if(!id){
5126             return cs;
5127         }
5128         var r = [], ri = -1;
5129         for(var i = 0,ci; ci = cs[i]; i++){
5130             if(ci && ci.id == id){
5131                 r[++ri] = ci;
5132                 return r;
5133             }
5134         }
5135         return r;
5136     };
5137
5138     function byAttribute(cs, attr, value, op, custom){
5139         var r = [], ri = -1, st = custom=="{";
5140         var f = Roo.DomQuery.operators[op];
5141         for(var i = 0, ci; ci = cs[i]; i++){
5142             var a;
5143             if(st){
5144                 a = Roo.DomQuery.getStyle(ci, attr);
5145             }
5146             else if(attr == "class" || attr == "className"){
5147                 a = ci.className;
5148             }else if(attr == "for"){
5149                 a = ci.htmlFor;
5150             }else if(attr == "href"){
5151                 a = ci.getAttribute("href", 2);
5152             }else{
5153                 a = ci.getAttribute(attr);
5154             }
5155             if((f && f(a, value)) || (!f && a)){
5156                 r[++ri] = ci;
5157             }
5158         }
5159         return r;
5160     };
5161
5162     function byPseudo(cs, name, value){
5163         return Roo.DomQuery.pseudos[name](cs, value);
5164     };
5165
5166     // This is for IE MSXML which does not support expandos.
5167     // IE runs the same speed using setAttribute, however FF slows way down
5168     // and Safari completely fails so they need to continue to use expandos.
5169     var isIE = window.ActiveXObject ? true : false;
5170
5171     // this eval is stop the compressor from
5172     // renaming the variable to something shorter
5173     
5174     /** eval:var:batch */
5175     var batch = 30803; 
5176
5177     var key = 30803;
5178
5179     function nodupIEXml(cs){
5180         var d = ++key;
5181         cs[0].setAttribute("_nodup", d);
5182         var r = [cs[0]];
5183         for(var i = 1, len = cs.length; i < len; i++){
5184             var c = cs[i];
5185             if(!c.getAttribute("_nodup") != d){
5186                 c.setAttribute("_nodup", d);
5187                 r[r.length] = c;
5188             }
5189         }
5190         for(var i = 0, len = cs.length; i < len; i++){
5191             cs[i].removeAttribute("_nodup");
5192         }
5193         return r;
5194     }
5195
5196     function nodup(cs){
5197         if(!cs){
5198             return [];
5199         }
5200         var len = cs.length, c, i, r = cs, cj, ri = -1;
5201         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5202             return cs;
5203         }
5204         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5205             return nodupIEXml(cs);
5206         }
5207         var d = ++key;
5208         cs[0]._nodup = d;
5209         for(i = 1; c = cs[i]; i++){
5210             if(c._nodup != d){
5211                 c._nodup = d;
5212             }else{
5213                 r = [];
5214                 for(var j = 0; j < i; j++){
5215                     r[++ri] = cs[j];
5216                 }
5217                 for(j = i+1; cj = cs[j]; j++){
5218                     if(cj._nodup != d){
5219                         cj._nodup = d;
5220                         r[++ri] = cj;
5221                     }
5222                 }
5223                 return r;
5224             }
5225         }
5226         return r;
5227     }
5228
5229     function quickDiffIEXml(c1, c2){
5230         var d = ++key;
5231         for(var i = 0, len = c1.length; i < len; i++){
5232             c1[i].setAttribute("_qdiff", d);
5233         }
5234         var r = [];
5235         for(var i = 0, len = c2.length; i < len; i++){
5236             if(c2[i].getAttribute("_qdiff") != d){
5237                 r[r.length] = c2[i];
5238             }
5239         }
5240         for(var i = 0, len = c1.length; i < len; i++){
5241            c1[i].removeAttribute("_qdiff");
5242         }
5243         return r;
5244     }
5245
5246     function quickDiff(c1, c2){
5247         var len1 = c1.length;
5248         if(!len1){
5249             return c2;
5250         }
5251         if(isIE && c1[0].selectSingleNode){
5252             return quickDiffIEXml(c1, c2);
5253         }
5254         var d = ++key;
5255         for(var i = 0; i < len1; i++){
5256             c1[i]._qdiff = d;
5257         }
5258         var r = [];
5259         for(var i = 0, len = c2.length; i < len; i++){
5260             if(c2[i]._qdiff != d){
5261                 r[r.length] = c2[i];
5262             }
5263         }
5264         return r;
5265     }
5266
5267     function quickId(ns, mode, root, id){
5268         if(ns == root){
5269            var d = root.ownerDocument || root;
5270            return d.getElementById(id);
5271         }
5272         ns = getNodes(ns, mode, "*");
5273         return byId(ns, null, id);
5274     }
5275
5276     return {
5277         getStyle : function(el, name){
5278             return Roo.fly(el).getStyle(name);
5279         },
5280         /**
5281          * Compiles a selector/xpath query into a reusable function. The returned function
5282          * takes one parameter "root" (optional), which is the context node from where the query should start.
5283          * @param {String} selector The selector/xpath query
5284          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5285          * @return {Function}
5286          */
5287         compile : function(path, type){
5288             type = type || "select";
5289             
5290             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5291             var q = path, mode, lq;
5292             var tk = Roo.DomQuery.matchers;
5293             var tklen = tk.length;
5294             var mm;
5295
5296             // accept leading mode switch
5297             var lmode = q.match(modeRe);
5298             if(lmode && lmode[1]){
5299                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5300                 q = q.replace(lmode[1], "");
5301             }
5302             // strip leading slashes
5303             while(path.substr(0, 1)=="/"){
5304                 path = path.substr(1);
5305             }
5306
5307             while(q && lq != q){
5308                 lq = q;
5309                 var tm = q.match(tagTokenRe);
5310                 if(type == "select"){
5311                     if(tm){
5312                         if(tm[1] == "#"){
5313                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5314                         }else{
5315                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5316                         }
5317                         q = q.replace(tm[0], "");
5318                     }else if(q.substr(0, 1) != '@'){
5319                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5320                     }
5321                 }else{
5322                     if(tm){
5323                         if(tm[1] == "#"){
5324                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5325                         }else{
5326                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5327                         }
5328                         q = q.replace(tm[0], "");
5329                     }
5330                 }
5331                 while(!(mm = q.match(modeRe))){
5332                     var matched = false;
5333                     for(var j = 0; j < tklen; j++){
5334                         var t = tk[j];
5335                         var m = q.match(t.re);
5336                         if(m){
5337                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5338                                                     return m[i];
5339                                                 });
5340                             q = q.replace(m[0], "");
5341                             matched = true;
5342                             break;
5343                         }
5344                     }
5345                     // prevent infinite loop on bad selector
5346                     if(!matched){
5347                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5348                     }
5349                 }
5350                 if(mm[1]){
5351                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5352                     q = q.replace(mm[1], "");
5353                 }
5354             }
5355             fn[fn.length] = "return nodup(n);\n}";
5356             
5357              /** 
5358               * list of variables that need from compression as they are used by eval.
5359              *  eval:var:batch 
5360              *  eval:var:nodup
5361              *  eval:var:byTag
5362              *  eval:var:ById
5363              *  eval:var:getNodes
5364              *  eval:var:quickId
5365              *  eval:var:mode
5366              *  eval:var:root
5367              *  eval:var:n
5368              *  eval:var:byClassName
5369              *  eval:var:byPseudo
5370              *  eval:var:byAttribute
5371              *  eval:var:attrValue
5372              * 
5373              **/ 
5374             eval(fn.join(""));
5375             return f;
5376         },
5377
5378         /**
5379          * Selects a group of elements.
5380          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5381          * @param {Node} root (optional) The start of the query (defaults to document).
5382          * @return {Array}
5383          */
5384         select : function(path, root, type){
5385             if(!root || root == document){
5386                 root = document;
5387             }
5388             if(typeof root == "string"){
5389                 root = document.getElementById(root);
5390             }
5391             var paths = path.split(",");
5392             var results = [];
5393             for(var i = 0, len = paths.length; i < len; i++){
5394                 var p = paths[i].replace(trimRe, "");
5395                 if(!cache[p]){
5396                     cache[p] = Roo.DomQuery.compile(p);
5397                     if(!cache[p]){
5398                         throw p + " is not a valid selector";
5399                     }
5400                 }
5401                 var result = cache[p](root);
5402                 if(result && result != document){
5403                     results = results.concat(result);
5404                 }
5405             }
5406             if(paths.length > 1){
5407                 return nodup(results);
5408             }
5409             return results;
5410         },
5411
5412         /**
5413          * Selects a single element.
5414          * @param {String} selector The selector/xpath query
5415          * @param {Node} root (optional) The start of the query (defaults to document).
5416          * @return {Element}
5417          */
5418         selectNode : function(path, root){
5419             return Roo.DomQuery.select(path, root)[0];
5420         },
5421
5422         /**
5423          * Selects the value of a node, optionally replacing null with the defaultValue.
5424          * @param {String} selector The selector/xpath query
5425          * @param {Node} root (optional) The start of the query (defaults to document).
5426          * @param {String} defaultValue
5427          */
5428         selectValue : function(path, root, defaultValue){
5429             path = path.replace(trimRe, "");
5430             if(!valueCache[path]){
5431                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5432             }
5433             var n = valueCache[path](root);
5434             n = n[0] ? n[0] : n;
5435             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5436             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5437         },
5438
5439         /**
5440          * Selects the value of a node, parsing integers and floats.
5441          * @param {String} selector The selector/xpath query
5442          * @param {Node} root (optional) The start of the query (defaults to document).
5443          * @param {Number} defaultValue
5444          * @return {Number}
5445          */
5446         selectNumber : function(path, root, defaultValue){
5447             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5448             return parseFloat(v);
5449         },
5450
5451         /**
5452          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5453          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5454          * @param {String} selector The simple selector to test
5455          * @return {Boolean}
5456          */
5457         is : function(el, ss){
5458             if(typeof el == "string"){
5459                 el = document.getElementById(el);
5460             }
5461             var isArray = (el instanceof Array);
5462             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5463             return isArray ? (result.length == el.length) : (result.length > 0);
5464         },
5465
5466         /**
5467          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5468          * @param {Array} el An array of elements to filter
5469          * @param {String} selector The simple selector to test
5470          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5471          * the selector instead of the ones that match
5472          * @return {Array}
5473          */
5474         filter : function(els, ss, nonMatches){
5475             ss = ss.replace(trimRe, "");
5476             if(!simpleCache[ss]){
5477                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5478             }
5479             var result = simpleCache[ss](els);
5480             return nonMatches ? quickDiff(result, els) : result;
5481         },
5482
5483         /**
5484          * Collection of matching regular expressions and code snippets.
5485          */
5486         matchers : [{
5487                 re: /^\.([\w-]+)/,
5488                 select: 'n = byClassName(n, null, " {1} ");'
5489             }, {
5490                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5491                 select: 'n = byPseudo(n, "{1}", "{2}");'
5492             },{
5493                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5494                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5495             }, {
5496                 re: /^#([\w-]+)/,
5497                 select: 'n = byId(n, null, "{1}");'
5498             },{
5499                 re: /^@([\w-]+)/,
5500                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5501             }
5502         ],
5503
5504         /**
5505          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5506          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5507          */
5508         operators : {
5509             "=" : function(a, v){
5510                 return a == v;
5511             },
5512             "!=" : function(a, v){
5513                 return a != v;
5514             },
5515             "^=" : function(a, v){
5516                 return a && a.substr(0, v.length) == v;
5517             },
5518             "$=" : function(a, v){
5519                 return a && a.substr(a.length-v.length) == v;
5520             },
5521             "*=" : function(a, v){
5522                 return a && a.indexOf(v) !== -1;
5523             },
5524             "%=" : function(a, v){
5525                 return (a % v) == 0;
5526             },
5527             "|=" : function(a, v){
5528                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5529             },
5530             "~=" : function(a, v){
5531                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5532             }
5533         },
5534
5535         /**
5536          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5537          * and the argument (if any) supplied in the selector.
5538          */
5539         pseudos : {
5540             "first-child" : function(c){
5541                 var r = [], ri = -1, n;
5542                 for(var i = 0, ci; ci = n = c[i]; i++){
5543                     while((n = n.previousSibling) && n.nodeType != 1);
5544                     if(!n){
5545                         r[++ri] = ci;
5546                     }
5547                 }
5548                 return r;
5549             },
5550
5551             "last-child" : function(c){
5552                 var r = [], ri = -1, n;
5553                 for(var i = 0, ci; ci = n = c[i]; i++){
5554                     while((n = n.nextSibling) && n.nodeType != 1);
5555                     if(!n){
5556                         r[++ri] = ci;
5557                     }
5558                 }
5559                 return r;
5560             },
5561
5562             "nth-child" : function(c, a) {
5563                 var r = [], ri = -1;
5564                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5565                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5566                 for(var i = 0, n; n = c[i]; i++){
5567                     var pn = n.parentNode;
5568                     if (batch != pn._batch) {
5569                         var j = 0;
5570                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5571                             if(cn.nodeType == 1){
5572                                cn.nodeIndex = ++j;
5573                             }
5574                         }
5575                         pn._batch = batch;
5576                     }
5577                     if (f == 1) {
5578                         if (l == 0 || n.nodeIndex == l){
5579                             r[++ri] = n;
5580                         }
5581                     } else if ((n.nodeIndex + l) % f == 0){
5582                         r[++ri] = n;
5583                     }
5584                 }
5585
5586                 return r;
5587             },
5588
5589             "only-child" : function(c){
5590                 var r = [], ri = -1;;
5591                 for(var i = 0, ci; ci = c[i]; i++){
5592                     if(!prev(ci) && !next(ci)){
5593                         r[++ri] = ci;
5594                     }
5595                 }
5596                 return r;
5597             },
5598
5599             "empty" : function(c){
5600                 var r = [], ri = -1;
5601                 for(var i = 0, ci; ci = c[i]; i++){
5602                     var cns = ci.childNodes, j = 0, cn, empty = true;
5603                     while(cn = cns[j]){
5604                         ++j;
5605                         if(cn.nodeType == 1 || cn.nodeType == 3){
5606                             empty = false;
5607                             break;
5608                         }
5609                     }
5610                     if(empty){
5611                         r[++ri] = ci;
5612                     }
5613                 }
5614                 return r;
5615             },
5616
5617             "contains" : function(c, v){
5618                 var r = [], ri = -1;
5619                 for(var i = 0, ci; ci = c[i]; i++){
5620                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5621                         r[++ri] = ci;
5622                     }
5623                 }
5624                 return r;
5625             },
5626
5627             "nodeValue" : function(c, v){
5628                 var r = [], ri = -1;
5629                 for(var i = 0, ci; ci = c[i]; i++){
5630                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5631                         r[++ri] = ci;
5632                     }
5633                 }
5634                 return r;
5635             },
5636
5637             "checked" : function(c){
5638                 var r = [], ri = -1;
5639                 for(var i = 0, ci; ci = c[i]; i++){
5640                     if(ci.checked == true){
5641                         r[++ri] = ci;
5642                     }
5643                 }
5644                 return r;
5645             },
5646
5647             "not" : function(c, ss){
5648                 return Roo.DomQuery.filter(c, ss, true);
5649             },
5650
5651             "odd" : function(c){
5652                 return this["nth-child"](c, "odd");
5653             },
5654
5655             "even" : function(c){
5656                 return this["nth-child"](c, "even");
5657             },
5658
5659             "nth" : function(c, a){
5660                 return c[a-1] || [];
5661             },
5662
5663             "first" : function(c){
5664                 return c[0] || [];
5665             },
5666
5667             "last" : function(c){
5668                 return c[c.length-1] || [];
5669             },
5670
5671             "has" : function(c, ss){
5672                 var s = Roo.DomQuery.select;
5673                 var r = [], ri = -1;
5674                 for(var i = 0, ci; ci = c[i]; i++){
5675                     if(s(ss, ci).length > 0){
5676                         r[++ri] = ci;
5677                     }
5678                 }
5679                 return r;
5680             },
5681
5682             "next" : function(c, ss){
5683                 var is = Roo.DomQuery.is;
5684                 var r = [], ri = -1;
5685                 for(var i = 0, ci; ci = c[i]; i++){
5686                     var n = next(ci);
5687                     if(n && is(n, ss)){
5688                         r[++ri] = ci;
5689                     }
5690                 }
5691                 return r;
5692             },
5693
5694             "prev" : function(c, ss){
5695                 var is = Roo.DomQuery.is;
5696                 var r = [], ri = -1;
5697                 for(var i = 0, ci; ci = c[i]; i++){
5698                     var n = prev(ci);
5699                     if(n && is(n, ss)){
5700                         r[++ri] = ci;
5701                     }
5702                 }
5703                 return r;
5704             }
5705         }
5706     };
5707 }();
5708
5709 /**
5710  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5711  * @param {String} path The selector/xpath query
5712  * @param {Node} root (optional) The start of the query (defaults to document).
5713  * @return {Array}
5714  * @member Roo
5715  * @method query
5716  */
5717 Roo.query = Roo.DomQuery.select;
5718 /*
5719  * Based on:
5720  * Ext JS Library 1.1.1
5721  * Copyright(c) 2006-2007, Ext JS, LLC.
5722  *
5723  * Originally Released Under LGPL - original licence link has changed is not relivant.
5724  *
5725  * Fork - LGPL
5726  * <script type="text/javascript">
5727  */
5728
5729 /**
5730  * @class Roo.util.Observable
5731  * Base class that provides a common interface for publishing events. Subclasses are expected to
5732  * to have a property "events" with all the events defined.<br>
5733  * For example:
5734  * <pre><code>
5735  Employee = function(name){
5736     this.name = name;
5737     this.addEvents({
5738         "fired" : true,
5739         "quit" : true
5740     });
5741  }
5742  Roo.extend(Employee, Roo.util.Observable);
5743 </code></pre>
5744  * @param {Object} config properties to use (incuding events / listeners)
5745  */
5746
5747 Roo.util.Observable = function(cfg){
5748     
5749     cfg = cfg|| {};
5750     this.addEvents(cfg.events || {});
5751     if (cfg.events) {
5752         delete cfg.events; // make sure
5753     }
5754      
5755     Roo.apply(this, cfg);
5756     
5757     if(this.listeners){
5758         this.on(this.listeners);
5759         delete this.listeners;
5760     }
5761 };
5762 Roo.util.Observable.prototype = {
5763     /** 
5764  * @cfg {Object} listeners  list of events and functions to call for this object, 
5765  * For example :
5766  * <pre><code>
5767     listeners :  { 
5768        'click' : function(e) {
5769            ..... 
5770         } ,
5771         .... 
5772     } 
5773   </code></pre>
5774  */
5775     
5776     
5777     /**
5778      * Fires the specified event with the passed parameters (minus the event name).
5779      * @param {String} eventName
5780      * @param {Object...} args Variable number of parameters are passed to handlers
5781      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5782      */
5783     fireEvent : function(){
5784         var ce = this.events[arguments[0].toLowerCase()];
5785         if(typeof ce == "object"){
5786             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5787         }else{
5788             return true;
5789         }
5790     },
5791
5792     // private
5793     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5794
5795     /**
5796      * Appends an event handler to this component
5797      * @param {String}   eventName The type of event to listen for
5798      * @param {Function} handler The method the event invokes
5799      * @param {Object}   scope (optional) The scope in which to execute the handler
5800      * function. The handler function's "this" context.
5801      * @param {Object}   options (optional) An object containing handler configuration
5802      * properties. This may contain any of the following properties:<ul>
5803      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5804      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5805      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5806      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5807      * by the specified number of milliseconds. If the event fires again within that time, the original
5808      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5809      * </ul><br>
5810      * <p>
5811      * <b>Combining Options</b><br>
5812      * Using the options argument, it is possible to combine different types of listeners:<br>
5813      * <br>
5814      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5815                 <pre><code>
5816                 el.on('click', this.onClick, this, {
5817                         single: true,
5818                 delay: 100,
5819                 forumId: 4
5820                 });
5821                 </code></pre>
5822      * <p>
5823      * <b>Attaching multiple handlers in 1 call</b><br>
5824      * The method also allows for a single argument to be passed which is a config object containing properties
5825      * which specify multiple handlers.
5826      * <pre><code>
5827                 el.on({
5828                         'click': {
5829                         fn: this.onClick,
5830                         scope: this,
5831                         delay: 100
5832                 }, 
5833                 'mouseover': {
5834                         fn: this.onMouseOver,
5835                         scope: this
5836                 },
5837                 'mouseout': {
5838                         fn: this.onMouseOut,
5839                         scope: this
5840                 }
5841                 });
5842                 </code></pre>
5843      * <p>
5844      * Or a shorthand syntax which passes the same scope object to all handlers:
5845         <pre><code>
5846                 el.on({
5847                         'click': this.onClick,
5848                 'mouseover': this.onMouseOver,
5849                 'mouseout': this.onMouseOut,
5850                 scope: this
5851                 });
5852                 </code></pre>
5853      */
5854     addListener : function(eventName, fn, scope, o){
5855         if(typeof eventName == "object"){
5856             o = eventName;
5857             for(var e in o){
5858                 if(this.filterOptRe.test(e)){
5859                     continue;
5860                 }
5861                 if(typeof o[e] == "function"){
5862                     // shared options
5863                     this.addListener(e, o[e], o.scope,  o);
5864                 }else{
5865                     // individual options
5866                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5867                 }
5868             }
5869             return;
5870         }
5871         o = (!o || typeof o == "boolean") ? {} : o;
5872         eventName = eventName.toLowerCase();
5873         var ce = this.events[eventName] || true;
5874         if(typeof ce == "boolean"){
5875             ce = new Roo.util.Event(this, eventName);
5876             this.events[eventName] = ce;
5877         }
5878         ce.addListener(fn, scope, o);
5879     },
5880
5881     /**
5882      * Removes a listener
5883      * @param {String}   eventName     The type of event to listen for
5884      * @param {Function} handler        The handler to remove
5885      * @param {Object}   scope  (optional) The scope (this object) for the handler
5886      */
5887     removeListener : function(eventName, fn, scope){
5888         var ce = this.events[eventName.toLowerCase()];
5889         if(typeof ce == "object"){
5890             ce.removeListener(fn, scope);
5891         }
5892     },
5893
5894     /**
5895      * Removes all listeners for this object
5896      */
5897     purgeListeners : function(){
5898         for(var evt in this.events){
5899             if(typeof this.events[evt] == "object"){
5900                  this.events[evt].clearListeners();
5901             }
5902         }
5903     },
5904
5905     relayEvents : function(o, events){
5906         var createHandler = function(ename){
5907             return function(){
5908                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5909             };
5910         };
5911         for(var i = 0, len = events.length; i < len; i++){
5912             var ename = events[i];
5913             if(!this.events[ename]){ this.events[ename] = true; };
5914             o.on(ename, createHandler(ename), this);
5915         }
5916     },
5917
5918     /**
5919      * Used to define events on this Observable
5920      * @param {Object} object The object with the events defined
5921      */
5922     addEvents : function(o){
5923         if(!this.events){
5924             this.events = {};
5925         }
5926         Roo.applyIf(this.events, o);
5927     },
5928
5929     /**
5930      * Checks to see if this object has any listeners for a specified event
5931      * @param {String} eventName The name of the event to check for
5932      * @return {Boolean} True if the event is being listened for, else false
5933      */
5934     hasListener : function(eventName){
5935         var e = this.events[eventName];
5936         return typeof e == "object" && e.listeners.length > 0;
5937     }
5938 };
5939 /**
5940  * Appends an event handler to this element (shorthand for addListener)
5941  * @param {String}   eventName     The type of event to listen for
5942  * @param {Function} handler        The method the event invokes
5943  * @param {Object}   scope (optional) The scope in which to execute the handler
5944  * function. The handler function's "this" context.
5945  * @param {Object}   options  (optional)
5946  * @method
5947  */
5948 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5949 /**
5950  * Removes a listener (shorthand for removeListener)
5951  * @param {String}   eventName     The type of event to listen for
5952  * @param {Function} handler        The handler to remove
5953  * @param {Object}   scope  (optional) The scope (this object) for the handler
5954  * @method
5955  */
5956 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5957
5958 /**
5959  * Starts capture on the specified Observable. All events will be passed
5960  * to the supplied function with the event name + standard signature of the event
5961  * <b>before</b> the event is fired. If the supplied function returns false,
5962  * the event will not fire.
5963  * @param {Observable} o The Observable to capture
5964  * @param {Function} fn The function to call
5965  * @param {Object} scope (optional) The scope (this object) for the fn
5966  * @static
5967  */
5968 Roo.util.Observable.capture = function(o, fn, scope){
5969     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5970 };
5971
5972 /**
5973  * Removes <b>all</b> added captures from the Observable.
5974  * @param {Observable} o The Observable to release
5975  * @static
5976  */
5977 Roo.util.Observable.releaseCapture = function(o){
5978     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5979 };
5980
5981 (function(){
5982
5983     var createBuffered = function(h, o, scope){
5984         var task = new Roo.util.DelayedTask();
5985         return function(){
5986             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5987         };
5988     };
5989
5990     var createSingle = function(h, e, fn, scope){
5991         return function(){
5992             e.removeListener(fn, scope);
5993             return h.apply(scope, arguments);
5994         };
5995     };
5996
5997     var createDelayed = function(h, o, scope){
5998         return function(){
5999             var args = Array.prototype.slice.call(arguments, 0);
6000             setTimeout(function(){
6001                 h.apply(scope, args);
6002             }, o.delay || 10);
6003         };
6004     };
6005
6006     Roo.util.Event = function(obj, name){
6007         this.name = name;
6008         this.obj = obj;
6009         this.listeners = [];
6010     };
6011
6012     Roo.util.Event.prototype = {
6013         addListener : function(fn, scope, options){
6014             var o = options || {};
6015             scope = scope || this.obj;
6016             if(!this.isListening(fn, scope)){
6017                 var l = {fn: fn, scope: scope, options: o};
6018                 var h = fn;
6019                 if(o.delay){
6020                     h = createDelayed(h, o, scope);
6021                 }
6022                 if(o.single){
6023                     h = createSingle(h, this, fn, scope);
6024                 }
6025                 if(o.buffer){
6026                     h = createBuffered(h, o, scope);
6027                 }
6028                 l.fireFn = h;
6029                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6030                     this.listeners.push(l);
6031                 }else{
6032                     this.listeners = this.listeners.slice(0);
6033                     this.listeners.push(l);
6034                 }
6035             }
6036         },
6037
6038         findListener : function(fn, scope){
6039             scope = scope || this.obj;
6040             var ls = this.listeners;
6041             for(var i = 0, len = ls.length; i < len; i++){
6042                 var l = ls[i];
6043                 if(l.fn == fn && l.scope == scope){
6044                     return i;
6045                 }
6046             }
6047             return -1;
6048         },
6049
6050         isListening : function(fn, scope){
6051             return this.findListener(fn, scope) != -1;
6052         },
6053
6054         removeListener : function(fn, scope){
6055             var index;
6056             if((index = this.findListener(fn, scope)) != -1){
6057                 if(!this.firing){
6058                     this.listeners.splice(index, 1);
6059                 }else{
6060                     this.listeners = this.listeners.slice(0);
6061                     this.listeners.splice(index, 1);
6062                 }
6063                 return true;
6064             }
6065             return false;
6066         },
6067
6068         clearListeners : function(){
6069             this.listeners = [];
6070         },
6071
6072         fire : function(){
6073             var ls = this.listeners, scope, len = ls.length;
6074             if(len > 0){
6075                 this.firing = true;
6076                 var args = Array.prototype.slice.call(arguments, 0);
6077                 for(var i = 0; i < len; i++){
6078                     var l = ls[i];
6079                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6080                         this.firing = false;
6081                         return false;
6082                     }
6083                 }
6084                 this.firing = false;
6085             }
6086             return true;
6087         }
6088     };
6089 })();/*
6090  * RooJS Library 
6091  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6092  *
6093  * Licence LGPL 
6094  *
6095  */
6096  
6097 /**
6098  * @class Roo.Document
6099  * @extends Roo.util.Observable
6100  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6101  * 
6102  * @param {Object} config the methods and properties of the 'base' class for the application.
6103  * 
6104  *  Generic Page handler - implement this to start your app..
6105  * 
6106  * eg.
6107  *  MyProject = new Roo.Document({
6108         events : {
6109             'load' : true // your events..
6110         },
6111         listeners : {
6112             'ready' : function() {
6113                 // fired on Roo.onReady()
6114             }
6115         }
6116  * 
6117  */
6118 Roo.Document = function(cfg) {
6119      
6120     this.addEvents({ 
6121         'ready' : true
6122     });
6123     Roo.util.Observable.call(this,cfg);
6124     
6125     var _this = this;
6126     
6127     Roo.onReady(function() {
6128         _this.fireEvent('ready');
6129     },null,false);
6130     
6131     
6132 }
6133
6134 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6135  * Based on:
6136  * Ext JS Library 1.1.1
6137  * Copyright(c) 2006-2007, Ext JS, LLC.
6138  *
6139  * Originally Released Under LGPL - original licence link has changed is not relivant.
6140  *
6141  * Fork - LGPL
6142  * <script type="text/javascript">
6143  */
6144
6145 /**
6146  * @class Roo.EventManager
6147  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6148  * several useful events directly.
6149  * See {@link Roo.EventObject} for more details on normalized event objects.
6150  * @singleton
6151  */
6152 Roo.EventManager = function(){
6153     var docReadyEvent, docReadyProcId, docReadyState = false;
6154     var resizeEvent, resizeTask, textEvent, textSize;
6155     var E = Roo.lib.Event;
6156     var D = Roo.lib.Dom;
6157
6158     
6159     
6160
6161     var fireDocReady = function(){
6162         if(!docReadyState){
6163             docReadyState = true;
6164             Roo.isReady = true;
6165             if(docReadyProcId){
6166                 clearInterval(docReadyProcId);
6167             }
6168             if(Roo.isGecko || Roo.isOpera) {
6169                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6170             }
6171             if(Roo.isIE){
6172                 var defer = document.getElementById("ie-deferred-loader");
6173                 if(defer){
6174                     defer.onreadystatechange = null;
6175                     defer.parentNode.removeChild(defer);
6176                 }
6177             }
6178             if(docReadyEvent){
6179                 docReadyEvent.fire();
6180                 docReadyEvent.clearListeners();
6181             }
6182         }
6183     };
6184     
6185     var initDocReady = function(){
6186         docReadyEvent = new Roo.util.Event();
6187         if(Roo.isGecko || Roo.isOpera) {
6188             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6189         }else if(Roo.isIE){
6190             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6191             var defer = document.getElementById("ie-deferred-loader");
6192             defer.onreadystatechange = function(){
6193                 if(this.readyState == "complete"){
6194                     fireDocReady();
6195                 }
6196             };
6197         }else if(Roo.isSafari){ 
6198             docReadyProcId = setInterval(function(){
6199                 var rs = document.readyState;
6200                 if(rs == "complete") {
6201                     fireDocReady();     
6202                  }
6203             }, 10);
6204         }
6205         // no matter what, make sure it fires on load
6206         E.on(window, "load", fireDocReady);
6207     };
6208
6209     var createBuffered = function(h, o){
6210         var task = new Roo.util.DelayedTask(h);
6211         return function(e){
6212             // create new event object impl so new events don't wipe out properties
6213             e = new Roo.EventObjectImpl(e);
6214             task.delay(o.buffer, h, null, [e]);
6215         };
6216     };
6217
6218     var createSingle = function(h, el, ename, fn){
6219         return function(e){
6220             Roo.EventManager.removeListener(el, ename, fn);
6221             h(e);
6222         };
6223     };
6224
6225     var createDelayed = function(h, o){
6226         return function(e){
6227             // create new event object impl so new events don't wipe out properties
6228             e = new Roo.EventObjectImpl(e);
6229             setTimeout(function(){
6230                 h(e);
6231             }, o.delay || 10);
6232         };
6233     };
6234     var transitionEndVal = false;
6235     
6236     var transitionEnd = function()
6237     {
6238         if (transitionEndVal) {
6239             return transitionEndVal;
6240         }
6241         var el = document.createElement('div');
6242
6243         var transEndEventNames = {
6244             WebkitTransition : 'webkitTransitionEnd',
6245             MozTransition    : 'transitionend',
6246             OTransition      : 'oTransitionEnd otransitionend',
6247             transition       : 'transitionend'
6248         };
6249     
6250         for (var name in transEndEventNames) {
6251             if (el.style[name] !== undefined) {
6252                 transitionEndVal = transEndEventNames[name];
6253                 return  transitionEndVal ;
6254             }
6255         }
6256     }
6257     
6258
6259     var listen = function(element, ename, opt, fn, scope){
6260         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6261         fn = fn || o.fn; scope = scope || o.scope;
6262         var el = Roo.getDom(element);
6263         
6264         
6265         if(!el){
6266             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6267         }
6268         
6269         if (ename == 'transitionend') {
6270             ename = transitionEnd();
6271         }
6272         var h = function(e){
6273             e = Roo.EventObject.setEvent(e);
6274             var t;
6275             if(o.delegate){
6276                 t = e.getTarget(o.delegate, el);
6277                 if(!t){
6278                     return;
6279                 }
6280             }else{
6281                 t = e.target;
6282             }
6283             if(o.stopEvent === true){
6284                 e.stopEvent();
6285             }
6286             if(o.preventDefault === true){
6287                e.preventDefault();
6288             }
6289             if(o.stopPropagation === true){
6290                 e.stopPropagation();
6291             }
6292
6293             if(o.normalized === false){
6294                 e = e.browserEvent;
6295             }
6296
6297             fn.call(scope || el, e, t, o);
6298         };
6299         if(o.delay){
6300             h = createDelayed(h, o);
6301         }
6302         if(o.single){
6303             h = createSingle(h, el, ename, fn);
6304         }
6305         if(o.buffer){
6306             h = createBuffered(h, o);
6307         }
6308         
6309         fn._handlers = fn._handlers || [];
6310         
6311         
6312         fn._handlers.push([Roo.id(el), ename, h]);
6313         
6314         
6315          
6316         E.on(el, ename, h);
6317         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6318             el.addEventListener("DOMMouseScroll", h, false);
6319             E.on(window, 'unload', function(){
6320                 el.removeEventListener("DOMMouseScroll", h, false);
6321             });
6322         }
6323         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6324             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6325         }
6326         return h;
6327     };
6328
6329     var stopListening = function(el, ename, fn){
6330         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6331         if(hds){
6332             for(var i = 0, len = hds.length; i < len; i++){
6333                 var h = hds[i];
6334                 if(h[0] == id && h[1] == ename){
6335                     hd = h[2];
6336                     hds.splice(i, 1);
6337                     break;
6338                 }
6339             }
6340         }
6341         E.un(el, ename, hd);
6342         el = Roo.getDom(el);
6343         if(ename == "mousewheel" && el.addEventListener){
6344             el.removeEventListener("DOMMouseScroll", hd, false);
6345         }
6346         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6347             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6348         }
6349     };
6350
6351     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6352     
6353     var pub = {
6354         
6355         
6356         /** 
6357          * Fix for doc tools
6358          * @scope Roo.EventManager
6359          */
6360         
6361         
6362         /** 
6363          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6364          * object with a Roo.EventObject
6365          * @param {Function} fn        The method the event invokes
6366          * @param {Object}   scope    An object that becomes the scope of the handler
6367          * @param {boolean}  override If true, the obj passed in becomes
6368          *                             the execution scope of the listener
6369          * @return {Function} The wrapped function
6370          * @deprecated
6371          */
6372         wrap : function(fn, scope, override){
6373             return function(e){
6374                 Roo.EventObject.setEvent(e);
6375                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6376             };
6377         },
6378         
6379         /**
6380      * Appends an event handler to an element (shorthand for addListener)
6381      * @param {String/HTMLElement}   element        The html element or id to assign the
6382      * @param {String}   eventName The type of event to listen for
6383      * @param {Function} handler The method the event invokes
6384      * @param {Object}   scope (optional) The scope in which to execute the handler
6385      * function. The handler function's "this" context.
6386      * @param {Object}   options (optional) An object containing handler configuration
6387      * properties. This may contain any of the following properties:<ul>
6388      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6389      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6390      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6391      * <li>preventDefault {Boolean} True to prevent the default action</li>
6392      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6393      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6394      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6395      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6396      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6397      * by the specified number of milliseconds. If the event fires again within that time, the original
6398      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6399      * </ul><br>
6400      * <p>
6401      * <b>Combining Options</b><br>
6402      * Using the options argument, it is possible to combine different types of listeners:<br>
6403      * <br>
6404      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6405      * Code:<pre><code>
6406 el.on('click', this.onClick, this, {
6407     single: true,
6408     delay: 100,
6409     stopEvent : true,
6410     forumId: 4
6411 });</code></pre>
6412      * <p>
6413      * <b>Attaching multiple handlers in 1 call</b><br>
6414       * The method also allows for a single argument to be passed which is a config object containing properties
6415      * which specify multiple handlers.
6416      * <p>
6417      * Code:<pre><code>
6418 el.on({
6419     'click' : {
6420         fn: this.onClick
6421         scope: this,
6422         delay: 100
6423     },
6424     'mouseover' : {
6425         fn: this.onMouseOver
6426         scope: this
6427     },
6428     'mouseout' : {
6429         fn: this.onMouseOut
6430         scope: this
6431     }
6432 });</code></pre>
6433      * <p>
6434      * Or a shorthand syntax:<br>
6435      * Code:<pre><code>
6436 el.on({
6437     'click' : this.onClick,
6438     'mouseover' : this.onMouseOver,
6439     'mouseout' : this.onMouseOut
6440     scope: this
6441 });</code></pre>
6442      */
6443         addListener : function(element, eventName, fn, scope, options){
6444             if(typeof eventName == "object"){
6445                 var o = eventName;
6446                 for(var e in o){
6447                     if(propRe.test(e)){
6448                         continue;
6449                     }
6450                     if(typeof o[e] == "function"){
6451                         // shared options
6452                         listen(element, e, o, o[e], o.scope);
6453                     }else{
6454                         // individual options
6455                         listen(element, e, o[e]);
6456                     }
6457                 }
6458                 return;
6459             }
6460             return listen(element, eventName, options, fn, scope);
6461         },
6462         
6463         /**
6464          * Removes an event handler
6465          *
6466          * @param {String/HTMLElement}   element        The id or html element to remove the 
6467          *                             event from
6468          * @param {String}   eventName     The type of event
6469          * @param {Function} fn
6470          * @return {Boolean} True if a listener was actually removed
6471          */
6472         removeListener : function(element, eventName, fn){
6473             return stopListening(element, eventName, fn);
6474         },
6475         
6476         /**
6477          * Fires when the document is ready (before onload and before images are loaded). Can be 
6478          * accessed shorthanded Roo.onReady().
6479          * @param {Function} fn        The method the event invokes
6480          * @param {Object}   scope    An  object that becomes the scope of the handler
6481          * @param {boolean}  options
6482          */
6483         onDocumentReady : function(fn, scope, options){
6484             if(docReadyState){ // if it already fired
6485                 docReadyEvent.addListener(fn, scope, options);
6486                 docReadyEvent.fire();
6487                 docReadyEvent.clearListeners();
6488                 return;
6489             }
6490             if(!docReadyEvent){
6491                 initDocReady();
6492             }
6493             docReadyEvent.addListener(fn, scope, options);
6494         },
6495         
6496         /**
6497          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6498          * @param {Function} fn        The method the event invokes
6499          * @param {Object}   scope    An object that becomes the scope of the handler
6500          * @param {boolean}  options
6501          */
6502         onWindowResize : function(fn, scope, options){
6503             if(!resizeEvent){
6504                 resizeEvent = new Roo.util.Event();
6505                 resizeTask = new Roo.util.DelayedTask(function(){
6506                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6507                 });
6508                 E.on(window, "resize", function(){
6509                     if(Roo.isIE){
6510                         resizeTask.delay(50);
6511                     }else{
6512                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6513                     }
6514                 });
6515             }
6516             resizeEvent.addListener(fn, scope, options);
6517         },
6518
6519         /**
6520          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6521          * @param {Function} fn        The method the event invokes
6522          * @param {Object}   scope    An object that becomes the scope of the handler
6523          * @param {boolean}  options
6524          */
6525         onTextResize : function(fn, scope, options){
6526             if(!textEvent){
6527                 textEvent = new Roo.util.Event();
6528                 var textEl = new Roo.Element(document.createElement('div'));
6529                 textEl.dom.className = 'x-text-resize';
6530                 textEl.dom.innerHTML = 'X';
6531                 textEl.appendTo(document.body);
6532                 textSize = textEl.dom.offsetHeight;
6533                 setInterval(function(){
6534                     if(textEl.dom.offsetHeight != textSize){
6535                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6536                     }
6537                 }, this.textResizeInterval);
6538             }
6539             textEvent.addListener(fn, scope, options);
6540         },
6541
6542         /**
6543          * Removes the passed window resize listener.
6544          * @param {Function} fn        The method the event invokes
6545          * @param {Object}   scope    The scope of handler
6546          */
6547         removeResizeListener : function(fn, scope){
6548             if(resizeEvent){
6549                 resizeEvent.removeListener(fn, scope);
6550             }
6551         },
6552
6553         // private
6554         fireResize : function(){
6555             if(resizeEvent){
6556                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6557             }   
6558         },
6559         /**
6560          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6561          */
6562         ieDeferSrc : false,
6563         /**
6564          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6565          */
6566         textResizeInterval : 50
6567     };
6568     
6569     /**
6570      * Fix for doc tools
6571      * @scopeAlias pub=Roo.EventManager
6572      */
6573     
6574      /**
6575      * Appends an event handler to an element (shorthand for addListener)
6576      * @param {String/HTMLElement}   element        The html element or id to assign the
6577      * @param {String}   eventName The type of event to listen for
6578      * @param {Function} handler The method the event invokes
6579      * @param {Object}   scope (optional) The scope in which to execute the handler
6580      * function. The handler function's "this" context.
6581      * @param {Object}   options (optional) An object containing handler configuration
6582      * properties. This may contain any of the following properties:<ul>
6583      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6584      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6585      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6586      * <li>preventDefault {Boolean} True to prevent the default action</li>
6587      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6588      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6589      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6590      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6591      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6592      * by the specified number of milliseconds. If the event fires again within that time, the original
6593      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6594      * </ul><br>
6595      * <p>
6596      * <b>Combining Options</b><br>
6597      * Using the options argument, it is possible to combine different types of listeners:<br>
6598      * <br>
6599      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6600      * Code:<pre><code>
6601 el.on('click', this.onClick, this, {
6602     single: true,
6603     delay: 100,
6604     stopEvent : true,
6605     forumId: 4
6606 });</code></pre>
6607      * <p>
6608      * <b>Attaching multiple handlers in 1 call</b><br>
6609       * The method also allows for a single argument to be passed which is a config object containing properties
6610      * which specify multiple handlers.
6611      * <p>
6612      * Code:<pre><code>
6613 el.on({
6614     'click' : {
6615         fn: this.onClick
6616         scope: this,
6617         delay: 100
6618     },
6619     'mouseover' : {
6620         fn: this.onMouseOver
6621         scope: this
6622     },
6623     'mouseout' : {
6624         fn: this.onMouseOut
6625         scope: this
6626     }
6627 });</code></pre>
6628      * <p>
6629      * Or a shorthand syntax:<br>
6630      * Code:<pre><code>
6631 el.on({
6632     'click' : this.onClick,
6633     'mouseover' : this.onMouseOver,
6634     'mouseout' : this.onMouseOut
6635     scope: this
6636 });</code></pre>
6637      */
6638     pub.on = pub.addListener;
6639     pub.un = pub.removeListener;
6640
6641     pub.stoppedMouseDownEvent = new Roo.util.Event();
6642     return pub;
6643 }();
6644 /**
6645   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6646   * @param {Function} fn        The method the event invokes
6647   * @param {Object}   scope    An  object that becomes the scope of the handler
6648   * @param {boolean}  override If true, the obj passed in becomes
6649   *                             the execution scope of the listener
6650   * @member Roo
6651   * @method onReady
6652  */
6653 Roo.onReady = Roo.EventManager.onDocumentReady;
6654
6655 Roo.onReady(function(){
6656     var bd = Roo.get(document.body);
6657     if(!bd){ return; }
6658
6659     var cls = [
6660             Roo.isIE ? "roo-ie"
6661             : Roo.isIE11 ? "roo-ie11"
6662             : Roo.isEdge ? "roo-edge"
6663             : Roo.isGecko ? "roo-gecko"
6664             : Roo.isOpera ? "roo-opera"
6665             : Roo.isSafari ? "roo-safari" : ""];
6666
6667     if(Roo.isMac){
6668         cls.push("roo-mac");
6669     }
6670     if(Roo.isLinux){
6671         cls.push("roo-linux");
6672     }
6673     if(Roo.isIOS){
6674         cls.push("roo-ios");
6675     }
6676     if(Roo.isTouch){
6677         cls.push("roo-touch");
6678     }
6679     if(Roo.isBorderBox){
6680         cls.push('roo-border-box');
6681     }
6682     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6683         var p = bd.dom.parentNode;
6684         if(p){
6685             p.className += ' roo-strict';
6686         }
6687     }
6688     bd.addClass(cls.join(' '));
6689 });
6690
6691 /**
6692  * @class Roo.EventObject
6693  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6694  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6695  * Example:
6696  * <pre><code>
6697  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6698     e.preventDefault();
6699     var target = e.getTarget();
6700     ...
6701  }
6702  var myDiv = Roo.get("myDiv");
6703  myDiv.on("click", handleClick);
6704  //or
6705  Roo.EventManager.on("myDiv", 'click', handleClick);
6706  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6707  </code></pre>
6708  * @singleton
6709  */
6710 Roo.EventObject = function(){
6711     
6712     var E = Roo.lib.Event;
6713     
6714     // safari keypress events for special keys return bad keycodes
6715     var safariKeys = {
6716         63234 : 37, // left
6717         63235 : 39, // right
6718         63232 : 38, // up
6719         63233 : 40, // down
6720         63276 : 33, // page up
6721         63277 : 34, // page down
6722         63272 : 46, // delete
6723         63273 : 36, // home
6724         63275 : 35  // end
6725     };
6726
6727     // normalize button clicks
6728     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6729                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6730
6731     Roo.EventObjectImpl = function(e){
6732         if(e){
6733             this.setEvent(e.browserEvent || e);
6734         }
6735     };
6736     Roo.EventObjectImpl.prototype = {
6737         /**
6738          * Used to fix doc tools.
6739          * @scope Roo.EventObject.prototype
6740          */
6741             
6742
6743         
6744         
6745         /** The normal browser event */
6746         browserEvent : null,
6747         /** The button pressed in a mouse event */
6748         button : -1,
6749         /** True if the shift key was down during the event */
6750         shiftKey : false,
6751         /** True if the control key was down during the event */
6752         ctrlKey : false,
6753         /** True if the alt key was down during the event */
6754         altKey : false,
6755
6756         /** Key constant 
6757         * @type Number */
6758         BACKSPACE : 8,
6759         /** Key constant 
6760         * @type Number */
6761         TAB : 9,
6762         /** Key constant 
6763         * @type Number */
6764         RETURN : 13,
6765         /** Key constant 
6766         * @type Number */
6767         ENTER : 13,
6768         /** Key constant 
6769         * @type Number */
6770         SHIFT : 16,
6771         /** Key constant 
6772         * @type Number */
6773         CONTROL : 17,
6774         /** Key constant 
6775         * @type Number */
6776         ESC : 27,
6777         /** Key constant 
6778         * @type Number */
6779         SPACE : 32,
6780         /** Key constant 
6781         * @type Number */
6782         PAGEUP : 33,
6783         /** Key constant 
6784         * @type Number */
6785         PAGEDOWN : 34,
6786         /** Key constant 
6787         * @type Number */
6788         END : 35,
6789         /** Key constant 
6790         * @type Number */
6791         HOME : 36,
6792         /** Key constant 
6793         * @type Number */
6794         LEFT : 37,
6795         /** Key constant 
6796         * @type Number */
6797         UP : 38,
6798         /** Key constant 
6799         * @type Number */
6800         RIGHT : 39,
6801         /** Key constant 
6802         * @type Number */
6803         DOWN : 40,
6804         /** Key constant 
6805         * @type Number */
6806         DELETE : 46,
6807         /** Key constant 
6808         * @type Number */
6809         F5 : 116,
6810
6811            /** @private */
6812         setEvent : function(e){
6813             if(e == this || (e && e.browserEvent)){ // already wrapped
6814                 return e;
6815             }
6816             this.browserEvent = e;
6817             if(e){
6818                 // normalize buttons
6819                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6820                 if(e.type == 'click' && this.button == -1){
6821                     this.button = 0;
6822                 }
6823                 this.type = e.type;
6824                 this.shiftKey = e.shiftKey;
6825                 // mac metaKey behaves like ctrlKey
6826                 this.ctrlKey = e.ctrlKey || e.metaKey;
6827                 this.altKey = e.altKey;
6828                 // in getKey these will be normalized for the mac
6829                 this.keyCode = e.keyCode;
6830                 // keyup warnings on firefox.
6831                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6832                 // cache the target for the delayed and or buffered events
6833                 this.target = E.getTarget(e);
6834                 // same for XY
6835                 this.xy = E.getXY(e);
6836             }else{
6837                 this.button = -1;
6838                 this.shiftKey = false;
6839                 this.ctrlKey = false;
6840                 this.altKey = false;
6841                 this.keyCode = 0;
6842                 this.charCode =0;
6843                 this.target = null;
6844                 this.xy = [0, 0];
6845             }
6846             return this;
6847         },
6848
6849         /**
6850          * Stop the event (preventDefault and stopPropagation)
6851          */
6852         stopEvent : function(){
6853             if(this.browserEvent){
6854                 if(this.browserEvent.type == 'mousedown'){
6855                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6856                 }
6857                 E.stopEvent(this.browserEvent);
6858             }
6859         },
6860
6861         /**
6862          * Prevents the browsers default handling of the event.
6863          */
6864         preventDefault : function(){
6865             if(this.browserEvent){
6866                 E.preventDefault(this.browserEvent);
6867             }
6868         },
6869
6870         /** @private */
6871         isNavKeyPress : function(){
6872             var k = this.keyCode;
6873             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6874             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6875         },
6876
6877         isSpecialKey : function(){
6878             var k = this.keyCode;
6879             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6880             (k == 16) || (k == 17) ||
6881             (k >= 18 && k <= 20) ||
6882             (k >= 33 && k <= 35) ||
6883             (k >= 36 && k <= 39) ||
6884             (k >= 44 && k <= 45);
6885         },
6886         /**
6887          * Cancels bubbling of the event.
6888          */
6889         stopPropagation : function(){
6890             if(this.browserEvent){
6891                 if(this.type == 'mousedown'){
6892                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6893                 }
6894                 E.stopPropagation(this.browserEvent);
6895             }
6896         },
6897
6898         /**
6899          * Gets the key code for the event.
6900          * @return {Number}
6901          */
6902         getCharCode : function(){
6903             return this.charCode || this.keyCode;
6904         },
6905
6906         /**
6907          * Returns a normalized keyCode for the event.
6908          * @return {Number} The key code
6909          */
6910         getKey : function(){
6911             var k = this.keyCode || this.charCode;
6912             return Roo.isSafari ? (safariKeys[k] || k) : k;
6913         },
6914
6915         /**
6916          * Gets the x coordinate of the event.
6917          * @return {Number}
6918          */
6919         getPageX : function(){
6920             return this.xy[0];
6921         },
6922
6923         /**
6924          * Gets the y coordinate of the event.
6925          * @return {Number}
6926          */
6927         getPageY : function(){
6928             return this.xy[1];
6929         },
6930
6931         /**
6932          * Gets the time of the event.
6933          * @return {Number}
6934          */
6935         getTime : function(){
6936             if(this.browserEvent){
6937                 return E.getTime(this.browserEvent);
6938             }
6939             return null;
6940         },
6941
6942         /**
6943          * Gets the page coordinates of the event.
6944          * @return {Array} The xy values like [x, y]
6945          */
6946         getXY : function(){
6947             return this.xy;
6948         },
6949
6950         /**
6951          * Gets the target for the event.
6952          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6953          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6954                 search as a number or element (defaults to 10 || document.body)
6955          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6956          * @return {HTMLelement}
6957          */
6958         getTarget : function(selector, maxDepth, returnEl){
6959             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6960         },
6961         /**
6962          * Gets the related target.
6963          * @return {HTMLElement}
6964          */
6965         getRelatedTarget : function(){
6966             if(this.browserEvent){
6967                 return E.getRelatedTarget(this.browserEvent);
6968             }
6969             return null;
6970         },
6971
6972         /**
6973          * Normalizes mouse wheel delta across browsers
6974          * @return {Number} The delta
6975          */
6976         getWheelDelta : function(){
6977             var e = this.browserEvent;
6978             var delta = 0;
6979             if(e.wheelDelta){ /* IE/Opera. */
6980                 delta = e.wheelDelta/120;
6981             }else if(e.detail){ /* Mozilla case. */
6982                 delta = -e.detail/3;
6983             }
6984             return delta;
6985         },
6986
6987         /**
6988          * Returns true if the control, meta, shift or alt key was pressed during this event.
6989          * @return {Boolean}
6990          */
6991         hasModifier : function(){
6992             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6993         },
6994
6995         /**
6996          * Returns true if the target of this event equals el or is a child of el
6997          * @param {String/HTMLElement/Element} el
6998          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6999          * @return {Boolean}
7000          */
7001         within : function(el, related){
7002             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7003             return t && Roo.fly(el).contains(t);
7004         },
7005
7006         getPoint : function(){
7007             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7008         }
7009     };
7010
7011     return new Roo.EventObjectImpl();
7012 }();
7013             
7014     /*
7015  * Based on:
7016  * Ext JS Library 1.1.1
7017  * Copyright(c) 2006-2007, Ext JS, LLC.
7018  *
7019  * Originally Released Under LGPL - original licence link has changed is not relivant.
7020  *
7021  * Fork - LGPL
7022  * <script type="text/javascript">
7023  */
7024
7025  
7026 // was in Composite Element!??!?!
7027  
7028 (function(){
7029     var D = Roo.lib.Dom;
7030     var E = Roo.lib.Event;
7031     var A = Roo.lib.Anim;
7032
7033     // local style camelizing for speed
7034     var propCache = {};
7035     var camelRe = /(-[a-z])/gi;
7036     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7037     var view = document.defaultView;
7038
7039 /**
7040  * @class Roo.Element
7041  * Represents an Element in the DOM.<br><br>
7042  * Usage:<br>
7043 <pre><code>
7044 var el = Roo.get("my-div");
7045
7046 // or with getEl
7047 var el = getEl("my-div");
7048
7049 // or with a DOM element
7050 var el = Roo.get(myDivElement);
7051 </code></pre>
7052  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7053  * each call instead of constructing a new one.<br><br>
7054  * <b>Animations</b><br />
7055  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7056  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7057 <pre>
7058 Option    Default   Description
7059 --------- --------  ---------------------------------------------
7060 duration  .35       The duration of the animation in seconds
7061 easing    easeOut   The YUI easing method
7062 callback  none      A function to execute when the anim completes
7063 scope     this      The scope (this) of the callback function
7064 </pre>
7065 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7066 * manipulate the animation. Here's an example:
7067 <pre><code>
7068 var el = Roo.get("my-div");
7069
7070 // no animation
7071 el.setWidth(100);
7072
7073 // default animation
7074 el.setWidth(100, true);
7075
7076 // animation with some options set
7077 el.setWidth(100, {
7078     duration: 1,
7079     callback: this.foo,
7080     scope: this
7081 });
7082
7083 // using the "anim" property to get the Anim object
7084 var opt = {
7085     duration: 1,
7086     callback: this.foo,
7087     scope: this
7088 };
7089 el.setWidth(100, opt);
7090 ...
7091 if(opt.anim.isAnimated()){
7092     opt.anim.stop();
7093 }
7094 </code></pre>
7095 * <b> Composite (Collections of) Elements</b><br />
7096  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7097  * @constructor Create a new Element directly.
7098  * @param {String/HTMLElement} element
7099  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
7100  */
7101     Roo.Element = function(element, forceNew){
7102         var dom = typeof element == "string" ?
7103                 document.getElementById(element) : element;
7104         if(!dom){ // invalid id/element
7105             return null;
7106         }
7107         var id = dom.id;
7108         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7109             return Roo.Element.cache[id];
7110         }
7111
7112         /**
7113          * The DOM element
7114          * @type HTMLElement
7115          */
7116         this.dom = dom;
7117
7118         /**
7119          * The DOM element ID
7120          * @type String
7121          */
7122         this.id = id || Roo.id(dom);
7123     };
7124
7125     var El = Roo.Element;
7126
7127     El.prototype = {
7128         /**
7129          * The element's default display mode  (defaults to "")
7130          * @type String
7131          */
7132         originalDisplay : "",
7133
7134         visibilityMode : 1,
7135         /**
7136          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7137          * @type String
7138          */
7139         defaultUnit : "px",
7140         
7141         /**
7142          * Sets the element's visibility mode. When setVisible() is called it
7143          * will use this to determine whether to set the visibility or the display property.
7144          * @param visMode Element.VISIBILITY or Element.DISPLAY
7145          * @return {Roo.Element} this
7146          */
7147         setVisibilityMode : function(visMode){
7148             this.visibilityMode = visMode;
7149             return this;
7150         },
7151         /**
7152          * Convenience method for setVisibilityMode(Element.DISPLAY)
7153          * @param {String} display (optional) What to set display to when visible
7154          * @return {Roo.Element} this
7155          */
7156         enableDisplayMode : function(display){
7157             this.setVisibilityMode(El.DISPLAY);
7158             if(typeof display != "undefined") { this.originalDisplay = display; }
7159             return this;
7160         },
7161
7162         /**
7163          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7164          * @param {String} selector The simple selector to test
7165          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7166                 search as a number or element (defaults to 10 || document.body)
7167          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7168          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7169          */
7170         findParent : function(simpleSelector, maxDepth, returnEl){
7171             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7172             maxDepth = maxDepth || 50;
7173             if(typeof maxDepth != "number"){
7174                 stopEl = Roo.getDom(maxDepth);
7175                 maxDepth = 10;
7176             }
7177             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7178                 if(dq.is(p, simpleSelector)){
7179                     return returnEl ? Roo.get(p) : p;
7180                 }
7181                 depth++;
7182                 p = p.parentNode;
7183             }
7184             return null;
7185         },
7186
7187
7188         /**
7189          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7190          * @param {String} selector The simple selector to test
7191          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7192                 search as a number or element (defaults to 10 || document.body)
7193          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7194          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7195          */
7196         findParentNode : function(simpleSelector, maxDepth, returnEl){
7197             var p = Roo.fly(this.dom.parentNode, '_internal');
7198             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7199         },
7200         
7201         /**
7202          * Looks at  the scrollable parent element
7203          */
7204         findScrollableParent : function()
7205         {
7206             var overflowRegex = /(auto|scroll)/;
7207             
7208             if(this.getStyle('position') === 'fixed'){
7209                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7210             }
7211             
7212             var excludeStaticParent = this.getStyle('position') === "absolute";
7213             
7214             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7215                 
7216                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7217                     continue;
7218                 }
7219                 
7220                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7221                     return parent;
7222                 }
7223                 
7224                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7225                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7226                 }
7227             }
7228             
7229             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7230         },
7231
7232         /**
7233          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7234          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7235          * @param {String} selector The simple selector to test
7236          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7237                 search as a number or element (defaults to 10 || document.body)
7238          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7239          */
7240         up : function(simpleSelector, maxDepth){
7241             return this.findParentNode(simpleSelector, maxDepth, true);
7242         },
7243
7244
7245
7246         /**
7247          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7248          * @param {String} selector The simple selector to test
7249          * @return {Boolean} True if this element matches the selector, else false
7250          */
7251         is : function(simpleSelector){
7252             return Roo.DomQuery.is(this.dom, simpleSelector);
7253         },
7254
7255         /**
7256          * Perform animation on this element.
7257          * @param {Object} args The YUI animation control args
7258          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7259          * @param {Function} onComplete (optional) Function to call when animation completes
7260          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7261          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7262          * @return {Roo.Element} this
7263          */
7264         animate : function(args, duration, onComplete, easing, animType){
7265             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7266             return this;
7267         },
7268
7269         /*
7270          * @private Internal animation call
7271          */
7272         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7273             animType = animType || 'run';
7274             opt = opt || {};
7275             var anim = Roo.lib.Anim[animType](
7276                 this.dom, args,
7277                 (opt.duration || defaultDur) || .35,
7278                 (opt.easing || defaultEase) || 'easeOut',
7279                 function(){
7280                     Roo.callback(cb, this);
7281                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7282                 },
7283                 this
7284             );
7285             opt.anim = anim;
7286             return anim;
7287         },
7288
7289         // private legacy anim prep
7290         preanim : function(a, i){
7291             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7292         },
7293
7294         /**
7295          * Removes worthless text nodes
7296          * @param {Boolean} forceReclean (optional) By default the element
7297          * keeps track if it has been cleaned already so
7298          * you can call this over and over. However, if you update the element and
7299          * need to force a reclean, you can pass true.
7300          */
7301         clean : function(forceReclean){
7302             if(this.isCleaned && forceReclean !== true){
7303                 return this;
7304             }
7305             var ns = /\S/;
7306             var d = this.dom, n = d.firstChild, ni = -1;
7307             while(n){
7308                 var nx = n.nextSibling;
7309                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7310                     d.removeChild(n);
7311                 }else{
7312                     n.nodeIndex = ++ni;
7313                 }
7314                 n = nx;
7315             }
7316             this.isCleaned = true;
7317             return this;
7318         },
7319
7320         // private
7321         calcOffsetsTo : function(el){
7322             el = Roo.get(el);
7323             var d = el.dom;
7324             var restorePos = false;
7325             if(el.getStyle('position') == 'static'){
7326                 el.position('relative');
7327                 restorePos = true;
7328             }
7329             var x = 0, y =0;
7330             var op = this.dom;
7331             while(op && op != d && op.tagName != 'HTML'){
7332                 x+= op.offsetLeft;
7333                 y+= op.offsetTop;
7334                 op = op.offsetParent;
7335             }
7336             if(restorePos){
7337                 el.position('static');
7338             }
7339             return [x, y];
7340         },
7341
7342         /**
7343          * Scrolls this element into view within the passed container.
7344          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7345          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7346          * @return {Roo.Element} this
7347          */
7348         scrollIntoView : function(container, hscroll){
7349             var c = Roo.getDom(container) || document.body;
7350             var el = this.dom;
7351
7352             var o = this.calcOffsetsTo(c),
7353                 l = o[0],
7354                 t = o[1],
7355                 b = t+el.offsetHeight,
7356                 r = l+el.offsetWidth;
7357
7358             var ch = c.clientHeight;
7359             var ct = parseInt(c.scrollTop, 10);
7360             var cl = parseInt(c.scrollLeft, 10);
7361             var cb = ct + ch;
7362             var cr = cl + c.clientWidth;
7363
7364             if(t < ct){
7365                 c.scrollTop = t;
7366             }else if(b > cb){
7367                 c.scrollTop = b-ch;
7368             }
7369
7370             if(hscroll !== false){
7371                 if(l < cl){
7372                     c.scrollLeft = l;
7373                 }else if(r > cr){
7374                     c.scrollLeft = r-c.clientWidth;
7375                 }
7376             }
7377             return this;
7378         },
7379
7380         // private
7381         scrollChildIntoView : function(child, hscroll){
7382             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7383         },
7384
7385         /**
7386          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7387          * the new height may not be available immediately.
7388          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7389          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7390          * @param {Function} onComplete (optional) Function to call when animation completes
7391          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7392          * @return {Roo.Element} this
7393          */
7394         autoHeight : function(animate, duration, onComplete, easing){
7395             var oldHeight = this.getHeight();
7396             this.clip();
7397             this.setHeight(1); // force clipping
7398             setTimeout(function(){
7399                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7400                 if(!animate){
7401                     this.setHeight(height);
7402                     this.unclip();
7403                     if(typeof onComplete == "function"){
7404                         onComplete();
7405                     }
7406                 }else{
7407                     this.setHeight(oldHeight); // restore original height
7408                     this.setHeight(height, animate, duration, function(){
7409                         this.unclip();
7410                         if(typeof onComplete == "function") { onComplete(); }
7411                     }.createDelegate(this), easing);
7412                 }
7413             }.createDelegate(this), 0);
7414             return this;
7415         },
7416
7417         /**
7418          * Returns true if this element is an ancestor of the passed element
7419          * @param {HTMLElement/String} el The element to check
7420          * @return {Boolean} True if this element is an ancestor of el, else false
7421          */
7422         contains : function(el){
7423             if(!el){return false;}
7424             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7425         },
7426
7427         /**
7428          * Checks whether the element is currently visible using both visibility and display properties.
7429          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7430          * @return {Boolean} True if the element is currently visible, else false
7431          */
7432         isVisible : function(deep) {
7433             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7434             if(deep !== true || !vis){
7435                 return vis;
7436             }
7437             var p = this.dom.parentNode;
7438             while(p && p.tagName.toLowerCase() != "body"){
7439                 if(!Roo.fly(p, '_isVisible').isVisible()){
7440                     return false;
7441                 }
7442                 p = p.parentNode;
7443             }
7444             return true;
7445         },
7446
7447         /**
7448          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7449          * @param {String} selector The CSS selector
7450          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7451          * @return {CompositeElement/CompositeElementLite} The composite element
7452          */
7453         select : function(selector, unique){
7454             return El.select(selector, unique, this.dom);
7455         },
7456
7457         /**
7458          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7459          * @param {String} selector The CSS selector
7460          * @return {Array} An array of the matched nodes
7461          */
7462         query : function(selector, unique){
7463             return Roo.DomQuery.select(selector, this.dom);
7464         },
7465
7466         /**
7467          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7468          * @param {String} selector The CSS selector
7469          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7470          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7471          */
7472         child : function(selector, returnDom){
7473             var n = Roo.DomQuery.selectNode(selector, this.dom);
7474             return returnDom ? n : Roo.get(n);
7475         },
7476
7477         /**
7478          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7479          * @param {String} selector The CSS selector
7480          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7481          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7482          */
7483         down : function(selector, returnDom){
7484             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7485             return returnDom ? n : Roo.get(n);
7486         },
7487
7488         /**
7489          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7490          * @param {String} group The group the DD object is member of
7491          * @param {Object} config The DD config object
7492          * @param {Object} overrides An object containing methods to override/implement on the DD object
7493          * @return {Roo.dd.DD} The DD object
7494          */
7495         initDD : function(group, config, overrides){
7496             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7497             return Roo.apply(dd, overrides);
7498         },
7499
7500         /**
7501          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7502          * @param {String} group The group the DDProxy object is member of
7503          * @param {Object} config The DDProxy config object
7504          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7505          * @return {Roo.dd.DDProxy} The DDProxy object
7506          */
7507         initDDProxy : function(group, config, overrides){
7508             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7509             return Roo.apply(dd, overrides);
7510         },
7511
7512         /**
7513          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7514          * @param {String} group The group the DDTarget object is member of
7515          * @param {Object} config The DDTarget config object
7516          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7517          * @return {Roo.dd.DDTarget} The DDTarget object
7518          */
7519         initDDTarget : function(group, config, overrides){
7520             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7521             return Roo.apply(dd, overrides);
7522         },
7523
7524         /**
7525          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7526          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7527          * @param {Boolean} visible Whether the element is visible
7528          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7529          * @return {Roo.Element} this
7530          */
7531          setVisible : function(visible, animate){
7532             if(!animate || !A){
7533                 if(this.visibilityMode == El.DISPLAY){
7534                     this.setDisplayed(visible);
7535                 }else{
7536                     this.fixDisplay();
7537                     this.dom.style.visibility = visible ? "visible" : "hidden";
7538                 }
7539             }else{
7540                 // closure for composites
7541                 var dom = this.dom;
7542                 var visMode = this.visibilityMode;
7543                 if(visible){
7544                     this.setOpacity(.01);
7545                     this.setVisible(true);
7546                 }
7547                 this.anim({opacity: { to: (visible?1:0) }},
7548                       this.preanim(arguments, 1),
7549                       null, .35, 'easeIn', function(){
7550                          if(!visible){
7551                              if(visMode == El.DISPLAY){
7552                                  dom.style.display = "none";
7553                              }else{
7554                                  dom.style.visibility = "hidden";
7555                              }
7556                              Roo.get(dom).setOpacity(1);
7557                          }
7558                      });
7559             }
7560             return this;
7561         },
7562
7563         /**
7564          * Returns true if display is not "none"
7565          * @return {Boolean}
7566          */
7567         isDisplayed : function() {
7568             return this.getStyle("display") != "none";
7569         },
7570
7571         /**
7572          * Toggles the element's visibility or display, depending on visibility mode.
7573          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7574          * @return {Roo.Element} this
7575          */
7576         toggle : function(animate){
7577             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7578             return this;
7579         },
7580
7581         /**
7582          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7583          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7584          * @return {Roo.Element} this
7585          */
7586         setDisplayed : function(value) {
7587             if(typeof value == "boolean"){
7588                value = value ? this.originalDisplay : "none";
7589             }
7590             this.setStyle("display", value);
7591             return this;
7592         },
7593
7594         /**
7595          * Tries to focus the element. Any exceptions are caught and ignored.
7596          * @return {Roo.Element} this
7597          */
7598         focus : function() {
7599             try{
7600                 this.dom.focus();
7601             }catch(e){}
7602             return this;
7603         },
7604
7605         /**
7606          * Tries to blur the element. Any exceptions are caught and ignored.
7607          * @return {Roo.Element} this
7608          */
7609         blur : function() {
7610             try{
7611                 this.dom.blur();
7612             }catch(e){}
7613             return this;
7614         },
7615
7616         /**
7617          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7618          * @param {String/Array} className The CSS class to add, or an array of classes
7619          * @return {Roo.Element} this
7620          */
7621         addClass : function(className){
7622             if(className instanceof Array){
7623                 for(var i = 0, len = className.length; i < len; i++) {
7624                     this.addClass(className[i]);
7625                 }
7626             }else{
7627                 if(className && !this.hasClass(className)){
7628                     this.dom.className = this.dom.className + " " + className;
7629                 }
7630             }
7631             return this;
7632         },
7633
7634         /**
7635          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7636          * @param {String/Array} className The CSS class to add, or an array of classes
7637          * @return {Roo.Element} this
7638          */
7639         radioClass : function(className){
7640             var siblings = this.dom.parentNode.childNodes;
7641             for(var i = 0; i < siblings.length; i++) {
7642                 var s = siblings[i];
7643                 if(s.nodeType == 1){
7644                     Roo.get(s).removeClass(className);
7645                 }
7646             }
7647             this.addClass(className);
7648             return this;
7649         },
7650
7651         /**
7652          * Removes one or more CSS classes from the element.
7653          * @param {String/Array} className The CSS class to remove, or an array of classes
7654          * @return {Roo.Element} this
7655          */
7656         removeClass : function(className){
7657             if(!className || !this.dom.className){
7658                 return this;
7659             }
7660             if(className instanceof Array){
7661                 for(var i = 0, len = className.length; i < len; i++) {
7662                     this.removeClass(className[i]);
7663                 }
7664             }else{
7665                 if(this.hasClass(className)){
7666                     var re = this.classReCache[className];
7667                     if (!re) {
7668                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7669                        this.classReCache[className] = re;
7670                     }
7671                     this.dom.className =
7672                         this.dom.className.replace(re, " ");
7673                 }
7674             }
7675             return this;
7676         },
7677
7678         // private
7679         classReCache: {},
7680
7681         /**
7682          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7683          * @param {String} className The CSS class to toggle
7684          * @return {Roo.Element} this
7685          */
7686         toggleClass : function(className){
7687             if(this.hasClass(className)){
7688                 this.removeClass(className);
7689             }else{
7690                 this.addClass(className);
7691             }
7692             return this;
7693         },
7694
7695         /**
7696          * Checks if the specified CSS class exists on this element's DOM node.
7697          * @param {String} className The CSS class to check for
7698          * @return {Boolean} True if the class exists, else false
7699          */
7700         hasClass : function(className){
7701             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7702         },
7703
7704         /**
7705          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7706          * @param {String} oldClassName The CSS class to replace
7707          * @param {String} newClassName The replacement CSS class
7708          * @return {Roo.Element} this
7709          */
7710         replaceClass : function(oldClassName, newClassName){
7711             this.removeClass(oldClassName);
7712             this.addClass(newClassName);
7713             return this;
7714         },
7715
7716         /**
7717          * Returns an object with properties matching the styles requested.
7718          * For example, el.getStyles('color', 'font-size', 'width') might return
7719          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7720          * @param {String} style1 A style name
7721          * @param {String} style2 A style name
7722          * @param {String} etc.
7723          * @return {Object} The style object
7724          */
7725         getStyles : function(){
7726             var a = arguments, len = a.length, r = {};
7727             for(var i = 0; i < len; i++){
7728                 r[a[i]] = this.getStyle(a[i]);
7729             }
7730             return r;
7731         },
7732
7733         /**
7734          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7735          * @param {String} property The style property whose value is returned.
7736          * @return {String} The current value of the style property for this element.
7737          */
7738         getStyle : function(){
7739             return view && view.getComputedStyle ?
7740                 function(prop){
7741                     var el = this.dom, v, cs, camel;
7742                     if(prop == 'float'){
7743                         prop = "cssFloat";
7744                     }
7745                     if(el.style && (v = el.style[prop])){
7746                         return v;
7747                     }
7748                     if(cs = view.getComputedStyle(el, "")){
7749                         if(!(camel = propCache[prop])){
7750                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7751                         }
7752                         return cs[camel];
7753                     }
7754                     return null;
7755                 } :
7756                 function(prop){
7757                     var el = this.dom, v, cs, camel;
7758                     if(prop == 'opacity'){
7759                         if(typeof el.style.filter == 'string'){
7760                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7761                             if(m){
7762                                 var fv = parseFloat(m[1]);
7763                                 if(!isNaN(fv)){
7764                                     return fv ? fv / 100 : 0;
7765                                 }
7766                             }
7767                         }
7768                         return 1;
7769                     }else if(prop == 'float'){
7770                         prop = "styleFloat";
7771                     }
7772                     if(!(camel = propCache[prop])){
7773                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7774                     }
7775                     if(v = el.style[camel]){
7776                         return v;
7777                     }
7778                     if(cs = el.currentStyle){
7779                         return cs[camel];
7780                     }
7781                     return null;
7782                 };
7783         }(),
7784
7785         /**
7786          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7787          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7788          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7789          * @return {Roo.Element} this
7790          */
7791         setStyle : function(prop, value){
7792             if(typeof prop == "string"){
7793                 
7794                 if (prop == 'float') {
7795                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7796                     return this;
7797                 }
7798                 
7799                 var camel;
7800                 if(!(camel = propCache[prop])){
7801                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7802                 }
7803                 
7804                 if(camel == 'opacity') {
7805                     this.setOpacity(value);
7806                 }else{
7807                     this.dom.style[camel] = value;
7808                 }
7809             }else{
7810                 for(var style in prop){
7811                     if(typeof prop[style] != "function"){
7812                        this.setStyle(style, prop[style]);
7813                     }
7814                 }
7815             }
7816             return this;
7817         },
7818
7819         /**
7820          * More flexible version of {@link #setStyle} for setting style properties.
7821          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7822          * a function which returns such a specification.
7823          * @return {Roo.Element} this
7824          */
7825         applyStyles : function(style){
7826             Roo.DomHelper.applyStyles(this.dom, style);
7827             return this;
7828         },
7829
7830         /**
7831           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7832           * @return {Number} The X position of the element
7833           */
7834         getX : function(){
7835             return D.getX(this.dom);
7836         },
7837
7838         /**
7839           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7840           * @return {Number} The Y position of the element
7841           */
7842         getY : function(){
7843             return D.getY(this.dom);
7844         },
7845
7846         /**
7847           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7848           * @return {Array} The XY position of the element
7849           */
7850         getXY : function(){
7851             return D.getXY(this.dom);
7852         },
7853
7854         /**
7855          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7856          * @param {Number} The X position of the element
7857          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7858          * @return {Roo.Element} this
7859          */
7860         setX : function(x, animate){
7861             if(!animate || !A){
7862                 D.setX(this.dom, x);
7863             }else{
7864                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7865             }
7866             return this;
7867         },
7868
7869         /**
7870          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7871          * @param {Number} The Y position of the element
7872          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7873          * @return {Roo.Element} this
7874          */
7875         setY : function(y, animate){
7876             if(!animate || !A){
7877                 D.setY(this.dom, y);
7878             }else{
7879                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7880             }
7881             return this;
7882         },
7883
7884         /**
7885          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7886          * @param {String} left The left CSS property value
7887          * @return {Roo.Element} this
7888          */
7889         setLeft : function(left){
7890             this.setStyle("left", this.addUnits(left));
7891             return this;
7892         },
7893
7894         /**
7895          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7896          * @param {String} top The top CSS property value
7897          * @return {Roo.Element} this
7898          */
7899         setTop : function(top){
7900             this.setStyle("top", this.addUnits(top));
7901             return this;
7902         },
7903
7904         /**
7905          * Sets the element's CSS right style.
7906          * @param {String} right The right CSS property value
7907          * @return {Roo.Element} this
7908          */
7909         setRight : function(right){
7910             this.setStyle("right", this.addUnits(right));
7911             return this;
7912         },
7913
7914         /**
7915          * Sets the element's CSS bottom style.
7916          * @param {String} bottom The bottom CSS property value
7917          * @return {Roo.Element} this
7918          */
7919         setBottom : function(bottom){
7920             this.setStyle("bottom", this.addUnits(bottom));
7921             return this;
7922         },
7923
7924         /**
7925          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7926          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7927          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7928          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7929          * @return {Roo.Element} this
7930          */
7931         setXY : function(pos, animate){
7932             if(!animate || !A){
7933                 D.setXY(this.dom, pos);
7934             }else{
7935                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7936             }
7937             return this;
7938         },
7939
7940         /**
7941          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7942          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7943          * @param {Number} x X value for new position (coordinates are page-based)
7944          * @param {Number} y Y value for new position (coordinates are page-based)
7945          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7946          * @return {Roo.Element} this
7947          */
7948         setLocation : function(x, y, animate){
7949             this.setXY([x, y], this.preanim(arguments, 2));
7950             return this;
7951         },
7952
7953         /**
7954          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7955          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7956          * @param {Number} x X value for new position (coordinates are page-based)
7957          * @param {Number} y Y value for new position (coordinates are page-based)
7958          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7959          * @return {Roo.Element} this
7960          */
7961         moveTo : function(x, y, animate){
7962             this.setXY([x, y], this.preanim(arguments, 2));
7963             return this;
7964         },
7965
7966         /**
7967          * Returns the region of the given element.
7968          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7969          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7970          */
7971         getRegion : function(){
7972             return D.getRegion(this.dom);
7973         },
7974
7975         /**
7976          * Returns the offset height of the element
7977          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7978          * @return {Number} The element's height
7979          */
7980         getHeight : function(contentHeight){
7981             var h = this.dom.offsetHeight || 0;
7982             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7983         },
7984
7985         /**
7986          * Returns the offset width of the element
7987          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7988          * @return {Number} The element's width
7989          */
7990         getWidth : function(contentWidth){
7991             var w = this.dom.offsetWidth || 0;
7992             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7993         },
7994
7995         /**
7996          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7997          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7998          * if a height has not been set using CSS.
7999          * @return {Number}
8000          */
8001         getComputedHeight : function(){
8002             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8003             if(!h){
8004                 h = parseInt(this.getStyle('height'), 10) || 0;
8005                 if(!this.isBorderBox()){
8006                     h += this.getFrameWidth('tb');
8007                 }
8008             }
8009             return h;
8010         },
8011
8012         /**
8013          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8014          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8015          * if a width has not been set using CSS.
8016          * @return {Number}
8017          */
8018         getComputedWidth : function(){
8019             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8020             if(!w){
8021                 w = parseInt(this.getStyle('width'), 10) || 0;
8022                 if(!this.isBorderBox()){
8023                     w += this.getFrameWidth('lr');
8024                 }
8025             }
8026             return w;
8027         },
8028
8029         /**
8030          * Returns the size of the element.
8031          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8032          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8033          */
8034         getSize : function(contentSize){
8035             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8036         },
8037
8038         /**
8039          * Returns the width and height of the viewport.
8040          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8041          */
8042         getViewSize : function(){
8043             var d = this.dom, doc = document, aw = 0, ah = 0;
8044             if(d == doc || d == doc.body){
8045                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8046             }else{
8047                 return {
8048                     width : d.clientWidth,
8049                     height: d.clientHeight
8050                 };
8051             }
8052         },
8053
8054         /**
8055          * Returns the value of the "value" attribute
8056          * @param {Boolean} asNumber true to parse the value as a number
8057          * @return {String/Number}
8058          */
8059         getValue : function(asNumber){
8060             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8061         },
8062
8063         // private
8064         adjustWidth : function(width){
8065             if(typeof width == "number"){
8066                 if(this.autoBoxAdjust && !this.isBorderBox()){
8067                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8068                 }
8069                 if(width < 0){
8070                     width = 0;
8071                 }
8072             }
8073             return width;
8074         },
8075
8076         // private
8077         adjustHeight : function(height){
8078             if(typeof height == "number"){
8079                if(this.autoBoxAdjust && !this.isBorderBox()){
8080                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8081                }
8082                if(height < 0){
8083                    height = 0;
8084                }
8085             }
8086             return height;
8087         },
8088
8089         /**
8090          * Set the width of the element
8091          * @param {Number} width The new width
8092          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8093          * @return {Roo.Element} this
8094          */
8095         setWidth : function(width, animate){
8096             width = this.adjustWidth(width);
8097             if(!animate || !A){
8098                 this.dom.style.width = this.addUnits(width);
8099             }else{
8100                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8101             }
8102             return this;
8103         },
8104
8105         /**
8106          * Set the height of the element
8107          * @param {Number} height The new height
8108          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8109          * @return {Roo.Element} this
8110          */
8111          setHeight : function(height, animate){
8112             height = this.adjustHeight(height);
8113             if(!animate || !A){
8114                 this.dom.style.height = this.addUnits(height);
8115             }else{
8116                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8117             }
8118             return this;
8119         },
8120
8121         /**
8122          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8123          * @param {Number} width The new width
8124          * @param {Number} height The new height
8125          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8126          * @return {Roo.Element} this
8127          */
8128          setSize : function(width, height, animate){
8129             if(typeof width == "object"){ // in case of object from getSize()
8130                 height = width.height; width = width.width;
8131             }
8132             width = this.adjustWidth(width); height = this.adjustHeight(height);
8133             if(!animate || !A){
8134                 this.dom.style.width = this.addUnits(width);
8135                 this.dom.style.height = this.addUnits(height);
8136             }else{
8137                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8138             }
8139             return this;
8140         },
8141
8142         /**
8143          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8144          * @param {Number} x X value for new position (coordinates are page-based)
8145          * @param {Number} y Y value for new position (coordinates are page-based)
8146          * @param {Number} width The new width
8147          * @param {Number} height The new height
8148          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8149          * @return {Roo.Element} this
8150          */
8151         setBounds : function(x, y, width, height, animate){
8152             if(!animate || !A){
8153                 this.setSize(width, height);
8154                 this.setLocation(x, y);
8155             }else{
8156                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8157                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8158                               this.preanim(arguments, 4), 'motion');
8159             }
8160             return this;
8161         },
8162
8163         /**
8164          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
8165          * @param {Roo.lib.Region} region The region to fill
8166          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8167          * @return {Roo.Element} this
8168          */
8169         setRegion : function(region, animate){
8170             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8171             return this;
8172         },
8173
8174         /**
8175          * Appends an event handler
8176          *
8177          * @param {String}   eventName     The type of event to append
8178          * @param {Function} fn        The method the event invokes
8179          * @param {Object} scope       (optional) The scope (this object) of the fn
8180          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8181          */
8182         addListener : function(eventName, fn, scope, options){
8183             if (this.dom) {
8184                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8185             }
8186         },
8187
8188         /**
8189          * Removes an event handler from this element
8190          * @param {String} eventName the type of event to remove
8191          * @param {Function} fn the method the event invokes
8192          * @return {Roo.Element} this
8193          */
8194         removeListener : function(eventName, fn){
8195             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8196             return this;
8197         },
8198
8199         /**
8200          * Removes all previous added listeners from this element
8201          * @return {Roo.Element} this
8202          */
8203         removeAllListeners : function(){
8204             E.purgeElement(this.dom);
8205             return this;
8206         },
8207
8208         relayEvent : function(eventName, observable){
8209             this.on(eventName, function(e){
8210                 observable.fireEvent(eventName, e);
8211             });
8212         },
8213
8214         /**
8215          * Set the opacity of the element
8216          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8217          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8218          * @return {Roo.Element} this
8219          */
8220          setOpacity : function(opacity, animate){
8221             if(!animate || !A){
8222                 var s = this.dom.style;
8223                 if(Roo.isIE){
8224                     s.zoom = 1;
8225                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8226                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8227                 }else{
8228                     s.opacity = opacity;
8229                 }
8230             }else{
8231                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8232             }
8233             return this;
8234         },
8235
8236         /**
8237          * Gets the left X coordinate
8238          * @param {Boolean} local True to get the local css position instead of page coordinate
8239          * @return {Number}
8240          */
8241         getLeft : function(local){
8242             if(!local){
8243                 return this.getX();
8244             }else{
8245                 return parseInt(this.getStyle("left"), 10) || 0;
8246             }
8247         },
8248
8249         /**
8250          * Gets the right X coordinate of the element (element X position + element width)
8251          * @param {Boolean} local True to get the local css position instead of page coordinate
8252          * @return {Number}
8253          */
8254         getRight : function(local){
8255             if(!local){
8256                 return this.getX() + this.getWidth();
8257             }else{
8258                 return (this.getLeft(true) + this.getWidth()) || 0;
8259             }
8260         },
8261
8262         /**
8263          * Gets the top Y coordinate
8264          * @param {Boolean} local True to get the local css position instead of page coordinate
8265          * @return {Number}
8266          */
8267         getTop : function(local) {
8268             if(!local){
8269                 return this.getY();
8270             }else{
8271                 return parseInt(this.getStyle("top"), 10) || 0;
8272             }
8273         },
8274
8275         /**
8276          * Gets the bottom Y coordinate of the element (element Y position + element height)
8277          * @param {Boolean} local True to get the local css position instead of page coordinate
8278          * @return {Number}
8279          */
8280         getBottom : function(local){
8281             if(!local){
8282                 return this.getY() + this.getHeight();
8283             }else{
8284                 return (this.getTop(true) + this.getHeight()) || 0;
8285             }
8286         },
8287
8288         /**
8289         * Initializes positioning on this element. If a desired position is not passed, it will make the
8290         * the element positioned relative IF it is not already positioned.
8291         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8292         * @param {Number} zIndex (optional) The zIndex to apply
8293         * @param {Number} x (optional) Set the page X position
8294         * @param {Number} y (optional) Set the page Y position
8295         */
8296         position : function(pos, zIndex, x, y){
8297             if(!pos){
8298                if(this.getStyle('position') == 'static'){
8299                    this.setStyle('position', 'relative');
8300                }
8301             }else{
8302                 this.setStyle("position", pos);
8303             }
8304             if(zIndex){
8305                 this.setStyle("z-index", zIndex);
8306             }
8307             if(x !== undefined && y !== undefined){
8308                 this.setXY([x, y]);
8309             }else if(x !== undefined){
8310                 this.setX(x);
8311             }else if(y !== undefined){
8312                 this.setY(y);
8313             }
8314         },
8315
8316         /**
8317         * Clear positioning back to the default when the document was loaded
8318         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8319         * @return {Roo.Element} this
8320          */
8321         clearPositioning : function(value){
8322             value = value ||'';
8323             this.setStyle({
8324                 "left": value,
8325                 "right": value,
8326                 "top": value,
8327                 "bottom": value,
8328                 "z-index": "",
8329                 "position" : "static"
8330             });
8331             return this;
8332         },
8333
8334         /**
8335         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8336         * snapshot before performing an update and then restoring the element.
8337         * @return {Object}
8338         */
8339         getPositioning : function(){
8340             var l = this.getStyle("left");
8341             var t = this.getStyle("top");
8342             return {
8343                 "position" : this.getStyle("position"),
8344                 "left" : l,
8345                 "right" : l ? "" : this.getStyle("right"),
8346                 "top" : t,
8347                 "bottom" : t ? "" : this.getStyle("bottom"),
8348                 "z-index" : this.getStyle("z-index")
8349             };
8350         },
8351
8352         /**
8353          * Gets the width of the border(s) for the specified side(s)
8354          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8355          * passing lr would get the border (l)eft width + the border (r)ight width.
8356          * @return {Number} The width of the sides passed added together
8357          */
8358         getBorderWidth : function(side){
8359             return this.addStyles(side, El.borders);
8360         },
8361
8362         /**
8363          * Gets the width of the padding(s) for the specified side(s)
8364          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8365          * passing lr would get the padding (l)eft + the padding (r)ight.
8366          * @return {Number} The padding of the sides passed added together
8367          */
8368         getPadding : function(side){
8369             return this.addStyles(side, El.paddings);
8370         },
8371
8372         /**
8373         * Set positioning with an object returned by getPositioning().
8374         * @param {Object} posCfg
8375         * @return {Roo.Element} this
8376          */
8377         setPositioning : function(pc){
8378             this.applyStyles(pc);
8379             if(pc.right == "auto"){
8380                 this.dom.style.right = "";
8381             }
8382             if(pc.bottom == "auto"){
8383                 this.dom.style.bottom = "";
8384             }
8385             return this;
8386         },
8387
8388         // private
8389         fixDisplay : function(){
8390             if(this.getStyle("display") == "none"){
8391                 this.setStyle("visibility", "hidden");
8392                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8393                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8394                     this.setStyle("display", "block");
8395                 }
8396             }
8397         },
8398
8399         /**
8400          * Quick set left and top adding default units
8401          * @param {String} left The left CSS property value
8402          * @param {String} top The top CSS property value
8403          * @return {Roo.Element} this
8404          */
8405          setLeftTop : function(left, top){
8406             this.dom.style.left = this.addUnits(left);
8407             this.dom.style.top = this.addUnits(top);
8408             return this;
8409         },
8410
8411         /**
8412          * Move this element relative to its current position.
8413          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8414          * @param {Number} distance How far to move the element in pixels
8415          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8416          * @return {Roo.Element} this
8417          */
8418          move : function(direction, distance, animate){
8419             var xy = this.getXY();
8420             direction = direction.toLowerCase();
8421             switch(direction){
8422                 case "l":
8423                 case "left":
8424                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8425                     break;
8426                case "r":
8427                case "right":
8428                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8429                     break;
8430                case "t":
8431                case "top":
8432                case "up":
8433                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8434                     break;
8435                case "b":
8436                case "bottom":
8437                case "down":
8438                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8439                     break;
8440             }
8441             return this;
8442         },
8443
8444         /**
8445          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8446          * @return {Roo.Element} this
8447          */
8448         clip : function(){
8449             if(!this.isClipped){
8450                this.isClipped = true;
8451                this.originalClip = {
8452                    "o": this.getStyle("overflow"),
8453                    "x": this.getStyle("overflow-x"),
8454                    "y": this.getStyle("overflow-y")
8455                };
8456                this.setStyle("overflow", "hidden");
8457                this.setStyle("overflow-x", "hidden");
8458                this.setStyle("overflow-y", "hidden");
8459             }
8460             return this;
8461         },
8462
8463         /**
8464          *  Return clipping (overflow) to original clipping before clip() was called
8465          * @return {Roo.Element} this
8466          */
8467         unclip : function(){
8468             if(this.isClipped){
8469                 this.isClipped = false;
8470                 var o = this.originalClip;
8471                 if(o.o){this.setStyle("overflow", o.o);}
8472                 if(o.x){this.setStyle("overflow-x", o.x);}
8473                 if(o.y){this.setStyle("overflow-y", o.y);}
8474             }
8475             return this;
8476         },
8477
8478
8479         /**
8480          * Gets the x,y coordinates specified by the anchor position on the element.
8481          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8482          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8483          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8484          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8485          * @return {Array} [x, y] An array containing the element's x and y coordinates
8486          */
8487         getAnchorXY : function(anchor, local, s){
8488             //Passing a different size is useful for pre-calculating anchors,
8489             //especially for anchored animations that change the el size.
8490
8491             var w, h, vp = false;
8492             if(!s){
8493                 var d = this.dom;
8494                 if(d == document.body || d == document){
8495                     vp = true;
8496                     w = D.getViewWidth(); h = D.getViewHeight();
8497                 }else{
8498                     w = this.getWidth(); h = this.getHeight();
8499                 }
8500             }else{
8501                 w = s.width;  h = s.height;
8502             }
8503             var x = 0, y = 0, r = Math.round;
8504             switch((anchor || "tl").toLowerCase()){
8505                 case "c":
8506                     x = r(w*.5);
8507                     y = r(h*.5);
8508                 break;
8509                 case "t":
8510                     x = r(w*.5);
8511                     y = 0;
8512                 break;
8513                 case "l":
8514                     x = 0;
8515                     y = r(h*.5);
8516                 break;
8517                 case "r":
8518                     x = w;
8519                     y = r(h*.5);
8520                 break;
8521                 case "b":
8522                     x = r(w*.5);
8523                     y = h;
8524                 break;
8525                 case "tl":
8526                     x = 0;
8527                     y = 0;
8528                 break;
8529                 case "bl":
8530                     x = 0;
8531                     y = h;
8532                 break;
8533                 case "br":
8534                     x = w;
8535                     y = h;
8536                 break;
8537                 case "tr":
8538                     x = w;
8539                     y = 0;
8540                 break;
8541             }
8542             if(local === true){
8543                 return [x, y];
8544             }
8545             if(vp){
8546                 var sc = this.getScroll();
8547                 return [x + sc.left, y + sc.top];
8548             }
8549             //Add the element's offset xy
8550             var o = this.getXY();
8551             return [x+o[0], y+o[1]];
8552         },
8553
8554         /**
8555          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8556          * supported position values.
8557          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8558          * @param {String} position The position to align to.
8559          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8560          * @return {Array} [x, y]
8561          */
8562         getAlignToXY : function(el, p, o){
8563             el = Roo.get(el);
8564             var d = this.dom;
8565             if(!el.dom){
8566                 throw "Element.alignTo with an element that doesn't exist";
8567             }
8568             var c = false; //constrain to viewport
8569             var p1 = "", p2 = "";
8570             o = o || [0,0];
8571
8572             if(!p){
8573                 p = "tl-bl";
8574             }else if(p == "?"){
8575                 p = "tl-bl?";
8576             }else if(p.indexOf("-") == -1){
8577                 p = "tl-" + p;
8578             }
8579             p = p.toLowerCase();
8580             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8581             if(!m){
8582                throw "Element.alignTo with an invalid alignment " + p;
8583             }
8584             p1 = m[1]; p2 = m[2]; c = !!m[3];
8585
8586             //Subtract the aligned el's internal xy from the target's offset xy
8587             //plus custom offset to get the aligned el's new offset xy
8588             var a1 = this.getAnchorXY(p1, true);
8589             var a2 = el.getAnchorXY(p2, false);
8590             var x = a2[0] - a1[0] + o[0];
8591             var y = a2[1] - a1[1] + o[1];
8592             if(c){
8593                 //constrain the aligned el to viewport if necessary
8594                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8595                 // 5px of margin for ie
8596                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8597
8598                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8599                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8600                 //otherwise swap the aligned el to the opposite border of the target.
8601                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8602                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8603                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8604                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8605
8606                var doc = document;
8607                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8608                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8609
8610                if((x+w) > dw + scrollX){
8611                     x = swapX ? r.left-w : dw+scrollX-w;
8612                 }
8613                if(x < scrollX){
8614                    x = swapX ? r.right : scrollX;
8615                }
8616                if((y+h) > dh + scrollY){
8617                     y = swapY ? r.top-h : dh+scrollY-h;
8618                 }
8619                if (y < scrollY){
8620                    y = swapY ? r.bottom : scrollY;
8621                }
8622             }
8623             return [x,y];
8624         },
8625
8626         // private
8627         getConstrainToXY : function(){
8628             var os = {top:0, left:0, bottom:0, right: 0};
8629
8630             return function(el, local, offsets, proposedXY){
8631                 el = Roo.get(el);
8632                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8633
8634                 var vw, vh, vx = 0, vy = 0;
8635                 if(el.dom == document.body || el.dom == document){
8636                     vw = Roo.lib.Dom.getViewWidth();
8637                     vh = Roo.lib.Dom.getViewHeight();
8638                 }else{
8639                     vw = el.dom.clientWidth;
8640                     vh = el.dom.clientHeight;
8641                     if(!local){
8642                         var vxy = el.getXY();
8643                         vx = vxy[0];
8644                         vy = vxy[1];
8645                     }
8646                 }
8647
8648                 var s = el.getScroll();
8649
8650                 vx += offsets.left + s.left;
8651                 vy += offsets.top + s.top;
8652
8653                 vw -= offsets.right;
8654                 vh -= offsets.bottom;
8655
8656                 var vr = vx+vw;
8657                 var vb = vy+vh;
8658
8659                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8660                 var x = xy[0], y = xy[1];
8661                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8662
8663                 // only move it if it needs it
8664                 var moved = false;
8665
8666                 // first validate right/bottom
8667                 if((x + w) > vr){
8668                     x = vr - w;
8669                     moved = true;
8670                 }
8671                 if((y + h) > vb){
8672                     y = vb - h;
8673                     moved = true;
8674                 }
8675                 // then make sure top/left isn't negative
8676                 if(x < vx){
8677                     x = vx;
8678                     moved = true;
8679                 }
8680                 if(y < vy){
8681                     y = vy;
8682                     moved = true;
8683                 }
8684                 return moved ? [x, y] : false;
8685             };
8686         }(),
8687
8688         // private
8689         adjustForConstraints : function(xy, parent, offsets){
8690             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8691         },
8692
8693         /**
8694          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8695          * document it aligns it to the viewport.
8696          * The position parameter is optional, and can be specified in any one of the following formats:
8697          * <ul>
8698          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8699          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8700          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8701          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8702          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8703          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8704          * </ul>
8705          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8706          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8707          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8708          * that specified in order to enforce the viewport constraints.
8709          * Following are all of the supported anchor positions:
8710     <pre>
8711     Value  Description
8712     -----  -----------------------------
8713     tl     The top left corner (default)
8714     t      The center of the top edge
8715     tr     The top right corner
8716     l      The center of the left edge
8717     c      In the center of the element
8718     r      The center of the right edge
8719     bl     The bottom left corner
8720     b      The center of the bottom edge
8721     br     The bottom right corner
8722     </pre>
8723     Example Usage:
8724     <pre><code>
8725     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8726     el.alignTo("other-el");
8727
8728     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8729     el.alignTo("other-el", "tr?");
8730
8731     // align the bottom right corner of el with the center left edge of other-el
8732     el.alignTo("other-el", "br-l?");
8733
8734     // align the center of el with the bottom left corner of other-el and
8735     // adjust the x position by -6 pixels (and the y position by 0)
8736     el.alignTo("other-el", "c-bl", [-6, 0]);
8737     </code></pre>
8738          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8739          * @param {String} position The position to align to.
8740          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8741          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8742          * @return {Roo.Element} this
8743          */
8744         alignTo : function(element, position, offsets, animate){
8745             var xy = this.getAlignToXY(element, position, offsets);
8746             this.setXY(xy, this.preanim(arguments, 3));
8747             return this;
8748         },
8749
8750         /**
8751          * Anchors an element to another element and realigns it when the window is resized.
8752          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8753          * @param {String} position The position to align to.
8754          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8755          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8756          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8757          * is a number, it is used as the buffer delay (defaults to 50ms).
8758          * @param {Function} callback The function to call after the animation finishes
8759          * @return {Roo.Element} this
8760          */
8761         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8762             var action = function(){
8763                 this.alignTo(el, alignment, offsets, animate);
8764                 Roo.callback(callback, this);
8765             };
8766             Roo.EventManager.onWindowResize(action, this);
8767             var tm = typeof monitorScroll;
8768             if(tm != 'undefined'){
8769                 Roo.EventManager.on(window, 'scroll', action, this,
8770                     {buffer: tm == 'number' ? monitorScroll : 50});
8771             }
8772             action.call(this); // align immediately
8773             return this;
8774         },
8775         /**
8776          * Clears any opacity settings from this element. Required in some cases for IE.
8777          * @return {Roo.Element} this
8778          */
8779         clearOpacity : function(){
8780             if (window.ActiveXObject) {
8781                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8782                     this.dom.style.filter = "";
8783                 }
8784             } else {
8785                 this.dom.style.opacity = "";
8786                 this.dom.style["-moz-opacity"] = "";
8787                 this.dom.style["-khtml-opacity"] = "";
8788             }
8789             return this;
8790         },
8791
8792         /**
8793          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8794          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8795          * @return {Roo.Element} this
8796          */
8797         hide : function(animate){
8798             this.setVisible(false, this.preanim(arguments, 0));
8799             return this;
8800         },
8801
8802         /**
8803         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8804         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8805          * @return {Roo.Element} this
8806          */
8807         show : function(animate){
8808             this.setVisible(true, this.preanim(arguments, 0));
8809             return this;
8810         },
8811
8812         /**
8813          * @private Test if size has a unit, otherwise appends the default
8814          */
8815         addUnits : function(size){
8816             return Roo.Element.addUnits(size, this.defaultUnit);
8817         },
8818
8819         /**
8820          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8821          * @return {Roo.Element} this
8822          */
8823         beginMeasure : function(){
8824             var el = this.dom;
8825             if(el.offsetWidth || el.offsetHeight){
8826                 return this; // offsets work already
8827             }
8828             var changed = [];
8829             var p = this.dom, b = document.body; // start with this element
8830             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8831                 var pe = Roo.get(p);
8832                 if(pe.getStyle('display') == 'none'){
8833                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8834                     p.style.visibility = "hidden";
8835                     p.style.display = "block";
8836                 }
8837                 p = p.parentNode;
8838             }
8839             this._measureChanged = changed;
8840             return this;
8841
8842         },
8843
8844         /**
8845          * Restores displays to before beginMeasure was called
8846          * @return {Roo.Element} this
8847          */
8848         endMeasure : function(){
8849             var changed = this._measureChanged;
8850             if(changed){
8851                 for(var i = 0, len = changed.length; i < len; i++) {
8852                     var r = changed[i];
8853                     r.el.style.visibility = r.visibility;
8854                     r.el.style.display = "none";
8855                 }
8856                 this._measureChanged = null;
8857             }
8858             return this;
8859         },
8860
8861         /**
8862         * Update the innerHTML of this element, optionally searching for and processing scripts
8863         * @param {String} html The new HTML
8864         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8865         * @param {Function} callback For async script loading you can be noticed when the update completes
8866         * @return {Roo.Element} this
8867          */
8868         update : function(html, loadScripts, callback){
8869             if(typeof html == "undefined"){
8870                 html = "";
8871             }
8872             if(loadScripts !== true){
8873                 this.dom.innerHTML = html;
8874                 if(typeof callback == "function"){
8875                     callback();
8876                 }
8877                 return this;
8878             }
8879             var id = Roo.id();
8880             var dom = this.dom;
8881
8882             html += '<span id="' + id + '"></span>';
8883
8884             E.onAvailable(id, function(){
8885                 var hd = document.getElementsByTagName("head")[0];
8886                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8887                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8888                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8889
8890                 var match;
8891                 while(match = re.exec(html)){
8892                     var attrs = match[1];
8893                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8894                     if(srcMatch && srcMatch[2]){
8895                        var s = document.createElement("script");
8896                        s.src = srcMatch[2];
8897                        var typeMatch = attrs.match(typeRe);
8898                        if(typeMatch && typeMatch[2]){
8899                            s.type = typeMatch[2];
8900                        }
8901                        hd.appendChild(s);
8902                     }else if(match[2] && match[2].length > 0){
8903                         if(window.execScript) {
8904                            window.execScript(match[2]);
8905                         } else {
8906                             /**
8907                              * eval:var:id
8908                              * eval:var:dom
8909                              * eval:var:html
8910                              * 
8911                              */
8912                            window.eval(match[2]);
8913                         }
8914                     }
8915                 }
8916                 var el = document.getElementById(id);
8917                 if(el){el.parentNode.removeChild(el);}
8918                 if(typeof callback == "function"){
8919                     callback();
8920                 }
8921             });
8922             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8923             return this;
8924         },
8925
8926         /**
8927          * Direct access to the UpdateManager update() method (takes the same parameters).
8928          * @param {String/Function} url The url for this request or a function to call to get the url
8929          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8930          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8931          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8932          * @return {Roo.Element} this
8933          */
8934         load : function(){
8935             var um = this.getUpdateManager();
8936             um.update.apply(um, arguments);
8937             return this;
8938         },
8939
8940         /**
8941         * Gets this element's UpdateManager
8942         * @return {Roo.UpdateManager} The UpdateManager
8943         */
8944         getUpdateManager : function(){
8945             if(!this.updateManager){
8946                 this.updateManager = new Roo.UpdateManager(this);
8947             }
8948             return this.updateManager;
8949         },
8950
8951         /**
8952          * Disables text selection for this element (normalized across browsers)
8953          * @return {Roo.Element} this
8954          */
8955         unselectable : function(){
8956             this.dom.unselectable = "on";
8957             this.swallowEvent("selectstart", true);
8958             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8959             this.addClass("x-unselectable");
8960             return this;
8961         },
8962
8963         /**
8964         * Calculates the x, y to center this element on the screen
8965         * @return {Array} The x, y values [x, y]
8966         */
8967         getCenterXY : function(){
8968             return this.getAlignToXY(document, 'c-c');
8969         },
8970
8971         /**
8972         * Centers the Element in either the viewport, or another Element.
8973         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8974         */
8975         center : function(centerIn){
8976             this.alignTo(centerIn || document, 'c-c');
8977             return this;
8978         },
8979
8980         /**
8981          * Tests various css rules/browsers to determine if this element uses a border box
8982          * @return {Boolean}
8983          */
8984         isBorderBox : function(){
8985             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8986         },
8987
8988         /**
8989          * Return a box {x, y, width, height} that can be used to set another elements
8990          * size/location to match this element.
8991          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8992          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8993          * @return {Object} box An object in the format {x, y, width, height}
8994          */
8995         getBox : function(contentBox, local){
8996             var xy;
8997             if(!local){
8998                 xy = this.getXY();
8999             }else{
9000                 var left = parseInt(this.getStyle("left"), 10) || 0;
9001                 var top = parseInt(this.getStyle("top"), 10) || 0;
9002                 xy = [left, top];
9003             }
9004             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9005             if(!contentBox){
9006                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9007             }else{
9008                 var l = this.getBorderWidth("l")+this.getPadding("l");
9009                 var r = this.getBorderWidth("r")+this.getPadding("r");
9010                 var t = this.getBorderWidth("t")+this.getPadding("t");
9011                 var b = this.getBorderWidth("b")+this.getPadding("b");
9012                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
9013             }
9014             bx.right = bx.x + bx.width;
9015             bx.bottom = bx.y + bx.height;
9016             return bx;
9017         },
9018
9019         /**
9020          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9021          for more information about the sides.
9022          * @param {String} sides
9023          * @return {Number}
9024          */
9025         getFrameWidth : function(sides, onlyContentBox){
9026             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9027         },
9028
9029         /**
9030          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
9031          * @param {Object} box The box to fill {x, y, width, height}
9032          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9033          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9034          * @return {Roo.Element} this
9035          */
9036         setBox : function(box, adjust, animate){
9037             var w = box.width, h = box.height;
9038             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9039                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9040                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9041             }
9042             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9043             return this;
9044         },
9045
9046         /**
9047          * Forces the browser to repaint this element
9048          * @return {Roo.Element} this
9049          */
9050          repaint : function(){
9051             var dom = this.dom;
9052             this.addClass("x-repaint");
9053             setTimeout(function(){
9054                 Roo.get(dom).removeClass("x-repaint");
9055             }, 1);
9056             return this;
9057         },
9058
9059         /**
9060          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9061          * then it returns the calculated width of the sides (see getPadding)
9062          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9063          * @return {Object/Number}
9064          */
9065         getMargins : function(side){
9066             if(!side){
9067                 return {
9068                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9069                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9070                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9071                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9072                 };
9073             }else{
9074                 return this.addStyles(side, El.margins);
9075              }
9076         },
9077
9078         // private
9079         addStyles : function(sides, styles){
9080             var val = 0, v, w;
9081             for(var i = 0, len = sides.length; i < len; i++){
9082                 v = this.getStyle(styles[sides.charAt(i)]);
9083                 if(v){
9084                      w = parseInt(v, 10);
9085                      if(w){ val += w; }
9086                 }
9087             }
9088             return val;
9089         },
9090
9091         /**
9092          * Creates a proxy element of this element
9093          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9094          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9095          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9096          * @return {Roo.Element} The new proxy element
9097          */
9098         createProxy : function(config, renderTo, matchBox){
9099             if(renderTo){
9100                 renderTo = Roo.getDom(renderTo);
9101             }else{
9102                 renderTo = document.body;
9103             }
9104             config = typeof config == "object" ?
9105                 config : {tag : "div", cls: config};
9106             var proxy = Roo.DomHelper.append(renderTo, config, true);
9107             if(matchBox){
9108                proxy.setBox(this.getBox());
9109             }
9110             return proxy;
9111         },
9112
9113         /**
9114          * Puts a mask over this element to disable user interaction. Requires core.css.
9115          * This method can only be applied to elements which accept child nodes.
9116          * @param {String} msg (optional) A message to display in the mask
9117          * @param {String} msgCls (optional) A css class to apply to the msg element
9118          * @return {Element} The mask  element
9119          */
9120         mask : function(msg, msgCls)
9121         {
9122             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9123                 this.setStyle("position", "relative");
9124             }
9125             if(!this._mask){
9126                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9127             }
9128             
9129             this.addClass("x-masked");
9130             this._mask.setDisplayed(true);
9131             
9132             // we wander
9133             var z = 0;
9134             var dom = this.dom;
9135             while (dom && dom.style) {
9136                 if (!isNaN(parseInt(dom.style.zIndex))) {
9137                     z = Math.max(z, parseInt(dom.style.zIndex));
9138                 }
9139                 dom = dom.parentNode;
9140             }
9141             // if we are masking the body - then it hides everything..
9142             if (this.dom == document.body) {
9143                 z = 1000000;
9144                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9145                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9146             }
9147            
9148             if(typeof msg == 'string'){
9149                 if(!this._maskMsg){
9150                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9151                         cls: "roo-el-mask-msg", 
9152                         cn: [
9153                             {
9154                                 tag: 'i',
9155                                 cls: 'fa fa-spinner fa-spin'
9156                             },
9157                             {
9158                                 tag: 'div'
9159                             }   
9160                         ]
9161                     }, true);
9162                 }
9163                 var mm = this._maskMsg;
9164                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9165                 if (mm.dom.lastChild) { // weird IE issue?
9166                     mm.dom.lastChild.innerHTML = msg;
9167                 }
9168                 mm.setDisplayed(true);
9169                 mm.center(this);
9170                 mm.setStyle('z-index', z + 102);
9171             }
9172             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9173                 this._mask.setHeight(this.getHeight());
9174             }
9175             this._mask.setStyle('z-index', z + 100);
9176             
9177             return this._mask;
9178         },
9179
9180         /**
9181          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9182          * it is cached for reuse.
9183          */
9184         unmask : function(removeEl){
9185             if(this._mask){
9186                 if(removeEl === true){
9187                     this._mask.remove();
9188                     delete this._mask;
9189                     if(this._maskMsg){
9190                         this._maskMsg.remove();
9191                         delete this._maskMsg;
9192                     }
9193                 }else{
9194                     this._mask.setDisplayed(false);
9195                     if(this._maskMsg){
9196                         this._maskMsg.setDisplayed(false);
9197                     }
9198                 }
9199             }
9200             this.removeClass("x-masked");
9201         },
9202
9203         /**
9204          * Returns true if this element is masked
9205          * @return {Boolean}
9206          */
9207         isMasked : function(){
9208             return this._mask && this._mask.isVisible();
9209         },
9210
9211         /**
9212          * Creates an iframe shim for this element to keep selects and other windowed objects from
9213          * showing through.
9214          * @return {Roo.Element} The new shim element
9215          */
9216         createShim : function(){
9217             var el = document.createElement('iframe');
9218             el.frameBorder = 'no';
9219             el.className = 'roo-shim';
9220             if(Roo.isIE && Roo.isSecure){
9221                 el.src = Roo.SSL_SECURE_URL;
9222             }
9223             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9224             shim.autoBoxAdjust = false;
9225             return shim;
9226         },
9227
9228         /**
9229          * Removes this element from the DOM and deletes it from the cache
9230          */
9231         remove : function(){
9232             if(this.dom.parentNode){
9233                 this.dom.parentNode.removeChild(this.dom);
9234             }
9235             delete El.cache[this.dom.id];
9236         },
9237
9238         /**
9239          * Sets up event handlers to add and remove a css class when the mouse is over this element
9240          * @param {String} className
9241          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9242          * mouseout events for children elements
9243          * @return {Roo.Element} this
9244          */
9245         addClassOnOver : function(className, preventFlicker){
9246             this.on("mouseover", function(){
9247                 Roo.fly(this, '_internal').addClass(className);
9248             }, this.dom);
9249             var removeFn = function(e){
9250                 if(preventFlicker !== true || !e.within(this, true)){
9251                     Roo.fly(this, '_internal').removeClass(className);
9252                 }
9253             };
9254             this.on("mouseout", removeFn, this.dom);
9255             return this;
9256         },
9257
9258         /**
9259          * Sets up event handlers to add and remove a css class when this element has the focus
9260          * @param {String} className
9261          * @return {Roo.Element} this
9262          */
9263         addClassOnFocus : function(className){
9264             this.on("focus", function(){
9265                 Roo.fly(this, '_internal').addClass(className);
9266             }, this.dom);
9267             this.on("blur", function(){
9268                 Roo.fly(this, '_internal').removeClass(className);
9269             }, this.dom);
9270             return this;
9271         },
9272         /**
9273          * 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)
9274          * @param {String} className
9275          * @return {Roo.Element} this
9276          */
9277         addClassOnClick : function(className){
9278             var dom = this.dom;
9279             this.on("mousedown", function(){
9280                 Roo.fly(dom, '_internal').addClass(className);
9281                 var d = Roo.get(document);
9282                 var fn = function(){
9283                     Roo.fly(dom, '_internal').removeClass(className);
9284                     d.removeListener("mouseup", fn);
9285                 };
9286                 d.on("mouseup", fn);
9287             });
9288             return this;
9289         },
9290
9291         /**
9292          * Stops the specified event from bubbling and optionally prevents the default action
9293          * @param {String} eventName
9294          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9295          * @return {Roo.Element} this
9296          */
9297         swallowEvent : function(eventName, preventDefault){
9298             var fn = function(e){
9299                 e.stopPropagation();
9300                 if(preventDefault){
9301                     e.preventDefault();
9302                 }
9303             };
9304             if(eventName instanceof Array){
9305                 for(var i = 0, len = eventName.length; i < len; i++){
9306                      this.on(eventName[i], fn);
9307                 }
9308                 return this;
9309             }
9310             this.on(eventName, fn);
9311             return this;
9312         },
9313
9314         /**
9315          * @private
9316          */
9317       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9318
9319         /**
9320          * Sizes this element to its parent element's dimensions performing
9321          * neccessary box adjustments.
9322          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9323          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9324          * @return {Roo.Element} this
9325          */
9326         fitToParent : function(monitorResize, targetParent) {
9327           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9328           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9329           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9330             return;
9331           }
9332           var p = Roo.get(targetParent || this.dom.parentNode);
9333           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9334           if (monitorResize === true) {
9335             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9336             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9337           }
9338           return this;
9339         },
9340
9341         /**
9342          * Gets the next sibling, skipping text nodes
9343          * @return {HTMLElement} The next sibling or null
9344          */
9345         getNextSibling : function(){
9346             var n = this.dom.nextSibling;
9347             while(n && n.nodeType != 1){
9348                 n = n.nextSibling;
9349             }
9350             return n;
9351         },
9352
9353         /**
9354          * Gets the previous sibling, skipping text nodes
9355          * @return {HTMLElement} The previous sibling or null
9356          */
9357         getPrevSibling : function(){
9358             var n = this.dom.previousSibling;
9359             while(n && n.nodeType != 1){
9360                 n = n.previousSibling;
9361             }
9362             return n;
9363         },
9364
9365
9366         /**
9367          * Appends the passed element(s) to this element
9368          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9369          * @return {Roo.Element} this
9370          */
9371         appendChild: function(el){
9372             el = Roo.get(el);
9373             el.appendTo(this);
9374             return this;
9375         },
9376
9377         /**
9378          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9379          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9380          * automatically generated with the specified attributes.
9381          * @param {HTMLElement} insertBefore (optional) a child element of this element
9382          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9383          * @return {Roo.Element} The new child element
9384          */
9385         createChild: function(config, insertBefore, returnDom){
9386             config = config || {tag:'div'};
9387             if(insertBefore){
9388                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9389             }
9390             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9391         },
9392
9393         /**
9394          * Appends this element to the passed element
9395          * @param {String/HTMLElement/Element} el The new parent element
9396          * @return {Roo.Element} this
9397          */
9398         appendTo: function(el){
9399             el = Roo.getDom(el);
9400             el.appendChild(this.dom);
9401             return this;
9402         },
9403
9404         /**
9405          * Inserts this element before the passed element in the DOM
9406          * @param {String/HTMLElement/Element} el The element to insert before
9407          * @return {Roo.Element} this
9408          */
9409         insertBefore: function(el){
9410             el = Roo.getDom(el);
9411             el.parentNode.insertBefore(this.dom, el);
9412             return this;
9413         },
9414
9415         /**
9416          * Inserts this element after the passed element in the DOM
9417          * @param {String/HTMLElement/Element} el The element to insert after
9418          * @return {Roo.Element} this
9419          */
9420         insertAfter: function(el){
9421             el = Roo.getDom(el);
9422             el.parentNode.insertBefore(this.dom, el.nextSibling);
9423             return this;
9424         },
9425
9426         /**
9427          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9428          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9429          * @return {Roo.Element} The new child
9430          */
9431         insertFirst: function(el, returnDom){
9432             el = el || {};
9433             if(typeof el == 'object' && !el.nodeType){ // dh config
9434                 return this.createChild(el, this.dom.firstChild, returnDom);
9435             }else{
9436                 el = Roo.getDom(el);
9437                 this.dom.insertBefore(el, this.dom.firstChild);
9438                 return !returnDom ? Roo.get(el) : el;
9439             }
9440         },
9441
9442         /**
9443          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9444          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9445          * @param {String} where (optional) 'before' or 'after' defaults to before
9446          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9447          * @return {Roo.Element} the inserted Element
9448          */
9449         insertSibling: function(el, where, returnDom){
9450             where = where ? where.toLowerCase() : 'before';
9451             el = el || {};
9452             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9453
9454             if(typeof el == 'object' && !el.nodeType){ // dh config
9455                 if(where == 'after' && !this.dom.nextSibling){
9456                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9457                 }else{
9458                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9459                 }
9460
9461             }else{
9462                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9463                             where == 'before' ? this.dom : this.dom.nextSibling);
9464                 if(!returnDom){
9465                     rt = Roo.get(rt);
9466                 }
9467             }
9468             return rt;
9469         },
9470
9471         /**
9472          * Creates and wraps this element with another element
9473          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9474          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9475          * @return {HTMLElement/Element} The newly created wrapper element
9476          */
9477         wrap: function(config, returnDom){
9478             if(!config){
9479                 config = {tag: "div"};
9480             }
9481             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9482             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9483             return newEl;
9484         },
9485
9486         /**
9487          * Replaces the passed element with this element
9488          * @param {String/HTMLElement/Element} el The element to replace
9489          * @return {Roo.Element} this
9490          */
9491         replace: function(el){
9492             el = Roo.get(el);
9493             this.insertBefore(el);
9494             el.remove();
9495             return this;
9496         },
9497
9498         /**
9499          * Inserts an html fragment into this element
9500          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9501          * @param {String} html The HTML fragment
9502          * @param {Boolean} returnEl True to return an Roo.Element
9503          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9504          */
9505         insertHtml : function(where, html, returnEl){
9506             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9507             return returnEl ? Roo.get(el) : el;
9508         },
9509
9510         /**
9511          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9512          * @param {Object} o The object with the attributes
9513          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9514          * @return {Roo.Element} this
9515          */
9516         set : function(o, useSet){
9517             var el = this.dom;
9518             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9519             for(var attr in o){
9520                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9521                 if(attr=="cls"){
9522                     el.className = o["cls"];
9523                 }else{
9524                     if(useSet) {
9525                         el.setAttribute(attr, o[attr]);
9526                     } else {
9527                         el[attr] = o[attr];
9528                     }
9529                 }
9530             }
9531             if(o.style){
9532                 Roo.DomHelper.applyStyles(el, o.style);
9533             }
9534             return this;
9535         },
9536
9537         /**
9538          * Convenience method for constructing a KeyMap
9539          * @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:
9540          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9541          * @param {Function} fn The function to call
9542          * @param {Object} scope (optional) The scope of the function
9543          * @return {Roo.KeyMap} The KeyMap created
9544          */
9545         addKeyListener : function(key, fn, scope){
9546             var config;
9547             if(typeof key != "object" || key instanceof Array){
9548                 config = {
9549                     key: key,
9550                     fn: fn,
9551                     scope: scope
9552                 };
9553             }else{
9554                 config = {
9555                     key : key.key,
9556                     shift : key.shift,
9557                     ctrl : key.ctrl,
9558                     alt : key.alt,
9559                     fn: fn,
9560                     scope: scope
9561                 };
9562             }
9563             return new Roo.KeyMap(this, config);
9564         },
9565
9566         /**
9567          * Creates a KeyMap for this element
9568          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9569          * @return {Roo.KeyMap} The KeyMap created
9570          */
9571         addKeyMap : function(config){
9572             return new Roo.KeyMap(this, config);
9573         },
9574
9575         /**
9576          * Returns true if this element is scrollable.
9577          * @return {Boolean}
9578          */
9579          isScrollable : function(){
9580             var dom = this.dom;
9581             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9582         },
9583
9584         /**
9585          * 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().
9586          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9587          * @param {Number} value The new scroll value
9588          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9589          * @return {Element} this
9590          */
9591
9592         scrollTo : function(side, value, animate){
9593             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9594             if(!animate || !A){
9595                 this.dom[prop] = value;
9596             }else{
9597                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9598                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9599             }
9600             return this;
9601         },
9602
9603         /**
9604          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9605          * within this element's scrollable range.
9606          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9607          * @param {Number} distance How far to scroll the element in pixels
9608          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9609          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9610          * was scrolled as far as it could go.
9611          */
9612          scroll : function(direction, distance, animate){
9613              if(!this.isScrollable()){
9614                  return;
9615              }
9616              var el = this.dom;
9617              var l = el.scrollLeft, t = el.scrollTop;
9618              var w = el.scrollWidth, h = el.scrollHeight;
9619              var cw = el.clientWidth, ch = el.clientHeight;
9620              direction = direction.toLowerCase();
9621              var scrolled = false;
9622              var a = this.preanim(arguments, 2);
9623              switch(direction){
9624                  case "l":
9625                  case "left":
9626                      if(w - l > cw){
9627                          var v = Math.min(l + distance, w-cw);
9628                          this.scrollTo("left", v, a);
9629                          scrolled = true;
9630                      }
9631                      break;
9632                 case "r":
9633                 case "right":
9634                      if(l > 0){
9635                          var v = Math.max(l - distance, 0);
9636                          this.scrollTo("left", v, a);
9637                          scrolled = true;
9638                      }
9639                      break;
9640                 case "t":
9641                 case "top":
9642                 case "up":
9643                      if(t > 0){
9644                          var v = Math.max(t - distance, 0);
9645                          this.scrollTo("top", v, a);
9646                          scrolled = true;
9647                      }
9648                      break;
9649                 case "b":
9650                 case "bottom":
9651                 case "down":
9652                      if(h - t > ch){
9653                          var v = Math.min(t + distance, h-ch);
9654                          this.scrollTo("top", v, a);
9655                          scrolled = true;
9656                      }
9657                      break;
9658              }
9659              return scrolled;
9660         },
9661
9662         /**
9663          * Translates the passed page coordinates into left/top css values for this element
9664          * @param {Number/Array} x The page x or an array containing [x, y]
9665          * @param {Number} y The page y
9666          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9667          */
9668         translatePoints : function(x, y){
9669             if(typeof x == 'object' || x instanceof Array){
9670                 y = x[1]; x = x[0];
9671             }
9672             var p = this.getStyle('position');
9673             var o = this.getXY();
9674
9675             var l = parseInt(this.getStyle('left'), 10);
9676             var t = parseInt(this.getStyle('top'), 10);
9677
9678             if(isNaN(l)){
9679                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9680             }
9681             if(isNaN(t)){
9682                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9683             }
9684
9685             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9686         },
9687
9688         /**
9689          * Returns the current scroll position of the element.
9690          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9691          */
9692         getScroll : function(){
9693             var d = this.dom, doc = document;
9694             if(d == doc || d == doc.body){
9695                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9696                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9697                 return {left: l, top: t};
9698             }else{
9699                 return {left: d.scrollLeft, top: d.scrollTop};
9700             }
9701         },
9702
9703         /**
9704          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9705          * are convert to standard 6 digit hex color.
9706          * @param {String} attr The css attribute
9707          * @param {String} defaultValue The default value to use when a valid color isn't found
9708          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9709          * YUI color anims.
9710          */
9711         getColor : function(attr, defaultValue, prefix){
9712             var v = this.getStyle(attr);
9713             if(!v || v == "transparent" || v == "inherit") {
9714                 return defaultValue;
9715             }
9716             var color = typeof prefix == "undefined" ? "#" : prefix;
9717             if(v.substr(0, 4) == "rgb("){
9718                 var rvs = v.slice(4, v.length -1).split(",");
9719                 for(var i = 0; i < 3; i++){
9720                     var h = parseInt(rvs[i]).toString(16);
9721                     if(h < 16){
9722                         h = "0" + h;
9723                     }
9724                     color += h;
9725                 }
9726             } else {
9727                 if(v.substr(0, 1) == "#"){
9728                     if(v.length == 4) {
9729                         for(var i = 1; i < 4; i++){
9730                             var c = v.charAt(i);
9731                             color +=  c + c;
9732                         }
9733                     }else if(v.length == 7){
9734                         color += v.substr(1);
9735                     }
9736                 }
9737             }
9738             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9739         },
9740
9741         /**
9742          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9743          * gradient background, rounded corners and a 4-way shadow.
9744          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9745          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9746          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9747          * @return {Roo.Element} this
9748          */
9749         boxWrap : function(cls){
9750             cls = cls || 'x-box';
9751             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9752             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9753             return el;
9754         },
9755
9756         /**
9757          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9758          * @param {String} namespace The namespace in which to look for the attribute
9759          * @param {String} name The attribute name
9760          * @return {String} The attribute value
9761          */
9762         getAttributeNS : Roo.isIE ? function(ns, name){
9763             var d = this.dom;
9764             var type = typeof d[ns+":"+name];
9765             if(type != 'undefined' && type != 'unknown'){
9766                 return d[ns+":"+name];
9767             }
9768             return d[name];
9769         } : function(ns, name){
9770             var d = this.dom;
9771             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9772         },
9773         
9774         
9775         /**
9776          * Sets or Returns the value the dom attribute value
9777          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9778          * @param {String} value (optional) The value to set the attribute to
9779          * @return {String} The attribute value
9780          */
9781         attr : function(name){
9782             if (arguments.length > 1) {
9783                 this.dom.setAttribute(name, arguments[1]);
9784                 return arguments[1];
9785             }
9786             if (typeof(name) == 'object') {
9787                 for(var i in name) {
9788                     this.attr(i, name[i]);
9789                 }
9790                 return name;
9791             }
9792             
9793             
9794             if (!this.dom.hasAttribute(name)) {
9795                 return undefined;
9796             }
9797             return this.dom.getAttribute(name);
9798         }
9799         
9800         
9801         
9802     };
9803
9804     var ep = El.prototype;
9805
9806     /**
9807      * Appends an event handler (Shorthand for addListener)
9808      * @param {String}   eventName     The type of event to append
9809      * @param {Function} fn        The method the event invokes
9810      * @param {Object} scope       (optional) The scope (this object) of the fn
9811      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9812      * @method
9813      */
9814     ep.on = ep.addListener;
9815         // backwards compat
9816     ep.mon = ep.addListener;
9817
9818     /**
9819      * Removes an event handler from this element (shorthand for removeListener)
9820      * @param {String} eventName the type of event to remove
9821      * @param {Function} fn the method the event invokes
9822      * @return {Roo.Element} this
9823      * @method
9824      */
9825     ep.un = ep.removeListener;
9826
9827     /**
9828      * true to automatically adjust width and height settings for box-model issues (default to true)
9829      */
9830     ep.autoBoxAdjust = true;
9831
9832     // private
9833     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9834
9835     // private
9836     El.addUnits = function(v, defaultUnit){
9837         if(v === "" || v == "auto"){
9838             return v;
9839         }
9840         if(v === undefined){
9841             return '';
9842         }
9843         if(typeof v == "number" || !El.unitPattern.test(v)){
9844             return v + (defaultUnit || 'px');
9845         }
9846         return v;
9847     };
9848
9849     // special markup used throughout Roo when box wrapping elements
9850     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>';
9851     /**
9852      * Visibility mode constant - Use visibility to hide element
9853      * @static
9854      * @type Number
9855      */
9856     El.VISIBILITY = 1;
9857     /**
9858      * Visibility mode constant - Use display to hide element
9859      * @static
9860      * @type Number
9861      */
9862     El.DISPLAY = 2;
9863
9864     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9865     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9866     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9867
9868
9869
9870     /**
9871      * @private
9872      */
9873     El.cache = {};
9874
9875     var docEl;
9876
9877     /**
9878      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9879      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9880      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9881      * @return {Element} The Element object
9882      * @static
9883      */
9884     El.get = function(el){
9885         var ex, elm, id;
9886         if(!el){ return null; }
9887         if(typeof el == "string"){ // element id
9888             if(!(elm = document.getElementById(el))){
9889                 return null;
9890             }
9891             if(ex = El.cache[el]){
9892                 ex.dom = elm;
9893             }else{
9894                 ex = El.cache[el] = new El(elm);
9895             }
9896             return ex;
9897         }else if(el.tagName){ // dom element
9898             if(!(id = el.id)){
9899                 id = Roo.id(el);
9900             }
9901             if(ex = El.cache[id]){
9902                 ex.dom = el;
9903             }else{
9904                 ex = El.cache[id] = new El(el);
9905             }
9906             return ex;
9907         }else if(el instanceof El){
9908             if(el != docEl){
9909                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9910                                                               // catch case where it hasn't been appended
9911                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9912             }
9913             return el;
9914         }else if(el.isComposite){
9915             return el;
9916         }else if(el instanceof Array){
9917             return El.select(el);
9918         }else if(el == document){
9919             // create a bogus element object representing the document object
9920             if(!docEl){
9921                 var f = function(){};
9922                 f.prototype = El.prototype;
9923                 docEl = new f();
9924                 docEl.dom = document;
9925             }
9926             return docEl;
9927         }
9928         return null;
9929     };
9930
9931     // private
9932     El.uncache = function(el){
9933         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9934             if(a[i]){
9935                 delete El.cache[a[i].id || a[i]];
9936             }
9937         }
9938     };
9939
9940     // private
9941     // Garbage collection - uncache elements/purge listeners on orphaned elements
9942     // so we don't hold a reference and cause the browser to retain them
9943     El.garbageCollect = function(){
9944         if(!Roo.enableGarbageCollector){
9945             clearInterval(El.collectorThread);
9946             return;
9947         }
9948         for(var eid in El.cache){
9949             var el = El.cache[eid], d = el.dom;
9950             // -------------------------------------------------------
9951             // Determining what is garbage:
9952             // -------------------------------------------------------
9953             // !d
9954             // dom node is null, definitely garbage
9955             // -------------------------------------------------------
9956             // !d.parentNode
9957             // no parentNode == direct orphan, definitely garbage
9958             // -------------------------------------------------------
9959             // !d.offsetParent && !document.getElementById(eid)
9960             // display none elements have no offsetParent so we will
9961             // also try to look it up by it's id. However, check
9962             // offsetParent first so we don't do unneeded lookups.
9963             // This enables collection of elements that are not orphans
9964             // directly, but somewhere up the line they have an orphan
9965             // parent.
9966             // -------------------------------------------------------
9967             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9968                 delete El.cache[eid];
9969                 if(d && Roo.enableListenerCollection){
9970                     E.purgeElement(d);
9971                 }
9972             }
9973         }
9974     }
9975     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9976
9977
9978     // dom is optional
9979     El.Flyweight = function(dom){
9980         this.dom = dom;
9981     };
9982     El.Flyweight.prototype = El.prototype;
9983
9984     El._flyweights = {};
9985     /**
9986      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9987      * the dom node can be overwritten by other code.
9988      * @param {String/HTMLElement} el The dom node or id
9989      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9990      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9991      * @static
9992      * @return {Element} The shared Element object
9993      */
9994     El.fly = function(el, named){
9995         named = named || '_global';
9996         el = Roo.getDom(el);
9997         if(!el){
9998             return null;
9999         }
10000         if(!El._flyweights[named]){
10001             El._flyweights[named] = new El.Flyweight();
10002         }
10003         El._flyweights[named].dom = el;
10004         return El._flyweights[named];
10005     };
10006
10007     /**
10008      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10009      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10010      * Shorthand of {@link Roo.Element#get}
10011      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10012      * @return {Element} The Element object
10013      * @member Roo
10014      * @method get
10015      */
10016     Roo.get = El.get;
10017     /**
10018      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10019      * the dom node can be overwritten by other code.
10020      * Shorthand of {@link Roo.Element#fly}
10021      * @param {String/HTMLElement} el The dom node or id
10022      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10023      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10024      * @static
10025      * @return {Element} The shared Element object
10026      * @member Roo
10027      * @method fly
10028      */
10029     Roo.fly = El.fly;
10030
10031     // speedy lookup for elements never to box adjust
10032     var noBoxAdjust = Roo.isStrict ? {
10033         select:1
10034     } : {
10035         input:1, select:1, textarea:1
10036     };
10037     if(Roo.isIE || Roo.isGecko){
10038         noBoxAdjust['button'] = 1;
10039     }
10040
10041
10042     Roo.EventManager.on(window, 'unload', function(){
10043         delete El.cache;
10044         delete El._flyweights;
10045     });
10046 })();
10047
10048
10049
10050
10051 if(Roo.DomQuery){
10052     Roo.Element.selectorFunction = Roo.DomQuery.select;
10053 }
10054
10055 Roo.Element.select = function(selector, unique, root){
10056     var els;
10057     if(typeof selector == "string"){
10058         els = Roo.Element.selectorFunction(selector, root);
10059     }else if(selector.length !== undefined){
10060         els = selector;
10061     }else{
10062         throw "Invalid selector";
10063     }
10064     if(unique === true){
10065         return new Roo.CompositeElement(els);
10066     }else{
10067         return new Roo.CompositeElementLite(els);
10068     }
10069 };
10070 /**
10071  * Selects elements based on the passed CSS selector to enable working on them as 1.
10072  * @param {String/Array} selector The CSS selector or an array of elements
10073  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10074  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10075  * @return {CompositeElementLite/CompositeElement}
10076  * @member Roo
10077  * @method select
10078  */
10079 Roo.select = Roo.Element.select;
10080
10081
10082
10083
10084
10085
10086
10087
10088
10089
10090
10091
10092
10093
10094 /*
10095  * Based on:
10096  * Ext JS Library 1.1.1
10097  * Copyright(c) 2006-2007, Ext JS, LLC.
10098  *
10099  * Originally Released Under LGPL - original licence link has changed is not relivant.
10100  *
10101  * Fork - LGPL
10102  * <script type="text/javascript">
10103  */
10104
10105
10106
10107 //Notifies Element that fx methods are available
10108 Roo.enableFx = true;
10109
10110 /**
10111  * @class Roo.Fx
10112  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10113  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10114  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10115  * Element effects to work.</p><br/>
10116  *
10117  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10118  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10119  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10120  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10121  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10122  * expected results and should be done with care.</p><br/>
10123  *
10124  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10125  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10126 <pre>
10127 Value  Description
10128 -----  -----------------------------
10129 tl     The top left corner
10130 t      The center of the top edge
10131 tr     The top right corner
10132 l      The center of the left edge
10133 r      The center of the right edge
10134 bl     The bottom left corner
10135 b      The center of the bottom edge
10136 br     The bottom right corner
10137 </pre>
10138  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10139  * below are common options that can be passed to any Fx method.</b>
10140  * @cfg {Function} callback A function called when the effect is finished
10141  * @cfg {Object} scope The scope of the effect function
10142  * @cfg {String} easing A valid Easing value for the effect
10143  * @cfg {String} afterCls A css class to apply after the effect
10144  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10145  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10146  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10147  * effects that end with the element being visually hidden, ignored otherwise)
10148  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10149  * a function which returns such a specification that will be applied to the Element after the effect finishes
10150  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10151  * @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
10152  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10153  */
10154 Roo.Fx = {
10155         /**
10156          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10157          * origin for the slide effect.  This function automatically handles wrapping the element with
10158          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10159          * Usage:
10160          *<pre><code>
10161 // default: slide the element in from the top
10162 el.slideIn();
10163
10164 // custom: slide the element in from the right with a 2-second duration
10165 el.slideIn('r', { duration: 2 });
10166
10167 // common config options shown with default values
10168 el.slideIn('t', {
10169     easing: 'easeOut',
10170     duration: .5
10171 });
10172 </code></pre>
10173          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10174          * @param {Object} options (optional) Object literal with any of the Fx config options
10175          * @return {Roo.Element} The Element
10176          */
10177     slideIn : function(anchor, o){
10178         var el = this.getFxEl();
10179         o = o || {};
10180
10181         el.queueFx(o, function(){
10182
10183             anchor = anchor || "t";
10184
10185             // fix display to visibility
10186             this.fixDisplay();
10187
10188             // restore values after effect
10189             var r = this.getFxRestore();
10190             var b = this.getBox();
10191             // fixed size for slide
10192             this.setSize(b);
10193
10194             // wrap if needed
10195             var wrap = this.fxWrap(r.pos, o, "hidden");
10196
10197             var st = this.dom.style;
10198             st.visibility = "visible";
10199             st.position = "absolute";
10200
10201             // clear out temp styles after slide and unwrap
10202             var after = function(){
10203                 el.fxUnwrap(wrap, r.pos, o);
10204                 st.width = r.width;
10205                 st.height = r.height;
10206                 el.afterFx(o);
10207             };
10208             // time to calc the positions
10209             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10210
10211             switch(anchor.toLowerCase()){
10212                 case "t":
10213                     wrap.setSize(b.width, 0);
10214                     st.left = st.bottom = "0";
10215                     a = {height: bh};
10216                 break;
10217                 case "l":
10218                     wrap.setSize(0, b.height);
10219                     st.right = st.top = "0";
10220                     a = {width: bw};
10221                 break;
10222                 case "r":
10223                     wrap.setSize(0, b.height);
10224                     wrap.setX(b.right);
10225                     st.left = st.top = "0";
10226                     a = {width: bw, points: pt};
10227                 break;
10228                 case "b":
10229                     wrap.setSize(b.width, 0);
10230                     wrap.setY(b.bottom);
10231                     st.left = st.top = "0";
10232                     a = {height: bh, points: pt};
10233                 break;
10234                 case "tl":
10235                     wrap.setSize(0, 0);
10236                     st.right = st.bottom = "0";
10237                     a = {width: bw, height: bh};
10238                 break;
10239                 case "bl":
10240                     wrap.setSize(0, 0);
10241                     wrap.setY(b.y+b.height);
10242                     st.right = st.top = "0";
10243                     a = {width: bw, height: bh, points: pt};
10244                 break;
10245                 case "br":
10246                     wrap.setSize(0, 0);
10247                     wrap.setXY([b.right, b.bottom]);
10248                     st.left = st.top = "0";
10249                     a = {width: bw, height: bh, points: pt};
10250                 break;
10251                 case "tr":
10252                     wrap.setSize(0, 0);
10253                     wrap.setX(b.x+b.width);
10254                     st.left = st.bottom = "0";
10255                     a = {width: bw, height: bh, points: pt};
10256                 break;
10257             }
10258             this.dom.style.visibility = "visible";
10259             wrap.show();
10260
10261             arguments.callee.anim = wrap.fxanim(a,
10262                 o,
10263                 'motion',
10264                 .5,
10265                 'easeOut', after);
10266         });
10267         return this;
10268     },
10269     
10270         /**
10271          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10272          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10273          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10274          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10275          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10276          * Usage:
10277          *<pre><code>
10278 // default: slide the element out to the top
10279 el.slideOut();
10280
10281 // custom: slide the element out to the right with a 2-second duration
10282 el.slideOut('r', { duration: 2 });
10283
10284 // common config options shown with default values
10285 el.slideOut('t', {
10286     easing: 'easeOut',
10287     duration: .5,
10288     remove: false,
10289     useDisplay: false
10290 });
10291 </code></pre>
10292          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10293          * @param {Object} options (optional) Object literal with any of the Fx config options
10294          * @return {Roo.Element} The Element
10295          */
10296     slideOut : function(anchor, o){
10297         var el = this.getFxEl();
10298         o = o || {};
10299
10300         el.queueFx(o, function(){
10301
10302             anchor = anchor || "t";
10303
10304             // restore values after effect
10305             var r = this.getFxRestore();
10306             
10307             var b = this.getBox();
10308             // fixed size for slide
10309             this.setSize(b);
10310
10311             // wrap if needed
10312             var wrap = this.fxWrap(r.pos, o, "visible");
10313
10314             var st = this.dom.style;
10315             st.visibility = "visible";
10316             st.position = "absolute";
10317
10318             wrap.setSize(b);
10319
10320             var after = function(){
10321                 if(o.useDisplay){
10322                     el.setDisplayed(false);
10323                 }else{
10324                     el.hide();
10325                 }
10326
10327                 el.fxUnwrap(wrap, r.pos, o);
10328
10329                 st.width = r.width;
10330                 st.height = r.height;
10331
10332                 el.afterFx(o);
10333             };
10334
10335             var a, zero = {to: 0};
10336             switch(anchor.toLowerCase()){
10337                 case "t":
10338                     st.left = st.bottom = "0";
10339                     a = {height: zero};
10340                 break;
10341                 case "l":
10342                     st.right = st.top = "0";
10343                     a = {width: zero};
10344                 break;
10345                 case "r":
10346                     st.left = st.top = "0";
10347                     a = {width: zero, points: {to:[b.right, b.y]}};
10348                 break;
10349                 case "b":
10350                     st.left = st.top = "0";
10351                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10352                 break;
10353                 case "tl":
10354                     st.right = st.bottom = "0";
10355                     a = {width: zero, height: zero};
10356                 break;
10357                 case "bl":
10358                     st.right = st.top = "0";
10359                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10360                 break;
10361                 case "br":
10362                     st.left = st.top = "0";
10363                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10364                 break;
10365                 case "tr":
10366                     st.left = st.bottom = "0";
10367                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10368                 break;
10369             }
10370
10371             arguments.callee.anim = wrap.fxanim(a,
10372                 o,
10373                 'motion',
10374                 .5,
10375                 "easeOut", after);
10376         });
10377         return this;
10378     },
10379
10380         /**
10381          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10382          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10383          * The element must be removed from the DOM using the 'remove' config option if desired.
10384          * Usage:
10385          *<pre><code>
10386 // default
10387 el.puff();
10388
10389 // common config options shown with default values
10390 el.puff({
10391     easing: 'easeOut',
10392     duration: .5,
10393     remove: false,
10394     useDisplay: false
10395 });
10396 </code></pre>
10397          * @param {Object} options (optional) Object literal with any of the Fx config options
10398          * @return {Roo.Element} The Element
10399          */
10400     puff : function(o){
10401         var el = this.getFxEl();
10402         o = o || {};
10403
10404         el.queueFx(o, function(){
10405             this.clearOpacity();
10406             this.show();
10407
10408             // restore values after effect
10409             var r = this.getFxRestore();
10410             var st = this.dom.style;
10411
10412             var after = function(){
10413                 if(o.useDisplay){
10414                     el.setDisplayed(false);
10415                 }else{
10416                     el.hide();
10417                 }
10418
10419                 el.clearOpacity();
10420
10421                 el.setPositioning(r.pos);
10422                 st.width = r.width;
10423                 st.height = r.height;
10424                 st.fontSize = '';
10425                 el.afterFx(o);
10426             };
10427
10428             var width = this.getWidth();
10429             var height = this.getHeight();
10430
10431             arguments.callee.anim = this.fxanim({
10432                     width : {to: this.adjustWidth(width * 2)},
10433                     height : {to: this.adjustHeight(height * 2)},
10434                     points : {by: [-(width * .5), -(height * .5)]},
10435                     opacity : {to: 0},
10436                     fontSize: {to:200, unit: "%"}
10437                 },
10438                 o,
10439                 'motion',
10440                 .5,
10441                 "easeOut", after);
10442         });
10443         return this;
10444     },
10445
10446         /**
10447          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10448          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10449          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10450          * Usage:
10451          *<pre><code>
10452 // default
10453 el.switchOff();
10454
10455 // all config options shown with default values
10456 el.switchOff({
10457     easing: 'easeIn',
10458     duration: .3,
10459     remove: false,
10460     useDisplay: false
10461 });
10462 </code></pre>
10463          * @param {Object} options (optional) Object literal with any of the Fx config options
10464          * @return {Roo.Element} The Element
10465          */
10466     switchOff : function(o){
10467         var el = this.getFxEl();
10468         o = o || {};
10469
10470         el.queueFx(o, function(){
10471             this.clearOpacity();
10472             this.clip();
10473
10474             // restore values after effect
10475             var r = this.getFxRestore();
10476             var st = this.dom.style;
10477
10478             var after = function(){
10479                 if(o.useDisplay){
10480                     el.setDisplayed(false);
10481                 }else{
10482                     el.hide();
10483                 }
10484
10485                 el.clearOpacity();
10486                 el.setPositioning(r.pos);
10487                 st.width = r.width;
10488                 st.height = r.height;
10489
10490                 el.afterFx(o);
10491             };
10492
10493             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10494                 this.clearOpacity();
10495                 (function(){
10496                     this.fxanim({
10497                         height:{to:1},
10498                         points:{by:[0, this.getHeight() * .5]}
10499                     }, o, 'motion', 0.3, 'easeIn', after);
10500                 }).defer(100, this);
10501             });
10502         });
10503         return this;
10504     },
10505
10506     /**
10507      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10508      * changed using the "attr" config option) and then fading back to the original color. If no original
10509      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10510      * Usage:
10511 <pre><code>
10512 // default: highlight background to yellow
10513 el.highlight();
10514
10515 // custom: highlight foreground text to blue for 2 seconds
10516 el.highlight("0000ff", { attr: 'color', duration: 2 });
10517
10518 // common config options shown with default values
10519 el.highlight("ffff9c", {
10520     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10521     endColor: (current color) or "ffffff",
10522     easing: 'easeIn',
10523     duration: 1
10524 });
10525 </code></pre>
10526      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10527      * @param {Object} options (optional) Object literal with any of the Fx config options
10528      * @return {Roo.Element} The Element
10529      */ 
10530     highlight : function(color, o){
10531         var el = this.getFxEl();
10532         o = o || {};
10533
10534         el.queueFx(o, function(){
10535             color = color || "ffff9c";
10536             attr = o.attr || "backgroundColor";
10537
10538             this.clearOpacity();
10539             this.show();
10540
10541             var origColor = this.getColor(attr);
10542             var restoreColor = this.dom.style[attr];
10543             endColor = (o.endColor || origColor) || "ffffff";
10544
10545             var after = function(){
10546                 el.dom.style[attr] = restoreColor;
10547                 el.afterFx(o);
10548             };
10549
10550             var a = {};
10551             a[attr] = {from: color, to: endColor};
10552             arguments.callee.anim = this.fxanim(a,
10553                 o,
10554                 'color',
10555                 1,
10556                 'easeIn', after);
10557         });
10558         return this;
10559     },
10560
10561    /**
10562     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10563     * Usage:
10564 <pre><code>
10565 // default: a single light blue ripple
10566 el.frame();
10567
10568 // custom: 3 red ripples lasting 3 seconds total
10569 el.frame("ff0000", 3, { duration: 3 });
10570
10571 // common config options shown with default values
10572 el.frame("C3DAF9", 1, {
10573     duration: 1 //duration of entire animation (not each individual ripple)
10574     // Note: Easing is not configurable and will be ignored if included
10575 });
10576 </code></pre>
10577     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10578     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10579     * @param {Object} options (optional) Object literal with any of the Fx config options
10580     * @return {Roo.Element} The Element
10581     */
10582     frame : function(color, count, o){
10583         var el = this.getFxEl();
10584         o = o || {};
10585
10586         el.queueFx(o, function(){
10587             color = color || "#C3DAF9";
10588             if(color.length == 6){
10589                 color = "#" + color;
10590             }
10591             count = count || 1;
10592             duration = o.duration || 1;
10593             this.show();
10594
10595             var b = this.getBox();
10596             var animFn = function(){
10597                 var proxy = this.createProxy({
10598
10599                      style:{
10600                         visbility:"hidden",
10601                         position:"absolute",
10602                         "z-index":"35000", // yee haw
10603                         border:"0px solid " + color
10604                      }
10605                   });
10606                 var scale = Roo.isBorderBox ? 2 : 1;
10607                 proxy.animate({
10608                     top:{from:b.y, to:b.y - 20},
10609                     left:{from:b.x, to:b.x - 20},
10610                     borderWidth:{from:0, to:10},
10611                     opacity:{from:1, to:0},
10612                     height:{from:b.height, to:(b.height + (20*scale))},
10613                     width:{from:b.width, to:(b.width + (20*scale))}
10614                 }, duration, function(){
10615                     proxy.remove();
10616                 });
10617                 if(--count > 0){
10618                      animFn.defer((duration/2)*1000, this);
10619                 }else{
10620                     el.afterFx(o);
10621                 }
10622             };
10623             animFn.call(this);
10624         });
10625         return this;
10626     },
10627
10628    /**
10629     * Creates a pause before any subsequent queued effects begin.  If there are
10630     * no effects queued after the pause it will have no effect.
10631     * Usage:
10632 <pre><code>
10633 el.pause(1);
10634 </code></pre>
10635     * @param {Number} seconds The length of time to pause (in seconds)
10636     * @return {Roo.Element} The Element
10637     */
10638     pause : function(seconds){
10639         var el = this.getFxEl();
10640         var o = {};
10641
10642         el.queueFx(o, function(){
10643             setTimeout(function(){
10644                 el.afterFx(o);
10645             }, seconds * 1000);
10646         });
10647         return this;
10648     },
10649
10650    /**
10651     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10652     * using the "endOpacity" config option.
10653     * Usage:
10654 <pre><code>
10655 // default: fade in from opacity 0 to 100%
10656 el.fadeIn();
10657
10658 // custom: fade in from opacity 0 to 75% over 2 seconds
10659 el.fadeIn({ endOpacity: .75, duration: 2});
10660
10661 // common config options shown with default values
10662 el.fadeIn({
10663     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10664     easing: 'easeOut',
10665     duration: .5
10666 });
10667 </code></pre>
10668     * @param {Object} options (optional) Object literal with any of the Fx config options
10669     * @return {Roo.Element} The Element
10670     */
10671     fadeIn : function(o){
10672         var el = this.getFxEl();
10673         o = o || {};
10674         el.queueFx(o, function(){
10675             this.setOpacity(0);
10676             this.fixDisplay();
10677             this.dom.style.visibility = 'visible';
10678             var to = o.endOpacity || 1;
10679             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10680                 o, null, .5, "easeOut", function(){
10681                 if(to == 1){
10682                     this.clearOpacity();
10683                 }
10684                 el.afterFx(o);
10685             });
10686         });
10687         return this;
10688     },
10689
10690    /**
10691     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10692     * using the "endOpacity" config option.
10693     * Usage:
10694 <pre><code>
10695 // default: fade out from the element's current opacity to 0
10696 el.fadeOut();
10697
10698 // custom: fade out from the element's current opacity to 25% over 2 seconds
10699 el.fadeOut({ endOpacity: .25, duration: 2});
10700
10701 // common config options shown with default values
10702 el.fadeOut({
10703     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10704     easing: 'easeOut',
10705     duration: .5
10706     remove: false,
10707     useDisplay: false
10708 });
10709 </code></pre>
10710     * @param {Object} options (optional) Object literal with any of the Fx config options
10711     * @return {Roo.Element} The Element
10712     */
10713     fadeOut : function(o){
10714         var el = this.getFxEl();
10715         o = o || {};
10716         el.queueFx(o, function(){
10717             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10718                 o, null, .5, "easeOut", function(){
10719                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10720                      this.dom.style.display = "none";
10721                 }else{
10722                      this.dom.style.visibility = "hidden";
10723                 }
10724                 this.clearOpacity();
10725                 el.afterFx(o);
10726             });
10727         });
10728         return this;
10729     },
10730
10731    /**
10732     * Animates the transition of an element's dimensions from a starting height/width
10733     * to an ending height/width.
10734     * Usage:
10735 <pre><code>
10736 // change height and width to 100x100 pixels
10737 el.scale(100, 100);
10738
10739 // common config options shown with default values.  The height and width will default to
10740 // the element's existing values if passed as null.
10741 el.scale(
10742     [element's width],
10743     [element's height], {
10744     easing: 'easeOut',
10745     duration: .35
10746 });
10747 </code></pre>
10748     * @param {Number} width  The new width (pass undefined to keep the original width)
10749     * @param {Number} height  The new height (pass undefined to keep the original height)
10750     * @param {Object} options (optional) Object literal with any of the Fx config options
10751     * @return {Roo.Element} The Element
10752     */
10753     scale : function(w, h, o){
10754         this.shift(Roo.apply({}, o, {
10755             width: w,
10756             height: h
10757         }));
10758         return this;
10759     },
10760
10761    /**
10762     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10763     * Any of these properties not specified in the config object will not be changed.  This effect 
10764     * requires that at least one new dimension, position or opacity setting must be passed in on
10765     * the config object in order for the function to have any effect.
10766     * Usage:
10767 <pre><code>
10768 // slide the element horizontally to x position 200 while changing the height and opacity
10769 el.shift({ x: 200, height: 50, opacity: .8 });
10770
10771 // common config options shown with default values.
10772 el.shift({
10773     width: [element's width],
10774     height: [element's height],
10775     x: [element's x position],
10776     y: [element's y position],
10777     opacity: [element's opacity],
10778     easing: 'easeOut',
10779     duration: .35
10780 });
10781 </code></pre>
10782     * @param {Object} options  Object literal with any of the Fx config options
10783     * @return {Roo.Element} The Element
10784     */
10785     shift : function(o){
10786         var el = this.getFxEl();
10787         o = o || {};
10788         el.queueFx(o, function(){
10789             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10790             if(w !== undefined){
10791                 a.width = {to: this.adjustWidth(w)};
10792             }
10793             if(h !== undefined){
10794                 a.height = {to: this.adjustHeight(h)};
10795             }
10796             if(x !== undefined || y !== undefined){
10797                 a.points = {to: [
10798                     x !== undefined ? x : this.getX(),
10799                     y !== undefined ? y : this.getY()
10800                 ]};
10801             }
10802             if(op !== undefined){
10803                 a.opacity = {to: op};
10804             }
10805             if(o.xy !== undefined){
10806                 a.points = {to: o.xy};
10807             }
10808             arguments.callee.anim = this.fxanim(a,
10809                 o, 'motion', .35, "easeOut", function(){
10810                 el.afterFx(o);
10811             });
10812         });
10813         return this;
10814     },
10815
10816         /**
10817          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10818          * ending point of the effect.
10819          * Usage:
10820          *<pre><code>
10821 // default: slide the element downward while fading out
10822 el.ghost();
10823
10824 // custom: slide the element out to the right with a 2-second duration
10825 el.ghost('r', { duration: 2 });
10826
10827 // common config options shown with default values
10828 el.ghost('b', {
10829     easing: 'easeOut',
10830     duration: .5
10831     remove: false,
10832     useDisplay: false
10833 });
10834 </code></pre>
10835          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10836          * @param {Object} options (optional) Object literal with any of the Fx config options
10837          * @return {Roo.Element} The Element
10838          */
10839     ghost : function(anchor, o){
10840         var el = this.getFxEl();
10841         o = o || {};
10842
10843         el.queueFx(o, function(){
10844             anchor = anchor || "b";
10845
10846             // restore values after effect
10847             var r = this.getFxRestore();
10848             var w = this.getWidth(),
10849                 h = this.getHeight();
10850
10851             var st = this.dom.style;
10852
10853             var after = function(){
10854                 if(o.useDisplay){
10855                     el.setDisplayed(false);
10856                 }else{
10857                     el.hide();
10858                 }
10859
10860                 el.clearOpacity();
10861                 el.setPositioning(r.pos);
10862                 st.width = r.width;
10863                 st.height = r.height;
10864
10865                 el.afterFx(o);
10866             };
10867
10868             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10869             switch(anchor.toLowerCase()){
10870                 case "t":
10871                     pt.by = [0, -h];
10872                 break;
10873                 case "l":
10874                     pt.by = [-w, 0];
10875                 break;
10876                 case "r":
10877                     pt.by = [w, 0];
10878                 break;
10879                 case "b":
10880                     pt.by = [0, h];
10881                 break;
10882                 case "tl":
10883                     pt.by = [-w, -h];
10884                 break;
10885                 case "bl":
10886                     pt.by = [-w, h];
10887                 break;
10888                 case "br":
10889                     pt.by = [w, h];
10890                 break;
10891                 case "tr":
10892                     pt.by = [w, -h];
10893                 break;
10894             }
10895
10896             arguments.callee.anim = this.fxanim(a,
10897                 o,
10898                 'motion',
10899                 .5,
10900                 "easeOut", after);
10901         });
10902         return this;
10903     },
10904
10905         /**
10906          * Ensures that all effects queued after syncFx is called on the element are
10907          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10908          * @return {Roo.Element} The Element
10909          */
10910     syncFx : function(){
10911         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10912             block : false,
10913             concurrent : true,
10914             stopFx : false
10915         });
10916         return this;
10917     },
10918
10919         /**
10920          * Ensures that all effects queued after sequenceFx is called on the element are
10921          * run in sequence.  This is the opposite of {@link #syncFx}.
10922          * @return {Roo.Element} The Element
10923          */
10924     sequenceFx : function(){
10925         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10926             block : false,
10927             concurrent : false,
10928             stopFx : false
10929         });
10930         return this;
10931     },
10932
10933         /* @private */
10934     nextFx : function(){
10935         var ef = this.fxQueue[0];
10936         if(ef){
10937             ef.call(this);
10938         }
10939     },
10940
10941         /**
10942          * Returns true if the element has any effects actively running or queued, else returns false.
10943          * @return {Boolean} True if element has active effects, else false
10944          */
10945     hasActiveFx : function(){
10946         return this.fxQueue && this.fxQueue[0];
10947     },
10948
10949         /**
10950          * Stops any running effects and clears the element's internal effects queue if it contains
10951          * any additional effects that haven't started yet.
10952          * @return {Roo.Element} The Element
10953          */
10954     stopFx : function(){
10955         if(this.hasActiveFx()){
10956             var cur = this.fxQueue[0];
10957             if(cur && cur.anim && cur.anim.isAnimated()){
10958                 this.fxQueue = [cur]; // clear out others
10959                 cur.anim.stop(true);
10960             }
10961         }
10962         return this;
10963     },
10964
10965         /* @private */
10966     beforeFx : function(o){
10967         if(this.hasActiveFx() && !o.concurrent){
10968            if(o.stopFx){
10969                this.stopFx();
10970                return true;
10971            }
10972            return false;
10973         }
10974         return true;
10975     },
10976
10977         /**
10978          * Returns true if the element is currently blocking so that no other effect can be queued
10979          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10980          * used to ensure that an effect initiated by a user action runs to completion prior to the
10981          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10982          * @return {Boolean} True if blocking, else false
10983          */
10984     hasFxBlock : function(){
10985         var q = this.fxQueue;
10986         return q && q[0] && q[0].block;
10987     },
10988
10989         /* @private */
10990     queueFx : function(o, fn){
10991         if(!this.fxQueue){
10992             this.fxQueue = [];
10993         }
10994         if(!this.hasFxBlock()){
10995             Roo.applyIf(o, this.fxDefaults);
10996             if(!o.concurrent){
10997                 var run = this.beforeFx(o);
10998                 fn.block = o.block;
10999                 this.fxQueue.push(fn);
11000                 if(run){
11001                     this.nextFx();
11002                 }
11003             }else{
11004                 fn.call(this);
11005             }
11006         }
11007         return this;
11008     },
11009
11010         /* @private */
11011     fxWrap : function(pos, o, vis){
11012         var wrap;
11013         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11014             var wrapXY;
11015             if(o.fixPosition){
11016                 wrapXY = this.getXY();
11017             }
11018             var div = document.createElement("div");
11019             div.style.visibility = vis;
11020             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11021             wrap.setPositioning(pos);
11022             if(wrap.getStyle("position") == "static"){
11023                 wrap.position("relative");
11024             }
11025             this.clearPositioning('auto');
11026             wrap.clip();
11027             wrap.dom.appendChild(this.dom);
11028             if(wrapXY){
11029                 wrap.setXY(wrapXY);
11030             }
11031         }
11032         return wrap;
11033     },
11034
11035         /* @private */
11036     fxUnwrap : function(wrap, pos, o){
11037         this.clearPositioning();
11038         this.setPositioning(pos);
11039         if(!o.wrap){
11040             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11041             wrap.remove();
11042         }
11043     },
11044
11045         /* @private */
11046     getFxRestore : function(){
11047         var st = this.dom.style;
11048         return {pos: this.getPositioning(), width: st.width, height : st.height};
11049     },
11050
11051         /* @private */
11052     afterFx : function(o){
11053         if(o.afterStyle){
11054             this.applyStyles(o.afterStyle);
11055         }
11056         if(o.afterCls){
11057             this.addClass(o.afterCls);
11058         }
11059         if(o.remove === true){
11060             this.remove();
11061         }
11062         Roo.callback(o.callback, o.scope, [this]);
11063         if(!o.concurrent){
11064             this.fxQueue.shift();
11065             this.nextFx();
11066         }
11067     },
11068
11069         /* @private */
11070     getFxEl : function(){ // support for composite element fx
11071         return Roo.get(this.dom);
11072     },
11073
11074         /* @private */
11075     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11076         animType = animType || 'run';
11077         opt = opt || {};
11078         var anim = Roo.lib.Anim[animType](
11079             this.dom, args,
11080             (opt.duration || defaultDur) || .35,
11081             (opt.easing || defaultEase) || 'easeOut',
11082             function(){
11083                 Roo.callback(cb, this);
11084             },
11085             this
11086         );
11087         opt.anim = anim;
11088         return anim;
11089     }
11090 };
11091
11092 // backwords compat
11093 Roo.Fx.resize = Roo.Fx.scale;
11094
11095 //When included, Roo.Fx is automatically applied to Element so that all basic
11096 //effects are available directly via the Element API
11097 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11098  * Based on:
11099  * Ext JS Library 1.1.1
11100  * Copyright(c) 2006-2007, Ext JS, LLC.
11101  *
11102  * Originally Released Under LGPL - original licence link has changed is not relivant.
11103  *
11104  * Fork - LGPL
11105  * <script type="text/javascript">
11106  */
11107
11108
11109 /**
11110  * @class Roo.CompositeElement
11111  * Standard composite class. Creates a Roo.Element for every element in the collection.
11112  * <br><br>
11113  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11114  * actions will be performed on all the elements in this collection.</b>
11115  * <br><br>
11116  * All methods return <i>this</i> and can be chained.
11117  <pre><code>
11118  var els = Roo.select("#some-el div.some-class", true);
11119  // or select directly from an existing element
11120  var el = Roo.get('some-el');
11121  el.select('div.some-class', true);
11122
11123  els.setWidth(100); // all elements become 100 width
11124  els.hide(true); // all elements fade out and hide
11125  // or
11126  els.setWidth(100).hide(true);
11127  </code></pre>
11128  */
11129 Roo.CompositeElement = function(els){
11130     this.elements = [];
11131     this.addElements(els);
11132 };
11133 Roo.CompositeElement.prototype = {
11134     isComposite: true,
11135     addElements : function(els){
11136         if(!els) {
11137             return this;
11138         }
11139         if(typeof els == "string"){
11140             els = Roo.Element.selectorFunction(els);
11141         }
11142         var yels = this.elements;
11143         var index = yels.length-1;
11144         for(var i = 0, len = els.length; i < len; i++) {
11145                 yels[++index] = Roo.get(els[i]);
11146         }
11147         return this;
11148     },
11149
11150     /**
11151     * Clears this composite and adds the elements returned by the passed selector.
11152     * @param {String/Array} els A string CSS selector, an array of elements or an element
11153     * @return {CompositeElement} this
11154     */
11155     fill : function(els){
11156         this.elements = [];
11157         this.add(els);
11158         return this;
11159     },
11160
11161     /**
11162     * Filters this composite to only elements that match the passed selector.
11163     * @param {String} selector A string CSS selector
11164     * @param {Boolean} inverse return inverse filter (not matches)
11165     * @return {CompositeElement} this
11166     */
11167     filter : function(selector, inverse){
11168         var els = [];
11169         inverse = inverse || false;
11170         this.each(function(el){
11171             var match = inverse ? !el.is(selector) : el.is(selector);
11172             if(match){
11173                 els[els.length] = el.dom;
11174             }
11175         });
11176         this.fill(els);
11177         return this;
11178     },
11179
11180     invoke : function(fn, args){
11181         var els = this.elements;
11182         for(var i = 0, len = els.length; i < len; i++) {
11183                 Roo.Element.prototype[fn].apply(els[i], args);
11184         }
11185         return this;
11186     },
11187     /**
11188     * Adds elements to this composite.
11189     * @param {String/Array} els A string CSS selector, an array of elements or an element
11190     * @return {CompositeElement} this
11191     */
11192     add : function(els){
11193         if(typeof els == "string"){
11194             this.addElements(Roo.Element.selectorFunction(els));
11195         }else if(els.length !== undefined){
11196             this.addElements(els);
11197         }else{
11198             this.addElements([els]);
11199         }
11200         return this;
11201     },
11202     /**
11203     * Calls the passed function passing (el, this, index) for each element in this composite.
11204     * @param {Function} fn The function to call
11205     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11206     * @return {CompositeElement} this
11207     */
11208     each : function(fn, scope){
11209         var els = this.elements;
11210         for(var i = 0, len = els.length; i < len; i++){
11211             if(fn.call(scope || els[i], els[i], this, i) === false) {
11212                 break;
11213             }
11214         }
11215         return this;
11216     },
11217
11218     /**
11219      * Returns the Element object at the specified index
11220      * @param {Number} index
11221      * @return {Roo.Element}
11222      */
11223     item : function(index){
11224         return this.elements[index] || null;
11225     },
11226
11227     /**
11228      * Returns the first Element
11229      * @return {Roo.Element}
11230      */
11231     first : function(){
11232         return this.item(0);
11233     },
11234
11235     /**
11236      * Returns the last Element
11237      * @return {Roo.Element}
11238      */
11239     last : function(){
11240         return this.item(this.elements.length-1);
11241     },
11242
11243     /**
11244      * Returns the number of elements in this composite
11245      * @return Number
11246      */
11247     getCount : function(){
11248         return this.elements.length;
11249     },
11250
11251     /**
11252      * Returns true if this composite contains the passed element
11253      * @return Boolean
11254      */
11255     contains : function(el){
11256         return this.indexOf(el) !== -1;
11257     },
11258
11259     /**
11260      * Returns true if this composite contains the passed element
11261      * @return Boolean
11262      */
11263     indexOf : function(el){
11264         return this.elements.indexOf(Roo.get(el));
11265     },
11266
11267
11268     /**
11269     * Removes the specified element(s).
11270     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11271     * or an array of any of those.
11272     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11273     * @return {CompositeElement} this
11274     */
11275     removeElement : function(el, removeDom){
11276         if(el instanceof Array){
11277             for(var i = 0, len = el.length; i < len; i++){
11278                 this.removeElement(el[i]);
11279             }
11280             return this;
11281         }
11282         var index = typeof el == 'number' ? el : this.indexOf(el);
11283         if(index !== -1){
11284             if(removeDom){
11285                 var d = this.elements[index];
11286                 if(d.dom){
11287                     d.remove();
11288                 }else{
11289                     d.parentNode.removeChild(d);
11290                 }
11291             }
11292             this.elements.splice(index, 1);
11293         }
11294         return this;
11295     },
11296
11297     /**
11298     * Replaces the specified element with the passed element.
11299     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11300     * to replace.
11301     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11302     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11303     * @return {CompositeElement} this
11304     */
11305     replaceElement : function(el, replacement, domReplace){
11306         var index = typeof el == 'number' ? el : this.indexOf(el);
11307         if(index !== -1){
11308             if(domReplace){
11309                 this.elements[index].replaceWith(replacement);
11310             }else{
11311                 this.elements.splice(index, 1, Roo.get(replacement))
11312             }
11313         }
11314         return this;
11315     },
11316
11317     /**
11318      * Removes all elements.
11319      */
11320     clear : function(){
11321         this.elements = [];
11322     }
11323 };
11324 (function(){
11325     Roo.CompositeElement.createCall = function(proto, fnName){
11326         if(!proto[fnName]){
11327             proto[fnName] = function(){
11328                 return this.invoke(fnName, arguments);
11329             };
11330         }
11331     };
11332     for(var fnName in Roo.Element.prototype){
11333         if(typeof Roo.Element.prototype[fnName] == "function"){
11334             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11335         }
11336     };
11337 })();
11338 /*
11339  * Based on:
11340  * Ext JS Library 1.1.1
11341  * Copyright(c) 2006-2007, Ext JS, LLC.
11342  *
11343  * Originally Released Under LGPL - original licence link has changed is not relivant.
11344  *
11345  * Fork - LGPL
11346  * <script type="text/javascript">
11347  */
11348
11349 /**
11350  * @class Roo.CompositeElementLite
11351  * @extends Roo.CompositeElement
11352  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11353  <pre><code>
11354  var els = Roo.select("#some-el div.some-class");
11355  // or select directly from an existing element
11356  var el = Roo.get('some-el');
11357  el.select('div.some-class');
11358
11359  els.setWidth(100); // all elements become 100 width
11360  els.hide(true); // all elements fade out and hide
11361  // or
11362  els.setWidth(100).hide(true);
11363  </code></pre><br><br>
11364  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11365  * actions will be performed on all the elements in this collection.</b>
11366  */
11367 Roo.CompositeElementLite = function(els){
11368     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11369     this.el = new Roo.Element.Flyweight();
11370 };
11371 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11372     addElements : function(els){
11373         if(els){
11374             if(els instanceof Array){
11375                 this.elements = this.elements.concat(els);
11376             }else{
11377                 var yels = this.elements;
11378                 var index = yels.length-1;
11379                 for(var i = 0, len = els.length; i < len; i++) {
11380                     yels[++index] = els[i];
11381                 }
11382             }
11383         }
11384         return this;
11385     },
11386     invoke : function(fn, args){
11387         var els = this.elements;
11388         var el = this.el;
11389         for(var i = 0, len = els.length; i < len; i++) {
11390             el.dom = els[i];
11391                 Roo.Element.prototype[fn].apply(el, args);
11392         }
11393         return this;
11394     },
11395     /**
11396      * Returns a flyweight Element of the dom element object at the specified index
11397      * @param {Number} index
11398      * @return {Roo.Element}
11399      */
11400     item : function(index){
11401         if(!this.elements[index]){
11402             return null;
11403         }
11404         this.el.dom = this.elements[index];
11405         return this.el;
11406     },
11407
11408     // fixes scope with flyweight
11409     addListener : function(eventName, handler, scope, opt){
11410         var els = this.elements;
11411         for(var i = 0, len = els.length; i < len; i++) {
11412             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11413         }
11414         return this;
11415     },
11416
11417     /**
11418     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11419     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11420     * a reference to the dom node, use el.dom.</b>
11421     * @param {Function} fn The function to call
11422     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11423     * @return {CompositeElement} this
11424     */
11425     each : function(fn, scope){
11426         var els = this.elements;
11427         var el = this.el;
11428         for(var i = 0, len = els.length; i < len; i++){
11429             el.dom = els[i];
11430                 if(fn.call(scope || el, el, this, i) === false){
11431                 break;
11432             }
11433         }
11434         return this;
11435     },
11436
11437     indexOf : function(el){
11438         return this.elements.indexOf(Roo.getDom(el));
11439     },
11440
11441     replaceElement : function(el, replacement, domReplace){
11442         var index = typeof el == 'number' ? el : this.indexOf(el);
11443         if(index !== -1){
11444             replacement = Roo.getDom(replacement);
11445             if(domReplace){
11446                 var d = this.elements[index];
11447                 d.parentNode.insertBefore(replacement, d);
11448                 d.parentNode.removeChild(d);
11449             }
11450             this.elements.splice(index, 1, replacement);
11451         }
11452         return this;
11453     }
11454 });
11455 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11456
11457 /*
11458  * Based on:
11459  * Ext JS Library 1.1.1
11460  * Copyright(c) 2006-2007, Ext JS, LLC.
11461  *
11462  * Originally Released Under LGPL - original licence link has changed is not relivant.
11463  *
11464  * Fork - LGPL
11465  * <script type="text/javascript">
11466  */
11467
11468  
11469
11470 /**
11471  * @class Roo.data.Connection
11472  * @extends Roo.util.Observable
11473  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11474  * either to a configured URL, or to a URL specified at request time.<br><br>
11475  * <p>
11476  * Requests made by this class are asynchronous, and will return immediately. No data from
11477  * the server will be available to the statement immediately following the {@link #request} call.
11478  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11479  * <p>
11480  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11481  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11482  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11483  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11484  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11485  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11486  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11487  * standard DOM methods.
11488  * @constructor
11489  * @param {Object} config a configuration object.
11490  */
11491 Roo.data.Connection = function(config){
11492     Roo.apply(this, config);
11493     this.addEvents({
11494         /**
11495          * @event beforerequest
11496          * Fires before a network request is made to retrieve a data object.
11497          * @param {Connection} conn This Connection object.
11498          * @param {Object} options The options config object passed to the {@link #request} method.
11499          */
11500         "beforerequest" : true,
11501         /**
11502          * @event requestcomplete
11503          * Fires if the request was successfully completed.
11504          * @param {Connection} conn This Connection object.
11505          * @param {Object} response The XHR object containing the response data.
11506          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11507          * @param {Object} options The options config object passed to the {@link #request} method.
11508          */
11509         "requestcomplete" : true,
11510         /**
11511          * @event requestexception
11512          * Fires if an error HTTP status was returned from the server.
11513          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11514          * @param {Connection} conn This Connection object.
11515          * @param {Object} response The XHR object containing the response data.
11516          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11517          * @param {Object} options The options config object passed to the {@link #request} method.
11518          */
11519         "requestexception" : true
11520     });
11521     Roo.data.Connection.superclass.constructor.call(this);
11522 };
11523
11524 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11525     /**
11526      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11527      */
11528     /**
11529      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11530      * extra parameters to each request made by this object. (defaults to undefined)
11531      */
11532     /**
11533      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11534      *  to each request made by this object. (defaults to undefined)
11535      */
11536     /**
11537      * @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)
11538      */
11539     /**
11540      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11541      */
11542     timeout : 30000,
11543     /**
11544      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11545      * @type Boolean
11546      */
11547     autoAbort:false,
11548
11549     /**
11550      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11551      * @type Boolean
11552      */
11553     disableCaching: true,
11554
11555     /**
11556      * Sends an HTTP request to a remote server.
11557      * @param {Object} options An object which may contain the following properties:<ul>
11558      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11559      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11560      * request, a url encoded string or a function to call to get either.</li>
11561      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11562      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11563      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11564      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11565      * <li>options {Object} The parameter to the request call.</li>
11566      * <li>success {Boolean} True if the request succeeded.</li>
11567      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11568      * </ul></li>
11569      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11570      * The callback is passed the following parameters:<ul>
11571      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11572      * <li>options {Object} The parameter to the request call.</li>
11573      * </ul></li>
11574      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11575      * The callback is passed the following parameters:<ul>
11576      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11577      * <li>options {Object} The parameter to the request call.</li>
11578      * </ul></li>
11579      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11580      * for the callback function. Defaults to the browser window.</li>
11581      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11582      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11583      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11584      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11585      * params for the post data. Any params will be appended to the URL.</li>
11586      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11587      * </ul>
11588      * @return {Number} transactionId
11589      */
11590     request : function(o){
11591         if(this.fireEvent("beforerequest", this, o) !== false){
11592             var p = o.params;
11593
11594             if(typeof p == "function"){
11595                 p = p.call(o.scope||window, o);
11596             }
11597             if(typeof p == "object"){
11598                 p = Roo.urlEncode(o.params);
11599             }
11600             if(this.extraParams){
11601                 var extras = Roo.urlEncode(this.extraParams);
11602                 p = p ? (p + '&' + extras) : extras;
11603             }
11604
11605             var url = o.url || this.url;
11606             if(typeof url == 'function'){
11607                 url = url.call(o.scope||window, o);
11608             }
11609
11610             if(o.form){
11611                 var form = Roo.getDom(o.form);
11612                 url = url || form.action;
11613
11614                 var enctype = form.getAttribute("enctype");
11615                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11616                     return this.doFormUpload(o, p, url);
11617                 }
11618                 var f = Roo.lib.Ajax.serializeForm(form);
11619                 p = p ? (p + '&' + f) : f;
11620             }
11621
11622             var hs = o.headers;
11623             if(this.defaultHeaders){
11624                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11625                 if(!o.headers){
11626                     o.headers = hs;
11627                 }
11628             }
11629
11630             var cb = {
11631                 success: this.handleResponse,
11632                 failure: this.handleFailure,
11633                 scope: this,
11634                 argument: {options: o},
11635                 timeout : o.timeout || this.timeout
11636             };
11637
11638             var method = o.method||this.method||(p ? "POST" : "GET");
11639
11640             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11641                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11642             }
11643
11644             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11645                 if(o.autoAbort){
11646                     this.abort();
11647                 }
11648             }else if(this.autoAbort !== false){
11649                 this.abort();
11650             }
11651
11652             if((method == 'GET' && p) || o.xmlData){
11653                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11654                 p = '';
11655             }
11656             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11657             return this.transId;
11658         }else{
11659             Roo.callback(o.callback, o.scope, [o, null, null]);
11660             return null;
11661         }
11662     },
11663
11664     /**
11665      * Determine whether this object has a request outstanding.
11666      * @param {Number} transactionId (Optional) defaults to the last transaction
11667      * @return {Boolean} True if there is an outstanding request.
11668      */
11669     isLoading : function(transId){
11670         if(transId){
11671             return Roo.lib.Ajax.isCallInProgress(transId);
11672         }else{
11673             return this.transId ? true : false;
11674         }
11675     },
11676
11677     /**
11678      * Aborts any outstanding request.
11679      * @param {Number} transactionId (Optional) defaults to the last transaction
11680      */
11681     abort : function(transId){
11682         if(transId || this.isLoading()){
11683             Roo.lib.Ajax.abort(transId || this.transId);
11684         }
11685     },
11686
11687     // private
11688     handleResponse : function(response){
11689         this.transId = false;
11690         var options = response.argument.options;
11691         response.argument = options ? options.argument : null;
11692         this.fireEvent("requestcomplete", this, response, options);
11693         Roo.callback(options.success, options.scope, [response, options]);
11694         Roo.callback(options.callback, options.scope, [options, true, response]);
11695     },
11696
11697     // private
11698     handleFailure : function(response, e){
11699         this.transId = false;
11700         var options = response.argument.options;
11701         response.argument = options ? options.argument : null;
11702         this.fireEvent("requestexception", this, response, options, e);
11703         Roo.callback(options.failure, options.scope, [response, options]);
11704         Roo.callback(options.callback, options.scope, [options, false, response]);
11705     },
11706
11707     // private
11708     doFormUpload : function(o, ps, url){
11709         var id = Roo.id();
11710         var frame = document.createElement('iframe');
11711         frame.id = id;
11712         frame.name = id;
11713         frame.className = 'x-hidden';
11714         if(Roo.isIE){
11715             frame.src = Roo.SSL_SECURE_URL;
11716         }
11717         document.body.appendChild(frame);
11718
11719         if(Roo.isIE){
11720            document.frames[id].name = id;
11721         }
11722
11723         var form = Roo.getDom(o.form);
11724         form.target = id;
11725         form.method = 'POST';
11726         form.enctype = form.encoding = 'multipart/form-data';
11727         if(url){
11728             form.action = url;
11729         }
11730
11731         var hiddens, hd;
11732         if(ps){ // add dynamic params
11733             hiddens = [];
11734             ps = Roo.urlDecode(ps, false);
11735             for(var k in ps){
11736                 if(ps.hasOwnProperty(k)){
11737                     hd = document.createElement('input');
11738                     hd.type = 'hidden';
11739                     hd.name = k;
11740                     hd.value = ps[k];
11741                     form.appendChild(hd);
11742                     hiddens.push(hd);
11743                 }
11744             }
11745         }
11746
11747         function cb(){
11748             var r = {  // bogus response object
11749                 responseText : '',
11750                 responseXML : null
11751             };
11752
11753             r.argument = o ? o.argument : null;
11754
11755             try { //
11756                 var doc;
11757                 if(Roo.isIE){
11758                     doc = frame.contentWindow.document;
11759                 }else {
11760                     doc = (frame.contentDocument || window.frames[id].document);
11761                 }
11762                 if(doc && doc.body){
11763                     r.responseText = doc.body.innerHTML;
11764                 }
11765                 if(doc && doc.XMLDocument){
11766                     r.responseXML = doc.XMLDocument;
11767                 }else {
11768                     r.responseXML = doc;
11769                 }
11770             }
11771             catch(e) {
11772                 // ignore
11773             }
11774
11775             Roo.EventManager.removeListener(frame, 'load', cb, this);
11776
11777             this.fireEvent("requestcomplete", this, r, o);
11778             Roo.callback(o.success, o.scope, [r, o]);
11779             Roo.callback(o.callback, o.scope, [o, true, r]);
11780
11781             setTimeout(function(){document.body.removeChild(frame);}, 100);
11782         }
11783
11784         Roo.EventManager.on(frame, 'load', cb, this);
11785         form.submit();
11786
11787         if(hiddens){ // remove dynamic params
11788             for(var i = 0, len = hiddens.length; i < len; i++){
11789                 form.removeChild(hiddens[i]);
11790             }
11791         }
11792     }
11793 });
11794 /*
11795  * Based on:
11796  * Ext JS Library 1.1.1
11797  * Copyright(c) 2006-2007, Ext JS, LLC.
11798  *
11799  * Originally Released Under LGPL - original licence link has changed is not relivant.
11800  *
11801  * Fork - LGPL
11802  * <script type="text/javascript">
11803  */
11804  
11805 /**
11806  * Global Ajax request class.
11807  * 
11808  * @class Roo.Ajax
11809  * @extends Roo.data.Connection
11810  * @static
11811  * 
11812  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11813  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11814  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11815  * @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)
11816  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11817  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11818  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11819  */
11820 Roo.Ajax = new Roo.data.Connection({
11821     // fix up the docs
11822     /**
11823      * @scope Roo.Ajax
11824      * @type {Boolear} 
11825      */
11826     autoAbort : false,
11827
11828     /**
11829      * Serialize the passed form into a url encoded string
11830      * @scope Roo.Ajax
11831      * @param {String/HTMLElement} form
11832      * @return {String}
11833      */
11834     serializeForm : function(form){
11835         return Roo.lib.Ajax.serializeForm(form);
11836     }
11837 });/*
11838  * Based on:
11839  * Ext JS Library 1.1.1
11840  * Copyright(c) 2006-2007, Ext JS, LLC.
11841  *
11842  * Originally Released Under LGPL - original licence link has changed is not relivant.
11843  *
11844  * Fork - LGPL
11845  * <script type="text/javascript">
11846  */
11847
11848  
11849 /**
11850  * @class Roo.UpdateManager
11851  * @extends Roo.util.Observable
11852  * Provides AJAX-style update for Element object.<br><br>
11853  * Usage:<br>
11854  * <pre><code>
11855  * // Get it from a Roo.Element object
11856  * var el = Roo.get("foo");
11857  * var mgr = el.getUpdateManager();
11858  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11859  * ...
11860  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11861  * <br>
11862  * // or directly (returns the same UpdateManager instance)
11863  * var mgr = new Roo.UpdateManager("myElementId");
11864  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11865  * mgr.on("update", myFcnNeedsToKnow);
11866  * <br>
11867    // short handed call directly from the element object
11868    Roo.get("foo").load({
11869         url: "bar.php",
11870         scripts:true,
11871         params: "for=bar",
11872         text: "Loading Foo..."
11873    });
11874  * </code></pre>
11875  * @constructor
11876  * Create new UpdateManager directly.
11877  * @param {String/HTMLElement/Roo.Element} el The element to update
11878  * @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).
11879  */
11880 Roo.UpdateManager = function(el, forceNew){
11881     el = Roo.get(el);
11882     if(!forceNew && el.updateManager){
11883         return el.updateManager;
11884     }
11885     /**
11886      * The Element object
11887      * @type Roo.Element
11888      */
11889     this.el = el;
11890     /**
11891      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11892      * @type String
11893      */
11894     this.defaultUrl = null;
11895
11896     this.addEvents({
11897         /**
11898          * @event beforeupdate
11899          * Fired before an update is made, return false from your handler and the update is cancelled.
11900          * @param {Roo.Element} el
11901          * @param {String/Object/Function} url
11902          * @param {String/Object} params
11903          */
11904         "beforeupdate": true,
11905         /**
11906          * @event update
11907          * Fired after successful update is made.
11908          * @param {Roo.Element} el
11909          * @param {Object} oResponseObject The response Object
11910          */
11911         "update": true,
11912         /**
11913          * @event failure
11914          * Fired on update failure.
11915          * @param {Roo.Element} el
11916          * @param {Object} oResponseObject The response Object
11917          */
11918         "failure": true
11919     });
11920     var d = Roo.UpdateManager.defaults;
11921     /**
11922      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11923      * @type String
11924      */
11925     this.sslBlankUrl = d.sslBlankUrl;
11926     /**
11927      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11928      * @type Boolean
11929      */
11930     this.disableCaching = d.disableCaching;
11931     /**
11932      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11933      * @type String
11934      */
11935     this.indicatorText = d.indicatorText;
11936     /**
11937      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11938      * @type String
11939      */
11940     this.showLoadIndicator = d.showLoadIndicator;
11941     /**
11942      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11943      * @type Number
11944      */
11945     this.timeout = d.timeout;
11946
11947     /**
11948      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11949      * @type Boolean
11950      */
11951     this.loadScripts = d.loadScripts;
11952
11953     /**
11954      * Transaction object of current executing transaction
11955      */
11956     this.transaction = null;
11957
11958     /**
11959      * @private
11960      */
11961     this.autoRefreshProcId = null;
11962     /**
11963      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11964      * @type Function
11965      */
11966     this.refreshDelegate = this.refresh.createDelegate(this);
11967     /**
11968      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11969      * @type Function
11970      */
11971     this.updateDelegate = this.update.createDelegate(this);
11972     /**
11973      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11974      * @type Function
11975      */
11976     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11977     /**
11978      * @private
11979      */
11980     this.successDelegate = this.processSuccess.createDelegate(this);
11981     /**
11982      * @private
11983      */
11984     this.failureDelegate = this.processFailure.createDelegate(this);
11985
11986     if(!this.renderer){
11987      /**
11988       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11989       */
11990     this.renderer = new Roo.UpdateManager.BasicRenderer();
11991     }
11992     
11993     Roo.UpdateManager.superclass.constructor.call(this);
11994 };
11995
11996 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11997     /**
11998      * Get the Element this UpdateManager is bound to
11999      * @return {Roo.Element} The element
12000      */
12001     getEl : function(){
12002         return this.el;
12003     },
12004     /**
12005      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12006      * @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:
12007 <pre><code>
12008 um.update({<br/>
12009     url: "your-url.php",<br/>
12010     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12011     callback: yourFunction,<br/>
12012     scope: yourObject, //(optional scope)  <br/>
12013     discardUrl: false, <br/>
12014     nocache: false,<br/>
12015     text: "Loading...",<br/>
12016     timeout: 30,<br/>
12017     scripts: false<br/>
12018 });
12019 </code></pre>
12020      * The only required property is url. The optional properties nocache, text and scripts
12021      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12022      * @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}
12023      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12024      * @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.
12025      */
12026     update : function(url, params, callback, discardUrl){
12027         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12028             var method = this.method,
12029                 cfg;
12030             if(typeof url == "object"){ // must be config object
12031                 cfg = url;
12032                 url = cfg.url;
12033                 params = params || cfg.params;
12034                 callback = callback || cfg.callback;
12035                 discardUrl = discardUrl || cfg.discardUrl;
12036                 if(callback && cfg.scope){
12037                     callback = callback.createDelegate(cfg.scope);
12038                 }
12039                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12040                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12041                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12042                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12043                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12044             }
12045             this.showLoading();
12046             if(!discardUrl){
12047                 this.defaultUrl = url;
12048             }
12049             if(typeof url == "function"){
12050                 url = url.call(this);
12051             }
12052
12053             method = method || (params ? "POST" : "GET");
12054             if(method == "GET"){
12055                 url = this.prepareUrl(url);
12056             }
12057
12058             var o = Roo.apply(cfg ||{}, {
12059                 url : url,
12060                 params: params,
12061                 success: this.successDelegate,
12062                 failure: this.failureDelegate,
12063                 callback: undefined,
12064                 timeout: (this.timeout*1000),
12065                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12066             });
12067             Roo.log("updated manager called with timeout of " + o.timeout);
12068             this.transaction = Roo.Ajax.request(o);
12069         }
12070     },
12071
12072     /**
12073      * 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.
12074      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12075      * @param {String/HTMLElement} form The form Id or form element
12076      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12077      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12078      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12079      */
12080     formUpdate : function(form, url, reset, callback){
12081         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12082             if(typeof url == "function"){
12083                 url = url.call(this);
12084             }
12085             form = Roo.getDom(form);
12086             this.transaction = Roo.Ajax.request({
12087                 form: form,
12088                 url:url,
12089                 success: this.successDelegate,
12090                 failure: this.failureDelegate,
12091                 timeout: (this.timeout*1000),
12092                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12093             });
12094             this.showLoading.defer(1, this);
12095         }
12096     },
12097
12098     /**
12099      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12100      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12101      */
12102     refresh : function(callback){
12103         if(this.defaultUrl == null){
12104             return;
12105         }
12106         this.update(this.defaultUrl, null, callback, true);
12107     },
12108
12109     /**
12110      * Set this element to auto refresh.
12111      * @param {Number} interval How often to update (in seconds).
12112      * @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)
12113      * @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}
12114      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12115      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12116      */
12117     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12118         if(refreshNow){
12119             this.update(url || this.defaultUrl, params, callback, true);
12120         }
12121         if(this.autoRefreshProcId){
12122             clearInterval(this.autoRefreshProcId);
12123         }
12124         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12125     },
12126
12127     /**
12128      * Stop auto refresh on this element.
12129      */
12130      stopAutoRefresh : function(){
12131         if(this.autoRefreshProcId){
12132             clearInterval(this.autoRefreshProcId);
12133             delete this.autoRefreshProcId;
12134         }
12135     },
12136
12137     isAutoRefreshing : function(){
12138        return this.autoRefreshProcId ? true : false;
12139     },
12140     /**
12141      * Called to update the element to "Loading" state. Override to perform custom action.
12142      */
12143     showLoading : function(){
12144         if(this.showLoadIndicator){
12145             this.el.update(this.indicatorText);
12146         }
12147     },
12148
12149     /**
12150      * Adds unique parameter to query string if disableCaching = true
12151      * @private
12152      */
12153     prepareUrl : function(url){
12154         if(this.disableCaching){
12155             var append = "_dc=" + (new Date().getTime());
12156             if(url.indexOf("?") !== -1){
12157                 url += "&" + append;
12158             }else{
12159                 url += "?" + append;
12160             }
12161         }
12162         return url;
12163     },
12164
12165     /**
12166      * @private
12167      */
12168     processSuccess : function(response){
12169         this.transaction = null;
12170         if(response.argument.form && response.argument.reset){
12171             try{ // put in try/catch since some older FF releases had problems with this
12172                 response.argument.form.reset();
12173             }catch(e){}
12174         }
12175         if(this.loadScripts){
12176             this.renderer.render(this.el, response, this,
12177                 this.updateComplete.createDelegate(this, [response]));
12178         }else{
12179             this.renderer.render(this.el, response, this);
12180             this.updateComplete(response);
12181         }
12182     },
12183
12184     updateComplete : function(response){
12185         this.fireEvent("update", this.el, response);
12186         if(typeof response.argument.callback == "function"){
12187             response.argument.callback(this.el, true, response);
12188         }
12189     },
12190
12191     /**
12192      * @private
12193      */
12194     processFailure : function(response){
12195         this.transaction = null;
12196         this.fireEvent("failure", this.el, response);
12197         if(typeof response.argument.callback == "function"){
12198             response.argument.callback(this.el, false, response);
12199         }
12200     },
12201
12202     /**
12203      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12204      * @param {Object} renderer The object implementing the render() method
12205      */
12206     setRenderer : function(renderer){
12207         this.renderer = renderer;
12208     },
12209
12210     getRenderer : function(){
12211        return this.renderer;
12212     },
12213
12214     /**
12215      * Set the defaultUrl used for updates
12216      * @param {String/Function} defaultUrl The url or a function to call to get the url
12217      */
12218     setDefaultUrl : function(defaultUrl){
12219         this.defaultUrl = defaultUrl;
12220     },
12221
12222     /**
12223      * Aborts the executing transaction
12224      */
12225     abort : function(){
12226         if(this.transaction){
12227             Roo.Ajax.abort(this.transaction);
12228         }
12229     },
12230
12231     /**
12232      * Returns true if an update is in progress
12233      * @return {Boolean}
12234      */
12235     isUpdating : function(){
12236         if(this.transaction){
12237             return Roo.Ajax.isLoading(this.transaction);
12238         }
12239         return false;
12240     }
12241 });
12242
12243 /**
12244  * @class Roo.UpdateManager.defaults
12245  * @static (not really - but it helps the doc tool)
12246  * The defaults collection enables customizing the default properties of UpdateManager
12247  */
12248    Roo.UpdateManager.defaults = {
12249        /**
12250          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12251          * @type Number
12252          */
12253          timeout : 30,
12254
12255          /**
12256          * True to process scripts by default (Defaults to false).
12257          * @type Boolean
12258          */
12259         loadScripts : false,
12260
12261         /**
12262         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12263         * @type String
12264         */
12265         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12266         /**
12267          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12268          * @type Boolean
12269          */
12270         disableCaching : false,
12271         /**
12272          * Whether to show indicatorText when loading (Defaults to true).
12273          * @type Boolean
12274          */
12275         showLoadIndicator : true,
12276         /**
12277          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12278          * @type String
12279          */
12280         indicatorText : '<div class="loading-indicator">Loading...</div>'
12281    };
12282
12283 /**
12284  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12285  *Usage:
12286  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12287  * @param {String/HTMLElement/Roo.Element} el The element to update
12288  * @param {String} url The url
12289  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12290  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12291  * @static
12292  * @deprecated
12293  * @member Roo.UpdateManager
12294  */
12295 Roo.UpdateManager.updateElement = function(el, url, params, options){
12296     var um = Roo.get(el, true).getUpdateManager();
12297     Roo.apply(um, options);
12298     um.update(url, params, options ? options.callback : null);
12299 };
12300 // alias for backwards compat
12301 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12302 /**
12303  * @class Roo.UpdateManager.BasicRenderer
12304  * Default Content renderer. Updates the elements innerHTML with the responseText.
12305  */
12306 Roo.UpdateManager.BasicRenderer = function(){};
12307
12308 Roo.UpdateManager.BasicRenderer.prototype = {
12309     /**
12310      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12311      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12312      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12313      * @param {Roo.Element} el The element being rendered
12314      * @param {Object} response The YUI Connect response object
12315      * @param {UpdateManager} updateManager The calling update manager
12316      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12317      */
12318      render : function(el, response, updateManager, callback){
12319         el.update(response.responseText, updateManager.loadScripts, callback);
12320     }
12321 };
12322 /*
12323  * Based on:
12324  * Roo JS
12325  * (c)) Alan Knowles
12326  * Licence : LGPL
12327  */
12328
12329
12330 /**
12331  * @class Roo.DomTemplate
12332  * @extends Roo.Template
12333  * An effort at a dom based template engine..
12334  *
12335  * Similar to XTemplate, except it uses dom parsing to create the template..
12336  *
12337  * Supported features:
12338  *
12339  *  Tags:
12340
12341 <pre><code>
12342       {a_variable} - output encoded.
12343       {a_variable.format:("Y-m-d")} - call a method on the variable
12344       {a_variable:raw} - unencoded output
12345       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12346       {a_variable:this.method_on_template(...)} - call a method on the template object.
12347  
12348 </code></pre>
12349  *  The tpl tag:
12350 <pre><code>
12351         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12352         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12353         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12354         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12355   
12356 </code></pre>
12357  *      
12358  */
12359 Roo.DomTemplate = function()
12360 {
12361      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12362      if (this.html) {
12363         this.compile();
12364      }
12365 };
12366
12367
12368 Roo.extend(Roo.DomTemplate, Roo.Template, {
12369     /**
12370      * id counter for sub templates.
12371      */
12372     id : 0,
12373     /**
12374      * flag to indicate if dom parser is inside a pre,
12375      * it will strip whitespace if not.
12376      */
12377     inPre : false,
12378     
12379     /**
12380      * The various sub templates
12381      */
12382     tpls : false,
12383     
12384     
12385     
12386     /**
12387      *
12388      * basic tag replacing syntax
12389      * WORD:WORD()
12390      *
12391      * // you can fake an object call by doing this
12392      *  x.t:(test,tesT) 
12393      * 
12394      */
12395     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12396     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12397     
12398     iterChild : function (node, method) {
12399         
12400         var oldPre = this.inPre;
12401         if (node.tagName == 'PRE') {
12402             this.inPre = true;
12403         }
12404         for( var i = 0; i < node.childNodes.length; i++) {
12405             method.call(this, node.childNodes[i]);
12406         }
12407         this.inPre = oldPre;
12408     },
12409     
12410     
12411     
12412     /**
12413      * compile the template
12414      *
12415      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12416      *
12417      */
12418     compile: function()
12419     {
12420         var s = this.html;
12421         
12422         // covert the html into DOM...
12423         var doc = false;
12424         var div =false;
12425         try {
12426             doc = document.implementation.createHTMLDocument("");
12427             doc.documentElement.innerHTML =   this.html  ;
12428             div = doc.documentElement;
12429         } catch (e) {
12430             // old IE... - nasty -- it causes all sorts of issues.. with
12431             // images getting pulled from server..
12432             div = document.createElement('div');
12433             div.innerHTML = this.html;
12434         }
12435         //doc.documentElement.innerHTML = htmlBody
12436          
12437         
12438         
12439         this.tpls = [];
12440         var _t = this;
12441         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12442         
12443         var tpls = this.tpls;
12444         
12445         // create a top level template from the snippet..
12446         
12447         //Roo.log(div.innerHTML);
12448         
12449         var tpl = {
12450             uid : 'master',
12451             id : this.id++,
12452             attr : false,
12453             value : false,
12454             body : div.innerHTML,
12455             
12456             forCall : false,
12457             execCall : false,
12458             dom : div,
12459             isTop : true
12460             
12461         };
12462         tpls.unshift(tpl);
12463         
12464         
12465         // compile them...
12466         this.tpls = [];
12467         Roo.each(tpls, function(tp){
12468             this.compileTpl(tp);
12469             this.tpls[tp.id] = tp;
12470         }, this);
12471         
12472         this.master = tpls[0];
12473         return this;
12474         
12475         
12476     },
12477     
12478     compileNode : function(node, istop) {
12479         // test for
12480         //Roo.log(node);
12481         
12482         
12483         // skip anything not a tag..
12484         if (node.nodeType != 1) {
12485             if (node.nodeType == 3 && !this.inPre) {
12486                 // reduce white space..
12487                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12488                 
12489             }
12490             return;
12491         }
12492         
12493         var tpl = {
12494             uid : false,
12495             id : false,
12496             attr : false,
12497             value : false,
12498             body : '',
12499             
12500             forCall : false,
12501             execCall : false,
12502             dom : false,
12503             isTop : istop
12504             
12505             
12506         };
12507         
12508         
12509         switch(true) {
12510             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12511             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12512             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12513             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12514             // no default..
12515         }
12516         
12517         
12518         if (!tpl.attr) {
12519             // just itterate children..
12520             this.iterChild(node,this.compileNode);
12521             return;
12522         }
12523         tpl.uid = this.id++;
12524         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12525         node.removeAttribute('roo-'+ tpl.attr);
12526         if (tpl.attr != 'name') {
12527             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12528             node.parentNode.replaceChild(placeholder,  node);
12529         } else {
12530             
12531             var placeholder =  document.createElement('span');
12532             placeholder.className = 'roo-tpl-' + tpl.value;
12533             node.parentNode.replaceChild(placeholder,  node);
12534         }
12535         
12536         // parent now sees '{domtplXXXX}
12537         this.iterChild(node,this.compileNode);
12538         
12539         // we should now have node body...
12540         var div = document.createElement('div');
12541         div.appendChild(node);
12542         tpl.dom = node;
12543         // this has the unfortunate side effect of converting tagged attributes
12544         // eg. href="{...}" into %7C...%7D
12545         // this has been fixed by searching for those combo's although it's a bit hacky..
12546         
12547         
12548         tpl.body = div.innerHTML;
12549         
12550         
12551          
12552         tpl.id = tpl.uid;
12553         switch(tpl.attr) {
12554             case 'for' :
12555                 switch (tpl.value) {
12556                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12557                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12558                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12559                 }
12560                 break;
12561             
12562             case 'exec':
12563                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12564                 break;
12565             
12566             case 'if':     
12567                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12568                 break;
12569             
12570             case 'name':
12571                 tpl.id  = tpl.value; // replace non characters???
12572                 break;
12573             
12574         }
12575         
12576         
12577         this.tpls.push(tpl);
12578         
12579         
12580         
12581     },
12582     
12583     
12584     
12585     
12586     /**
12587      * Compile a segment of the template into a 'sub-template'
12588      *
12589      * 
12590      * 
12591      *
12592      */
12593     compileTpl : function(tpl)
12594     {
12595         var fm = Roo.util.Format;
12596         var useF = this.disableFormats !== true;
12597         
12598         var sep = Roo.isGecko ? "+\n" : ",\n";
12599         
12600         var undef = function(str) {
12601             Roo.debug && Roo.log("Property not found :"  + str);
12602             return '';
12603         };
12604           
12605         //Roo.log(tpl.body);
12606         
12607         
12608         
12609         var fn = function(m, lbrace, name, format, args)
12610         {
12611             //Roo.log("ARGS");
12612             //Roo.log(arguments);
12613             args = args ? args.replace(/\\'/g,"'") : args;
12614             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12615             if (typeof(format) == 'undefined') {
12616                 format =  'htmlEncode'; 
12617             }
12618             if (format == 'raw' ) {
12619                 format = false;
12620             }
12621             
12622             if(name.substr(0, 6) == 'domtpl'){
12623                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12624             }
12625             
12626             // build an array of options to determine if value is undefined..
12627             
12628             // basically get 'xxxx.yyyy' then do
12629             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12630             //    (function () { Roo.log("Property not found"); return ''; })() :
12631             //    ......
12632             
12633             var udef_ar = [];
12634             var lookfor = '';
12635             Roo.each(name.split('.'), function(st) {
12636                 lookfor += (lookfor.length ? '.': '') + st;
12637                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12638             });
12639             
12640             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12641             
12642             
12643             if(format && useF){
12644                 
12645                 args = args ? ',' + args : "";
12646                  
12647                 if(format.substr(0, 5) != "this."){
12648                     format = "fm." + format + '(';
12649                 }else{
12650                     format = 'this.call("'+ format.substr(5) + '", ';
12651                     args = ", values";
12652                 }
12653                 
12654                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12655             }
12656              
12657             if (args && args.length) {
12658                 // called with xxyx.yuu:(test,test)
12659                 // change to ()
12660                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12661             }
12662             // raw.. - :raw modifier..
12663             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12664             
12665         };
12666         var body;
12667         // branched to use + in gecko and [].join() in others
12668         if(Roo.isGecko){
12669             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12670                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12671                     "';};};";
12672         }else{
12673             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12674             body.push(tpl.body.replace(/(\r\n|\n)/g,
12675                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12676             body.push("'].join('');};};");
12677             body = body.join('');
12678         }
12679         
12680         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12681        
12682         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12683         eval(body);
12684         
12685         return this;
12686     },
12687      
12688     /**
12689      * same as applyTemplate, except it's done to one of the subTemplates
12690      * when using named templates, you can do:
12691      *
12692      * var str = pl.applySubTemplate('your-name', values);
12693      *
12694      * 
12695      * @param {Number} id of the template
12696      * @param {Object} values to apply to template
12697      * @param {Object} parent (normaly the instance of this object)
12698      */
12699     applySubTemplate : function(id, values, parent)
12700     {
12701         
12702         
12703         var t = this.tpls[id];
12704         
12705         
12706         try { 
12707             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12708                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12709                 return '';
12710             }
12711         } catch(e) {
12712             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12713             Roo.log(values);
12714           
12715             return '';
12716         }
12717         try { 
12718             
12719             if(t.execCall && t.execCall.call(this, values, parent)){
12720                 return '';
12721             }
12722         } catch(e) {
12723             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12724             Roo.log(values);
12725             return '';
12726         }
12727         
12728         try {
12729             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12730             parent = t.target ? values : parent;
12731             if(t.forCall && vs instanceof Array){
12732                 var buf = [];
12733                 for(var i = 0, len = vs.length; i < len; i++){
12734                     try {
12735                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12736                     } catch (e) {
12737                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12738                         Roo.log(e.body);
12739                         //Roo.log(t.compiled);
12740                         Roo.log(vs[i]);
12741                     }   
12742                 }
12743                 return buf.join('');
12744             }
12745         } catch (e) {
12746             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12747             Roo.log(values);
12748             return '';
12749         }
12750         try {
12751             return t.compiled.call(this, vs, parent);
12752         } catch (e) {
12753             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12754             Roo.log(e.body);
12755             //Roo.log(t.compiled);
12756             Roo.log(values);
12757             return '';
12758         }
12759     },
12760
12761    
12762
12763     applyTemplate : function(values){
12764         return this.master.compiled.call(this, values, {});
12765         //var s = this.subs;
12766     },
12767
12768     apply : function(){
12769         return this.applyTemplate.apply(this, arguments);
12770     }
12771
12772  });
12773
12774 Roo.DomTemplate.from = function(el){
12775     el = Roo.getDom(el);
12776     return new Roo.Domtemplate(el.value || el.innerHTML);
12777 };/*
12778  * Based on:
12779  * Ext JS Library 1.1.1
12780  * Copyright(c) 2006-2007, Ext JS, LLC.
12781  *
12782  * Originally Released Under LGPL - original licence link has changed is not relivant.
12783  *
12784  * Fork - LGPL
12785  * <script type="text/javascript">
12786  */
12787
12788 /**
12789  * @class Roo.util.DelayedTask
12790  * Provides a convenient method of performing setTimeout where a new
12791  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12792  * You can use this class to buffer
12793  * the keypress events for a certain number of milliseconds, and perform only if they stop
12794  * for that amount of time.
12795  * @constructor The parameters to this constructor serve as defaults and are not required.
12796  * @param {Function} fn (optional) The default function to timeout
12797  * @param {Object} scope (optional) The default scope of that timeout
12798  * @param {Array} args (optional) The default Array of arguments
12799  */
12800 Roo.util.DelayedTask = function(fn, scope, args){
12801     var id = null, d, t;
12802
12803     var call = function(){
12804         var now = new Date().getTime();
12805         if(now - t >= d){
12806             clearInterval(id);
12807             id = null;
12808             fn.apply(scope, args || []);
12809         }
12810     };
12811     /**
12812      * Cancels any pending timeout and queues a new one
12813      * @param {Number} delay The milliseconds to delay
12814      * @param {Function} newFn (optional) Overrides function passed to constructor
12815      * @param {Object} newScope (optional) Overrides scope passed to constructor
12816      * @param {Array} newArgs (optional) Overrides args passed to constructor
12817      */
12818     this.delay = function(delay, newFn, newScope, newArgs){
12819         if(id && delay != d){
12820             this.cancel();
12821         }
12822         d = delay;
12823         t = new Date().getTime();
12824         fn = newFn || fn;
12825         scope = newScope || scope;
12826         args = newArgs || args;
12827         if(!id){
12828             id = setInterval(call, d);
12829         }
12830     };
12831
12832     /**
12833      * Cancel the last queued timeout
12834      */
12835     this.cancel = function(){
12836         if(id){
12837             clearInterval(id);
12838             id = null;
12839         }
12840     };
12841 };/*
12842  * Based on:
12843  * Ext JS Library 1.1.1
12844  * Copyright(c) 2006-2007, Ext JS, LLC.
12845  *
12846  * Originally Released Under LGPL - original licence link has changed is not relivant.
12847  *
12848  * Fork - LGPL
12849  * <script type="text/javascript">
12850  */
12851  
12852  
12853 Roo.util.TaskRunner = function(interval){
12854     interval = interval || 10;
12855     var tasks = [], removeQueue = [];
12856     var id = 0;
12857     var running = false;
12858
12859     var stopThread = function(){
12860         running = false;
12861         clearInterval(id);
12862         id = 0;
12863     };
12864
12865     var startThread = function(){
12866         if(!running){
12867             running = true;
12868             id = setInterval(runTasks, interval);
12869         }
12870     };
12871
12872     var removeTask = function(task){
12873         removeQueue.push(task);
12874         if(task.onStop){
12875             task.onStop();
12876         }
12877     };
12878
12879     var runTasks = function(){
12880         if(removeQueue.length > 0){
12881             for(var i = 0, len = removeQueue.length; i < len; i++){
12882                 tasks.remove(removeQueue[i]);
12883             }
12884             removeQueue = [];
12885             if(tasks.length < 1){
12886                 stopThread();
12887                 return;
12888             }
12889         }
12890         var now = new Date().getTime();
12891         for(var i = 0, len = tasks.length; i < len; ++i){
12892             var t = tasks[i];
12893             var itime = now - t.taskRunTime;
12894             if(t.interval <= itime){
12895                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12896                 t.taskRunTime = now;
12897                 if(rt === false || t.taskRunCount === t.repeat){
12898                     removeTask(t);
12899                     return;
12900                 }
12901             }
12902             if(t.duration && t.duration <= (now - t.taskStartTime)){
12903                 removeTask(t);
12904             }
12905         }
12906     };
12907
12908     /**
12909      * Queues a new task.
12910      * @param {Object} task
12911      */
12912     this.start = function(task){
12913         tasks.push(task);
12914         task.taskStartTime = new Date().getTime();
12915         task.taskRunTime = 0;
12916         task.taskRunCount = 0;
12917         startThread();
12918         return task;
12919     };
12920
12921     this.stop = function(task){
12922         removeTask(task);
12923         return task;
12924     };
12925
12926     this.stopAll = function(){
12927         stopThread();
12928         for(var i = 0, len = tasks.length; i < len; i++){
12929             if(tasks[i].onStop){
12930                 tasks[i].onStop();
12931             }
12932         }
12933         tasks = [];
12934         removeQueue = [];
12935     };
12936 };
12937
12938 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12939  * Based on:
12940  * Ext JS Library 1.1.1
12941  * Copyright(c) 2006-2007, Ext JS, LLC.
12942  *
12943  * Originally Released Under LGPL - original licence link has changed is not relivant.
12944  *
12945  * Fork - LGPL
12946  * <script type="text/javascript">
12947  */
12948
12949  
12950 /**
12951  * @class Roo.util.MixedCollection
12952  * @extends Roo.util.Observable
12953  * A Collection class that maintains both numeric indexes and keys and exposes events.
12954  * @constructor
12955  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12956  * collection (defaults to false)
12957  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12958  * and return the key value for that item.  This is used when available to look up the key on items that
12959  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12960  * equivalent to providing an implementation for the {@link #getKey} method.
12961  */
12962 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12963     this.items = [];
12964     this.map = {};
12965     this.keys = [];
12966     this.length = 0;
12967     this.addEvents({
12968         /**
12969          * @event clear
12970          * Fires when the collection is cleared.
12971          */
12972         "clear" : true,
12973         /**
12974          * @event add
12975          * Fires when an item is added to the collection.
12976          * @param {Number} index The index at which the item was added.
12977          * @param {Object} o The item added.
12978          * @param {String} key The key associated with the added item.
12979          */
12980         "add" : true,
12981         /**
12982          * @event replace
12983          * Fires when an item is replaced in the collection.
12984          * @param {String} key he key associated with the new added.
12985          * @param {Object} old The item being replaced.
12986          * @param {Object} new The new item.
12987          */
12988         "replace" : true,
12989         /**
12990          * @event remove
12991          * Fires when an item is removed from the collection.
12992          * @param {Object} o The item being removed.
12993          * @param {String} key (optional) The key associated with the removed item.
12994          */
12995         "remove" : true,
12996         "sort" : true
12997     });
12998     this.allowFunctions = allowFunctions === true;
12999     if(keyFn){
13000         this.getKey = keyFn;
13001     }
13002     Roo.util.MixedCollection.superclass.constructor.call(this);
13003 };
13004
13005 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13006     allowFunctions : false,
13007     
13008 /**
13009  * Adds an item to the collection.
13010  * @param {String} key The key to associate with the item
13011  * @param {Object} o The item to add.
13012  * @return {Object} The item added.
13013  */
13014     add : function(key, o){
13015         if(arguments.length == 1){
13016             o = arguments[0];
13017             key = this.getKey(o);
13018         }
13019         if(typeof key == "undefined" || key === null){
13020             this.length++;
13021             this.items.push(o);
13022             this.keys.push(null);
13023         }else{
13024             var old = this.map[key];
13025             if(old){
13026                 return this.replace(key, o);
13027             }
13028             this.length++;
13029             this.items.push(o);
13030             this.map[key] = o;
13031             this.keys.push(key);
13032         }
13033         this.fireEvent("add", this.length-1, o, key);
13034         return o;
13035     },
13036        
13037 /**
13038   * MixedCollection has a generic way to fetch keys if you implement getKey.
13039 <pre><code>
13040 // normal way
13041 var mc = new Roo.util.MixedCollection();
13042 mc.add(someEl.dom.id, someEl);
13043 mc.add(otherEl.dom.id, otherEl);
13044 //and so on
13045
13046 // using getKey
13047 var mc = new Roo.util.MixedCollection();
13048 mc.getKey = function(el){
13049    return el.dom.id;
13050 };
13051 mc.add(someEl);
13052 mc.add(otherEl);
13053
13054 // or via the constructor
13055 var mc = new Roo.util.MixedCollection(false, function(el){
13056    return el.dom.id;
13057 });
13058 mc.add(someEl);
13059 mc.add(otherEl);
13060 </code></pre>
13061  * @param o {Object} The item for which to find the key.
13062  * @return {Object} The key for the passed item.
13063  */
13064     getKey : function(o){
13065          return o.id; 
13066     },
13067    
13068 /**
13069  * Replaces an item in the collection.
13070  * @param {String} key The key associated with the item to replace, or the item to replace.
13071  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13072  * @return {Object}  The new item.
13073  */
13074     replace : function(key, o){
13075         if(arguments.length == 1){
13076             o = arguments[0];
13077             key = this.getKey(o);
13078         }
13079         var old = this.item(key);
13080         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13081              return this.add(key, o);
13082         }
13083         var index = this.indexOfKey(key);
13084         this.items[index] = o;
13085         this.map[key] = o;
13086         this.fireEvent("replace", key, old, o);
13087         return o;
13088     },
13089    
13090 /**
13091  * Adds all elements of an Array or an Object to the collection.
13092  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13093  * an Array of values, each of which are added to the collection.
13094  */
13095     addAll : function(objs){
13096         if(arguments.length > 1 || objs instanceof Array){
13097             var args = arguments.length > 1 ? arguments : objs;
13098             for(var i = 0, len = args.length; i < len; i++){
13099                 this.add(args[i]);
13100             }
13101         }else{
13102             for(var key in objs){
13103                 if(this.allowFunctions || typeof objs[key] != "function"){
13104                     this.add(key, objs[key]);
13105                 }
13106             }
13107         }
13108     },
13109    
13110 /**
13111  * Executes the specified function once for every item in the collection, passing each
13112  * item as the first and only parameter. returning false from the function will stop the iteration.
13113  * @param {Function} fn The function to execute for each item.
13114  * @param {Object} scope (optional) The scope in which to execute the function.
13115  */
13116     each : function(fn, scope){
13117         var items = [].concat(this.items); // each safe for removal
13118         for(var i = 0, len = items.length; i < len; i++){
13119             if(fn.call(scope || items[i], items[i], i, len) === false){
13120                 break;
13121             }
13122         }
13123     },
13124    
13125 /**
13126  * Executes the specified function once for every key in the collection, passing each
13127  * key, and its associated item as the first two parameters.
13128  * @param {Function} fn The function to execute for each item.
13129  * @param {Object} scope (optional) The scope in which to execute the function.
13130  */
13131     eachKey : function(fn, scope){
13132         for(var i = 0, len = this.keys.length; i < len; i++){
13133             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13134         }
13135     },
13136    
13137 /**
13138  * Returns the first item in the collection which elicits a true return value from the
13139  * passed selection function.
13140  * @param {Function} fn The selection function to execute for each item.
13141  * @param {Object} scope (optional) The scope in which to execute the function.
13142  * @return {Object} The first item in the collection which returned true from the selection function.
13143  */
13144     find : function(fn, scope){
13145         for(var i = 0, len = this.items.length; i < len; i++){
13146             if(fn.call(scope || window, this.items[i], this.keys[i])){
13147                 return this.items[i];
13148             }
13149         }
13150         return null;
13151     },
13152    
13153 /**
13154  * Inserts an item at the specified index in the collection.
13155  * @param {Number} index The index to insert the item at.
13156  * @param {String} key The key to associate with the new item, or the item itself.
13157  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13158  * @return {Object} The item inserted.
13159  */
13160     insert : function(index, key, o){
13161         if(arguments.length == 2){
13162             o = arguments[1];
13163             key = this.getKey(o);
13164         }
13165         if(index >= this.length){
13166             return this.add(key, o);
13167         }
13168         this.length++;
13169         this.items.splice(index, 0, o);
13170         if(typeof key != "undefined" && key != null){
13171             this.map[key] = o;
13172         }
13173         this.keys.splice(index, 0, key);
13174         this.fireEvent("add", index, o, key);
13175         return o;
13176     },
13177    
13178 /**
13179  * Removed an item from the collection.
13180  * @param {Object} o The item to remove.
13181  * @return {Object} The item removed.
13182  */
13183     remove : function(o){
13184         return this.removeAt(this.indexOf(o));
13185     },
13186    
13187 /**
13188  * Remove an item from a specified index in the collection.
13189  * @param {Number} index The index within the collection of the item to remove.
13190  */
13191     removeAt : function(index){
13192         if(index < this.length && index >= 0){
13193             this.length--;
13194             var o = this.items[index];
13195             this.items.splice(index, 1);
13196             var key = this.keys[index];
13197             if(typeof key != "undefined"){
13198                 delete this.map[key];
13199             }
13200             this.keys.splice(index, 1);
13201             this.fireEvent("remove", o, key);
13202         }
13203     },
13204    
13205 /**
13206  * Removed an item associated with the passed key fom the collection.
13207  * @param {String} key The key of the item to remove.
13208  */
13209     removeKey : function(key){
13210         return this.removeAt(this.indexOfKey(key));
13211     },
13212    
13213 /**
13214  * Returns the number of items in the collection.
13215  * @return {Number} the number of items in the collection.
13216  */
13217     getCount : function(){
13218         return this.length; 
13219     },
13220    
13221 /**
13222  * Returns index within the collection of the passed Object.
13223  * @param {Object} o The item to find the index of.
13224  * @return {Number} index of the item.
13225  */
13226     indexOf : function(o){
13227         if(!this.items.indexOf){
13228             for(var i = 0, len = this.items.length; i < len; i++){
13229                 if(this.items[i] == o) {
13230                     return i;
13231                 }
13232             }
13233             return -1;
13234         }else{
13235             return this.items.indexOf(o);
13236         }
13237     },
13238    
13239 /**
13240  * Returns index within the collection of the passed key.
13241  * @param {String} key The key to find the index of.
13242  * @return {Number} index of the key.
13243  */
13244     indexOfKey : function(key){
13245         if(!this.keys.indexOf){
13246             for(var i = 0, len = this.keys.length; i < len; i++){
13247                 if(this.keys[i] == key) {
13248                     return i;
13249                 }
13250             }
13251             return -1;
13252         }else{
13253             return this.keys.indexOf(key);
13254         }
13255     },
13256    
13257 /**
13258  * Returns the item associated with the passed key OR index. Key has priority over index.
13259  * @param {String/Number} key The key or index of the item.
13260  * @return {Object} The item associated with the passed key.
13261  */
13262     item : function(key){
13263         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13264         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13265     },
13266     
13267 /**
13268  * Returns the item at the specified index.
13269  * @param {Number} index The index of the item.
13270  * @return {Object}
13271  */
13272     itemAt : function(index){
13273         return this.items[index];
13274     },
13275     
13276 /**
13277  * Returns the item associated with the passed key.
13278  * @param {String/Number} key The key of the item.
13279  * @return {Object} The item associated with the passed key.
13280  */
13281     key : function(key){
13282         return this.map[key];
13283     },
13284    
13285 /**
13286  * Returns true if the collection contains the passed Object as an item.
13287  * @param {Object} o  The Object to look for in the collection.
13288  * @return {Boolean} True if the collection contains the Object as an item.
13289  */
13290     contains : function(o){
13291         return this.indexOf(o) != -1;
13292     },
13293    
13294 /**
13295  * Returns true if the collection contains the passed Object as a key.
13296  * @param {String} key The key to look for in the collection.
13297  * @return {Boolean} True if the collection contains the Object as a key.
13298  */
13299     containsKey : function(key){
13300         return typeof this.map[key] != "undefined";
13301     },
13302    
13303 /**
13304  * Removes all items from the collection.
13305  */
13306     clear : function(){
13307         this.length = 0;
13308         this.items = [];
13309         this.keys = [];
13310         this.map = {};
13311         this.fireEvent("clear");
13312     },
13313    
13314 /**
13315  * Returns the first item in the collection.
13316  * @return {Object} the first item in the collection..
13317  */
13318     first : function(){
13319         return this.items[0]; 
13320     },
13321    
13322 /**
13323  * Returns the last item in the collection.
13324  * @return {Object} the last item in the collection..
13325  */
13326     last : function(){
13327         return this.items[this.length-1];   
13328     },
13329     
13330     _sort : function(property, dir, fn){
13331         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13332         fn = fn || function(a, b){
13333             return a-b;
13334         };
13335         var c = [], k = this.keys, items = this.items;
13336         for(var i = 0, len = items.length; i < len; i++){
13337             c[c.length] = {key: k[i], value: items[i], index: i};
13338         }
13339         c.sort(function(a, b){
13340             var v = fn(a[property], b[property]) * dsc;
13341             if(v == 0){
13342                 v = (a.index < b.index ? -1 : 1);
13343             }
13344             return v;
13345         });
13346         for(var i = 0, len = c.length; i < len; i++){
13347             items[i] = c[i].value;
13348             k[i] = c[i].key;
13349         }
13350         this.fireEvent("sort", this);
13351     },
13352     
13353     /**
13354      * Sorts this collection with the passed comparison function
13355      * @param {String} direction (optional) "ASC" or "DESC"
13356      * @param {Function} fn (optional) comparison function
13357      */
13358     sort : function(dir, fn){
13359         this._sort("value", dir, fn);
13360     },
13361     
13362     /**
13363      * Sorts this collection by keys
13364      * @param {String} direction (optional) "ASC" or "DESC"
13365      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13366      */
13367     keySort : function(dir, fn){
13368         this._sort("key", dir, fn || function(a, b){
13369             return String(a).toUpperCase()-String(b).toUpperCase();
13370         });
13371     },
13372     
13373     /**
13374      * Returns a range of items in this collection
13375      * @param {Number} startIndex (optional) defaults to 0
13376      * @param {Number} endIndex (optional) default to the last item
13377      * @return {Array} An array of items
13378      */
13379     getRange : function(start, end){
13380         var items = this.items;
13381         if(items.length < 1){
13382             return [];
13383         }
13384         start = start || 0;
13385         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13386         var r = [];
13387         if(start <= end){
13388             for(var i = start; i <= end; i++) {
13389                     r[r.length] = items[i];
13390             }
13391         }else{
13392             for(var i = start; i >= end; i--) {
13393                     r[r.length] = items[i];
13394             }
13395         }
13396         return r;
13397     },
13398         
13399     /**
13400      * Filter the <i>objects</i> in this collection by a specific property. 
13401      * Returns a new collection that has been filtered.
13402      * @param {String} property A property on your objects
13403      * @param {String/RegExp} value Either string that the property values 
13404      * should start with or a RegExp to test against the property
13405      * @return {MixedCollection} The new filtered collection
13406      */
13407     filter : function(property, value){
13408         if(!value.exec){ // not a regex
13409             value = String(value);
13410             if(value.length == 0){
13411                 return this.clone();
13412             }
13413             value = new RegExp("^" + Roo.escapeRe(value), "i");
13414         }
13415         return this.filterBy(function(o){
13416             return o && value.test(o[property]);
13417         });
13418         },
13419     
13420     /**
13421      * Filter by a function. * Returns a new collection that has been filtered.
13422      * The passed function will be called with each 
13423      * object in the collection. If the function returns true, the value is included 
13424      * otherwise it is filtered.
13425      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13426      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13427      * @return {MixedCollection} The new filtered collection
13428      */
13429     filterBy : function(fn, scope){
13430         var r = new Roo.util.MixedCollection();
13431         r.getKey = this.getKey;
13432         var k = this.keys, it = this.items;
13433         for(var i = 0, len = it.length; i < len; i++){
13434             if(fn.call(scope||this, it[i], k[i])){
13435                                 r.add(k[i], it[i]);
13436                         }
13437         }
13438         return r;
13439     },
13440     
13441     /**
13442      * Creates a duplicate of this collection
13443      * @return {MixedCollection}
13444      */
13445     clone : function(){
13446         var r = new Roo.util.MixedCollection();
13447         var k = this.keys, it = this.items;
13448         for(var i = 0, len = it.length; i < len; i++){
13449             r.add(k[i], it[i]);
13450         }
13451         r.getKey = this.getKey;
13452         return r;
13453     }
13454 });
13455 /**
13456  * Returns the item associated with the passed key or index.
13457  * @method
13458  * @param {String/Number} key The key or index of the item.
13459  * @return {Object} The item associated with the passed key.
13460  */
13461 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13462  * Based on:
13463  * Ext JS Library 1.1.1
13464  * Copyright(c) 2006-2007, Ext JS, LLC.
13465  *
13466  * Originally Released Under LGPL - original licence link has changed is not relivant.
13467  *
13468  * Fork - LGPL
13469  * <script type="text/javascript">
13470  */
13471 /**
13472  * @class Roo.util.JSON
13473  * Modified version of Douglas Crockford"s json.js that doesn"t
13474  * mess with the Object prototype 
13475  * http://www.json.org/js.html
13476  * @singleton
13477  */
13478 Roo.util.JSON = new (function(){
13479     var useHasOwn = {}.hasOwnProperty ? true : false;
13480     
13481     // crashes Safari in some instances
13482     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13483     
13484     var pad = function(n) {
13485         return n < 10 ? "0" + n : n;
13486     };
13487     
13488     var m = {
13489         "\b": '\\b',
13490         "\t": '\\t',
13491         "\n": '\\n',
13492         "\f": '\\f',
13493         "\r": '\\r',
13494         '"' : '\\"',
13495         "\\": '\\\\'
13496     };
13497
13498     var encodeString = function(s){
13499         if (/["\\\x00-\x1f]/.test(s)) {
13500             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13501                 var c = m[b];
13502                 if(c){
13503                     return c;
13504                 }
13505                 c = b.charCodeAt();
13506                 return "\\u00" +
13507                     Math.floor(c / 16).toString(16) +
13508                     (c % 16).toString(16);
13509             }) + '"';
13510         }
13511         return '"' + s + '"';
13512     };
13513     
13514     var encodeArray = function(o){
13515         var a = ["["], b, i, l = o.length, v;
13516             for (i = 0; i < l; i += 1) {
13517                 v = o[i];
13518                 switch (typeof v) {
13519                     case "undefined":
13520                     case "function":
13521                     case "unknown":
13522                         break;
13523                     default:
13524                         if (b) {
13525                             a.push(',');
13526                         }
13527                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13528                         b = true;
13529                 }
13530             }
13531             a.push("]");
13532             return a.join("");
13533     };
13534     
13535     var encodeDate = function(o){
13536         return '"' + o.getFullYear() + "-" +
13537                 pad(o.getMonth() + 1) + "-" +
13538                 pad(o.getDate()) + "T" +
13539                 pad(o.getHours()) + ":" +
13540                 pad(o.getMinutes()) + ":" +
13541                 pad(o.getSeconds()) + '"';
13542     };
13543     
13544     /**
13545      * Encodes an Object, Array or other value
13546      * @param {Mixed} o The variable to encode
13547      * @return {String} The JSON string
13548      */
13549     this.encode = function(o)
13550     {
13551         // should this be extended to fully wrap stringify..
13552         
13553         if(typeof o == "undefined" || o === null){
13554             return "null";
13555         }else if(o instanceof Array){
13556             return encodeArray(o);
13557         }else if(o instanceof Date){
13558             return encodeDate(o);
13559         }else if(typeof o == "string"){
13560             return encodeString(o);
13561         }else if(typeof o == "number"){
13562             return isFinite(o) ? String(o) : "null";
13563         }else if(typeof o == "boolean"){
13564             return String(o);
13565         }else {
13566             var a = ["{"], b, i, v;
13567             for (i in o) {
13568                 if(!useHasOwn || o.hasOwnProperty(i)) {
13569                     v = o[i];
13570                     switch (typeof v) {
13571                     case "undefined":
13572                     case "function":
13573                     case "unknown":
13574                         break;
13575                     default:
13576                         if(b){
13577                             a.push(',');
13578                         }
13579                         a.push(this.encode(i), ":",
13580                                 v === null ? "null" : this.encode(v));
13581                         b = true;
13582                     }
13583                 }
13584             }
13585             a.push("}");
13586             return a.join("");
13587         }
13588     };
13589     
13590     /**
13591      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13592      * @param {String} json The JSON string
13593      * @return {Object} The resulting object
13594      */
13595     this.decode = function(json){
13596         
13597         return  /** eval:var:json */ eval("(" + json + ')');
13598     };
13599 })();
13600 /** 
13601  * Shorthand for {@link Roo.util.JSON#encode}
13602  * @member Roo encode 
13603  * @method */
13604 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13605 /** 
13606  * Shorthand for {@link Roo.util.JSON#decode}
13607  * @member Roo decode 
13608  * @method */
13609 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13610 /*
13611  * Based on:
13612  * Ext JS Library 1.1.1
13613  * Copyright(c) 2006-2007, Ext JS, LLC.
13614  *
13615  * Originally Released Under LGPL - original licence link has changed is not relivant.
13616  *
13617  * Fork - LGPL
13618  * <script type="text/javascript">
13619  */
13620  
13621 /**
13622  * @class Roo.util.Format
13623  * Reusable data formatting functions
13624  * @singleton
13625  */
13626 Roo.util.Format = function(){
13627     var trimRe = /^\s+|\s+$/g;
13628     return {
13629         /**
13630          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13631          * @param {String} value The string to truncate
13632          * @param {Number} length The maximum length to allow before truncating
13633          * @return {String} The converted text
13634          */
13635         ellipsis : function(value, len){
13636             if(value && value.length > len){
13637                 return value.substr(0, len-3)+"...";
13638             }
13639             return value;
13640         },
13641
13642         /**
13643          * Checks a reference and converts it to empty string if it is undefined
13644          * @param {Mixed} value Reference to check
13645          * @return {Mixed} Empty string if converted, otherwise the original value
13646          */
13647         undef : function(value){
13648             return typeof value != "undefined" ? value : "";
13649         },
13650
13651         /**
13652          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13653          * @param {String} value The string to encode
13654          * @return {String} The encoded text
13655          */
13656         htmlEncode : function(value){
13657             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13658         },
13659
13660         /**
13661          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13662          * @param {String} value The string to decode
13663          * @return {String} The decoded text
13664          */
13665         htmlDecode : function(value){
13666             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13667         },
13668
13669         /**
13670          * Trims any whitespace from either side of a string
13671          * @param {String} value The text to trim
13672          * @return {String} The trimmed text
13673          */
13674         trim : function(value){
13675             return String(value).replace(trimRe, "");
13676         },
13677
13678         /**
13679          * Returns a substring from within an original string
13680          * @param {String} value The original text
13681          * @param {Number} start The start index of the substring
13682          * @param {Number} length The length of the substring
13683          * @return {String} The substring
13684          */
13685         substr : function(value, start, length){
13686             return String(value).substr(start, length);
13687         },
13688
13689         /**
13690          * Converts a string to all lower case letters
13691          * @param {String} value The text to convert
13692          * @return {String} The converted text
13693          */
13694         lowercase : function(value){
13695             return String(value).toLowerCase();
13696         },
13697
13698         /**
13699          * Converts a string to all upper case letters
13700          * @param {String} value The text to convert
13701          * @return {String} The converted text
13702          */
13703         uppercase : function(value){
13704             return String(value).toUpperCase();
13705         },
13706
13707         /**
13708          * Converts the first character only of a string to upper case
13709          * @param {String} value The text to convert
13710          * @return {String} The converted text
13711          */
13712         capitalize : function(value){
13713             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13714         },
13715
13716         // private
13717         call : function(value, fn){
13718             if(arguments.length > 2){
13719                 var args = Array.prototype.slice.call(arguments, 2);
13720                 args.unshift(value);
13721                  
13722                 return /** eval:var:value */  eval(fn).apply(window, args);
13723             }else{
13724                 /** eval:var:value */
13725                 return /** eval:var:value */ eval(fn).call(window, value);
13726             }
13727         },
13728
13729        
13730         /**
13731          * safer version of Math.toFixed..??/
13732          * @param {Number/String} value The numeric value to format
13733          * @param {Number/String} value Decimal places 
13734          * @return {String} The formatted currency string
13735          */
13736         toFixed : function(v, n)
13737         {
13738             // why not use to fixed - precision is buggered???
13739             if (!n) {
13740                 return Math.round(v-0);
13741             }
13742             var fact = Math.pow(10,n+1);
13743             v = (Math.round((v-0)*fact))/fact;
13744             var z = (''+fact).substring(2);
13745             if (v == Math.floor(v)) {
13746                 return Math.floor(v) + '.' + z;
13747             }
13748             
13749             // now just padd decimals..
13750             var ps = String(v).split('.');
13751             var fd = (ps[1] + z);
13752             var r = fd.substring(0,n); 
13753             var rm = fd.substring(n); 
13754             if (rm < 5) {
13755                 return ps[0] + '.' + r;
13756             }
13757             r*=1; // turn it into a number;
13758             r++;
13759             if (String(r).length != n) {
13760                 ps[0]*=1;
13761                 ps[0]++;
13762                 r = String(r).substring(1); // chop the end off.
13763             }
13764             
13765             return ps[0] + '.' + r;
13766              
13767         },
13768         
13769         /**
13770          * Format a number as US currency
13771          * @param {Number/String} value The numeric value to format
13772          * @return {String} The formatted currency string
13773          */
13774         usMoney : function(v){
13775             return '$' + Roo.util.Format.number(v);
13776         },
13777         
13778         /**
13779          * Format a number
13780          * eventually this should probably emulate php's number_format
13781          * @param {Number/String} value The numeric value to format
13782          * @param {Number} decimals number of decimal places
13783          * @param {String} delimiter for thousands (default comma)
13784          * @return {String} The formatted currency string
13785          */
13786         number : function(v, decimals, thousandsDelimiter)
13787         {
13788             // multiply and round.
13789             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13790             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
13791             
13792             var mul = Math.pow(10, decimals);
13793             var zero = String(mul).substring(1);
13794             v = (Math.round((v-0)*mul))/mul;
13795             
13796             // if it's '0' number.. then
13797             
13798             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13799             v = String(v);
13800             var ps = v.split('.');
13801             var whole = ps[0];
13802             
13803             var r = /(\d+)(\d{3})/;
13804             // add comma's
13805             
13806             if(thousandsDelimiter.length != 0) {
13807                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
13808             } 
13809             
13810             var sub = ps[1] ?
13811                     // has decimals..
13812                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13813                     // does not have decimals
13814                     (decimals ? ('.' + zero) : '');
13815             
13816             
13817             return whole + sub ;
13818         },
13819         
13820         /**
13821          * Parse a value into a formatted date using the specified format pattern.
13822          * @param {Mixed} value The value to format
13823          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13824          * @return {String} The formatted date string
13825          */
13826         date : function(v, format){
13827             if(!v){
13828                 return "";
13829             }
13830             if(!(v instanceof Date)){
13831                 v = new Date(Date.parse(v));
13832             }
13833             return v.dateFormat(format || Roo.util.Format.defaults.date);
13834         },
13835
13836         /**
13837          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13838          * @param {String} format Any valid date format string
13839          * @return {Function} The date formatting function
13840          */
13841         dateRenderer : function(format){
13842             return function(v){
13843                 return Roo.util.Format.date(v, format);  
13844             };
13845         },
13846
13847         // private
13848         stripTagsRE : /<\/?[^>]+>/gi,
13849         
13850         /**
13851          * Strips all HTML tags
13852          * @param {Mixed} value The text from which to strip tags
13853          * @return {String} The stripped text
13854          */
13855         stripTags : function(v){
13856             return !v ? v : String(v).replace(this.stripTagsRE, "");
13857         }
13858     };
13859 }();
13860 Roo.util.Format.defaults = {
13861     date : 'd/M/Y'
13862 };/*
13863  * Based on:
13864  * Ext JS Library 1.1.1
13865  * Copyright(c) 2006-2007, Ext JS, LLC.
13866  *
13867  * Originally Released Under LGPL - original licence link has changed is not relivant.
13868  *
13869  * Fork - LGPL
13870  * <script type="text/javascript">
13871  */
13872
13873
13874  
13875
13876 /**
13877  * @class Roo.MasterTemplate
13878  * @extends Roo.Template
13879  * Provides a template that can have child templates. The syntax is:
13880 <pre><code>
13881 var t = new Roo.MasterTemplate(
13882         '&lt;select name="{name}"&gt;',
13883                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13884         '&lt;/select&gt;'
13885 );
13886 t.add('options', {value: 'foo', text: 'bar'});
13887 // or you can add multiple child elements in one shot
13888 t.addAll('options', [
13889     {value: 'foo', text: 'bar'},
13890     {value: 'foo2', text: 'bar2'},
13891     {value: 'foo3', text: 'bar3'}
13892 ]);
13893 // then append, applying the master template values
13894 t.append('my-form', {name: 'my-select'});
13895 </code></pre>
13896 * A name attribute for the child template is not required if you have only one child
13897 * template or you want to refer to them by index.
13898  */
13899 Roo.MasterTemplate = function(){
13900     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13901     this.originalHtml = this.html;
13902     var st = {};
13903     var m, re = this.subTemplateRe;
13904     re.lastIndex = 0;
13905     var subIndex = 0;
13906     while(m = re.exec(this.html)){
13907         var name = m[1], content = m[2];
13908         st[subIndex] = {
13909             name: name,
13910             index: subIndex,
13911             buffer: [],
13912             tpl : new Roo.Template(content)
13913         };
13914         if(name){
13915             st[name] = st[subIndex];
13916         }
13917         st[subIndex].tpl.compile();
13918         st[subIndex].tpl.call = this.call.createDelegate(this);
13919         subIndex++;
13920     }
13921     this.subCount = subIndex;
13922     this.subs = st;
13923 };
13924 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13925     /**
13926     * The regular expression used to match sub templates
13927     * @type RegExp
13928     * @property
13929     */
13930     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13931
13932     /**
13933      * Applies the passed values to a child template.
13934      * @param {String/Number} name (optional) The name or index of the child template
13935      * @param {Array/Object} values The values to be applied to the template
13936      * @return {MasterTemplate} this
13937      */
13938      add : function(name, values){
13939         if(arguments.length == 1){
13940             values = arguments[0];
13941             name = 0;
13942         }
13943         var s = this.subs[name];
13944         s.buffer[s.buffer.length] = s.tpl.apply(values);
13945         return this;
13946     },
13947
13948     /**
13949      * Applies all the passed values to a child template.
13950      * @param {String/Number} name (optional) The name or index of the child template
13951      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13952      * @param {Boolean} reset (optional) True to reset the template first
13953      * @return {MasterTemplate} this
13954      */
13955     fill : function(name, values, reset){
13956         var a = arguments;
13957         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13958             values = a[0];
13959             name = 0;
13960             reset = a[1];
13961         }
13962         if(reset){
13963             this.reset();
13964         }
13965         for(var i = 0, len = values.length; i < len; i++){
13966             this.add(name, values[i]);
13967         }
13968         return this;
13969     },
13970
13971     /**
13972      * Resets the template for reuse
13973      * @return {MasterTemplate} this
13974      */
13975      reset : function(){
13976         var s = this.subs;
13977         for(var i = 0; i < this.subCount; i++){
13978             s[i].buffer = [];
13979         }
13980         return this;
13981     },
13982
13983     applyTemplate : function(values){
13984         var s = this.subs;
13985         var replaceIndex = -1;
13986         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13987             return s[++replaceIndex].buffer.join("");
13988         });
13989         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13990     },
13991
13992     apply : function(){
13993         return this.applyTemplate.apply(this, arguments);
13994     },
13995
13996     compile : function(){return this;}
13997 });
13998
13999 /**
14000  * Alias for fill().
14001  * @method
14002  */
14003 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14004  /**
14005  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14006  * var tpl = Roo.MasterTemplate.from('element-id');
14007  * @param {String/HTMLElement} el
14008  * @param {Object} config
14009  * @static
14010  */
14011 Roo.MasterTemplate.from = function(el, config){
14012     el = Roo.getDom(el);
14013     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14014 };/*
14015  * Based on:
14016  * Ext JS Library 1.1.1
14017  * Copyright(c) 2006-2007, Ext JS, LLC.
14018  *
14019  * Originally Released Under LGPL - original licence link has changed is not relivant.
14020  *
14021  * Fork - LGPL
14022  * <script type="text/javascript">
14023  */
14024
14025  
14026 /**
14027  * @class Roo.util.CSS
14028  * Utility class for manipulating CSS rules
14029  * @singleton
14030  */
14031 Roo.util.CSS = function(){
14032         var rules = null;
14033         var doc = document;
14034
14035     var camelRe = /(-[a-z])/gi;
14036     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14037
14038    return {
14039    /**
14040     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14041     * tag and appended to the HEAD of the document.
14042     * @param {String|Object} cssText The text containing the css rules
14043     * @param {String} id An id to add to the stylesheet for later removal
14044     * @return {StyleSheet}
14045     */
14046     createStyleSheet : function(cssText, id){
14047         var ss;
14048         var head = doc.getElementsByTagName("head")[0];
14049         var nrules = doc.createElement("style");
14050         nrules.setAttribute("type", "text/css");
14051         if(id){
14052             nrules.setAttribute("id", id);
14053         }
14054         if (typeof(cssText) != 'string') {
14055             // support object maps..
14056             // not sure if this a good idea.. 
14057             // perhaps it should be merged with the general css handling
14058             // and handle js style props.
14059             var cssTextNew = [];
14060             for(var n in cssText) {
14061                 var citems = [];
14062                 for(var k in cssText[n]) {
14063                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14064                 }
14065                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14066                 
14067             }
14068             cssText = cssTextNew.join("\n");
14069             
14070         }
14071        
14072        
14073        if(Roo.isIE){
14074            head.appendChild(nrules);
14075            ss = nrules.styleSheet;
14076            ss.cssText = cssText;
14077        }else{
14078            try{
14079                 nrules.appendChild(doc.createTextNode(cssText));
14080            }catch(e){
14081                nrules.cssText = cssText; 
14082            }
14083            head.appendChild(nrules);
14084            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14085        }
14086        this.cacheStyleSheet(ss);
14087        return ss;
14088    },
14089
14090    /**
14091     * Removes a style or link tag by id
14092     * @param {String} id The id of the tag
14093     */
14094    removeStyleSheet : function(id){
14095        var existing = doc.getElementById(id);
14096        if(existing){
14097            existing.parentNode.removeChild(existing);
14098        }
14099    },
14100
14101    /**
14102     * Dynamically swaps an existing stylesheet reference for a new one
14103     * @param {String} id The id of an existing link tag to remove
14104     * @param {String} url The href of the new stylesheet to include
14105     */
14106    swapStyleSheet : function(id, url){
14107        this.removeStyleSheet(id);
14108        var ss = doc.createElement("link");
14109        ss.setAttribute("rel", "stylesheet");
14110        ss.setAttribute("type", "text/css");
14111        ss.setAttribute("id", id);
14112        ss.setAttribute("href", url);
14113        doc.getElementsByTagName("head")[0].appendChild(ss);
14114    },
14115    
14116    /**
14117     * Refresh the rule cache if you have dynamically added stylesheets
14118     * @return {Object} An object (hash) of rules indexed by selector
14119     */
14120    refreshCache : function(){
14121        return this.getRules(true);
14122    },
14123
14124    // private
14125    cacheStyleSheet : function(stylesheet){
14126        if(!rules){
14127            rules = {};
14128        }
14129        try{// try catch for cross domain access issue
14130            var ssRules = stylesheet.cssRules || stylesheet.rules;
14131            for(var j = ssRules.length-1; j >= 0; --j){
14132                rules[ssRules[j].selectorText] = ssRules[j];
14133            }
14134        }catch(e){}
14135    },
14136    
14137    /**
14138     * Gets all css rules for the document
14139     * @param {Boolean} refreshCache true to refresh the internal cache
14140     * @return {Object} An object (hash) of rules indexed by selector
14141     */
14142    getRules : function(refreshCache){
14143                 if(rules == null || refreshCache){
14144                         rules = {};
14145                         var ds = doc.styleSheets;
14146                         for(var i =0, len = ds.length; i < len; i++){
14147                             try{
14148                         this.cacheStyleSheet(ds[i]);
14149                     }catch(e){} 
14150                 }
14151                 }
14152                 return rules;
14153         },
14154         
14155         /**
14156     * Gets an an individual CSS rule by selector(s)
14157     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14158     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14159     * @return {CSSRule} The CSS rule or null if one is not found
14160     */
14161    getRule : function(selector, refreshCache){
14162                 var rs = this.getRules(refreshCache);
14163                 if(!(selector instanceof Array)){
14164                     return rs[selector];
14165                 }
14166                 for(var i = 0; i < selector.length; i++){
14167                         if(rs[selector[i]]){
14168                                 return rs[selector[i]];
14169                         }
14170                 }
14171                 return null;
14172         },
14173         
14174         
14175         /**
14176     * Updates a rule property
14177     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14178     * @param {String} property The css property
14179     * @param {String} value The new value for the property
14180     * @return {Boolean} true If a rule was found and updated
14181     */
14182    updateRule : function(selector, property, value){
14183                 if(!(selector instanceof Array)){
14184                         var rule = this.getRule(selector);
14185                         if(rule){
14186                                 rule.style[property.replace(camelRe, camelFn)] = value;
14187                                 return true;
14188                         }
14189                 }else{
14190                         for(var i = 0; i < selector.length; i++){
14191                                 if(this.updateRule(selector[i], property, value)){
14192                                         return true;
14193                                 }
14194                         }
14195                 }
14196                 return false;
14197         }
14198    };   
14199 }();/*
14200  * Based on:
14201  * Ext JS Library 1.1.1
14202  * Copyright(c) 2006-2007, Ext JS, LLC.
14203  *
14204  * Originally Released Under LGPL - original licence link has changed is not relivant.
14205  *
14206  * Fork - LGPL
14207  * <script type="text/javascript">
14208  */
14209
14210  
14211
14212 /**
14213  * @class Roo.util.ClickRepeater
14214  * @extends Roo.util.Observable
14215  * 
14216  * A wrapper class which can be applied to any element. Fires a "click" event while the
14217  * mouse is pressed. The interval between firings may be specified in the config but
14218  * defaults to 10 milliseconds.
14219  * 
14220  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14221  * 
14222  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14223  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14224  * Similar to an autorepeat key delay.
14225  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14226  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14227  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14228  *           "interval" and "delay" are ignored. "immediate" is honored.
14229  * @cfg {Boolean} preventDefault True to prevent the default click event
14230  * @cfg {Boolean} stopDefault True to stop the default click event
14231  * 
14232  * @history
14233  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14234  *     2007-02-02 jvs Renamed to ClickRepeater
14235  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14236  *
14237  *  @constructor
14238  * @param {String/HTMLElement/Element} el The element to listen on
14239  * @param {Object} config
14240  **/
14241 Roo.util.ClickRepeater = function(el, config)
14242 {
14243     this.el = Roo.get(el);
14244     this.el.unselectable();
14245
14246     Roo.apply(this, config);
14247
14248     this.addEvents({
14249     /**
14250      * @event mousedown
14251      * Fires when the mouse button is depressed.
14252      * @param {Roo.util.ClickRepeater} this
14253      */
14254         "mousedown" : true,
14255     /**
14256      * @event click
14257      * Fires on a specified interval during the time the element is pressed.
14258      * @param {Roo.util.ClickRepeater} this
14259      */
14260         "click" : true,
14261     /**
14262      * @event mouseup
14263      * Fires when the mouse key is released.
14264      * @param {Roo.util.ClickRepeater} this
14265      */
14266         "mouseup" : true
14267     });
14268
14269     this.el.on("mousedown", this.handleMouseDown, this);
14270     if(this.preventDefault || this.stopDefault){
14271         this.el.on("click", function(e){
14272             if(this.preventDefault){
14273                 e.preventDefault();
14274             }
14275             if(this.stopDefault){
14276                 e.stopEvent();
14277             }
14278         }, this);
14279     }
14280
14281     // allow inline handler
14282     if(this.handler){
14283         this.on("click", this.handler,  this.scope || this);
14284     }
14285
14286     Roo.util.ClickRepeater.superclass.constructor.call(this);
14287 };
14288
14289 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14290     interval : 20,
14291     delay: 250,
14292     preventDefault : true,
14293     stopDefault : false,
14294     timer : 0,
14295
14296     // private
14297     handleMouseDown : function(){
14298         clearTimeout(this.timer);
14299         this.el.blur();
14300         if(this.pressClass){
14301             this.el.addClass(this.pressClass);
14302         }
14303         this.mousedownTime = new Date();
14304
14305         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14306         this.el.on("mouseout", this.handleMouseOut, this);
14307
14308         this.fireEvent("mousedown", this);
14309         this.fireEvent("click", this);
14310         
14311         this.timer = this.click.defer(this.delay || this.interval, this);
14312     },
14313
14314     // private
14315     click : function(){
14316         this.fireEvent("click", this);
14317         this.timer = this.click.defer(this.getInterval(), this);
14318     },
14319
14320     // private
14321     getInterval: function(){
14322         if(!this.accelerate){
14323             return this.interval;
14324         }
14325         var pressTime = this.mousedownTime.getElapsed();
14326         if(pressTime < 500){
14327             return 400;
14328         }else if(pressTime < 1700){
14329             return 320;
14330         }else if(pressTime < 2600){
14331             return 250;
14332         }else if(pressTime < 3500){
14333             return 180;
14334         }else if(pressTime < 4400){
14335             return 140;
14336         }else if(pressTime < 5300){
14337             return 80;
14338         }else if(pressTime < 6200){
14339             return 50;
14340         }else{
14341             return 10;
14342         }
14343     },
14344
14345     // private
14346     handleMouseOut : function(){
14347         clearTimeout(this.timer);
14348         if(this.pressClass){
14349             this.el.removeClass(this.pressClass);
14350         }
14351         this.el.on("mouseover", this.handleMouseReturn, this);
14352     },
14353
14354     // private
14355     handleMouseReturn : function(){
14356         this.el.un("mouseover", this.handleMouseReturn);
14357         if(this.pressClass){
14358             this.el.addClass(this.pressClass);
14359         }
14360         this.click();
14361     },
14362
14363     // private
14364     handleMouseUp : function(){
14365         clearTimeout(this.timer);
14366         this.el.un("mouseover", this.handleMouseReturn);
14367         this.el.un("mouseout", this.handleMouseOut);
14368         Roo.get(document).un("mouseup", this.handleMouseUp);
14369         this.el.removeClass(this.pressClass);
14370         this.fireEvent("mouseup", this);
14371     }
14372 });/*
14373  * Based on:
14374  * Ext JS Library 1.1.1
14375  * Copyright(c) 2006-2007, Ext JS, LLC.
14376  *
14377  * Originally Released Under LGPL - original licence link has changed is not relivant.
14378  *
14379  * Fork - LGPL
14380  * <script type="text/javascript">
14381  */
14382
14383  
14384 /**
14385  * @class Roo.KeyNav
14386  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14387  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14388  * way to implement custom navigation schemes for any UI component.</p>
14389  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14390  * pageUp, pageDown, del, home, end.  Usage:</p>
14391  <pre><code>
14392 var nav = new Roo.KeyNav("my-element", {
14393     "left" : function(e){
14394         this.moveLeft(e.ctrlKey);
14395     },
14396     "right" : function(e){
14397         this.moveRight(e.ctrlKey);
14398     },
14399     "enter" : function(e){
14400         this.save();
14401     },
14402     scope : this
14403 });
14404 </code></pre>
14405  * @constructor
14406  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14407  * @param {Object} config The config
14408  */
14409 Roo.KeyNav = function(el, config){
14410     this.el = Roo.get(el);
14411     Roo.apply(this, config);
14412     if(!this.disabled){
14413         this.disabled = true;
14414         this.enable();
14415     }
14416 };
14417
14418 Roo.KeyNav.prototype = {
14419     /**
14420      * @cfg {Boolean} disabled
14421      * True to disable this KeyNav instance (defaults to false)
14422      */
14423     disabled : false,
14424     /**
14425      * @cfg {String} defaultEventAction
14426      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14427      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14428      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14429      */
14430     defaultEventAction: "stopEvent",
14431     /**
14432      * @cfg {Boolean} forceKeyDown
14433      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14434      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14435      * handle keydown instead of keypress.
14436      */
14437     forceKeyDown : false,
14438
14439     // private
14440     prepareEvent : function(e){
14441         var k = e.getKey();
14442         var h = this.keyToHandler[k];
14443         //if(h && this[h]){
14444         //    e.stopPropagation();
14445         //}
14446         if(Roo.isSafari && h && k >= 37 && k <= 40){
14447             e.stopEvent();
14448         }
14449     },
14450
14451     // private
14452     relay : function(e){
14453         var k = e.getKey();
14454         var h = this.keyToHandler[k];
14455         if(h && this[h]){
14456             if(this.doRelay(e, this[h], h) !== true){
14457                 e[this.defaultEventAction]();
14458             }
14459         }
14460     },
14461
14462     // private
14463     doRelay : function(e, h, hname){
14464         return h.call(this.scope || this, e);
14465     },
14466
14467     // possible handlers
14468     enter : false,
14469     left : false,
14470     right : false,
14471     up : false,
14472     down : false,
14473     tab : false,
14474     esc : false,
14475     pageUp : false,
14476     pageDown : false,
14477     del : false,
14478     home : false,
14479     end : false,
14480
14481     // quick lookup hash
14482     keyToHandler : {
14483         37 : "left",
14484         39 : "right",
14485         38 : "up",
14486         40 : "down",
14487         33 : "pageUp",
14488         34 : "pageDown",
14489         46 : "del",
14490         36 : "home",
14491         35 : "end",
14492         13 : "enter",
14493         27 : "esc",
14494         9  : "tab"
14495     },
14496
14497         /**
14498          * Enable this KeyNav
14499          */
14500         enable: function(){
14501                 if(this.disabled){
14502             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14503             // the EventObject will normalize Safari automatically
14504             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14505                 this.el.on("keydown", this.relay,  this);
14506             }else{
14507                 this.el.on("keydown", this.prepareEvent,  this);
14508                 this.el.on("keypress", this.relay,  this);
14509             }
14510                     this.disabled = false;
14511                 }
14512         },
14513
14514         /**
14515          * Disable this KeyNav
14516          */
14517         disable: function(){
14518                 if(!this.disabled){
14519                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14520                 this.el.un("keydown", this.relay);
14521             }else{
14522                 this.el.un("keydown", this.prepareEvent);
14523                 this.el.un("keypress", this.relay);
14524             }
14525                     this.disabled = true;
14526                 }
14527         }
14528 };/*
14529  * Based on:
14530  * Ext JS Library 1.1.1
14531  * Copyright(c) 2006-2007, Ext JS, LLC.
14532  *
14533  * Originally Released Under LGPL - original licence link has changed is not relivant.
14534  *
14535  * Fork - LGPL
14536  * <script type="text/javascript">
14537  */
14538
14539  
14540 /**
14541  * @class Roo.KeyMap
14542  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14543  * The constructor accepts the same config object as defined by {@link #addBinding}.
14544  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14545  * combination it will call the function with this signature (if the match is a multi-key
14546  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14547  * A KeyMap can also handle a string representation of keys.<br />
14548  * Usage:
14549  <pre><code>
14550 // map one key by key code
14551 var map = new Roo.KeyMap("my-element", {
14552     key: 13, // or Roo.EventObject.ENTER
14553     fn: myHandler,
14554     scope: myObject
14555 });
14556
14557 // map multiple keys to one action by string
14558 var map = new Roo.KeyMap("my-element", {
14559     key: "a\r\n\t",
14560     fn: myHandler,
14561     scope: myObject
14562 });
14563
14564 // map multiple keys to multiple actions by strings and array of codes
14565 var map = new Roo.KeyMap("my-element", [
14566     {
14567         key: [10,13],
14568         fn: function(){ alert("Return was pressed"); }
14569     }, {
14570         key: "abc",
14571         fn: function(){ alert('a, b or c was pressed'); }
14572     }, {
14573         key: "\t",
14574         ctrl:true,
14575         shift:true,
14576         fn: function(){ alert('Control + shift + tab was pressed.'); }
14577     }
14578 ]);
14579 </code></pre>
14580  * <b>Note: A KeyMap starts enabled</b>
14581  * @constructor
14582  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14583  * @param {Object} config The config (see {@link #addBinding})
14584  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14585  */
14586 Roo.KeyMap = function(el, config, eventName){
14587     this.el  = Roo.get(el);
14588     this.eventName = eventName || "keydown";
14589     this.bindings = [];
14590     if(config){
14591         this.addBinding(config);
14592     }
14593     this.enable();
14594 };
14595
14596 Roo.KeyMap.prototype = {
14597     /**
14598      * True to stop the event from bubbling and prevent the default browser action if the
14599      * key was handled by the KeyMap (defaults to false)
14600      * @type Boolean
14601      */
14602     stopEvent : false,
14603
14604     /**
14605      * Add a new binding to this KeyMap. The following config object properties are supported:
14606      * <pre>
14607 Property    Type             Description
14608 ----------  ---------------  ----------------------------------------------------------------------
14609 key         String/Array     A single keycode or an array of keycodes to handle
14610 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14611 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14612 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14613 fn          Function         The function to call when KeyMap finds the expected key combination
14614 scope       Object           The scope of the callback function
14615 </pre>
14616      *
14617      * Usage:
14618      * <pre><code>
14619 // Create a KeyMap
14620 var map = new Roo.KeyMap(document, {
14621     key: Roo.EventObject.ENTER,
14622     fn: handleKey,
14623     scope: this
14624 });
14625
14626 //Add a new binding to the existing KeyMap later
14627 map.addBinding({
14628     key: 'abc',
14629     shift: true,
14630     fn: handleKey,
14631     scope: this
14632 });
14633 </code></pre>
14634      * @param {Object/Array} config A single KeyMap config or an array of configs
14635      */
14636         addBinding : function(config){
14637         if(config instanceof Array){
14638             for(var i = 0, len = config.length; i < len; i++){
14639                 this.addBinding(config[i]);
14640             }
14641             return;
14642         }
14643         var keyCode = config.key,
14644             shift = config.shift, 
14645             ctrl = config.ctrl, 
14646             alt = config.alt,
14647             fn = config.fn,
14648             scope = config.scope;
14649         if(typeof keyCode == "string"){
14650             var ks = [];
14651             var keyString = keyCode.toUpperCase();
14652             for(var j = 0, len = keyString.length; j < len; j++){
14653                 ks.push(keyString.charCodeAt(j));
14654             }
14655             keyCode = ks;
14656         }
14657         var keyArray = keyCode instanceof Array;
14658         var handler = function(e){
14659             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14660                 var k = e.getKey();
14661                 if(keyArray){
14662                     for(var i = 0, len = keyCode.length; i < len; i++){
14663                         if(keyCode[i] == k){
14664                           if(this.stopEvent){
14665                               e.stopEvent();
14666                           }
14667                           fn.call(scope || window, k, e);
14668                           return;
14669                         }
14670                     }
14671                 }else{
14672                     if(k == keyCode){
14673                         if(this.stopEvent){
14674                            e.stopEvent();
14675                         }
14676                         fn.call(scope || window, k, e);
14677                     }
14678                 }
14679             }
14680         };
14681         this.bindings.push(handler);  
14682         },
14683
14684     /**
14685      * Shorthand for adding a single key listener
14686      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14687      * following options:
14688      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14689      * @param {Function} fn The function to call
14690      * @param {Object} scope (optional) The scope of the function
14691      */
14692     on : function(key, fn, scope){
14693         var keyCode, shift, ctrl, alt;
14694         if(typeof key == "object" && !(key instanceof Array)){
14695             keyCode = key.key;
14696             shift = key.shift;
14697             ctrl = key.ctrl;
14698             alt = key.alt;
14699         }else{
14700             keyCode = key;
14701         }
14702         this.addBinding({
14703             key: keyCode,
14704             shift: shift,
14705             ctrl: ctrl,
14706             alt: alt,
14707             fn: fn,
14708             scope: scope
14709         })
14710     },
14711
14712     // private
14713     handleKeyDown : function(e){
14714             if(this.enabled){ //just in case
14715             var b = this.bindings;
14716             for(var i = 0, len = b.length; i < len; i++){
14717                 b[i].call(this, e);
14718             }
14719             }
14720         },
14721         
14722         /**
14723          * Returns true if this KeyMap is enabled
14724          * @return {Boolean} 
14725          */
14726         isEnabled : function(){
14727             return this.enabled;  
14728         },
14729         
14730         /**
14731          * Enables this KeyMap
14732          */
14733         enable: function(){
14734                 if(!this.enabled){
14735                     this.el.on(this.eventName, this.handleKeyDown, this);
14736                     this.enabled = true;
14737                 }
14738         },
14739
14740         /**
14741          * Disable this KeyMap
14742          */
14743         disable: function(){
14744                 if(this.enabled){
14745                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14746                     this.enabled = false;
14747                 }
14748         }
14749 };/*
14750  * Based on:
14751  * Ext JS Library 1.1.1
14752  * Copyright(c) 2006-2007, Ext JS, LLC.
14753  *
14754  * Originally Released Under LGPL - original licence link has changed is not relivant.
14755  *
14756  * Fork - LGPL
14757  * <script type="text/javascript">
14758  */
14759
14760  
14761 /**
14762  * @class Roo.util.TextMetrics
14763  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14764  * wide, in pixels, a given block of text will be.
14765  * @singleton
14766  */
14767 Roo.util.TextMetrics = function(){
14768     var shared;
14769     return {
14770         /**
14771          * Measures the size of the specified text
14772          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14773          * that can affect the size of the rendered text
14774          * @param {String} text The text to measure
14775          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14776          * in order to accurately measure the text height
14777          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14778          */
14779         measure : function(el, text, fixedWidth){
14780             if(!shared){
14781                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14782             }
14783             shared.bind(el);
14784             shared.setFixedWidth(fixedWidth || 'auto');
14785             return shared.getSize(text);
14786         },
14787
14788         /**
14789          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14790          * the overhead of multiple calls to initialize the style properties on each measurement.
14791          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14792          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14793          * in order to accurately measure the text height
14794          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14795          */
14796         createInstance : function(el, fixedWidth){
14797             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14798         }
14799     };
14800 }();
14801
14802  
14803
14804 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14805     var ml = new Roo.Element(document.createElement('div'));
14806     document.body.appendChild(ml.dom);
14807     ml.position('absolute');
14808     ml.setLeftTop(-1000, -1000);
14809     ml.hide();
14810
14811     if(fixedWidth){
14812         ml.setWidth(fixedWidth);
14813     }
14814      
14815     var instance = {
14816         /**
14817          * Returns the size of the specified text based on the internal element's style and width properties
14818          * @memberOf Roo.util.TextMetrics.Instance#
14819          * @param {String} text The text to measure
14820          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14821          */
14822         getSize : function(text){
14823             ml.update(text);
14824             var s = ml.getSize();
14825             ml.update('');
14826             return s;
14827         },
14828
14829         /**
14830          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14831          * that can affect the size of the rendered text
14832          * @memberOf Roo.util.TextMetrics.Instance#
14833          * @param {String/HTMLElement} el The element, dom node or id
14834          */
14835         bind : function(el){
14836             ml.setStyle(
14837                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14838             );
14839         },
14840
14841         /**
14842          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14843          * to set a fixed width in order to accurately measure the text height.
14844          * @memberOf Roo.util.TextMetrics.Instance#
14845          * @param {Number} width The width to set on the element
14846          */
14847         setFixedWidth : function(width){
14848             ml.setWidth(width);
14849         },
14850
14851         /**
14852          * Returns the measured width of the specified text
14853          * @memberOf Roo.util.TextMetrics.Instance#
14854          * @param {String} text The text to measure
14855          * @return {Number} width The width in pixels
14856          */
14857         getWidth : function(text){
14858             ml.dom.style.width = 'auto';
14859             return this.getSize(text).width;
14860         },
14861
14862         /**
14863          * Returns the measured height of the specified text.  For multiline text, be sure to call
14864          * {@link #setFixedWidth} if necessary.
14865          * @memberOf Roo.util.TextMetrics.Instance#
14866          * @param {String} text The text to measure
14867          * @return {Number} height The height in pixels
14868          */
14869         getHeight : function(text){
14870             return this.getSize(text).height;
14871         }
14872     };
14873
14874     instance.bind(bindTo);
14875
14876     return instance;
14877 };
14878
14879 // backwards compat
14880 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14881  * Based on:
14882  * Ext JS Library 1.1.1
14883  * Copyright(c) 2006-2007, Ext JS, LLC.
14884  *
14885  * Originally Released Under LGPL - original licence link has changed is not relivant.
14886  *
14887  * Fork - LGPL
14888  * <script type="text/javascript">
14889  */
14890
14891 /**
14892  * @class Roo.state.Provider
14893  * Abstract base class for state provider implementations. This class provides methods
14894  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14895  * Provider interface.
14896  */
14897 Roo.state.Provider = function(){
14898     /**
14899      * @event statechange
14900      * Fires when a state change occurs.
14901      * @param {Provider} this This state provider
14902      * @param {String} key The state key which was changed
14903      * @param {String} value The encoded value for the state
14904      */
14905     this.addEvents({
14906         "statechange": true
14907     });
14908     this.state = {};
14909     Roo.state.Provider.superclass.constructor.call(this);
14910 };
14911 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14912     /**
14913      * Returns the current value for a key
14914      * @param {String} name The key name
14915      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14916      * @return {Mixed} The state data
14917      */
14918     get : function(name, defaultValue){
14919         return typeof this.state[name] == "undefined" ?
14920             defaultValue : this.state[name];
14921     },
14922     
14923     /**
14924      * Clears a value from the state
14925      * @param {String} name The key name
14926      */
14927     clear : function(name){
14928         delete this.state[name];
14929         this.fireEvent("statechange", this, name, null);
14930     },
14931     
14932     /**
14933      * Sets the value for a key
14934      * @param {String} name The key name
14935      * @param {Mixed} value The value to set
14936      */
14937     set : function(name, value){
14938         this.state[name] = value;
14939         this.fireEvent("statechange", this, name, value);
14940     },
14941     
14942     /**
14943      * Decodes a string previously encoded with {@link #encodeValue}.
14944      * @param {String} value The value to decode
14945      * @return {Mixed} The decoded value
14946      */
14947     decodeValue : function(cookie){
14948         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14949         var matches = re.exec(unescape(cookie));
14950         if(!matches || !matches[1]) {
14951             return; // non state cookie
14952         }
14953         var type = matches[1];
14954         var v = matches[2];
14955         switch(type){
14956             case "n":
14957                 return parseFloat(v);
14958             case "d":
14959                 return new Date(Date.parse(v));
14960             case "b":
14961                 return (v == "1");
14962             case "a":
14963                 var all = [];
14964                 var values = v.split("^");
14965                 for(var i = 0, len = values.length; i < len; i++){
14966                     all.push(this.decodeValue(values[i]));
14967                 }
14968                 return all;
14969            case "o":
14970                 var all = {};
14971                 var values = v.split("^");
14972                 for(var i = 0, len = values.length; i < len; i++){
14973                     var kv = values[i].split("=");
14974                     all[kv[0]] = this.decodeValue(kv[1]);
14975                 }
14976                 return all;
14977            default:
14978                 return v;
14979         }
14980     },
14981     
14982     /**
14983      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14984      * @param {Mixed} value The value to encode
14985      * @return {String} The encoded value
14986      */
14987     encodeValue : function(v){
14988         var enc;
14989         if(typeof v == "number"){
14990             enc = "n:" + v;
14991         }else if(typeof v == "boolean"){
14992             enc = "b:" + (v ? "1" : "0");
14993         }else if(v instanceof Date){
14994             enc = "d:" + v.toGMTString();
14995         }else if(v instanceof Array){
14996             var flat = "";
14997             for(var i = 0, len = v.length; i < len; i++){
14998                 flat += this.encodeValue(v[i]);
14999                 if(i != len-1) {
15000                     flat += "^";
15001                 }
15002             }
15003             enc = "a:" + flat;
15004         }else if(typeof v == "object"){
15005             var flat = "";
15006             for(var key in v){
15007                 if(typeof v[key] != "function"){
15008                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15009                 }
15010             }
15011             enc = "o:" + flat.substring(0, flat.length-1);
15012         }else{
15013             enc = "s:" + v;
15014         }
15015         return escape(enc);        
15016     }
15017 });
15018
15019 /*
15020  * Based on:
15021  * Ext JS Library 1.1.1
15022  * Copyright(c) 2006-2007, Ext JS, LLC.
15023  *
15024  * Originally Released Under LGPL - original licence link has changed is not relivant.
15025  *
15026  * Fork - LGPL
15027  * <script type="text/javascript">
15028  */
15029 /**
15030  * @class Roo.state.Manager
15031  * This is the global state manager. By default all components that are "state aware" check this class
15032  * for state information if you don't pass them a custom state provider. In order for this class
15033  * to be useful, it must be initialized with a provider when your application initializes.
15034  <pre><code>
15035 // in your initialization function
15036 init : function(){
15037    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15038    ...
15039    // supposed you have a {@link Roo.BorderLayout}
15040    var layout = new Roo.BorderLayout(...);
15041    layout.restoreState();
15042    // or a {Roo.BasicDialog}
15043    var dialog = new Roo.BasicDialog(...);
15044    dialog.restoreState();
15045  </code></pre>
15046  * @singleton
15047  */
15048 Roo.state.Manager = function(){
15049     var provider = new Roo.state.Provider();
15050     
15051     return {
15052         /**
15053          * Configures the default state provider for your application
15054          * @param {Provider} stateProvider The state provider to set
15055          */
15056         setProvider : function(stateProvider){
15057             provider = stateProvider;
15058         },
15059         
15060         /**
15061          * Returns the current value for a key
15062          * @param {String} name The key name
15063          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15064          * @return {Mixed} The state data
15065          */
15066         get : function(key, defaultValue){
15067             return provider.get(key, defaultValue);
15068         },
15069         
15070         /**
15071          * Sets the value for a key
15072          * @param {String} name The key name
15073          * @param {Mixed} value The state data
15074          */
15075          set : function(key, value){
15076             provider.set(key, value);
15077         },
15078         
15079         /**
15080          * Clears a value from the state
15081          * @param {String} name The key name
15082          */
15083         clear : function(key){
15084             provider.clear(key);
15085         },
15086         
15087         /**
15088          * Gets the currently configured state provider
15089          * @return {Provider} The state provider
15090          */
15091         getProvider : function(){
15092             return provider;
15093         }
15094     };
15095 }();
15096 /*
15097  * Based on:
15098  * Ext JS Library 1.1.1
15099  * Copyright(c) 2006-2007, Ext JS, LLC.
15100  *
15101  * Originally Released Under LGPL - original licence link has changed is not relivant.
15102  *
15103  * Fork - LGPL
15104  * <script type="text/javascript">
15105  */
15106 /**
15107  * @class Roo.state.CookieProvider
15108  * @extends Roo.state.Provider
15109  * The default Provider implementation which saves state via cookies.
15110  * <br />Usage:
15111  <pre><code>
15112    var cp = new Roo.state.CookieProvider({
15113        path: "/cgi-bin/",
15114        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15115        domain: "roojs.com"
15116    })
15117    Roo.state.Manager.setProvider(cp);
15118  </code></pre>
15119  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15120  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15121  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15122  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15123  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15124  * domain the page is running on including the 'www' like 'www.roojs.com')
15125  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15126  * @constructor
15127  * Create a new CookieProvider
15128  * @param {Object} config The configuration object
15129  */
15130 Roo.state.CookieProvider = function(config){
15131     Roo.state.CookieProvider.superclass.constructor.call(this);
15132     this.path = "/";
15133     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15134     this.domain = null;
15135     this.secure = false;
15136     Roo.apply(this, config);
15137     this.state = this.readCookies();
15138 };
15139
15140 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15141     // private
15142     set : function(name, value){
15143         if(typeof value == "undefined" || value === null){
15144             this.clear(name);
15145             return;
15146         }
15147         this.setCookie(name, value);
15148         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15149     },
15150
15151     // private
15152     clear : function(name){
15153         this.clearCookie(name);
15154         Roo.state.CookieProvider.superclass.clear.call(this, name);
15155     },
15156
15157     // private
15158     readCookies : function(){
15159         var cookies = {};
15160         var c = document.cookie + ";";
15161         var re = /\s?(.*?)=(.*?);/g;
15162         var matches;
15163         while((matches = re.exec(c)) != null){
15164             var name = matches[1];
15165             var value = matches[2];
15166             if(name && name.substring(0,3) == "ys-"){
15167                 cookies[name.substr(3)] = this.decodeValue(value);
15168             }
15169         }
15170         return cookies;
15171     },
15172
15173     // private
15174     setCookie : function(name, value){
15175         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15176            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15177            ((this.path == null) ? "" : ("; path=" + this.path)) +
15178            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15179            ((this.secure == true) ? "; secure" : "");
15180     },
15181
15182     // private
15183     clearCookie : function(name){
15184         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15185            ((this.path == null) ? "" : ("; path=" + this.path)) +
15186            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15187            ((this.secure == true) ? "; secure" : "");
15188     }
15189 });/*
15190  * Based on:
15191  * Ext JS Library 1.1.1
15192  * Copyright(c) 2006-2007, Ext JS, LLC.
15193  *
15194  * Originally Released Under LGPL - original licence link has changed is not relivant.
15195  *
15196  * Fork - LGPL
15197  * <script type="text/javascript">
15198  */
15199  
15200
15201 /**
15202  * @class Roo.ComponentMgr
15203  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15204  * @singleton
15205  */
15206 Roo.ComponentMgr = function(){
15207     var all = new Roo.util.MixedCollection();
15208
15209     return {
15210         /**
15211          * Registers a component.
15212          * @param {Roo.Component} c The component
15213          */
15214         register : function(c){
15215             all.add(c);
15216         },
15217
15218         /**
15219          * Unregisters a component.
15220          * @param {Roo.Component} c The component
15221          */
15222         unregister : function(c){
15223             all.remove(c);
15224         },
15225
15226         /**
15227          * Returns a component by id
15228          * @param {String} id The component id
15229          */
15230         get : function(id){
15231             return all.get(id);
15232         },
15233
15234         /**
15235          * Registers a function that will be called when a specified component is added to ComponentMgr
15236          * @param {String} id The component id
15237          * @param {Funtction} fn The callback function
15238          * @param {Object} scope The scope of the callback
15239          */
15240         onAvailable : function(id, fn, scope){
15241             all.on("add", function(index, o){
15242                 if(o.id == id){
15243                     fn.call(scope || o, o);
15244                     all.un("add", fn, scope);
15245                 }
15246             });
15247         }
15248     };
15249 }();/*
15250  * Based on:
15251  * Ext JS Library 1.1.1
15252  * Copyright(c) 2006-2007, Ext JS, LLC.
15253  *
15254  * Originally Released Under LGPL - original licence link has changed is not relivant.
15255  *
15256  * Fork - LGPL
15257  * <script type="text/javascript">
15258  */
15259  
15260 /**
15261  * @class Roo.Component
15262  * @extends Roo.util.Observable
15263  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15264  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15265  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15266  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15267  * All visual components (widgets) that require rendering into a layout should subclass Component.
15268  * @constructor
15269  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15270  * 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
15271  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15272  */
15273 Roo.Component = function(config){
15274     config = config || {};
15275     if(config.tagName || config.dom || typeof config == "string"){ // element object
15276         config = {el: config, id: config.id || config};
15277     }
15278     this.initialConfig = config;
15279
15280     Roo.apply(this, config);
15281     this.addEvents({
15282         /**
15283          * @event disable
15284          * Fires after the component is disabled.
15285              * @param {Roo.Component} this
15286              */
15287         disable : true,
15288         /**
15289          * @event enable
15290          * Fires after the component is enabled.
15291              * @param {Roo.Component} this
15292              */
15293         enable : true,
15294         /**
15295          * @event beforeshow
15296          * Fires before the component is shown.  Return false to stop the show.
15297              * @param {Roo.Component} this
15298              */
15299         beforeshow : true,
15300         /**
15301          * @event show
15302          * Fires after the component is shown.
15303              * @param {Roo.Component} this
15304              */
15305         show : true,
15306         /**
15307          * @event beforehide
15308          * Fires before the component is hidden. Return false to stop the hide.
15309              * @param {Roo.Component} this
15310              */
15311         beforehide : true,
15312         /**
15313          * @event hide
15314          * Fires after the component is hidden.
15315              * @param {Roo.Component} this
15316              */
15317         hide : true,
15318         /**
15319          * @event beforerender
15320          * Fires before the component is rendered. Return false to stop the render.
15321              * @param {Roo.Component} this
15322              */
15323         beforerender : true,
15324         /**
15325          * @event render
15326          * Fires after the component is rendered.
15327              * @param {Roo.Component} this
15328              */
15329         render : true,
15330         /**
15331          * @event beforedestroy
15332          * Fires before the component is destroyed. Return false to stop the destroy.
15333              * @param {Roo.Component} this
15334              */
15335         beforedestroy : true,
15336         /**
15337          * @event destroy
15338          * Fires after the component is destroyed.
15339              * @param {Roo.Component} this
15340              */
15341         destroy : true
15342     });
15343     if(!this.id){
15344         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15345     }
15346     Roo.ComponentMgr.register(this);
15347     Roo.Component.superclass.constructor.call(this);
15348     this.initComponent();
15349     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15350         this.render(this.renderTo);
15351         delete this.renderTo;
15352     }
15353 };
15354
15355 /** @private */
15356 Roo.Component.AUTO_ID = 1000;
15357
15358 Roo.extend(Roo.Component, Roo.util.Observable, {
15359     /**
15360      * @scope Roo.Component.prototype
15361      * @type {Boolean}
15362      * true if this component is hidden. Read-only.
15363      */
15364     hidden : false,
15365     /**
15366      * @type {Boolean}
15367      * true if this component is disabled. Read-only.
15368      */
15369     disabled : false,
15370     /**
15371      * @type {Boolean}
15372      * true if this component has been rendered. Read-only.
15373      */
15374     rendered : false,
15375     
15376     /** @cfg {String} disableClass
15377      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15378      */
15379     disabledClass : "x-item-disabled",
15380         /** @cfg {Boolean} allowDomMove
15381          * Whether the component can move the Dom node when rendering (defaults to true).
15382          */
15383     allowDomMove : true,
15384     /** @cfg {String} hideMode (display|visibility)
15385      * How this component should hidden. Supported values are
15386      * "visibility" (css visibility), "offsets" (negative offset position) and
15387      * "display" (css display) - defaults to "display".
15388      */
15389     hideMode: 'display',
15390
15391     /** @private */
15392     ctype : "Roo.Component",
15393
15394     /**
15395      * @cfg {String} actionMode 
15396      * which property holds the element that used for  hide() / show() / disable() / enable()
15397      * default is 'el' 
15398      */
15399     actionMode : "el",
15400
15401     /** @private */
15402     getActionEl : function(){
15403         return this[this.actionMode];
15404     },
15405
15406     initComponent : Roo.emptyFn,
15407     /**
15408      * If this is a lazy rendering component, render it to its container element.
15409      * @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.
15410      */
15411     render : function(container, position){
15412         
15413         if(this.rendered){
15414             return this;
15415         }
15416         
15417         if(this.fireEvent("beforerender", this) === false){
15418             return false;
15419         }
15420         
15421         if(!container && this.el){
15422             this.el = Roo.get(this.el);
15423             container = this.el.dom.parentNode;
15424             this.allowDomMove = false;
15425         }
15426         this.container = Roo.get(container);
15427         this.rendered = true;
15428         if(position !== undefined){
15429             if(typeof position == 'number'){
15430                 position = this.container.dom.childNodes[position];
15431             }else{
15432                 position = Roo.getDom(position);
15433             }
15434         }
15435         this.onRender(this.container, position || null);
15436         if(this.cls){
15437             this.el.addClass(this.cls);
15438             delete this.cls;
15439         }
15440         if(this.style){
15441             this.el.applyStyles(this.style);
15442             delete this.style;
15443         }
15444         this.fireEvent("render", this);
15445         this.afterRender(this.container);
15446         if(this.hidden){
15447             this.hide();
15448         }
15449         if(this.disabled){
15450             this.disable();
15451         }
15452
15453         return this;
15454         
15455     },
15456
15457     /** @private */
15458     // default function is not really useful
15459     onRender : function(ct, position){
15460         if(this.el){
15461             this.el = Roo.get(this.el);
15462             if(this.allowDomMove !== false){
15463                 ct.dom.insertBefore(this.el.dom, position);
15464             }
15465         }
15466     },
15467
15468     /** @private */
15469     getAutoCreate : function(){
15470         var cfg = typeof this.autoCreate == "object" ?
15471                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15472         if(this.id && !cfg.id){
15473             cfg.id = this.id;
15474         }
15475         return cfg;
15476     },
15477
15478     /** @private */
15479     afterRender : Roo.emptyFn,
15480
15481     /**
15482      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15483      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15484      */
15485     destroy : function(){
15486         if(this.fireEvent("beforedestroy", this) !== false){
15487             this.purgeListeners();
15488             this.beforeDestroy();
15489             if(this.rendered){
15490                 this.el.removeAllListeners();
15491                 this.el.remove();
15492                 if(this.actionMode == "container"){
15493                     this.container.remove();
15494                 }
15495             }
15496             this.onDestroy();
15497             Roo.ComponentMgr.unregister(this);
15498             this.fireEvent("destroy", this);
15499         }
15500     },
15501
15502         /** @private */
15503     beforeDestroy : function(){
15504
15505     },
15506
15507         /** @private */
15508         onDestroy : function(){
15509
15510     },
15511
15512     /**
15513      * Returns the underlying {@link Roo.Element}.
15514      * @return {Roo.Element} The element
15515      */
15516     getEl : function(){
15517         return this.el;
15518     },
15519
15520     /**
15521      * Returns the id of this component.
15522      * @return {String}
15523      */
15524     getId : function(){
15525         return this.id;
15526     },
15527
15528     /**
15529      * Try to focus this component.
15530      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15531      * @return {Roo.Component} this
15532      */
15533     focus : function(selectText){
15534         if(this.rendered){
15535             this.el.focus();
15536             if(selectText === true){
15537                 this.el.dom.select();
15538             }
15539         }
15540         return this;
15541     },
15542
15543     /** @private */
15544     blur : function(){
15545         if(this.rendered){
15546             this.el.blur();
15547         }
15548         return this;
15549     },
15550
15551     /**
15552      * Disable this component.
15553      * @return {Roo.Component} this
15554      */
15555     disable : function(){
15556         if(this.rendered){
15557             this.onDisable();
15558         }
15559         this.disabled = true;
15560         this.fireEvent("disable", this);
15561         return this;
15562     },
15563
15564         // private
15565     onDisable : function(){
15566         this.getActionEl().addClass(this.disabledClass);
15567         this.el.dom.disabled = true;
15568     },
15569
15570     /**
15571      * Enable this component.
15572      * @return {Roo.Component} this
15573      */
15574     enable : function(){
15575         if(this.rendered){
15576             this.onEnable();
15577         }
15578         this.disabled = false;
15579         this.fireEvent("enable", this);
15580         return this;
15581     },
15582
15583         // private
15584     onEnable : function(){
15585         this.getActionEl().removeClass(this.disabledClass);
15586         this.el.dom.disabled = false;
15587     },
15588
15589     /**
15590      * Convenience function for setting disabled/enabled by boolean.
15591      * @param {Boolean} disabled
15592      */
15593     setDisabled : function(disabled){
15594         this[disabled ? "disable" : "enable"]();
15595     },
15596
15597     /**
15598      * Show this component.
15599      * @return {Roo.Component} this
15600      */
15601     show: function(){
15602         if(this.fireEvent("beforeshow", this) !== false){
15603             this.hidden = false;
15604             if(this.rendered){
15605                 this.onShow();
15606             }
15607             this.fireEvent("show", this);
15608         }
15609         return this;
15610     },
15611
15612     // private
15613     onShow : function(){
15614         var ae = this.getActionEl();
15615         if(this.hideMode == 'visibility'){
15616             ae.dom.style.visibility = "visible";
15617         }else if(this.hideMode == 'offsets'){
15618             ae.removeClass('x-hidden');
15619         }else{
15620             ae.dom.style.display = "";
15621         }
15622     },
15623
15624     /**
15625      * Hide this component.
15626      * @return {Roo.Component} this
15627      */
15628     hide: function(){
15629         if(this.fireEvent("beforehide", this) !== false){
15630             this.hidden = true;
15631             if(this.rendered){
15632                 this.onHide();
15633             }
15634             this.fireEvent("hide", this);
15635         }
15636         return this;
15637     },
15638
15639     // private
15640     onHide : function(){
15641         var ae = this.getActionEl();
15642         if(this.hideMode == 'visibility'){
15643             ae.dom.style.visibility = "hidden";
15644         }else if(this.hideMode == 'offsets'){
15645             ae.addClass('x-hidden');
15646         }else{
15647             ae.dom.style.display = "none";
15648         }
15649     },
15650
15651     /**
15652      * Convenience function to hide or show this component by boolean.
15653      * @param {Boolean} visible True to show, false to hide
15654      * @return {Roo.Component} this
15655      */
15656     setVisible: function(visible){
15657         if(visible) {
15658             this.show();
15659         }else{
15660             this.hide();
15661         }
15662         return this;
15663     },
15664
15665     /**
15666      * Returns true if this component is visible.
15667      */
15668     isVisible : function(){
15669         return this.getActionEl().isVisible();
15670     },
15671
15672     cloneConfig : function(overrides){
15673         overrides = overrides || {};
15674         var id = overrides.id || Roo.id();
15675         var cfg = Roo.applyIf(overrides, this.initialConfig);
15676         cfg.id = id; // prevent dup id
15677         return new this.constructor(cfg);
15678     }
15679 });/*
15680  * Based on:
15681  * Ext JS Library 1.1.1
15682  * Copyright(c) 2006-2007, Ext JS, LLC.
15683  *
15684  * Originally Released Under LGPL - original licence link has changed is not relivant.
15685  *
15686  * Fork - LGPL
15687  * <script type="text/javascript">
15688  */
15689
15690 /**
15691  * @class Roo.BoxComponent
15692  * @extends Roo.Component
15693  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15694  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15695  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15696  * layout containers.
15697  * @constructor
15698  * @param {Roo.Element/String/Object} config The configuration options.
15699  */
15700 Roo.BoxComponent = function(config){
15701     Roo.Component.call(this, config);
15702     this.addEvents({
15703         /**
15704          * @event resize
15705          * Fires after the component is resized.
15706              * @param {Roo.Component} this
15707              * @param {Number} adjWidth The box-adjusted width that was set
15708              * @param {Number} adjHeight The box-adjusted height that was set
15709              * @param {Number} rawWidth The width that was originally specified
15710              * @param {Number} rawHeight The height that was originally specified
15711              */
15712         resize : true,
15713         /**
15714          * @event move
15715          * Fires after the component is moved.
15716              * @param {Roo.Component} this
15717              * @param {Number} x The new x position
15718              * @param {Number} y The new y position
15719              */
15720         move : true
15721     });
15722 };
15723
15724 Roo.extend(Roo.BoxComponent, Roo.Component, {
15725     // private, set in afterRender to signify that the component has been rendered
15726     boxReady : false,
15727     // private, used to defer height settings to subclasses
15728     deferHeight: false,
15729     /** @cfg {Number} width
15730      * width (optional) size of component
15731      */
15732      /** @cfg {Number} height
15733      * height (optional) size of component
15734      */
15735      
15736     /**
15737      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15738      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15739      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15740      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15741      * @return {Roo.BoxComponent} this
15742      */
15743     setSize : function(w, h){
15744         // support for standard size objects
15745         if(typeof w == 'object'){
15746             h = w.height;
15747             w = w.width;
15748         }
15749         // not rendered
15750         if(!this.boxReady){
15751             this.width = w;
15752             this.height = h;
15753             return this;
15754         }
15755
15756         // prevent recalcs when not needed
15757         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15758             return this;
15759         }
15760         this.lastSize = {width: w, height: h};
15761
15762         var adj = this.adjustSize(w, h);
15763         var aw = adj.width, ah = adj.height;
15764         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15765             var rz = this.getResizeEl();
15766             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15767                 rz.setSize(aw, ah);
15768             }else if(!this.deferHeight && ah !== undefined){
15769                 rz.setHeight(ah);
15770             }else if(aw !== undefined){
15771                 rz.setWidth(aw);
15772             }
15773             this.onResize(aw, ah, w, h);
15774             this.fireEvent('resize', this, aw, ah, w, h);
15775         }
15776         return this;
15777     },
15778
15779     /**
15780      * Gets the current size of the component's underlying element.
15781      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15782      */
15783     getSize : function(){
15784         return this.el.getSize();
15785     },
15786
15787     /**
15788      * Gets the current XY position of the component's underlying element.
15789      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15790      * @return {Array} The XY position of the element (e.g., [100, 200])
15791      */
15792     getPosition : function(local){
15793         if(local === true){
15794             return [this.el.getLeft(true), this.el.getTop(true)];
15795         }
15796         return this.xy || this.el.getXY();
15797     },
15798
15799     /**
15800      * Gets the current box measurements of the component's underlying element.
15801      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15802      * @returns {Object} box An object in the format {x, y, width, height}
15803      */
15804     getBox : function(local){
15805         var s = this.el.getSize();
15806         if(local){
15807             s.x = this.el.getLeft(true);
15808             s.y = this.el.getTop(true);
15809         }else{
15810             var xy = this.xy || this.el.getXY();
15811             s.x = xy[0];
15812             s.y = xy[1];
15813         }
15814         return s;
15815     },
15816
15817     /**
15818      * Sets the current box measurements of the component's underlying element.
15819      * @param {Object} box An object in the format {x, y, width, height}
15820      * @returns {Roo.BoxComponent} this
15821      */
15822     updateBox : function(box){
15823         this.setSize(box.width, box.height);
15824         this.setPagePosition(box.x, box.y);
15825         return this;
15826     },
15827
15828     // protected
15829     getResizeEl : function(){
15830         return this.resizeEl || this.el;
15831     },
15832
15833     // protected
15834     getPositionEl : function(){
15835         return this.positionEl || this.el;
15836     },
15837
15838     /**
15839      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15840      * This method fires the move event.
15841      * @param {Number} left The new left
15842      * @param {Number} top The new top
15843      * @returns {Roo.BoxComponent} this
15844      */
15845     setPosition : function(x, y){
15846         this.x = x;
15847         this.y = y;
15848         if(!this.boxReady){
15849             return this;
15850         }
15851         var adj = this.adjustPosition(x, y);
15852         var ax = adj.x, ay = adj.y;
15853
15854         var el = this.getPositionEl();
15855         if(ax !== undefined || ay !== undefined){
15856             if(ax !== undefined && ay !== undefined){
15857                 el.setLeftTop(ax, ay);
15858             }else if(ax !== undefined){
15859                 el.setLeft(ax);
15860             }else if(ay !== undefined){
15861                 el.setTop(ay);
15862             }
15863             this.onPosition(ax, ay);
15864             this.fireEvent('move', this, ax, ay);
15865         }
15866         return this;
15867     },
15868
15869     /**
15870      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15871      * This method fires the move event.
15872      * @param {Number} x The new x position
15873      * @param {Number} y The new y position
15874      * @returns {Roo.BoxComponent} this
15875      */
15876     setPagePosition : function(x, y){
15877         this.pageX = x;
15878         this.pageY = y;
15879         if(!this.boxReady){
15880             return;
15881         }
15882         if(x === undefined || y === undefined){ // cannot translate undefined points
15883             return;
15884         }
15885         var p = this.el.translatePoints(x, y);
15886         this.setPosition(p.left, p.top);
15887         return this;
15888     },
15889
15890     // private
15891     onRender : function(ct, position){
15892         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15893         if(this.resizeEl){
15894             this.resizeEl = Roo.get(this.resizeEl);
15895         }
15896         if(this.positionEl){
15897             this.positionEl = Roo.get(this.positionEl);
15898         }
15899     },
15900
15901     // private
15902     afterRender : function(){
15903         Roo.BoxComponent.superclass.afterRender.call(this);
15904         this.boxReady = true;
15905         this.setSize(this.width, this.height);
15906         if(this.x || this.y){
15907             this.setPosition(this.x, this.y);
15908         }
15909         if(this.pageX || this.pageY){
15910             this.setPagePosition(this.pageX, this.pageY);
15911         }
15912     },
15913
15914     /**
15915      * Force the component's size to recalculate based on the underlying element's current height and width.
15916      * @returns {Roo.BoxComponent} this
15917      */
15918     syncSize : function(){
15919         delete this.lastSize;
15920         this.setSize(this.el.getWidth(), this.el.getHeight());
15921         return this;
15922     },
15923
15924     /**
15925      * Called after the component is resized, this method is empty by default but can be implemented by any
15926      * subclass that needs to perform custom logic after a resize occurs.
15927      * @param {Number} adjWidth The box-adjusted width that was set
15928      * @param {Number} adjHeight The box-adjusted height that was set
15929      * @param {Number} rawWidth The width that was originally specified
15930      * @param {Number} rawHeight The height that was originally specified
15931      */
15932     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15933
15934     },
15935
15936     /**
15937      * Called after the component is moved, this method is empty by default but can be implemented by any
15938      * subclass that needs to perform custom logic after a move occurs.
15939      * @param {Number} x The new x position
15940      * @param {Number} y The new y position
15941      */
15942     onPosition : function(x, y){
15943
15944     },
15945
15946     // private
15947     adjustSize : function(w, h){
15948         if(this.autoWidth){
15949             w = 'auto';
15950         }
15951         if(this.autoHeight){
15952             h = 'auto';
15953         }
15954         return {width : w, height: h};
15955     },
15956
15957     // private
15958     adjustPosition : function(x, y){
15959         return {x : x, y: y};
15960     }
15961 });/*
15962  * Original code for Roojs - LGPL
15963  * <script type="text/javascript">
15964  */
15965  
15966 /**
15967  * @class Roo.XComponent
15968  * A delayed Element creator...
15969  * Or a way to group chunks of interface together.
15970  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15971  *  used in conjunction with XComponent.build() it will create an instance of each element,
15972  *  then call addxtype() to build the User interface.
15973  * 
15974  * Mypart.xyx = new Roo.XComponent({
15975
15976     parent : 'Mypart.xyz', // empty == document.element.!!
15977     order : '001',
15978     name : 'xxxx'
15979     region : 'xxxx'
15980     disabled : function() {} 
15981      
15982     tree : function() { // return an tree of xtype declared components
15983         var MODULE = this;
15984         return 
15985         {
15986             xtype : 'NestedLayoutPanel',
15987             // technicall
15988         }
15989      ]
15990  *})
15991  *
15992  *
15993  * It can be used to build a big heiracy, with parent etc.
15994  * or you can just use this to render a single compoent to a dom element
15995  * MYPART.render(Roo.Element | String(id) | dom_element )
15996  *
15997  *
15998  * Usage patterns.
15999  *
16000  * Classic Roo
16001  *
16002  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16003  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16004  *
16005  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16006  *
16007  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16008  * - if mulitple topModules exist, the last one is defined as the top module.
16009  *
16010  * Embeded Roo
16011  * 
16012  * When the top level or multiple modules are to embedded into a existing HTML page,
16013  * the parent element can container '#id' of the element where the module will be drawn.
16014  *
16015  * Bootstrap Roo
16016  *
16017  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16018  * it relies more on a include mechanism, where sub modules are included into an outer page.
16019  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16020  * 
16021  * Bootstrap Roo Included elements
16022  *
16023  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16024  * hence confusing the component builder as it thinks there are multiple top level elements. 
16025  *
16026  * String Over-ride & Translations
16027  *
16028  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16029  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16030  * are needed. @see Roo.XComponent.overlayString  
16031  * 
16032  * 
16033  * 
16034  * @extends Roo.util.Observable
16035  * @constructor
16036  * @param cfg {Object} configuration of component
16037  * 
16038  */
16039 Roo.XComponent = function(cfg) {
16040     Roo.apply(this, cfg);
16041     this.addEvents({ 
16042         /**
16043              * @event built
16044              * Fires when this the componnt is built
16045              * @param {Roo.XComponent} c the component
16046              */
16047         'built' : true
16048         
16049     });
16050     this.region = this.region || 'center'; // default..
16051     Roo.XComponent.register(this);
16052     this.modules = false;
16053     this.el = false; // where the layout goes..
16054     
16055     
16056 }
16057 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16058     /**
16059      * @property el
16060      * The created element (with Roo.factory())
16061      * @type {Roo.Layout}
16062      */
16063     el  : false,
16064     
16065     /**
16066      * @property el
16067      * for BC  - use el in new code
16068      * @type {Roo.Layout}
16069      */
16070     panel : false,
16071     
16072     /**
16073      * @property layout
16074      * for BC  - use el in new code
16075      * @type {Roo.Layout}
16076      */
16077     layout : false,
16078     
16079      /**
16080      * @cfg {Function|boolean} disabled
16081      * If this module is disabled by some rule, return true from the funtion
16082      */
16083     disabled : false,
16084     
16085     /**
16086      * @cfg {String} parent 
16087      * Name of parent element which it get xtype added to..
16088      */
16089     parent: false,
16090     
16091     /**
16092      * @cfg {String} order
16093      * Used to set the order in which elements are created (usefull for multiple tabs)
16094      */
16095     
16096     order : false,
16097     /**
16098      * @cfg {String} name
16099      * String to display while loading.
16100      */
16101     name : false,
16102     /**
16103      * @cfg {String} region
16104      * Region to render component to (defaults to center)
16105      */
16106     region : 'center',
16107     
16108     /**
16109      * @cfg {Array} items
16110      * A single item array - the first element is the root of the tree..
16111      * It's done this way to stay compatible with the Xtype system...
16112      */
16113     items : false,
16114     
16115     /**
16116      * @property _tree
16117      * The method that retuns the tree of parts that make up this compoennt 
16118      * @type {function}
16119      */
16120     _tree  : false,
16121     
16122      /**
16123      * render
16124      * render element to dom or tree
16125      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16126      */
16127     
16128     render : function(el)
16129     {
16130         
16131         el = el || false;
16132         var hp = this.parent ? 1 : 0;
16133         Roo.debug &&  Roo.log(this);
16134         
16135         var tree = this._tree ? this._tree() : this.tree();
16136
16137         
16138         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16139             // if parent is a '#.....' string, then let's use that..
16140             var ename = this.parent.substr(1);
16141             this.parent = false;
16142             Roo.debug && Roo.log(ename);
16143             switch (ename) {
16144                 case 'bootstrap-body':
16145                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16146                         // this is the BorderLayout standard?
16147                        this.parent = { el : true };
16148                        break;
16149                     }
16150                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16151                         // need to insert stuff...
16152                         this.parent =  {
16153                              el : new Roo.bootstrap.layout.Border({
16154                                  el : document.body, 
16155                      
16156                                  center: {
16157                                     titlebar: false,
16158                                     autoScroll:false,
16159                                     closeOnTab: true,
16160                                     tabPosition: 'top',
16161                                       //resizeTabs: true,
16162                                     alwaysShowTabs: true,
16163                                     hideTabs: false
16164                                      //minTabWidth: 140
16165                                  }
16166                              })
16167                         
16168                          };
16169                          break;
16170                     }
16171                          
16172                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16173                         this.parent = { el :  new  Roo.bootstrap.Body() };
16174                         Roo.debug && Roo.log("setting el to doc body");
16175                          
16176                     } else {
16177                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16178                     }
16179                     break;
16180                 case 'bootstrap':
16181                     this.parent = { el : true};
16182                     // fall through
16183                 default:
16184                     el = Roo.get(ename);
16185                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16186                         this.parent = { el : true};
16187                     }
16188                     
16189                     break;
16190             }
16191                 
16192             
16193             if (!el && !this.parent) {
16194                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16195                 return;
16196             }
16197         }
16198         
16199         Roo.debug && Roo.log("EL:");
16200         Roo.debug && Roo.log(el);
16201         Roo.debug && Roo.log("this.parent.el:");
16202         Roo.debug && Roo.log(this.parent.el);
16203         
16204
16205         // altertive root elements ??? - we need a better way to indicate these.
16206         var is_alt = Roo.XComponent.is_alt ||
16207                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16208                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16209                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16210         
16211         
16212         
16213         if (!this.parent && is_alt) {
16214             //el = Roo.get(document.body);
16215             this.parent = { el : true };
16216         }
16217             
16218             
16219         
16220         if (!this.parent) {
16221             
16222             Roo.debug && Roo.log("no parent - creating one");
16223             
16224             el = el ? Roo.get(el) : false;      
16225             
16226             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16227                 
16228                 this.parent =  {
16229                     el : new Roo.bootstrap.layout.Border({
16230                         el: el || document.body,
16231                     
16232                         center: {
16233                             titlebar: false,
16234                             autoScroll:false,
16235                             closeOnTab: true,
16236                             tabPosition: 'top',
16237                              //resizeTabs: true,
16238                             alwaysShowTabs: false,
16239                             hideTabs: true,
16240                             minTabWidth: 140,
16241                             overflow: 'visible'
16242                          }
16243                      })
16244                 };
16245             } else {
16246             
16247                 // it's a top level one..
16248                 this.parent =  {
16249                     el : new Roo.BorderLayout(el || document.body, {
16250                         center: {
16251                             titlebar: false,
16252                             autoScroll:false,
16253                             closeOnTab: true,
16254                             tabPosition: 'top',
16255                              //resizeTabs: true,
16256                             alwaysShowTabs: el && hp? false :  true,
16257                             hideTabs: el || !hp ? true :  false,
16258                             minTabWidth: 140
16259                          }
16260                     })
16261                 };
16262             }
16263         }
16264         
16265         if (!this.parent.el) {
16266                 // probably an old style ctor, which has been disabled.
16267                 return;
16268
16269         }
16270                 // The 'tree' method is  '_tree now' 
16271             
16272         tree.region = tree.region || this.region;
16273         var is_body = false;
16274         if (this.parent.el === true) {
16275             // bootstrap... - body..
16276             if (el) {
16277                 tree.el = el;
16278             }
16279             this.parent.el = Roo.factory(tree);
16280             is_body = true;
16281         }
16282         
16283         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16284         this.fireEvent('built', this);
16285         
16286         this.panel = this.el;
16287         this.layout = this.panel.layout;
16288         this.parentLayout = this.parent.layout  || false;  
16289          
16290     }
16291     
16292 });
16293
16294 Roo.apply(Roo.XComponent, {
16295     /**
16296      * @property  hideProgress
16297      * true to disable the building progress bar.. usefull on single page renders.
16298      * @type Boolean
16299      */
16300     hideProgress : false,
16301     /**
16302      * @property  buildCompleted
16303      * True when the builder has completed building the interface.
16304      * @type Boolean
16305      */
16306     buildCompleted : false,
16307      
16308     /**
16309      * @property  topModule
16310      * the upper most module - uses document.element as it's constructor.
16311      * @type Object
16312      */
16313      
16314     topModule  : false,
16315       
16316     /**
16317      * @property  modules
16318      * array of modules to be created by registration system.
16319      * @type {Array} of Roo.XComponent
16320      */
16321     
16322     modules : [],
16323     /**
16324      * @property  elmodules
16325      * array of modules to be created by which use #ID 
16326      * @type {Array} of Roo.XComponent
16327      */
16328      
16329     elmodules : [],
16330
16331      /**
16332      * @property  is_alt
16333      * Is an alternative Root - normally used by bootstrap or other systems,
16334      *    where the top element in the tree can wrap 'body' 
16335      * @type {boolean}  (default false)
16336      */
16337      
16338     is_alt : false,
16339     /**
16340      * @property  build_from_html
16341      * Build elements from html - used by bootstrap HTML stuff 
16342      *    - this is cleared after build is completed
16343      * @type {boolean}    (default false)
16344      */
16345      
16346     build_from_html : false,
16347     /**
16348      * Register components to be built later.
16349      *
16350      * This solves the following issues
16351      * - Building is not done on page load, but after an authentication process has occured.
16352      * - Interface elements are registered on page load
16353      * - Parent Interface elements may not be loaded before child, so this handles that..
16354      * 
16355      *
16356      * example:
16357      * 
16358      * MyApp.register({
16359           order : '000001',
16360           module : 'Pman.Tab.projectMgr',
16361           region : 'center',
16362           parent : 'Pman.layout',
16363           disabled : false,  // or use a function..
16364         })
16365      
16366      * * @param {Object} details about module
16367      */
16368     register : function(obj) {
16369                 
16370         Roo.XComponent.event.fireEvent('register', obj);
16371         switch(typeof(obj.disabled) ) {
16372                 
16373             case 'undefined':
16374                 break;
16375             
16376             case 'function':
16377                 if ( obj.disabled() ) {
16378                         return;
16379                 }
16380                 break;
16381             
16382             default:
16383                 if (obj.disabled) {
16384                         return;
16385                 }
16386                 break;
16387         }
16388                 
16389         this.modules.push(obj);
16390          
16391     },
16392     /**
16393      * convert a string to an object..
16394      * eg. 'AAA.BBB' -> finds AAA.BBB
16395
16396      */
16397     
16398     toObject : function(str)
16399     {
16400         if (!str || typeof(str) == 'object') {
16401             return str;
16402         }
16403         if (str.substring(0,1) == '#') {
16404             return str;
16405         }
16406
16407         var ar = str.split('.');
16408         var rt, o;
16409         rt = ar.shift();
16410             /** eval:var:o */
16411         try {
16412             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16413         } catch (e) {
16414             throw "Module not found : " + str;
16415         }
16416         
16417         if (o === false) {
16418             throw "Module not found : " + str;
16419         }
16420         Roo.each(ar, function(e) {
16421             if (typeof(o[e]) == 'undefined') {
16422                 throw "Module not found : " + str;
16423             }
16424             o = o[e];
16425         });
16426         
16427         return o;
16428         
16429     },
16430     
16431     
16432     /**
16433      * move modules into their correct place in the tree..
16434      * 
16435      */
16436     preBuild : function ()
16437     {
16438         var _t = this;
16439         Roo.each(this.modules , function (obj)
16440         {
16441             Roo.XComponent.event.fireEvent('beforebuild', obj);
16442             
16443             var opar = obj.parent;
16444             try { 
16445                 obj.parent = this.toObject(opar);
16446             } catch(e) {
16447                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16448                 return;
16449             }
16450             
16451             if (!obj.parent) {
16452                 Roo.debug && Roo.log("GOT top level module");
16453                 Roo.debug && Roo.log(obj);
16454                 obj.modules = new Roo.util.MixedCollection(false, 
16455                     function(o) { return o.order + '' }
16456                 );
16457                 this.topModule = obj;
16458                 return;
16459             }
16460                         // parent is a string (usually a dom element name..)
16461             if (typeof(obj.parent) == 'string') {
16462                 this.elmodules.push(obj);
16463                 return;
16464             }
16465             if (obj.parent.constructor != Roo.XComponent) {
16466                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16467             }
16468             if (!obj.parent.modules) {
16469                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16470                     function(o) { return o.order + '' }
16471                 );
16472             }
16473             if (obj.parent.disabled) {
16474                 obj.disabled = true;
16475             }
16476             obj.parent.modules.add(obj);
16477         }, this);
16478     },
16479     
16480      /**
16481      * make a list of modules to build.
16482      * @return {Array} list of modules. 
16483      */ 
16484     
16485     buildOrder : function()
16486     {
16487         var _this = this;
16488         var cmp = function(a,b) {   
16489             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16490         };
16491         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16492             throw "No top level modules to build";
16493         }
16494         
16495         // make a flat list in order of modules to build.
16496         var mods = this.topModule ? [ this.topModule ] : [];
16497                 
16498         
16499         // elmodules (is a list of DOM based modules )
16500         Roo.each(this.elmodules, function(e) {
16501             mods.push(e);
16502             if (!this.topModule &&
16503                 typeof(e.parent) == 'string' &&
16504                 e.parent.substring(0,1) == '#' &&
16505                 Roo.get(e.parent.substr(1))
16506                ) {
16507                 
16508                 _this.topModule = e;
16509             }
16510             
16511         });
16512
16513         
16514         // add modules to their parents..
16515         var addMod = function(m) {
16516             Roo.debug && Roo.log("build Order: add: " + m.name);
16517                 
16518             mods.push(m);
16519             if (m.modules && !m.disabled) {
16520                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16521                 m.modules.keySort('ASC',  cmp );
16522                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16523     
16524                 m.modules.each(addMod);
16525             } else {
16526                 Roo.debug && Roo.log("build Order: no child modules");
16527             }
16528             // not sure if this is used any more..
16529             if (m.finalize) {
16530                 m.finalize.name = m.name + " (clean up) ";
16531                 mods.push(m.finalize);
16532             }
16533             
16534         }
16535         if (this.topModule && this.topModule.modules) { 
16536             this.topModule.modules.keySort('ASC',  cmp );
16537             this.topModule.modules.each(addMod);
16538         } 
16539         return mods;
16540     },
16541     
16542      /**
16543      * Build the registered modules.
16544      * @param {Object} parent element.
16545      * @param {Function} optional method to call after module has been added.
16546      * 
16547      */ 
16548    
16549     build : function(opts) 
16550     {
16551         
16552         if (typeof(opts) != 'undefined') {
16553             Roo.apply(this,opts);
16554         }
16555         
16556         this.preBuild();
16557         var mods = this.buildOrder();
16558       
16559         //this.allmods = mods;
16560         //Roo.debug && Roo.log(mods);
16561         //return;
16562         if (!mods.length) { // should not happen
16563             throw "NO modules!!!";
16564         }
16565         
16566         
16567         var msg = "Building Interface...";
16568         // flash it up as modal - so we store the mask!?
16569         if (!this.hideProgress && Roo.MessageBox) {
16570             Roo.MessageBox.show({ title: 'loading' });
16571             Roo.MessageBox.show({
16572                title: "Please wait...",
16573                msg: msg,
16574                width:450,
16575                progress:true,
16576                closable:false,
16577                modal: false
16578               
16579             });
16580         }
16581         var total = mods.length;
16582         
16583         var _this = this;
16584         var progressRun = function() {
16585             if (!mods.length) {
16586                 Roo.debug && Roo.log('hide?');
16587                 if (!this.hideProgress && Roo.MessageBox) {
16588                     Roo.MessageBox.hide();
16589                 }
16590                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16591                 
16592                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16593                 
16594                 // THE END...
16595                 return false;   
16596             }
16597             
16598             var m = mods.shift();
16599             
16600             
16601             Roo.debug && Roo.log(m);
16602             // not sure if this is supported any more.. - modules that are are just function
16603             if (typeof(m) == 'function') { 
16604                 m.call(this);
16605                 return progressRun.defer(10, _this);
16606             } 
16607             
16608             
16609             msg = "Building Interface " + (total  - mods.length) + 
16610                     " of " + total + 
16611                     (m.name ? (' - ' + m.name) : '');
16612                         Roo.debug && Roo.log(msg);
16613             if (!_this.hideProgress &&  Roo.MessageBox) { 
16614                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16615             }
16616             
16617          
16618             // is the module disabled?
16619             var disabled = (typeof(m.disabled) == 'function') ?
16620                 m.disabled.call(m.module.disabled) : m.disabled;    
16621             
16622             
16623             if (disabled) {
16624                 return progressRun(); // we do not update the display!
16625             }
16626             
16627             // now build 
16628             
16629                         
16630                         
16631             m.render();
16632             // it's 10 on top level, and 1 on others??? why...
16633             return progressRun.defer(10, _this);
16634              
16635         }
16636         progressRun.defer(1, _this);
16637      
16638         
16639         
16640     },
16641     /**
16642      * Overlay a set of modified strings onto a component
16643      * This is dependant on our builder exporting the strings and 'named strings' elements.
16644      * 
16645      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
16646      * @param {Object} associative array of 'named' string and it's new value.
16647      * 
16648      */
16649         overlayStrings : function( component, strings )
16650     {
16651         if (typeof(component['_named_strings']) == 'undefined') {
16652             throw "ERROR: component does not have _named_strings";
16653         }
16654         for ( var k in strings ) {
16655             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
16656             if (md !== false) {
16657                 component['_strings'][md] = strings[k];
16658             } else {
16659                 Roo.log('could not find named string: ' + k + ' in');
16660                 Roo.log(component);
16661             }
16662             
16663         }
16664         
16665     },
16666     
16667         
16668         /**
16669          * Event Object.
16670          *
16671          *
16672          */
16673         event: false, 
16674     /**
16675          * wrapper for event.on - aliased later..  
16676          * Typically use to register a event handler for register:
16677          *
16678          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16679          *
16680          */
16681     on : false
16682    
16683     
16684     
16685 });
16686
16687 Roo.XComponent.event = new Roo.util.Observable({
16688                 events : { 
16689                         /**
16690                          * @event register
16691                          * Fires when an Component is registered,
16692                          * set the disable property on the Component to stop registration.
16693                          * @param {Roo.XComponent} c the component being registerd.
16694                          * 
16695                          */
16696                         'register' : true,
16697             /**
16698                          * @event beforebuild
16699                          * Fires before each Component is built
16700                          * can be used to apply permissions.
16701                          * @param {Roo.XComponent} c the component being registerd.
16702                          * 
16703                          */
16704                         'beforebuild' : true,
16705                         /**
16706                          * @event buildcomplete
16707                          * Fires on the top level element when all elements have been built
16708                          * @param {Roo.XComponent} the top level component.
16709                          */
16710                         'buildcomplete' : true
16711                         
16712                 }
16713 });
16714
16715 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16716  //
16717  /**
16718  * marked - a markdown parser
16719  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16720  * https://github.com/chjj/marked
16721  */
16722
16723
16724 /**
16725  *
16726  * Roo.Markdown - is a very crude wrapper around marked..
16727  *
16728  * usage:
16729  * 
16730  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16731  * 
16732  * Note: move the sample code to the bottom of this
16733  * file before uncommenting it.
16734  *
16735  */
16736
16737 Roo.Markdown = {};
16738 Roo.Markdown.toHtml = function(text) {
16739     
16740     var c = new Roo.Markdown.marked.setOptions({
16741             renderer: new Roo.Markdown.marked.Renderer(),
16742             gfm: true,
16743             tables: true,
16744             breaks: false,
16745             pedantic: false,
16746             sanitize: false,
16747             smartLists: true,
16748             smartypants: false
16749           });
16750     // A FEW HACKS!!?
16751     
16752     text = text.replace(/\\\n/g,' ');
16753     return Roo.Markdown.marked(text);
16754 };
16755 //
16756 // converter
16757 //
16758 // Wraps all "globals" so that the only thing
16759 // exposed is makeHtml().
16760 //
16761 (function() {
16762     
16763     /**
16764      * Block-Level Grammar
16765      */
16766     
16767     var block = {
16768       newline: /^\n+/,
16769       code: /^( {4}[^\n]+\n*)+/,
16770       fences: noop,
16771       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16772       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16773       nptable: noop,
16774       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16775       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16776       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16777       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16778       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16779       table: noop,
16780       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16781       text: /^[^\n]+/
16782     };
16783     
16784     block.bullet = /(?:[*+-]|\d+\.)/;
16785     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16786     block.item = replace(block.item, 'gm')
16787       (/bull/g, block.bullet)
16788       ();
16789     
16790     block.list = replace(block.list)
16791       (/bull/g, block.bullet)
16792       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16793       ('def', '\\n+(?=' + block.def.source + ')')
16794       ();
16795     
16796     block.blockquote = replace(block.blockquote)
16797       ('def', block.def)
16798       ();
16799     
16800     block._tag = '(?!(?:'
16801       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16802       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16803       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16804     
16805     block.html = replace(block.html)
16806       ('comment', /<!--[\s\S]*?-->/)
16807       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16808       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16809       (/tag/g, block._tag)
16810       ();
16811     
16812     block.paragraph = replace(block.paragraph)
16813       ('hr', block.hr)
16814       ('heading', block.heading)
16815       ('lheading', block.lheading)
16816       ('blockquote', block.blockquote)
16817       ('tag', '<' + block._tag)
16818       ('def', block.def)
16819       ();
16820     
16821     /**
16822      * Normal Block Grammar
16823      */
16824     
16825     block.normal = merge({}, block);
16826     
16827     /**
16828      * GFM Block Grammar
16829      */
16830     
16831     block.gfm = merge({}, block.normal, {
16832       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16833       paragraph: /^/,
16834       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16835     });
16836     
16837     block.gfm.paragraph = replace(block.paragraph)
16838       ('(?!', '(?!'
16839         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16840         + block.list.source.replace('\\1', '\\3') + '|')
16841       ();
16842     
16843     /**
16844      * GFM + Tables Block Grammar
16845      */
16846     
16847     block.tables = merge({}, block.gfm, {
16848       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16849       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16850     });
16851     
16852     /**
16853      * Block Lexer
16854      */
16855     
16856     function Lexer(options) {
16857       this.tokens = [];
16858       this.tokens.links = {};
16859       this.options = options || marked.defaults;
16860       this.rules = block.normal;
16861     
16862       if (this.options.gfm) {
16863         if (this.options.tables) {
16864           this.rules = block.tables;
16865         } else {
16866           this.rules = block.gfm;
16867         }
16868       }
16869     }
16870     
16871     /**
16872      * Expose Block Rules
16873      */
16874     
16875     Lexer.rules = block;
16876     
16877     /**
16878      * Static Lex Method
16879      */
16880     
16881     Lexer.lex = function(src, options) {
16882       var lexer = new Lexer(options);
16883       return lexer.lex(src);
16884     };
16885     
16886     /**
16887      * Preprocessing
16888      */
16889     
16890     Lexer.prototype.lex = function(src) {
16891       src = src
16892         .replace(/\r\n|\r/g, '\n')
16893         .replace(/\t/g, '    ')
16894         .replace(/\u00a0/g, ' ')
16895         .replace(/\u2424/g, '\n');
16896     
16897       return this.token(src, true);
16898     };
16899     
16900     /**
16901      * Lexing
16902      */
16903     
16904     Lexer.prototype.token = function(src, top, bq) {
16905       var src = src.replace(/^ +$/gm, '')
16906         , next
16907         , loose
16908         , cap
16909         , bull
16910         , b
16911         , item
16912         , space
16913         , i
16914         , l;
16915     
16916       while (src) {
16917         // newline
16918         if (cap = this.rules.newline.exec(src)) {
16919           src = src.substring(cap[0].length);
16920           if (cap[0].length > 1) {
16921             this.tokens.push({
16922               type: 'space'
16923             });
16924           }
16925         }
16926     
16927         // code
16928         if (cap = this.rules.code.exec(src)) {
16929           src = src.substring(cap[0].length);
16930           cap = cap[0].replace(/^ {4}/gm, '');
16931           this.tokens.push({
16932             type: 'code',
16933             text: !this.options.pedantic
16934               ? cap.replace(/\n+$/, '')
16935               : cap
16936           });
16937           continue;
16938         }
16939     
16940         // fences (gfm)
16941         if (cap = this.rules.fences.exec(src)) {
16942           src = src.substring(cap[0].length);
16943           this.tokens.push({
16944             type: 'code',
16945             lang: cap[2],
16946             text: cap[3] || ''
16947           });
16948           continue;
16949         }
16950     
16951         // heading
16952         if (cap = this.rules.heading.exec(src)) {
16953           src = src.substring(cap[0].length);
16954           this.tokens.push({
16955             type: 'heading',
16956             depth: cap[1].length,
16957             text: cap[2]
16958           });
16959           continue;
16960         }
16961     
16962         // table no leading pipe (gfm)
16963         if (top && (cap = this.rules.nptable.exec(src))) {
16964           src = src.substring(cap[0].length);
16965     
16966           item = {
16967             type: 'table',
16968             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
16969             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
16970             cells: cap[3].replace(/\n$/, '').split('\n')
16971           };
16972     
16973           for (i = 0; i < item.align.length; i++) {
16974             if (/^ *-+: *$/.test(item.align[i])) {
16975               item.align[i] = 'right';
16976             } else if (/^ *:-+: *$/.test(item.align[i])) {
16977               item.align[i] = 'center';
16978             } else if (/^ *:-+ *$/.test(item.align[i])) {
16979               item.align[i] = 'left';
16980             } else {
16981               item.align[i] = null;
16982             }
16983           }
16984     
16985           for (i = 0; i < item.cells.length; i++) {
16986             item.cells[i] = item.cells[i].split(/ *\| */);
16987           }
16988     
16989           this.tokens.push(item);
16990     
16991           continue;
16992         }
16993     
16994         // lheading
16995         if (cap = this.rules.lheading.exec(src)) {
16996           src = src.substring(cap[0].length);
16997           this.tokens.push({
16998             type: 'heading',
16999             depth: cap[2] === '=' ? 1 : 2,
17000             text: cap[1]
17001           });
17002           continue;
17003         }
17004     
17005         // hr
17006         if (cap = this.rules.hr.exec(src)) {
17007           src = src.substring(cap[0].length);
17008           this.tokens.push({
17009             type: 'hr'
17010           });
17011           continue;
17012         }
17013     
17014         // blockquote
17015         if (cap = this.rules.blockquote.exec(src)) {
17016           src = src.substring(cap[0].length);
17017     
17018           this.tokens.push({
17019             type: 'blockquote_start'
17020           });
17021     
17022           cap = cap[0].replace(/^ *> ?/gm, '');
17023     
17024           // Pass `top` to keep the current
17025           // "toplevel" state. This is exactly
17026           // how markdown.pl works.
17027           this.token(cap, top, true);
17028     
17029           this.tokens.push({
17030             type: 'blockquote_end'
17031           });
17032     
17033           continue;
17034         }
17035     
17036         // list
17037         if (cap = this.rules.list.exec(src)) {
17038           src = src.substring(cap[0].length);
17039           bull = cap[2];
17040     
17041           this.tokens.push({
17042             type: 'list_start',
17043             ordered: bull.length > 1
17044           });
17045     
17046           // Get each top-level item.
17047           cap = cap[0].match(this.rules.item);
17048     
17049           next = false;
17050           l = cap.length;
17051           i = 0;
17052     
17053           for (; i < l; i++) {
17054             item = cap[i];
17055     
17056             // Remove the list item's bullet
17057             // so it is seen as the next token.
17058             space = item.length;
17059             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17060     
17061             // Outdent whatever the
17062             // list item contains. Hacky.
17063             if (~item.indexOf('\n ')) {
17064               space -= item.length;
17065               item = !this.options.pedantic
17066                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17067                 : item.replace(/^ {1,4}/gm, '');
17068             }
17069     
17070             // Determine whether the next list item belongs here.
17071             // Backpedal if it does not belong in this list.
17072             if (this.options.smartLists && i !== l - 1) {
17073               b = block.bullet.exec(cap[i + 1])[0];
17074               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17075                 src = cap.slice(i + 1).join('\n') + src;
17076                 i = l - 1;
17077               }
17078             }
17079     
17080             // Determine whether item is loose or not.
17081             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17082             // for discount behavior.
17083             loose = next || /\n\n(?!\s*$)/.test(item);
17084             if (i !== l - 1) {
17085               next = item.charAt(item.length - 1) === '\n';
17086               if (!loose) { loose = next; }
17087             }
17088     
17089             this.tokens.push({
17090               type: loose
17091                 ? 'loose_item_start'
17092                 : 'list_item_start'
17093             });
17094     
17095             // Recurse.
17096             this.token(item, false, bq);
17097     
17098             this.tokens.push({
17099               type: 'list_item_end'
17100             });
17101           }
17102     
17103           this.tokens.push({
17104             type: 'list_end'
17105           });
17106     
17107           continue;
17108         }
17109     
17110         // html
17111         if (cap = this.rules.html.exec(src)) {
17112           src = src.substring(cap[0].length);
17113           this.tokens.push({
17114             type: this.options.sanitize
17115               ? 'paragraph'
17116               : 'html',
17117             pre: !this.options.sanitizer
17118               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17119             text: cap[0]
17120           });
17121           continue;
17122         }
17123     
17124         // def
17125         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17126           src = src.substring(cap[0].length);
17127           this.tokens.links[cap[1].toLowerCase()] = {
17128             href: cap[2],
17129             title: cap[3]
17130           };
17131           continue;
17132         }
17133     
17134         // table (gfm)
17135         if (top && (cap = this.rules.table.exec(src))) {
17136           src = src.substring(cap[0].length);
17137     
17138           item = {
17139             type: 'table',
17140             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17141             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17142             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17143           };
17144     
17145           for (i = 0; i < item.align.length; i++) {
17146             if (/^ *-+: *$/.test(item.align[i])) {
17147               item.align[i] = 'right';
17148             } else if (/^ *:-+: *$/.test(item.align[i])) {
17149               item.align[i] = 'center';
17150             } else if (/^ *:-+ *$/.test(item.align[i])) {
17151               item.align[i] = 'left';
17152             } else {
17153               item.align[i] = null;
17154             }
17155           }
17156     
17157           for (i = 0; i < item.cells.length; i++) {
17158             item.cells[i] = item.cells[i]
17159               .replace(/^ *\| *| *\| *$/g, '')
17160               .split(/ *\| */);
17161           }
17162     
17163           this.tokens.push(item);
17164     
17165           continue;
17166         }
17167     
17168         // top-level paragraph
17169         if (top && (cap = this.rules.paragraph.exec(src))) {
17170           src = src.substring(cap[0].length);
17171           this.tokens.push({
17172             type: 'paragraph',
17173             text: cap[1].charAt(cap[1].length - 1) === '\n'
17174               ? cap[1].slice(0, -1)
17175               : cap[1]
17176           });
17177           continue;
17178         }
17179     
17180         // text
17181         if (cap = this.rules.text.exec(src)) {
17182           // Top-level should never reach here.
17183           src = src.substring(cap[0].length);
17184           this.tokens.push({
17185             type: 'text',
17186             text: cap[0]
17187           });
17188           continue;
17189         }
17190     
17191         if (src) {
17192           throw new
17193             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17194         }
17195       }
17196     
17197       return this.tokens;
17198     };
17199     
17200     /**
17201      * Inline-Level Grammar
17202      */
17203     
17204     var inline = {
17205       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17206       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17207       url: noop,
17208       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17209       link: /^!?\[(inside)\]\(href\)/,
17210       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17211       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17212       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17213       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17214       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17215       br: /^ {2,}\n(?!\s*$)/,
17216       del: noop,
17217       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17218     };
17219     
17220     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17221     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17222     
17223     inline.link = replace(inline.link)
17224       ('inside', inline._inside)
17225       ('href', inline._href)
17226       ();
17227     
17228     inline.reflink = replace(inline.reflink)
17229       ('inside', inline._inside)
17230       ();
17231     
17232     /**
17233      * Normal Inline Grammar
17234      */
17235     
17236     inline.normal = merge({}, inline);
17237     
17238     /**
17239      * Pedantic Inline Grammar
17240      */
17241     
17242     inline.pedantic = merge({}, inline.normal, {
17243       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17244       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17245     });
17246     
17247     /**
17248      * GFM Inline Grammar
17249      */
17250     
17251     inline.gfm = merge({}, inline.normal, {
17252       escape: replace(inline.escape)('])', '~|])')(),
17253       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17254       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17255       text: replace(inline.text)
17256         (']|', '~]|')
17257         ('|', '|https?://|')
17258         ()
17259     });
17260     
17261     /**
17262      * GFM + Line Breaks Inline Grammar
17263      */
17264     
17265     inline.breaks = merge({}, inline.gfm, {
17266       br: replace(inline.br)('{2,}', '*')(),
17267       text: replace(inline.gfm.text)('{2,}', '*')()
17268     });
17269     
17270     /**
17271      * Inline Lexer & Compiler
17272      */
17273     
17274     function InlineLexer(links, options) {
17275       this.options = options || marked.defaults;
17276       this.links = links;
17277       this.rules = inline.normal;
17278       this.renderer = this.options.renderer || new Renderer;
17279       this.renderer.options = this.options;
17280     
17281       if (!this.links) {
17282         throw new
17283           Error('Tokens array requires a `links` property.');
17284       }
17285     
17286       if (this.options.gfm) {
17287         if (this.options.breaks) {
17288           this.rules = inline.breaks;
17289         } else {
17290           this.rules = inline.gfm;
17291         }
17292       } else if (this.options.pedantic) {
17293         this.rules = inline.pedantic;
17294       }
17295     }
17296     
17297     /**
17298      * Expose Inline Rules
17299      */
17300     
17301     InlineLexer.rules = inline;
17302     
17303     /**
17304      * Static Lexing/Compiling Method
17305      */
17306     
17307     InlineLexer.output = function(src, links, options) {
17308       var inline = new InlineLexer(links, options);
17309       return inline.output(src);
17310     };
17311     
17312     /**
17313      * Lexing/Compiling
17314      */
17315     
17316     InlineLexer.prototype.output = function(src) {
17317       var out = ''
17318         , link
17319         , text
17320         , href
17321         , cap;
17322     
17323       while (src) {
17324         // escape
17325         if (cap = this.rules.escape.exec(src)) {
17326           src = src.substring(cap[0].length);
17327           out += cap[1];
17328           continue;
17329         }
17330     
17331         // autolink
17332         if (cap = this.rules.autolink.exec(src)) {
17333           src = src.substring(cap[0].length);
17334           if (cap[2] === '@') {
17335             text = cap[1].charAt(6) === ':'
17336               ? this.mangle(cap[1].substring(7))
17337               : this.mangle(cap[1]);
17338             href = this.mangle('mailto:') + text;
17339           } else {
17340             text = escape(cap[1]);
17341             href = text;
17342           }
17343           out += this.renderer.link(href, null, text);
17344           continue;
17345         }
17346     
17347         // url (gfm)
17348         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17349           src = src.substring(cap[0].length);
17350           text = escape(cap[1]);
17351           href = text;
17352           out += this.renderer.link(href, null, text);
17353           continue;
17354         }
17355     
17356         // tag
17357         if (cap = this.rules.tag.exec(src)) {
17358           if (!this.inLink && /^<a /i.test(cap[0])) {
17359             this.inLink = true;
17360           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17361             this.inLink = false;
17362           }
17363           src = src.substring(cap[0].length);
17364           out += this.options.sanitize
17365             ? this.options.sanitizer
17366               ? this.options.sanitizer(cap[0])
17367               : escape(cap[0])
17368             : cap[0];
17369           continue;
17370         }
17371     
17372         // link
17373         if (cap = this.rules.link.exec(src)) {
17374           src = src.substring(cap[0].length);
17375           this.inLink = true;
17376           out += this.outputLink(cap, {
17377             href: cap[2],
17378             title: cap[3]
17379           });
17380           this.inLink = false;
17381           continue;
17382         }
17383     
17384         // reflink, nolink
17385         if ((cap = this.rules.reflink.exec(src))
17386             || (cap = this.rules.nolink.exec(src))) {
17387           src = src.substring(cap[0].length);
17388           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17389           link = this.links[link.toLowerCase()];
17390           if (!link || !link.href) {
17391             out += cap[0].charAt(0);
17392             src = cap[0].substring(1) + src;
17393             continue;
17394           }
17395           this.inLink = true;
17396           out += this.outputLink(cap, link);
17397           this.inLink = false;
17398           continue;
17399         }
17400     
17401         // strong
17402         if (cap = this.rules.strong.exec(src)) {
17403           src = src.substring(cap[0].length);
17404           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17405           continue;
17406         }
17407     
17408         // em
17409         if (cap = this.rules.em.exec(src)) {
17410           src = src.substring(cap[0].length);
17411           out += this.renderer.em(this.output(cap[2] || cap[1]));
17412           continue;
17413         }
17414     
17415         // code
17416         if (cap = this.rules.code.exec(src)) {
17417           src = src.substring(cap[0].length);
17418           out += this.renderer.codespan(escape(cap[2], true));
17419           continue;
17420         }
17421     
17422         // br
17423         if (cap = this.rules.br.exec(src)) {
17424           src = src.substring(cap[0].length);
17425           out += this.renderer.br();
17426           continue;
17427         }
17428     
17429         // del (gfm)
17430         if (cap = this.rules.del.exec(src)) {
17431           src = src.substring(cap[0].length);
17432           out += this.renderer.del(this.output(cap[1]));
17433           continue;
17434         }
17435     
17436         // text
17437         if (cap = this.rules.text.exec(src)) {
17438           src = src.substring(cap[0].length);
17439           out += this.renderer.text(escape(this.smartypants(cap[0])));
17440           continue;
17441         }
17442     
17443         if (src) {
17444           throw new
17445             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17446         }
17447       }
17448     
17449       return out;
17450     };
17451     
17452     /**
17453      * Compile Link
17454      */
17455     
17456     InlineLexer.prototype.outputLink = function(cap, link) {
17457       var href = escape(link.href)
17458         , title = link.title ? escape(link.title) : null;
17459     
17460       return cap[0].charAt(0) !== '!'
17461         ? this.renderer.link(href, title, this.output(cap[1]))
17462         : this.renderer.image(href, title, escape(cap[1]));
17463     };
17464     
17465     /**
17466      * Smartypants Transformations
17467      */
17468     
17469     InlineLexer.prototype.smartypants = function(text) {
17470       if (!this.options.smartypants)  { return text; }
17471       return text
17472         // em-dashes
17473         .replace(/---/g, '\u2014')
17474         // en-dashes
17475         .replace(/--/g, '\u2013')
17476         // opening singles
17477         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17478         // closing singles & apostrophes
17479         .replace(/'/g, '\u2019')
17480         // opening doubles
17481         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17482         // closing doubles
17483         .replace(/"/g, '\u201d')
17484         // ellipses
17485         .replace(/\.{3}/g, '\u2026');
17486     };
17487     
17488     /**
17489      * Mangle Links
17490      */
17491     
17492     InlineLexer.prototype.mangle = function(text) {
17493       if (!this.options.mangle) { return text; }
17494       var out = ''
17495         , l = text.length
17496         , i = 0
17497         , ch;
17498     
17499       for (; i < l; i++) {
17500         ch = text.charCodeAt(i);
17501         if (Math.random() > 0.5) {
17502           ch = 'x' + ch.toString(16);
17503         }
17504         out += '&#' + ch + ';';
17505       }
17506     
17507       return out;
17508     };
17509     
17510     /**
17511      * Renderer
17512      */
17513     
17514     function Renderer(options) {
17515       this.options = options || {};
17516     }
17517     
17518     Renderer.prototype.code = function(code, lang, escaped) {
17519       if (this.options.highlight) {
17520         var out = this.options.highlight(code, lang);
17521         if (out != null && out !== code) {
17522           escaped = true;
17523           code = out;
17524         }
17525       } else {
17526             // hack!!! - it's already escapeD?
17527             escaped = true;
17528       }
17529     
17530       if (!lang) {
17531         return '<pre><code>'
17532           + (escaped ? code : escape(code, true))
17533           + '\n</code></pre>';
17534       }
17535     
17536       return '<pre><code class="'
17537         + this.options.langPrefix
17538         + escape(lang, true)
17539         + '">'
17540         + (escaped ? code : escape(code, true))
17541         + '\n</code></pre>\n';
17542     };
17543     
17544     Renderer.prototype.blockquote = function(quote) {
17545       return '<blockquote>\n' + quote + '</blockquote>\n';
17546     };
17547     
17548     Renderer.prototype.html = function(html) {
17549       return html;
17550     };
17551     
17552     Renderer.prototype.heading = function(text, level, raw) {
17553       return '<h'
17554         + level
17555         + ' id="'
17556         + this.options.headerPrefix
17557         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17558         + '">'
17559         + text
17560         + '</h'
17561         + level
17562         + '>\n';
17563     };
17564     
17565     Renderer.prototype.hr = function() {
17566       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17567     };
17568     
17569     Renderer.prototype.list = function(body, ordered) {
17570       var type = ordered ? 'ol' : 'ul';
17571       return '<' + type + '>\n' + body + '</' + type + '>\n';
17572     };
17573     
17574     Renderer.prototype.listitem = function(text) {
17575       return '<li>' + text + '</li>\n';
17576     };
17577     
17578     Renderer.prototype.paragraph = function(text) {
17579       return '<p>' + text + '</p>\n';
17580     };
17581     
17582     Renderer.prototype.table = function(header, body) {
17583       return '<table class="table table-striped">\n'
17584         + '<thead>\n'
17585         + header
17586         + '</thead>\n'
17587         + '<tbody>\n'
17588         + body
17589         + '</tbody>\n'
17590         + '</table>\n';
17591     };
17592     
17593     Renderer.prototype.tablerow = function(content) {
17594       return '<tr>\n' + content + '</tr>\n';
17595     };
17596     
17597     Renderer.prototype.tablecell = function(content, flags) {
17598       var type = flags.header ? 'th' : 'td';
17599       var tag = flags.align
17600         ? '<' + type + ' style="text-align:' + flags.align + '">'
17601         : '<' + type + '>';
17602       return tag + content + '</' + type + '>\n';
17603     };
17604     
17605     // span level renderer
17606     Renderer.prototype.strong = function(text) {
17607       return '<strong>' + text + '</strong>';
17608     };
17609     
17610     Renderer.prototype.em = function(text) {
17611       return '<em>' + text + '</em>';
17612     };
17613     
17614     Renderer.prototype.codespan = function(text) {
17615       return '<code>' + text + '</code>';
17616     };
17617     
17618     Renderer.prototype.br = function() {
17619       return this.options.xhtml ? '<br/>' : '<br>';
17620     };
17621     
17622     Renderer.prototype.del = function(text) {
17623       return '<del>' + text + '</del>';
17624     };
17625     
17626     Renderer.prototype.link = function(href, title, text) {
17627       if (this.options.sanitize) {
17628         try {
17629           var prot = decodeURIComponent(unescape(href))
17630             .replace(/[^\w:]/g, '')
17631             .toLowerCase();
17632         } catch (e) {
17633           return '';
17634         }
17635         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17636           return '';
17637         }
17638       }
17639       var out = '<a href="' + href + '"';
17640       if (title) {
17641         out += ' title="' + title + '"';
17642       }
17643       out += '>' + text + '</a>';
17644       return out;
17645     };
17646     
17647     Renderer.prototype.image = function(href, title, text) {
17648       var out = '<img src="' + href + '" alt="' + text + '"';
17649       if (title) {
17650         out += ' title="' + title + '"';
17651       }
17652       out += this.options.xhtml ? '/>' : '>';
17653       return out;
17654     };
17655     
17656     Renderer.prototype.text = function(text) {
17657       return text;
17658     };
17659     
17660     /**
17661      * Parsing & Compiling
17662      */
17663     
17664     function Parser(options) {
17665       this.tokens = [];
17666       this.token = null;
17667       this.options = options || marked.defaults;
17668       this.options.renderer = this.options.renderer || new Renderer;
17669       this.renderer = this.options.renderer;
17670       this.renderer.options = this.options;
17671     }
17672     
17673     /**
17674      * Static Parse Method
17675      */
17676     
17677     Parser.parse = function(src, options, renderer) {
17678       var parser = new Parser(options, renderer);
17679       return parser.parse(src);
17680     };
17681     
17682     /**
17683      * Parse Loop
17684      */
17685     
17686     Parser.prototype.parse = function(src) {
17687       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17688       this.tokens = src.reverse();
17689     
17690       var out = '';
17691       while (this.next()) {
17692         out += this.tok();
17693       }
17694     
17695       return out;
17696     };
17697     
17698     /**
17699      * Next Token
17700      */
17701     
17702     Parser.prototype.next = function() {
17703       return this.token = this.tokens.pop();
17704     };
17705     
17706     /**
17707      * Preview Next Token
17708      */
17709     
17710     Parser.prototype.peek = function() {
17711       return this.tokens[this.tokens.length - 1] || 0;
17712     };
17713     
17714     /**
17715      * Parse Text Tokens
17716      */
17717     
17718     Parser.prototype.parseText = function() {
17719       var body = this.token.text;
17720     
17721       while (this.peek().type === 'text') {
17722         body += '\n' + this.next().text;
17723       }
17724     
17725       return this.inline.output(body);
17726     };
17727     
17728     /**
17729      * Parse Current Token
17730      */
17731     
17732     Parser.prototype.tok = function() {
17733       switch (this.token.type) {
17734         case 'space': {
17735           return '';
17736         }
17737         case 'hr': {
17738           return this.renderer.hr();
17739         }
17740         case 'heading': {
17741           return this.renderer.heading(
17742             this.inline.output(this.token.text),
17743             this.token.depth,
17744             this.token.text);
17745         }
17746         case 'code': {
17747           return this.renderer.code(this.token.text,
17748             this.token.lang,
17749             this.token.escaped);
17750         }
17751         case 'table': {
17752           var header = ''
17753             , body = ''
17754             , i
17755             , row
17756             , cell
17757             , flags
17758             , j;
17759     
17760           // header
17761           cell = '';
17762           for (i = 0; i < this.token.header.length; i++) {
17763             flags = { header: true, align: this.token.align[i] };
17764             cell += this.renderer.tablecell(
17765               this.inline.output(this.token.header[i]),
17766               { header: true, align: this.token.align[i] }
17767             );
17768           }
17769           header += this.renderer.tablerow(cell);
17770     
17771           for (i = 0; i < this.token.cells.length; i++) {
17772             row = this.token.cells[i];
17773     
17774             cell = '';
17775             for (j = 0; j < row.length; j++) {
17776               cell += this.renderer.tablecell(
17777                 this.inline.output(row[j]),
17778                 { header: false, align: this.token.align[j] }
17779               );
17780             }
17781     
17782             body += this.renderer.tablerow(cell);
17783           }
17784           return this.renderer.table(header, body);
17785         }
17786         case 'blockquote_start': {
17787           var body = '';
17788     
17789           while (this.next().type !== 'blockquote_end') {
17790             body += this.tok();
17791           }
17792     
17793           return this.renderer.blockquote(body);
17794         }
17795         case 'list_start': {
17796           var body = ''
17797             , ordered = this.token.ordered;
17798     
17799           while (this.next().type !== 'list_end') {
17800             body += this.tok();
17801           }
17802     
17803           return this.renderer.list(body, ordered);
17804         }
17805         case 'list_item_start': {
17806           var body = '';
17807     
17808           while (this.next().type !== 'list_item_end') {
17809             body += this.token.type === 'text'
17810               ? this.parseText()
17811               : this.tok();
17812           }
17813     
17814           return this.renderer.listitem(body);
17815         }
17816         case 'loose_item_start': {
17817           var body = '';
17818     
17819           while (this.next().type !== 'list_item_end') {
17820             body += this.tok();
17821           }
17822     
17823           return this.renderer.listitem(body);
17824         }
17825         case 'html': {
17826           var html = !this.token.pre && !this.options.pedantic
17827             ? this.inline.output(this.token.text)
17828             : this.token.text;
17829           return this.renderer.html(html);
17830         }
17831         case 'paragraph': {
17832           return this.renderer.paragraph(this.inline.output(this.token.text));
17833         }
17834         case 'text': {
17835           return this.renderer.paragraph(this.parseText());
17836         }
17837       }
17838     };
17839     
17840     /**
17841      * Helpers
17842      */
17843     
17844     function escape(html, encode) {
17845       return html
17846         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17847         .replace(/</g, '&lt;')
17848         .replace(/>/g, '&gt;')
17849         .replace(/"/g, '&quot;')
17850         .replace(/'/g, '&#39;');
17851     }
17852     
17853     function unescape(html) {
17854         // explicitly match decimal, hex, and named HTML entities 
17855       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17856         n = n.toLowerCase();
17857         if (n === 'colon') { return ':'; }
17858         if (n.charAt(0) === '#') {
17859           return n.charAt(1) === 'x'
17860             ? String.fromCharCode(parseInt(n.substring(2), 16))
17861             : String.fromCharCode(+n.substring(1));
17862         }
17863         return '';
17864       });
17865     }
17866     
17867     function replace(regex, opt) {
17868       regex = regex.source;
17869       opt = opt || '';
17870       return function self(name, val) {
17871         if (!name) { return new RegExp(regex, opt); }
17872         val = val.source || val;
17873         val = val.replace(/(^|[^\[])\^/g, '$1');
17874         regex = regex.replace(name, val);
17875         return self;
17876       };
17877     }
17878     
17879     function noop() {}
17880     noop.exec = noop;
17881     
17882     function merge(obj) {
17883       var i = 1
17884         , target
17885         , key;
17886     
17887       for (; i < arguments.length; i++) {
17888         target = arguments[i];
17889         for (key in target) {
17890           if (Object.prototype.hasOwnProperty.call(target, key)) {
17891             obj[key] = target[key];
17892           }
17893         }
17894       }
17895     
17896       return obj;
17897     }
17898     
17899     
17900     /**
17901      * Marked
17902      */
17903     
17904     function marked(src, opt, callback) {
17905       if (callback || typeof opt === 'function') {
17906         if (!callback) {
17907           callback = opt;
17908           opt = null;
17909         }
17910     
17911         opt = merge({}, marked.defaults, opt || {});
17912     
17913         var highlight = opt.highlight
17914           , tokens
17915           , pending
17916           , i = 0;
17917     
17918         try {
17919           tokens = Lexer.lex(src, opt)
17920         } catch (e) {
17921           return callback(e);
17922         }
17923     
17924         pending = tokens.length;
17925     
17926         var done = function(err) {
17927           if (err) {
17928             opt.highlight = highlight;
17929             return callback(err);
17930           }
17931     
17932           var out;
17933     
17934           try {
17935             out = Parser.parse(tokens, opt);
17936           } catch (e) {
17937             err = e;
17938           }
17939     
17940           opt.highlight = highlight;
17941     
17942           return err
17943             ? callback(err)
17944             : callback(null, out);
17945         };
17946     
17947         if (!highlight || highlight.length < 3) {
17948           return done();
17949         }
17950     
17951         delete opt.highlight;
17952     
17953         if (!pending) { return done(); }
17954     
17955         for (; i < tokens.length; i++) {
17956           (function(token) {
17957             if (token.type !== 'code') {
17958               return --pending || done();
17959             }
17960             return highlight(token.text, token.lang, function(err, code) {
17961               if (err) { return done(err); }
17962               if (code == null || code === token.text) {
17963                 return --pending || done();
17964               }
17965               token.text = code;
17966               token.escaped = true;
17967               --pending || done();
17968             });
17969           })(tokens[i]);
17970         }
17971     
17972         return;
17973       }
17974       try {
17975         if (opt) { opt = merge({}, marked.defaults, opt); }
17976         return Parser.parse(Lexer.lex(src, opt), opt);
17977       } catch (e) {
17978         e.message += '\nPlease report this to https://github.com/chjj/marked.';
17979         if ((opt || marked.defaults).silent) {
17980           return '<p>An error occured:</p><pre>'
17981             + escape(e.message + '', true)
17982             + '</pre>';
17983         }
17984         throw e;
17985       }
17986     }
17987     
17988     /**
17989      * Options
17990      */
17991     
17992     marked.options =
17993     marked.setOptions = function(opt) {
17994       merge(marked.defaults, opt);
17995       return marked;
17996     };
17997     
17998     marked.defaults = {
17999       gfm: true,
18000       tables: true,
18001       breaks: false,
18002       pedantic: false,
18003       sanitize: false,
18004       sanitizer: null,
18005       mangle: true,
18006       smartLists: false,
18007       silent: false,
18008       highlight: null,
18009       langPrefix: 'lang-',
18010       smartypants: false,
18011       headerPrefix: '',
18012       renderer: new Renderer,
18013       xhtml: false
18014     };
18015     
18016     /**
18017      * Expose
18018      */
18019     
18020     marked.Parser = Parser;
18021     marked.parser = Parser.parse;
18022     
18023     marked.Renderer = Renderer;
18024     
18025     marked.Lexer = Lexer;
18026     marked.lexer = Lexer.lex;
18027     
18028     marked.InlineLexer = InlineLexer;
18029     marked.inlineLexer = InlineLexer.output;
18030     
18031     marked.parse = marked;
18032     
18033     Roo.Markdown.marked = marked;
18034
18035 })();/*
18036  * Based on:
18037  * Ext JS Library 1.1.1
18038  * Copyright(c) 2006-2007, Ext JS, LLC.
18039  *
18040  * Originally Released Under LGPL - original licence link has changed is not relivant.
18041  *
18042  * Fork - LGPL
18043  * <script type="text/javascript">
18044  */
18045
18046
18047
18048 /*
18049  * These classes are derivatives of the similarly named classes in the YUI Library.
18050  * The original license:
18051  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18052  * Code licensed under the BSD License:
18053  * http://developer.yahoo.net/yui/license.txt
18054  */
18055
18056 (function() {
18057
18058 var Event=Roo.EventManager;
18059 var Dom=Roo.lib.Dom;
18060
18061 /**
18062  * @class Roo.dd.DragDrop
18063  * @extends Roo.util.Observable
18064  * Defines the interface and base operation of items that that can be
18065  * dragged or can be drop targets.  It was designed to be extended, overriding
18066  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18067  * Up to three html elements can be associated with a DragDrop instance:
18068  * <ul>
18069  * <li>linked element: the element that is passed into the constructor.
18070  * This is the element which defines the boundaries for interaction with
18071  * other DragDrop objects.</li>
18072  * <li>handle element(s): The drag operation only occurs if the element that
18073  * was clicked matches a handle element.  By default this is the linked
18074  * element, but there are times that you will want only a portion of the
18075  * linked element to initiate the drag operation, and the setHandleElId()
18076  * method provides a way to define this.</li>
18077  * <li>drag element: this represents the element that would be moved along
18078  * with the cursor during a drag operation.  By default, this is the linked
18079  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18080  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18081  * </li>
18082  * </ul>
18083  * This class should not be instantiated until the onload event to ensure that
18084  * the associated elements are available.
18085  * The following would define a DragDrop obj that would interact with any
18086  * other DragDrop obj in the "group1" group:
18087  * <pre>
18088  *  dd = new Roo.dd.DragDrop("div1", "group1");
18089  * </pre>
18090  * Since none of the event handlers have been implemented, nothing would
18091  * actually happen if you were to run the code above.  Normally you would
18092  * override this class or one of the default implementations, but you can
18093  * also override the methods you want on an instance of the class...
18094  * <pre>
18095  *  dd.onDragDrop = function(e, id) {
18096  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18097  *  }
18098  * </pre>
18099  * @constructor
18100  * @param {String} id of the element that is linked to this instance
18101  * @param {String} sGroup the group of related DragDrop objects
18102  * @param {object} config an object containing configurable attributes
18103  *                Valid properties for DragDrop:
18104  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18105  */
18106 Roo.dd.DragDrop = function(id, sGroup, config) {
18107     if (id) {
18108         this.init(id, sGroup, config);
18109     }
18110     
18111 };
18112
18113 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18114
18115     /**
18116      * The id of the element associated with this object.  This is what we
18117      * refer to as the "linked element" because the size and position of
18118      * this element is used to determine when the drag and drop objects have
18119      * interacted.
18120      * @property id
18121      * @type String
18122      */
18123     id: null,
18124
18125     /**
18126      * Configuration attributes passed into the constructor
18127      * @property config
18128      * @type object
18129      */
18130     config: null,
18131
18132     /**
18133      * The id of the element that will be dragged.  By default this is same
18134      * as the linked element , but could be changed to another element. Ex:
18135      * Roo.dd.DDProxy
18136      * @property dragElId
18137      * @type String
18138      * @private
18139      */
18140     dragElId: null,
18141
18142     /**
18143      * the id of the element that initiates the drag operation.  By default
18144      * this is the linked element, but could be changed to be a child of this
18145      * element.  This lets us do things like only starting the drag when the
18146      * header element within the linked html element is clicked.
18147      * @property handleElId
18148      * @type String
18149      * @private
18150      */
18151     handleElId: null,
18152
18153     /**
18154      * An associative array of HTML tags that will be ignored if clicked.
18155      * @property invalidHandleTypes
18156      * @type {string: string}
18157      */
18158     invalidHandleTypes: null,
18159
18160     /**
18161      * An associative array of ids for elements that will be ignored if clicked
18162      * @property invalidHandleIds
18163      * @type {string: string}
18164      */
18165     invalidHandleIds: null,
18166
18167     /**
18168      * An indexted array of css class names for elements that will be ignored
18169      * if clicked.
18170      * @property invalidHandleClasses
18171      * @type string[]
18172      */
18173     invalidHandleClasses: null,
18174
18175     /**
18176      * The linked element's absolute X position at the time the drag was
18177      * started
18178      * @property startPageX
18179      * @type int
18180      * @private
18181      */
18182     startPageX: 0,
18183
18184     /**
18185      * The linked element's absolute X position at the time the drag was
18186      * started
18187      * @property startPageY
18188      * @type int
18189      * @private
18190      */
18191     startPageY: 0,
18192
18193     /**
18194      * The group defines a logical collection of DragDrop objects that are
18195      * related.  Instances only get events when interacting with other
18196      * DragDrop object in the same group.  This lets us define multiple
18197      * groups using a single DragDrop subclass if we want.
18198      * @property groups
18199      * @type {string: string}
18200      */
18201     groups: null,
18202
18203     /**
18204      * Individual drag/drop instances can be locked.  This will prevent
18205      * onmousedown start drag.
18206      * @property locked
18207      * @type boolean
18208      * @private
18209      */
18210     locked: false,
18211
18212     /**
18213      * Lock this instance
18214      * @method lock
18215      */
18216     lock: function() { this.locked = true; },
18217
18218     /**
18219      * Unlock this instace
18220      * @method unlock
18221      */
18222     unlock: function() { this.locked = false; },
18223
18224     /**
18225      * By default, all insances can be a drop target.  This can be disabled by
18226      * setting isTarget to false.
18227      * @method isTarget
18228      * @type boolean
18229      */
18230     isTarget: true,
18231
18232     /**
18233      * The padding configured for this drag and drop object for calculating
18234      * the drop zone intersection with this object.
18235      * @method padding
18236      * @type int[]
18237      */
18238     padding: null,
18239
18240     /**
18241      * Cached reference to the linked element
18242      * @property _domRef
18243      * @private
18244      */
18245     _domRef: null,
18246
18247     /**
18248      * Internal typeof flag
18249      * @property __ygDragDrop
18250      * @private
18251      */
18252     __ygDragDrop: true,
18253
18254     /**
18255      * Set to true when horizontal contraints are applied
18256      * @property constrainX
18257      * @type boolean
18258      * @private
18259      */
18260     constrainX: false,
18261
18262     /**
18263      * Set to true when vertical contraints are applied
18264      * @property constrainY
18265      * @type boolean
18266      * @private
18267      */
18268     constrainY: false,
18269
18270     /**
18271      * The left constraint
18272      * @property minX
18273      * @type int
18274      * @private
18275      */
18276     minX: 0,
18277
18278     /**
18279      * The right constraint
18280      * @property maxX
18281      * @type int
18282      * @private
18283      */
18284     maxX: 0,
18285
18286     /**
18287      * The up constraint
18288      * @property minY
18289      * @type int
18290      * @type int
18291      * @private
18292      */
18293     minY: 0,
18294
18295     /**
18296      * The down constraint
18297      * @property maxY
18298      * @type int
18299      * @private
18300      */
18301     maxY: 0,
18302
18303     /**
18304      * Maintain offsets when we resetconstraints.  Set to true when you want
18305      * the position of the element relative to its parent to stay the same
18306      * when the page changes
18307      *
18308      * @property maintainOffset
18309      * @type boolean
18310      */
18311     maintainOffset: false,
18312
18313     /**
18314      * Array of pixel locations the element will snap to if we specified a
18315      * horizontal graduation/interval.  This array is generated automatically
18316      * when you define a tick interval.
18317      * @property xTicks
18318      * @type int[]
18319      */
18320     xTicks: null,
18321
18322     /**
18323      * Array of pixel locations the element will snap to if we specified a
18324      * vertical graduation/interval.  This array is generated automatically
18325      * when you define a tick interval.
18326      * @property yTicks
18327      * @type int[]
18328      */
18329     yTicks: null,
18330
18331     /**
18332      * By default the drag and drop instance will only respond to the primary
18333      * button click (left button for a right-handed mouse).  Set to true to
18334      * allow drag and drop to start with any mouse click that is propogated
18335      * by the browser
18336      * @property primaryButtonOnly
18337      * @type boolean
18338      */
18339     primaryButtonOnly: true,
18340
18341     /**
18342      * The availabe property is false until the linked dom element is accessible.
18343      * @property available
18344      * @type boolean
18345      */
18346     available: false,
18347
18348     /**
18349      * By default, drags can only be initiated if the mousedown occurs in the
18350      * region the linked element is.  This is done in part to work around a
18351      * bug in some browsers that mis-report the mousedown if the previous
18352      * mouseup happened outside of the window.  This property is set to true
18353      * if outer handles are defined.
18354      *
18355      * @property hasOuterHandles
18356      * @type boolean
18357      * @default false
18358      */
18359     hasOuterHandles: false,
18360
18361     /**
18362      * Code that executes immediately before the startDrag event
18363      * @method b4StartDrag
18364      * @private
18365      */
18366     b4StartDrag: function(x, y) { },
18367
18368     /**
18369      * Abstract method called after a drag/drop object is clicked
18370      * and the drag or mousedown time thresholds have beeen met.
18371      * @method startDrag
18372      * @param {int} X click location
18373      * @param {int} Y click location
18374      */
18375     startDrag: function(x, y) { /* override this */ },
18376
18377     /**
18378      * Code that executes immediately before the onDrag event
18379      * @method b4Drag
18380      * @private
18381      */
18382     b4Drag: function(e) { },
18383
18384     /**
18385      * Abstract method called during the onMouseMove event while dragging an
18386      * object.
18387      * @method onDrag
18388      * @param {Event} e the mousemove event
18389      */
18390     onDrag: function(e) { /* override this */ },
18391
18392     /**
18393      * Abstract method called when this element fist begins hovering over
18394      * another DragDrop obj
18395      * @method onDragEnter
18396      * @param {Event} e the mousemove event
18397      * @param {String|DragDrop[]} id In POINT mode, the element
18398      * id this is hovering over.  In INTERSECT mode, an array of one or more
18399      * dragdrop items being hovered over.
18400      */
18401     onDragEnter: function(e, id) { /* override this */ },
18402
18403     /**
18404      * Code that executes immediately before the onDragOver event
18405      * @method b4DragOver
18406      * @private
18407      */
18408     b4DragOver: function(e) { },
18409
18410     /**
18411      * Abstract method called when this element is hovering over another
18412      * DragDrop obj
18413      * @method onDragOver
18414      * @param {Event} e the mousemove event
18415      * @param {String|DragDrop[]} id In POINT mode, the element
18416      * id this is hovering over.  In INTERSECT mode, an array of dd items
18417      * being hovered over.
18418      */
18419     onDragOver: function(e, id) { /* override this */ },
18420
18421     /**
18422      * Code that executes immediately before the onDragOut event
18423      * @method b4DragOut
18424      * @private
18425      */
18426     b4DragOut: function(e) { },
18427
18428     /**
18429      * Abstract method called when we are no longer hovering over an element
18430      * @method onDragOut
18431      * @param {Event} e the mousemove event
18432      * @param {String|DragDrop[]} id In POINT mode, the element
18433      * id this was hovering over.  In INTERSECT mode, an array of dd items
18434      * that the mouse is no longer over.
18435      */
18436     onDragOut: function(e, id) { /* override this */ },
18437
18438     /**
18439      * Code that executes immediately before the onDragDrop event
18440      * @method b4DragDrop
18441      * @private
18442      */
18443     b4DragDrop: function(e) { },
18444
18445     /**
18446      * Abstract method called when this item is dropped on another DragDrop
18447      * obj
18448      * @method onDragDrop
18449      * @param {Event} e the mouseup event
18450      * @param {String|DragDrop[]} id In POINT mode, the element
18451      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18452      * was dropped on.
18453      */
18454     onDragDrop: function(e, id) { /* override this */ },
18455
18456     /**
18457      * Abstract method called when this item is dropped on an area with no
18458      * drop target
18459      * @method onInvalidDrop
18460      * @param {Event} e the mouseup event
18461      */
18462     onInvalidDrop: function(e) { /* override this */ },
18463
18464     /**
18465      * Code that executes immediately before the endDrag event
18466      * @method b4EndDrag
18467      * @private
18468      */
18469     b4EndDrag: function(e) { },
18470
18471     /**
18472      * Fired when we are done dragging the object
18473      * @method endDrag
18474      * @param {Event} e the mouseup event
18475      */
18476     endDrag: function(e) { /* override this */ },
18477
18478     /**
18479      * Code executed immediately before the onMouseDown event
18480      * @method b4MouseDown
18481      * @param {Event} e the mousedown event
18482      * @private
18483      */
18484     b4MouseDown: function(e) {  },
18485
18486     /**
18487      * Event handler that fires when a drag/drop obj gets a mousedown
18488      * @method onMouseDown
18489      * @param {Event} e the mousedown event
18490      */
18491     onMouseDown: function(e) { /* override this */ },
18492
18493     /**
18494      * Event handler that fires when a drag/drop obj gets a mouseup
18495      * @method onMouseUp
18496      * @param {Event} e the mouseup event
18497      */
18498     onMouseUp: function(e) { /* override this */ },
18499
18500     /**
18501      * Override the onAvailable method to do what is needed after the initial
18502      * position was determined.
18503      * @method onAvailable
18504      */
18505     onAvailable: function () {
18506     },
18507
18508     /*
18509      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18510      * @type Object
18511      */
18512     defaultPadding : {left:0, right:0, top:0, bottom:0},
18513
18514     /*
18515      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18516  *
18517  * Usage:
18518  <pre><code>
18519  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18520                 { dragElId: "existingProxyDiv" });
18521  dd.startDrag = function(){
18522      this.constrainTo("parent-id");
18523  };
18524  </code></pre>
18525  * Or you can initalize it using the {@link Roo.Element} object:
18526  <pre><code>
18527  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18528      startDrag : function(){
18529          this.constrainTo("parent-id");
18530      }
18531  });
18532  </code></pre>
18533      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18534      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18535      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18536      * an object containing the sides to pad. For example: {right:10, bottom:10}
18537      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18538      */
18539     constrainTo : function(constrainTo, pad, inContent){
18540         if(typeof pad == "number"){
18541             pad = {left: pad, right:pad, top:pad, bottom:pad};
18542         }
18543         pad = pad || this.defaultPadding;
18544         var b = Roo.get(this.getEl()).getBox();
18545         var ce = Roo.get(constrainTo);
18546         var s = ce.getScroll();
18547         var c, cd = ce.dom;
18548         if(cd == document.body){
18549             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18550         }else{
18551             xy = ce.getXY();
18552             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18553         }
18554
18555
18556         var topSpace = b.y - c.y;
18557         var leftSpace = b.x - c.x;
18558
18559         this.resetConstraints();
18560         this.setXConstraint(leftSpace - (pad.left||0), // left
18561                 c.width - leftSpace - b.width - (pad.right||0) //right
18562         );
18563         this.setYConstraint(topSpace - (pad.top||0), //top
18564                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18565         );
18566     },
18567
18568     /**
18569      * Returns a reference to the linked element
18570      * @method getEl
18571      * @return {HTMLElement} the html element
18572      */
18573     getEl: function() {
18574         if (!this._domRef) {
18575             this._domRef = Roo.getDom(this.id);
18576         }
18577
18578         return this._domRef;
18579     },
18580
18581     /**
18582      * Returns a reference to the actual element to drag.  By default this is
18583      * the same as the html element, but it can be assigned to another
18584      * element. An example of this can be found in Roo.dd.DDProxy
18585      * @method getDragEl
18586      * @return {HTMLElement} the html element
18587      */
18588     getDragEl: function() {
18589         return Roo.getDom(this.dragElId);
18590     },
18591
18592     /**
18593      * Sets up the DragDrop object.  Must be called in the constructor of any
18594      * Roo.dd.DragDrop subclass
18595      * @method init
18596      * @param id the id of the linked element
18597      * @param {String} sGroup the group of related items
18598      * @param {object} config configuration attributes
18599      */
18600     init: function(id, sGroup, config) {
18601         this.initTarget(id, sGroup, config);
18602         if (!Roo.isTouch) {
18603             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18604         }
18605         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18606         // Event.on(this.id, "selectstart", Event.preventDefault);
18607     },
18608
18609     /**
18610      * Initializes Targeting functionality only... the object does not
18611      * get a mousedown handler.
18612      * @method initTarget
18613      * @param id the id of the linked element
18614      * @param {String} sGroup the group of related items
18615      * @param {object} config configuration attributes
18616      */
18617     initTarget: function(id, sGroup, config) {
18618
18619         // configuration attributes
18620         this.config = config || {};
18621
18622         // create a local reference to the drag and drop manager
18623         this.DDM = Roo.dd.DDM;
18624         // initialize the groups array
18625         this.groups = {};
18626
18627         // assume that we have an element reference instead of an id if the
18628         // parameter is not a string
18629         if (typeof id !== "string") {
18630             id = Roo.id(id);
18631         }
18632
18633         // set the id
18634         this.id = id;
18635
18636         // add to an interaction group
18637         this.addToGroup((sGroup) ? sGroup : "default");
18638
18639         // We don't want to register this as the handle with the manager
18640         // so we just set the id rather than calling the setter.
18641         this.handleElId = id;
18642
18643         // the linked element is the element that gets dragged by default
18644         this.setDragElId(id);
18645
18646         // by default, clicked anchors will not start drag operations.
18647         this.invalidHandleTypes = { A: "A" };
18648         this.invalidHandleIds = {};
18649         this.invalidHandleClasses = [];
18650
18651         this.applyConfig();
18652
18653         this.handleOnAvailable();
18654     },
18655
18656     /**
18657      * Applies the configuration parameters that were passed into the constructor.
18658      * This is supposed to happen at each level through the inheritance chain.  So
18659      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18660      * DragDrop in order to get all of the parameters that are available in
18661      * each object.
18662      * @method applyConfig
18663      */
18664     applyConfig: function() {
18665
18666         // configurable properties:
18667         //    padding, isTarget, maintainOffset, primaryButtonOnly
18668         this.padding           = this.config.padding || [0, 0, 0, 0];
18669         this.isTarget          = (this.config.isTarget !== false);
18670         this.maintainOffset    = (this.config.maintainOffset);
18671         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18672
18673     },
18674
18675     /**
18676      * Executed when the linked element is available
18677      * @method handleOnAvailable
18678      * @private
18679      */
18680     handleOnAvailable: function() {
18681         this.available = true;
18682         this.resetConstraints();
18683         this.onAvailable();
18684     },
18685
18686      /**
18687      * Configures the padding for the target zone in px.  Effectively expands
18688      * (or reduces) the virtual object size for targeting calculations.
18689      * Supports css-style shorthand; if only one parameter is passed, all sides
18690      * will have that padding, and if only two are passed, the top and bottom
18691      * will have the first param, the left and right the second.
18692      * @method setPadding
18693      * @param {int} iTop    Top pad
18694      * @param {int} iRight  Right pad
18695      * @param {int} iBot    Bot pad
18696      * @param {int} iLeft   Left pad
18697      */
18698     setPadding: function(iTop, iRight, iBot, iLeft) {
18699         // this.padding = [iLeft, iRight, iTop, iBot];
18700         if (!iRight && 0 !== iRight) {
18701             this.padding = [iTop, iTop, iTop, iTop];
18702         } else if (!iBot && 0 !== iBot) {
18703             this.padding = [iTop, iRight, iTop, iRight];
18704         } else {
18705             this.padding = [iTop, iRight, iBot, iLeft];
18706         }
18707     },
18708
18709     /**
18710      * Stores the initial placement of the linked element.
18711      * @method setInitialPosition
18712      * @param {int} diffX   the X offset, default 0
18713      * @param {int} diffY   the Y offset, default 0
18714      */
18715     setInitPosition: function(diffX, diffY) {
18716         var el = this.getEl();
18717
18718         if (!this.DDM.verifyEl(el)) {
18719             return;
18720         }
18721
18722         var dx = diffX || 0;
18723         var dy = diffY || 0;
18724
18725         var p = Dom.getXY( el );
18726
18727         this.initPageX = p[0] - dx;
18728         this.initPageY = p[1] - dy;
18729
18730         this.lastPageX = p[0];
18731         this.lastPageY = p[1];
18732
18733
18734         this.setStartPosition(p);
18735     },
18736
18737     /**
18738      * Sets the start position of the element.  This is set when the obj
18739      * is initialized, the reset when a drag is started.
18740      * @method setStartPosition
18741      * @param pos current position (from previous lookup)
18742      * @private
18743      */
18744     setStartPosition: function(pos) {
18745         var p = pos || Dom.getXY( this.getEl() );
18746         this.deltaSetXY = null;
18747
18748         this.startPageX = p[0];
18749         this.startPageY = p[1];
18750     },
18751
18752     /**
18753      * Add this instance to a group of related drag/drop objects.  All
18754      * instances belong to at least one group, and can belong to as many
18755      * groups as needed.
18756      * @method addToGroup
18757      * @param sGroup {string} the name of the group
18758      */
18759     addToGroup: function(sGroup) {
18760         this.groups[sGroup] = true;
18761         this.DDM.regDragDrop(this, sGroup);
18762     },
18763
18764     /**
18765      * Remove's this instance from the supplied interaction group
18766      * @method removeFromGroup
18767      * @param {string}  sGroup  The group to drop
18768      */
18769     removeFromGroup: function(sGroup) {
18770         if (this.groups[sGroup]) {
18771             delete this.groups[sGroup];
18772         }
18773
18774         this.DDM.removeDDFromGroup(this, sGroup);
18775     },
18776
18777     /**
18778      * Allows you to specify that an element other than the linked element
18779      * will be moved with the cursor during a drag
18780      * @method setDragElId
18781      * @param id {string} the id of the element that will be used to initiate the drag
18782      */
18783     setDragElId: function(id) {
18784         this.dragElId = id;
18785     },
18786
18787     /**
18788      * Allows you to specify a child of the linked element that should be
18789      * used to initiate the drag operation.  An example of this would be if
18790      * you have a content div with text and links.  Clicking anywhere in the
18791      * content area would normally start the drag operation.  Use this method
18792      * to specify that an element inside of the content div is the element
18793      * that starts the drag operation.
18794      * @method setHandleElId
18795      * @param id {string} the id of the element that will be used to
18796      * initiate the drag.
18797      */
18798     setHandleElId: function(id) {
18799         if (typeof id !== "string") {
18800             id = Roo.id(id);
18801         }
18802         this.handleElId = id;
18803         this.DDM.regHandle(this.id, id);
18804     },
18805
18806     /**
18807      * Allows you to set an element outside of the linked element as a drag
18808      * handle
18809      * @method setOuterHandleElId
18810      * @param id the id of the element that will be used to initiate the drag
18811      */
18812     setOuterHandleElId: function(id) {
18813         if (typeof id !== "string") {
18814             id = Roo.id(id);
18815         }
18816         Event.on(id, "mousedown",
18817                 this.handleMouseDown, this);
18818         this.setHandleElId(id);
18819
18820         this.hasOuterHandles = true;
18821     },
18822
18823     /**
18824      * Remove all drag and drop hooks for this element
18825      * @method unreg
18826      */
18827     unreg: function() {
18828         Event.un(this.id, "mousedown",
18829                 this.handleMouseDown);
18830         Event.un(this.id, "touchstart",
18831                 this.handleMouseDown);
18832         this._domRef = null;
18833         this.DDM._remove(this);
18834     },
18835
18836     destroy : function(){
18837         this.unreg();
18838     },
18839
18840     /**
18841      * Returns true if this instance is locked, or the drag drop mgr is locked
18842      * (meaning that all drag/drop is disabled on the page.)
18843      * @method isLocked
18844      * @return {boolean} true if this obj or all drag/drop is locked, else
18845      * false
18846      */
18847     isLocked: function() {
18848         return (this.DDM.isLocked() || this.locked);
18849     },
18850
18851     /**
18852      * Fired when this object is clicked
18853      * @method handleMouseDown
18854      * @param {Event} e
18855      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18856      * @private
18857      */
18858     handleMouseDown: function(e, oDD){
18859      
18860         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18861             //Roo.log('not touch/ button !=0');
18862             return;
18863         }
18864         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18865             return; // double touch..
18866         }
18867         
18868
18869         if (this.isLocked()) {
18870             //Roo.log('locked');
18871             return;
18872         }
18873
18874         this.DDM.refreshCache(this.groups);
18875 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18876         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18877         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18878             //Roo.log('no outer handes or not over target');
18879                 // do nothing.
18880         } else {
18881 //            Roo.log('check validator');
18882             if (this.clickValidator(e)) {
18883 //                Roo.log('validate success');
18884                 // set the initial element position
18885                 this.setStartPosition();
18886
18887
18888                 this.b4MouseDown(e);
18889                 this.onMouseDown(e);
18890
18891                 this.DDM.handleMouseDown(e, this);
18892
18893                 this.DDM.stopEvent(e);
18894             } else {
18895
18896
18897             }
18898         }
18899     },
18900
18901     clickValidator: function(e) {
18902         var target = e.getTarget();
18903         return ( this.isValidHandleChild(target) &&
18904                     (this.id == this.handleElId ||
18905                         this.DDM.handleWasClicked(target, this.id)) );
18906     },
18907
18908     /**
18909      * Allows you to specify a tag name that should not start a drag operation
18910      * when clicked.  This is designed to facilitate embedding links within a
18911      * drag handle that do something other than start the drag.
18912      * @method addInvalidHandleType
18913      * @param {string} tagName the type of element to exclude
18914      */
18915     addInvalidHandleType: function(tagName) {
18916         var type = tagName.toUpperCase();
18917         this.invalidHandleTypes[type] = type;
18918     },
18919
18920     /**
18921      * Lets you to specify an element id for a child of a drag handle
18922      * that should not initiate a drag
18923      * @method addInvalidHandleId
18924      * @param {string} id the element id of the element you wish to ignore
18925      */
18926     addInvalidHandleId: function(id) {
18927         if (typeof id !== "string") {
18928             id = Roo.id(id);
18929         }
18930         this.invalidHandleIds[id] = id;
18931     },
18932
18933     /**
18934      * Lets you specify a css class of elements that will not initiate a drag
18935      * @method addInvalidHandleClass
18936      * @param {string} cssClass the class of the elements you wish to ignore
18937      */
18938     addInvalidHandleClass: function(cssClass) {
18939         this.invalidHandleClasses.push(cssClass);
18940     },
18941
18942     /**
18943      * Unsets an excluded tag name set by addInvalidHandleType
18944      * @method removeInvalidHandleType
18945      * @param {string} tagName the type of element to unexclude
18946      */
18947     removeInvalidHandleType: function(tagName) {
18948         var type = tagName.toUpperCase();
18949         // this.invalidHandleTypes[type] = null;
18950         delete this.invalidHandleTypes[type];
18951     },
18952
18953     /**
18954      * Unsets an invalid handle id
18955      * @method removeInvalidHandleId
18956      * @param {string} id the id of the element to re-enable
18957      */
18958     removeInvalidHandleId: function(id) {
18959         if (typeof id !== "string") {
18960             id = Roo.id(id);
18961         }
18962         delete this.invalidHandleIds[id];
18963     },
18964
18965     /**
18966      * Unsets an invalid css class
18967      * @method removeInvalidHandleClass
18968      * @param {string} cssClass the class of the element(s) you wish to
18969      * re-enable
18970      */
18971     removeInvalidHandleClass: function(cssClass) {
18972         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
18973             if (this.invalidHandleClasses[i] == cssClass) {
18974                 delete this.invalidHandleClasses[i];
18975             }
18976         }
18977     },
18978
18979     /**
18980      * Checks the tag exclusion list to see if this click should be ignored
18981      * @method isValidHandleChild
18982      * @param {HTMLElement} node the HTMLElement to evaluate
18983      * @return {boolean} true if this is a valid tag type, false if not
18984      */
18985     isValidHandleChild: function(node) {
18986
18987         var valid = true;
18988         // var n = (node.nodeName == "#text") ? node.parentNode : node;
18989         var nodeName;
18990         try {
18991             nodeName = node.nodeName.toUpperCase();
18992         } catch(e) {
18993             nodeName = node.nodeName;
18994         }
18995         valid = valid && !this.invalidHandleTypes[nodeName];
18996         valid = valid && !this.invalidHandleIds[node.id];
18997
18998         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
18999             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19000         }
19001
19002
19003         return valid;
19004
19005     },
19006
19007     /**
19008      * Create the array of horizontal tick marks if an interval was specified
19009      * in setXConstraint().
19010      * @method setXTicks
19011      * @private
19012      */
19013     setXTicks: function(iStartX, iTickSize) {
19014         this.xTicks = [];
19015         this.xTickSize = iTickSize;
19016
19017         var tickMap = {};
19018
19019         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19020             if (!tickMap[i]) {
19021                 this.xTicks[this.xTicks.length] = i;
19022                 tickMap[i] = true;
19023             }
19024         }
19025
19026         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19027             if (!tickMap[i]) {
19028                 this.xTicks[this.xTicks.length] = i;
19029                 tickMap[i] = true;
19030             }
19031         }
19032
19033         this.xTicks.sort(this.DDM.numericSort) ;
19034     },
19035
19036     /**
19037      * Create the array of vertical tick marks if an interval was specified in
19038      * setYConstraint().
19039      * @method setYTicks
19040      * @private
19041      */
19042     setYTicks: function(iStartY, iTickSize) {
19043         this.yTicks = [];
19044         this.yTickSize = iTickSize;
19045
19046         var tickMap = {};
19047
19048         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19049             if (!tickMap[i]) {
19050                 this.yTicks[this.yTicks.length] = i;
19051                 tickMap[i] = true;
19052             }
19053         }
19054
19055         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19056             if (!tickMap[i]) {
19057                 this.yTicks[this.yTicks.length] = i;
19058                 tickMap[i] = true;
19059             }
19060         }
19061
19062         this.yTicks.sort(this.DDM.numericSort) ;
19063     },
19064
19065     /**
19066      * By default, the element can be dragged any place on the screen.  Use
19067      * this method to limit the horizontal travel of the element.  Pass in
19068      * 0,0 for the parameters if you want to lock the drag to the y axis.
19069      * @method setXConstraint
19070      * @param {int} iLeft the number of pixels the element can move to the left
19071      * @param {int} iRight the number of pixels the element can move to the
19072      * right
19073      * @param {int} iTickSize optional parameter for specifying that the
19074      * element
19075      * should move iTickSize pixels at a time.
19076      */
19077     setXConstraint: function(iLeft, iRight, iTickSize) {
19078         this.leftConstraint = iLeft;
19079         this.rightConstraint = iRight;
19080
19081         this.minX = this.initPageX - iLeft;
19082         this.maxX = this.initPageX + iRight;
19083         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19084
19085         this.constrainX = true;
19086     },
19087
19088     /**
19089      * Clears any constraints applied to this instance.  Also clears ticks
19090      * since they can't exist independent of a constraint at this time.
19091      * @method clearConstraints
19092      */
19093     clearConstraints: function() {
19094         this.constrainX = false;
19095         this.constrainY = false;
19096         this.clearTicks();
19097     },
19098
19099     /**
19100      * Clears any tick interval defined for this instance
19101      * @method clearTicks
19102      */
19103     clearTicks: function() {
19104         this.xTicks = null;
19105         this.yTicks = null;
19106         this.xTickSize = 0;
19107         this.yTickSize = 0;
19108     },
19109
19110     /**
19111      * By default, the element can be dragged any place on the screen.  Set
19112      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19113      * parameters if you want to lock the drag to the x axis.
19114      * @method setYConstraint
19115      * @param {int} iUp the number of pixels the element can move up
19116      * @param {int} iDown the number of pixels the element can move down
19117      * @param {int} iTickSize optional parameter for specifying that the
19118      * element should move iTickSize pixels at a time.
19119      */
19120     setYConstraint: function(iUp, iDown, iTickSize) {
19121         this.topConstraint = iUp;
19122         this.bottomConstraint = iDown;
19123
19124         this.minY = this.initPageY - iUp;
19125         this.maxY = this.initPageY + iDown;
19126         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19127
19128         this.constrainY = true;
19129
19130     },
19131
19132     /**
19133      * resetConstraints must be called if you manually reposition a dd element.
19134      * @method resetConstraints
19135      * @param {boolean} maintainOffset
19136      */
19137     resetConstraints: function() {
19138
19139
19140         // Maintain offsets if necessary
19141         if (this.initPageX || this.initPageX === 0) {
19142             // figure out how much this thing has moved
19143             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19144             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19145
19146             this.setInitPosition(dx, dy);
19147
19148         // This is the first time we have detected the element's position
19149         } else {
19150             this.setInitPosition();
19151         }
19152
19153         if (this.constrainX) {
19154             this.setXConstraint( this.leftConstraint,
19155                                  this.rightConstraint,
19156                                  this.xTickSize        );
19157         }
19158
19159         if (this.constrainY) {
19160             this.setYConstraint( this.topConstraint,
19161                                  this.bottomConstraint,
19162                                  this.yTickSize         );
19163         }
19164     },
19165
19166     /**
19167      * Normally the drag element is moved pixel by pixel, but we can specify
19168      * that it move a number of pixels at a time.  This method resolves the
19169      * location when we have it set up like this.
19170      * @method getTick
19171      * @param {int} val where we want to place the object
19172      * @param {int[]} tickArray sorted array of valid points
19173      * @return {int} the closest tick
19174      * @private
19175      */
19176     getTick: function(val, tickArray) {
19177
19178         if (!tickArray) {
19179             // If tick interval is not defined, it is effectively 1 pixel,
19180             // so we return the value passed to us.
19181             return val;
19182         } else if (tickArray[0] >= val) {
19183             // The value is lower than the first tick, so we return the first
19184             // tick.
19185             return tickArray[0];
19186         } else {
19187             for (var i=0, len=tickArray.length; i<len; ++i) {
19188                 var next = i + 1;
19189                 if (tickArray[next] && tickArray[next] >= val) {
19190                     var diff1 = val - tickArray[i];
19191                     var diff2 = tickArray[next] - val;
19192                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19193                 }
19194             }
19195
19196             // The value is larger than the last tick, so we return the last
19197             // tick.
19198             return tickArray[tickArray.length - 1];
19199         }
19200     },
19201
19202     /**
19203      * toString method
19204      * @method toString
19205      * @return {string} string representation of the dd obj
19206      */
19207     toString: function() {
19208         return ("DragDrop " + this.id);
19209     }
19210
19211 });
19212
19213 })();
19214 /*
19215  * Based on:
19216  * Ext JS Library 1.1.1
19217  * Copyright(c) 2006-2007, Ext JS, LLC.
19218  *
19219  * Originally Released Under LGPL - original licence link has changed is not relivant.
19220  *
19221  * Fork - LGPL
19222  * <script type="text/javascript">
19223  */
19224
19225
19226 /**
19227  * The drag and drop utility provides a framework for building drag and drop
19228  * applications.  In addition to enabling drag and drop for specific elements,
19229  * the drag and drop elements are tracked by the manager class, and the
19230  * interactions between the various elements are tracked during the drag and
19231  * the implementing code is notified about these important moments.
19232  */
19233
19234 // Only load the library once.  Rewriting the manager class would orphan
19235 // existing drag and drop instances.
19236 if (!Roo.dd.DragDropMgr) {
19237
19238 /**
19239  * @class Roo.dd.DragDropMgr
19240  * DragDropMgr is a singleton that tracks the element interaction for
19241  * all DragDrop items in the window.  Generally, you will not call
19242  * this class directly, but it does have helper methods that could
19243  * be useful in your DragDrop implementations.
19244  * @singleton
19245  */
19246 Roo.dd.DragDropMgr = function() {
19247
19248     var Event = Roo.EventManager;
19249
19250     return {
19251
19252         /**
19253          * Two dimensional Array of registered DragDrop objects.  The first
19254          * dimension is the DragDrop item group, the second the DragDrop
19255          * object.
19256          * @property ids
19257          * @type {string: string}
19258          * @private
19259          * @static
19260          */
19261         ids: {},
19262
19263         /**
19264          * Array of element ids defined as drag handles.  Used to determine
19265          * if the element that generated the mousedown event is actually the
19266          * handle and not the html element itself.
19267          * @property handleIds
19268          * @type {string: string}
19269          * @private
19270          * @static
19271          */
19272         handleIds: {},
19273
19274         /**
19275          * the DragDrop object that is currently being dragged
19276          * @property dragCurrent
19277          * @type DragDrop
19278          * @private
19279          * @static
19280          **/
19281         dragCurrent: null,
19282
19283         /**
19284          * the DragDrop object(s) that are being hovered over
19285          * @property dragOvers
19286          * @type Array
19287          * @private
19288          * @static
19289          */
19290         dragOvers: {},
19291
19292         /**
19293          * the X distance between the cursor and the object being dragged
19294          * @property deltaX
19295          * @type int
19296          * @private
19297          * @static
19298          */
19299         deltaX: 0,
19300
19301         /**
19302          * the Y distance between the cursor and the object being dragged
19303          * @property deltaY
19304          * @type int
19305          * @private
19306          * @static
19307          */
19308         deltaY: 0,
19309
19310         /**
19311          * Flag to determine if we should prevent the default behavior of the
19312          * events we define. By default this is true, but this can be set to
19313          * false if you need the default behavior (not recommended)
19314          * @property preventDefault
19315          * @type boolean
19316          * @static
19317          */
19318         preventDefault: true,
19319
19320         /**
19321          * Flag to determine if we should stop the propagation of the events
19322          * we generate. This is true by default but you may want to set it to
19323          * false if the html element contains other features that require the
19324          * mouse click.
19325          * @property stopPropagation
19326          * @type boolean
19327          * @static
19328          */
19329         stopPropagation: true,
19330
19331         /**
19332          * Internal flag that is set to true when drag and drop has been
19333          * intialized
19334          * @property initialized
19335          * @private
19336          * @static
19337          */
19338         initalized: false,
19339
19340         /**
19341          * All drag and drop can be disabled.
19342          * @property locked
19343          * @private
19344          * @static
19345          */
19346         locked: false,
19347
19348         /**
19349          * Called the first time an element is registered.
19350          * @method init
19351          * @private
19352          * @static
19353          */
19354         init: function() {
19355             this.initialized = true;
19356         },
19357
19358         /**
19359          * In point mode, drag and drop interaction is defined by the
19360          * location of the cursor during the drag/drop
19361          * @property POINT
19362          * @type int
19363          * @static
19364          */
19365         POINT: 0,
19366
19367         /**
19368          * In intersect mode, drag and drop interactio nis defined by the
19369          * overlap of two or more drag and drop objects.
19370          * @property INTERSECT
19371          * @type int
19372          * @static
19373          */
19374         INTERSECT: 1,
19375
19376         /**
19377          * The current drag and drop mode.  Default: POINT
19378          * @property mode
19379          * @type int
19380          * @static
19381          */
19382         mode: 0,
19383
19384         /**
19385          * Runs method on all drag and drop objects
19386          * @method _execOnAll
19387          * @private
19388          * @static
19389          */
19390         _execOnAll: function(sMethod, args) {
19391             for (var i in this.ids) {
19392                 for (var j in this.ids[i]) {
19393                     var oDD = this.ids[i][j];
19394                     if (! this.isTypeOfDD(oDD)) {
19395                         continue;
19396                     }
19397                     oDD[sMethod].apply(oDD, args);
19398                 }
19399             }
19400         },
19401
19402         /**
19403          * Drag and drop initialization.  Sets up the global event handlers
19404          * @method _onLoad
19405          * @private
19406          * @static
19407          */
19408         _onLoad: function() {
19409
19410             this.init();
19411
19412             if (!Roo.isTouch) {
19413                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19414                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19415             }
19416             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19417             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19418             
19419             Event.on(window,   "unload",    this._onUnload, this, true);
19420             Event.on(window,   "resize",    this._onResize, this, true);
19421             // Event.on(window,   "mouseout",    this._test);
19422
19423         },
19424
19425         /**
19426          * Reset constraints on all drag and drop objs
19427          * @method _onResize
19428          * @private
19429          * @static
19430          */
19431         _onResize: function(e) {
19432             this._execOnAll("resetConstraints", []);
19433         },
19434
19435         /**
19436          * Lock all drag and drop functionality
19437          * @method lock
19438          * @static
19439          */
19440         lock: function() { this.locked = true; },
19441
19442         /**
19443          * Unlock all drag and drop functionality
19444          * @method unlock
19445          * @static
19446          */
19447         unlock: function() { this.locked = false; },
19448
19449         /**
19450          * Is drag and drop locked?
19451          * @method isLocked
19452          * @return {boolean} True if drag and drop is locked, false otherwise.
19453          * @static
19454          */
19455         isLocked: function() { return this.locked; },
19456
19457         /**
19458          * Location cache that is set for all drag drop objects when a drag is
19459          * initiated, cleared when the drag is finished.
19460          * @property locationCache
19461          * @private
19462          * @static
19463          */
19464         locationCache: {},
19465
19466         /**
19467          * Set useCache to false if you want to force object the lookup of each
19468          * drag and drop linked element constantly during a drag.
19469          * @property useCache
19470          * @type boolean
19471          * @static
19472          */
19473         useCache: true,
19474
19475         /**
19476          * The number of pixels that the mouse needs to move after the
19477          * mousedown before the drag is initiated.  Default=3;
19478          * @property clickPixelThresh
19479          * @type int
19480          * @static
19481          */
19482         clickPixelThresh: 3,
19483
19484         /**
19485          * The number of milliseconds after the mousedown event to initiate the
19486          * drag if we don't get a mouseup event. Default=1000
19487          * @property clickTimeThresh
19488          * @type int
19489          * @static
19490          */
19491         clickTimeThresh: 350,
19492
19493         /**
19494          * Flag that indicates that either the drag pixel threshold or the
19495          * mousdown time threshold has been met
19496          * @property dragThreshMet
19497          * @type boolean
19498          * @private
19499          * @static
19500          */
19501         dragThreshMet: false,
19502
19503         /**
19504          * Timeout used for the click time threshold
19505          * @property clickTimeout
19506          * @type Object
19507          * @private
19508          * @static
19509          */
19510         clickTimeout: null,
19511
19512         /**
19513          * The X position of the mousedown event stored for later use when a
19514          * drag threshold is met.
19515          * @property startX
19516          * @type int
19517          * @private
19518          * @static
19519          */
19520         startX: 0,
19521
19522         /**
19523          * The Y position of the mousedown event stored for later use when a
19524          * drag threshold is met.
19525          * @property startY
19526          * @type int
19527          * @private
19528          * @static
19529          */
19530         startY: 0,
19531
19532         /**
19533          * Each DragDrop instance must be registered with the DragDropMgr.
19534          * This is executed in DragDrop.init()
19535          * @method regDragDrop
19536          * @param {DragDrop} oDD the DragDrop object to register
19537          * @param {String} sGroup the name of the group this element belongs to
19538          * @static
19539          */
19540         regDragDrop: function(oDD, sGroup) {
19541             if (!this.initialized) { this.init(); }
19542
19543             if (!this.ids[sGroup]) {
19544                 this.ids[sGroup] = {};
19545             }
19546             this.ids[sGroup][oDD.id] = oDD;
19547         },
19548
19549         /**
19550          * Removes the supplied dd instance from the supplied group. Executed
19551          * by DragDrop.removeFromGroup, so don't call this function directly.
19552          * @method removeDDFromGroup
19553          * @private
19554          * @static
19555          */
19556         removeDDFromGroup: function(oDD, sGroup) {
19557             if (!this.ids[sGroup]) {
19558                 this.ids[sGroup] = {};
19559             }
19560
19561             var obj = this.ids[sGroup];
19562             if (obj && obj[oDD.id]) {
19563                 delete obj[oDD.id];
19564             }
19565         },
19566
19567         /**
19568          * Unregisters a drag and drop item.  This is executed in
19569          * DragDrop.unreg, use that method instead of calling this directly.
19570          * @method _remove
19571          * @private
19572          * @static
19573          */
19574         _remove: function(oDD) {
19575             for (var g in oDD.groups) {
19576                 if (g && this.ids[g][oDD.id]) {
19577                     delete this.ids[g][oDD.id];
19578                 }
19579             }
19580             delete this.handleIds[oDD.id];
19581         },
19582
19583         /**
19584          * Each DragDrop handle element must be registered.  This is done
19585          * automatically when executing DragDrop.setHandleElId()
19586          * @method regHandle
19587          * @param {String} sDDId the DragDrop id this element is a handle for
19588          * @param {String} sHandleId the id of the element that is the drag
19589          * handle
19590          * @static
19591          */
19592         regHandle: function(sDDId, sHandleId) {
19593             if (!this.handleIds[sDDId]) {
19594                 this.handleIds[sDDId] = {};
19595             }
19596             this.handleIds[sDDId][sHandleId] = sHandleId;
19597         },
19598
19599         /**
19600          * Utility function to determine if a given element has been
19601          * registered as a drag drop item.
19602          * @method isDragDrop
19603          * @param {String} id the element id to check
19604          * @return {boolean} true if this element is a DragDrop item,
19605          * false otherwise
19606          * @static
19607          */
19608         isDragDrop: function(id) {
19609             return ( this.getDDById(id) ) ? true : false;
19610         },
19611
19612         /**
19613          * Returns the drag and drop instances that are in all groups the
19614          * passed in instance belongs to.
19615          * @method getRelated
19616          * @param {DragDrop} p_oDD the obj to get related data for
19617          * @param {boolean} bTargetsOnly if true, only return targetable objs
19618          * @return {DragDrop[]} the related instances
19619          * @static
19620          */
19621         getRelated: function(p_oDD, bTargetsOnly) {
19622             var oDDs = [];
19623             for (var i in p_oDD.groups) {
19624                 for (j in this.ids[i]) {
19625                     var dd = this.ids[i][j];
19626                     if (! this.isTypeOfDD(dd)) {
19627                         continue;
19628                     }
19629                     if (!bTargetsOnly || dd.isTarget) {
19630                         oDDs[oDDs.length] = dd;
19631                     }
19632                 }
19633             }
19634
19635             return oDDs;
19636         },
19637
19638         /**
19639          * Returns true if the specified dd target is a legal target for
19640          * the specifice drag obj
19641          * @method isLegalTarget
19642          * @param {DragDrop} the drag obj
19643          * @param {DragDrop} the target
19644          * @return {boolean} true if the target is a legal target for the
19645          * dd obj
19646          * @static
19647          */
19648         isLegalTarget: function (oDD, oTargetDD) {
19649             var targets = this.getRelated(oDD, true);
19650             for (var i=0, len=targets.length;i<len;++i) {
19651                 if (targets[i].id == oTargetDD.id) {
19652                     return true;
19653                 }
19654             }
19655
19656             return false;
19657         },
19658
19659         /**
19660          * My goal is to be able to transparently determine if an object is
19661          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19662          * returns "object", oDD.constructor.toString() always returns
19663          * "DragDrop" and not the name of the subclass.  So for now it just
19664          * evaluates a well-known variable in DragDrop.
19665          * @method isTypeOfDD
19666          * @param {Object} the object to evaluate
19667          * @return {boolean} true if typeof oDD = DragDrop
19668          * @static
19669          */
19670         isTypeOfDD: function (oDD) {
19671             return (oDD && oDD.__ygDragDrop);
19672         },
19673
19674         /**
19675          * Utility function to determine if a given element has been
19676          * registered as a drag drop handle for the given Drag Drop object.
19677          * @method isHandle
19678          * @param {String} id the element id to check
19679          * @return {boolean} true if this element is a DragDrop handle, false
19680          * otherwise
19681          * @static
19682          */
19683         isHandle: function(sDDId, sHandleId) {
19684             return ( this.handleIds[sDDId] &&
19685                             this.handleIds[sDDId][sHandleId] );
19686         },
19687
19688         /**
19689          * Returns the DragDrop instance for a given id
19690          * @method getDDById
19691          * @param {String} id the id of the DragDrop object
19692          * @return {DragDrop} the drag drop object, null if it is not found
19693          * @static
19694          */
19695         getDDById: function(id) {
19696             for (var i in this.ids) {
19697                 if (this.ids[i][id]) {
19698                     return this.ids[i][id];
19699                 }
19700             }
19701             return null;
19702         },
19703
19704         /**
19705          * Fired after a registered DragDrop object gets the mousedown event.
19706          * Sets up the events required to track the object being dragged
19707          * @method handleMouseDown
19708          * @param {Event} e the event
19709          * @param oDD the DragDrop object being dragged
19710          * @private
19711          * @static
19712          */
19713         handleMouseDown: function(e, oDD) {
19714             if(Roo.QuickTips){
19715                 Roo.QuickTips.disable();
19716             }
19717             this.currentTarget = e.getTarget();
19718
19719             this.dragCurrent = oDD;
19720
19721             var el = oDD.getEl();
19722
19723             // track start position
19724             this.startX = e.getPageX();
19725             this.startY = e.getPageY();
19726
19727             this.deltaX = this.startX - el.offsetLeft;
19728             this.deltaY = this.startY - el.offsetTop;
19729
19730             this.dragThreshMet = false;
19731
19732             this.clickTimeout = setTimeout(
19733                     function() {
19734                         var DDM = Roo.dd.DDM;
19735                         DDM.startDrag(DDM.startX, DDM.startY);
19736                     },
19737                     this.clickTimeThresh );
19738         },
19739
19740         /**
19741          * Fired when either the drag pixel threshol or the mousedown hold
19742          * time threshold has been met.
19743          * @method startDrag
19744          * @param x {int} the X position of the original mousedown
19745          * @param y {int} the Y position of the original mousedown
19746          * @static
19747          */
19748         startDrag: function(x, y) {
19749             clearTimeout(this.clickTimeout);
19750             if (this.dragCurrent) {
19751                 this.dragCurrent.b4StartDrag(x, y);
19752                 this.dragCurrent.startDrag(x, y);
19753             }
19754             this.dragThreshMet = true;
19755         },
19756
19757         /**
19758          * Internal function to handle the mouseup event.  Will be invoked
19759          * from the context of the document.
19760          * @method handleMouseUp
19761          * @param {Event} e the event
19762          * @private
19763          * @static
19764          */
19765         handleMouseUp: function(e) {
19766
19767             if(Roo.QuickTips){
19768                 Roo.QuickTips.enable();
19769             }
19770             if (! this.dragCurrent) {
19771                 return;
19772             }
19773
19774             clearTimeout(this.clickTimeout);
19775
19776             if (this.dragThreshMet) {
19777                 this.fireEvents(e, true);
19778             } else {
19779             }
19780
19781             this.stopDrag(e);
19782
19783             this.stopEvent(e);
19784         },
19785
19786         /**
19787          * Utility to stop event propagation and event default, if these
19788          * features are turned on.
19789          * @method stopEvent
19790          * @param {Event} e the event as returned by this.getEvent()
19791          * @static
19792          */
19793         stopEvent: function(e){
19794             if(this.stopPropagation) {
19795                 e.stopPropagation();
19796             }
19797
19798             if (this.preventDefault) {
19799                 e.preventDefault();
19800             }
19801         },
19802
19803         /**
19804          * Internal function to clean up event handlers after the drag
19805          * operation is complete
19806          * @method stopDrag
19807          * @param {Event} e the event
19808          * @private
19809          * @static
19810          */
19811         stopDrag: function(e) {
19812             // Fire the drag end event for the item that was dragged
19813             if (this.dragCurrent) {
19814                 if (this.dragThreshMet) {
19815                     this.dragCurrent.b4EndDrag(e);
19816                     this.dragCurrent.endDrag(e);
19817                 }
19818
19819                 this.dragCurrent.onMouseUp(e);
19820             }
19821
19822             this.dragCurrent = null;
19823             this.dragOvers = {};
19824         },
19825
19826         /**
19827          * Internal function to handle the mousemove event.  Will be invoked
19828          * from the context of the html element.
19829          *
19830          * @TODO figure out what we can do about mouse events lost when the
19831          * user drags objects beyond the window boundary.  Currently we can
19832          * detect this in internet explorer by verifying that the mouse is
19833          * down during the mousemove event.  Firefox doesn't give us the
19834          * button state on the mousemove event.
19835          * @method handleMouseMove
19836          * @param {Event} e the event
19837          * @private
19838          * @static
19839          */
19840         handleMouseMove: function(e) {
19841             if (! this.dragCurrent) {
19842                 return true;
19843             }
19844
19845             // var button = e.which || e.button;
19846
19847             // check for IE mouseup outside of page boundary
19848             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19849                 this.stopEvent(e);
19850                 return this.handleMouseUp(e);
19851             }
19852
19853             if (!this.dragThreshMet) {
19854                 var diffX = Math.abs(this.startX - e.getPageX());
19855                 var diffY = Math.abs(this.startY - e.getPageY());
19856                 if (diffX > this.clickPixelThresh ||
19857                             diffY > this.clickPixelThresh) {
19858                     this.startDrag(this.startX, this.startY);
19859                 }
19860             }
19861
19862             if (this.dragThreshMet) {
19863                 this.dragCurrent.b4Drag(e);
19864                 this.dragCurrent.onDrag(e);
19865                 if(!this.dragCurrent.moveOnly){
19866                     this.fireEvents(e, false);
19867                 }
19868             }
19869
19870             this.stopEvent(e);
19871
19872             return true;
19873         },
19874
19875         /**
19876          * Iterates over all of the DragDrop elements to find ones we are
19877          * hovering over or dropping on
19878          * @method fireEvents
19879          * @param {Event} e the event
19880          * @param {boolean} isDrop is this a drop op or a mouseover op?
19881          * @private
19882          * @static
19883          */
19884         fireEvents: function(e, isDrop) {
19885             var dc = this.dragCurrent;
19886
19887             // If the user did the mouse up outside of the window, we could
19888             // get here even though we have ended the drag.
19889             if (!dc || dc.isLocked()) {
19890                 return;
19891             }
19892
19893             var pt = e.getPoint();
19894
19895             // cache the previous dragOver array
19896             var oldOvers = [];
19897
19898             var outEvts   = [];
19899             var overEvts  = [];
19900             var dropEvts  = [];
19901             var enterEvts = [];
19902
19903             // Check to see if the object(s) we were hovering over is no longer
19904             // being hovered over so we can fire the onDragOut event
19905             for (var i in this.dragOvers) {
19906
19907                 var ddo = this.dragOvers[i];
19908
19909                 if (! this.isTypeOfDD(ddo)) {
19910                     continue;
19911                 }
19912
19913                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19914                     outEvts.push( ddo );
19915                 }
19916
19917                 oldOvers[i] = true;
19918                 delete this.dragOvers[i];
19919             }
19920
19921             for (var sGroup in dc.groups) {
19922
19923                 if ("string" != typeof sGroup) {
19924                     continue;
19925                 }
19926
19927                 for (i in this.ids[sGroup]) {
19928                     var oDD = this.ids[sGroup][i];
19929                     if (! this.isTypeOfDD(oDD)) {
19930                         continue;
19931                     }
19932
19933                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
19934                         if (this.isOverTarget(pt, oDD, this.mode)) {
19935                             // look for drop interactions
19936                             if (isDrop) {
19937                                 dropEvts.push( oDD );
19938                             // look for drag enter and drag over interactions
19939                             } else {
19940
19941                                 // initial drag over: dragEnter fires
19942                                 if (!oldOvers[oDD.id]) {
19943                                     enterEvts.push( oDD );
19944                                 // subsequent drag overs: dragOver fires
19945                                 } else {
19946                                     overEvts.push( oDD );
19947                                 }
19948
19949                                 this.dragOvers[oDD.id] = oDD;
19950                             }
19951                         }
19952                     }
19953                 }
19954             }
19955
19956             if (this.mode) {
19957                 if (outEvts.length) {
19958                     dc.b4DragOut(e, outEvts);
19959                     dc.onDragOut(e, outEvts);
19960                 }
19961
19962                 if (enterEvts.length) {
19963                     dc.onDragEnter(e, enterEvts);
19964                 }
19965
19966                 if (overEvts.length) {
19967                     dc.b4DragOver(e, overEvts);
19968                     dc.onDragOver(e, overEvts);
19969                 }
19970
19971                 if (dropEvts.length) {
19972                     dc.b4DragDrop(e, dropEvts);
19973                     dc.onDragDrop(e, dropEvts);
19974                 }
19975
19976             } else {
19977                 // fire dragout events
19978                 var len = 0;
19979                 for (i=0, len=outEvts.length; i<len; ++i) {
19980                     dc.b4DragOut(e, outEvts[i].id);
19981                     dc.onDragOut(e, outEvts[i].id);
19982                 }
19983
19984                 // fire enter events
19985                 for (i=0,len=enterEvts.length; i<len; ++i) {
19986                     // dc.b4DragEnter(e, oDD.id);
19987                     dc.onDragEnter(e, enterEvts[i].id);
19988                 }
19989
19990                 // fire over events
19991                 for (i=0,len=overEvts.length; i<len; ++i) {
19992                     dc.b4DragOver(e, overEvts[i].id);
19993                     dc.onDragOver(e, overEvts[i].id);
19994                 }
19995
19996                 // fire drop events
19997                 for (i=0, len=dropEvts.length; i<len; ++i) {
19998                     dc.b4DragDrop(e, dropEvts[i].id);
19999                     dc.onDragDrop(e, dropEvts[i].id);
20000                 }
20001
20002             }
20003
20004             // notify about a drop that did not find a target
20005             if (isDrop && !dropEvts.length) {
20006                 dc.onInvalidDrop(e);
20007             }
20008
20009         },
20010
20011         /**
20012          * Helper function for getting the best match from the list of drag
20013          * and drop objects returned by the drag and drop events when we are
20014          * in INTERSECT mode.  It returns either the first object that the
20015          * cursor is over, or the object that has the greatest overlap with
20016          * the dragged element.
20017          * @method getBestMatch
20018          * @param  {DragDrop[]} dds The array of drag and drop objects
20019          * targeted
20020          * @return {DragDrop}       The best single match
20021          * @static
20022          */
20023         getBestMatch: function(dds) {
20024             var winner = null;
20025             // Return null if the input is not what we expect
20026             //if (!dds || !dds.length || dds.length == 0) {
20027                // winner = null;
20028             // If there is only one item, it wins
20029             //} else if (dds.length == 1) {
20030
20031             var len = dds.length;
20032
20033             if (len == 1) {
20034                 winner = dds[0];
20035             } else {
20036                 // Loop through the targeted items
20037                 for (var i=0; i<len; ++i) {
20038                     var dd = dds[i];
20039                     // If the cursor is over the object, it wins.  If the
20040                     // cursor is over multiple matches, the first one we come
20041                     // to wins.
20042                     if (dd.cursorIsOver) {
20043                         winner = dd;
20044                         break;
20045                     // Otherwise the object with the most overlap wins
20046                     } else {
20047                         if (!winner ||
20048                             winner.overlap.getArea() < dd.overlap.getArea()) {
20049                             winner = dd;
20050                         }
20051                     }
20052                 }
20053             }
20054
20055             return winner;
20056         },
20057
20058         /**
20059          * Refreshes the cache of the top-left and bottom-right points of the
20060          * drag and drop objects in the specified group(s).  This is in the
20061          * format that is stored in the drag and drop instance, so typical
20062          * usage is:
20063          * <code>
20064          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20065          * </code>
20066          * Alternatively:
20067          * <code>
20068          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20069          * </code>
20070          * @TODO this really should be an indexed array.  Alternatively this
20071          * method could accept both.
20072          * @method refreshCache
20073          * @param {Object} groups an associative array of groups to refresh
20074          * @static
20075          */
20076         refreshCache: function(groups) {
20077             for (var sGroup in groups) {
20078                 if ("string" != typeof sGroup) {
20079                     continue;
20080                 }
20081                 for (var i in this.ids[sGroup]) {
20082                     var oDD = this.ids[sGroup][i];
20083
20084                     if (this.isTypeOfDD(oDD)) {
20085                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20086                         var loc = this.getLocation(oDD);
20087                         if (loc) {
20088                             this.locationCache[oDD.id] = loc;
20089                         } else {
20090                             delete this.locationCache[oDD.id];
20091                             // this will unregister the drag and drop object if
20092                             // the element is not in a usable state
20093                             // oDD.unreg();
20094                         }
20095                     }
20096                 }
20097             }
20098         },
20099
20100         /**
20101          * This checks to make sure an element exists and is in the DOM.  The
20102          * main purpose is to handle cases where innerHTML is used to remove
20103          * drag and drop objects from the DOM.  IE provides an 'unspecified
20104          * error' when trying to access the offsetParent of such an element
20105          * @method verifyEl
20106          * @param {HTMLElement} el the element to check
20107          * @return {boolean} true if the element looks usable
20108          * @static
20109          */
20110         verifyEl: function(el) {
20111             if (el) {
20112                 var parent;
20113                 if(Roo.isIE){
20114                     try{
20115                         parent = el.offsetParent;
20116                     }catch(e){}
20117                 }else{
20118                     parent = el.offsetParent;
20119                 }
20120                 if (parent) {
20121                     return true;
20122                 }
20123             }
20124
20125             return false;
20126         },
20127
20128         /**
20129          * Returns a Region object containing the drag and drop element's position
20130          * and size, including the padding configured for it
20131          * @method getLocation
20132          * @param {DragDrop} oDD the drag and drop object to get the
20133          *                       location for
20134          * @return {Roo.lib.Region} a Region object representing the total area
20135          *                             the element occupies, including any padding
20136          *                             the instance is configured for.
20137          * @static
20138          */
20139         getLocation: function(oDD) {
20140             if (! this.isTypeOfDD(oDD)) {
20141                 return null;
20142             }
20143
20144             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20145
20146             try {
20147                 pos= Roo.lib.Dom.getXY(el);
20148             } catch (e) { }
20149
20150             if (!pos) {
20151                 return null;
20152             }
20153
20154             x1 = pos[0];
20155             x2 = x1 + el.offsetWidth;
20156             y1 = pos[1];
20157             y2 = y1 + el.offsetHeight;
20158
20159             t = y1 - oDD.padding[0];
20160             r = x2 + oDD.padding[1];
20161             b = y2 + oDD.padding[2];
20162             l = x1 - oDD.padding[3];
20163
20164             return new Roo.lib.Region( t, r, b, l );
20165         },
20166
20167         /**
20168          * Checks the cursor location to see if it over the target
20169          * @method isOverTarget
20170          * @param {Roo.lib.Point} pt The point to evaluate
20171          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20172          * @return {boolean} true if the mouse is over the target
20173          * @private
20174          * @static
20175          */
20176         isOverTarget: function(pt, oTarget, intersect) {
20177             // use cache if available
20178             var loc = this.locationCache[oTarget.id];
20179             if (!loc || !this.useCache) {
20180                 loc = this.getLocation(oTarget);
20181                 this.locationCache[oTarget.id] = loc;
20182
20183             }
20184
20185             if (!loc) {
20186                 return false;
20187             }
20188
20189             oTarget.cursorIsOver = loc.contains( pt );
20190
20191             // DragDrop is using this as a sanity check for the initial mousedown
20192             // in this case we are done.  In POINT mode, if the drag obj has no
20193             // contraints, we are also done. Otherwise we need to evaluate the
20194             // location of the target as related to the actual location of the
20195             // dragged element.
20196             var dc = this.dragCurrent;
20197             if (!dc || !dc.getTargetCoord ||
20198                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20199                 return oTarget.cursorIsOver;
20200             }
20201
20202             oTarget.overlap = null;
20203
20204             // Get the current location of the drag element, this is the
20205             // location of the mouse event less the delta that represents
20206             // where the original mousedown happened on the element.  We
20207             // need to consider constraints and ticks as well.
20208             var pos = dc.getTargetCoord(pt.x, pt.y);
20209
20210             var el = dc.getDragEl();
20211             var curRegion = new Roo.lib.Region( pos.y,
20212                                                    pos.x + el.offsetWidth,
20213                                                    pos.y + el.offsetHeight,
20214                                                    pos.x );
20215
20216             var overlap = curRegion.intersect(loc);
20217
20218             if (overlap) {
20219                 oTarget.overlap = overlap;
20220                 return (intersect) ? true : oTarget.cursorIsOver;
20221             } else {
20222                 return false;
20223             }
20224         },
20225
20226         /**
20227          * unload event handler
20228          * @method _onUnload
20229          * @private
20230          * @static
20231          */
20232         _onUnload: function(e, me) {
20233             Roo.dd.DragDropMgr.unregAll();
20234         },
20235
20236         /**
20237          * Cleans up the drag and drop events and objects.
20238          * @method unregAll
20239          * @private
20240          * @static
20241          */
20242         unregAll: function() {
20243
20244             if (this.dragCurrent) {
20245                 this.stopDrag();
20246                 this.dragCurrent = null;
20247             }
20248
20249             this._execOnAll("unreg", []);
20250
20251             for (i in this.elementCache) {
20252                 delete this.elementCache[i];
20253             }
20254
20255             this.elementCache = {};
20256             this.ids = {};
20257         },
20258
20259         /**
20260          * A cache of DOM elements
20261          * @property elementCache
20262          * @private
20263          * @static
20264          */
20265         elementCache: {},
20266
20267         /**
20268          * Get the wrapper for the DOM element specified
20269          * @method getElWrapper
20270          * @param {String} id the id of the element to get
20271          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20272          * @private
20273          * @deprecated This wrapper isn't that useful
20274          * @static
20275          */
20276         getElWrapper: function(id) {
20277             var oWrapper = this.elementCache[id];
20278             if (!oWrapper || !oWrapper.el) {
20279                 oWrapper = this.elementCache[id] =
20280                     new this.ElementWrapper(Roo.getDom(id));
20281             }
20282             return oWrapper;
20283         },
20284
20285         /**
20286          * Returns the actual DOM element
20287          * @method getElement
20288          * @param {String} id the id of the elment to get
20289          * @return {Object} The element
20290          * @deprecated use Roo.getDom instead
20291          * @static
20292          */
20293         getElement: function(id) {
20294             return Roo.getDom(id);
20295         },
20296
20297         /**
20298          * Returns the style property for the DOM element (i.e.,
20299          * document.getElById(id).style)
20300          * @method getCss
20301          * @param {String} id the id of the elment to get
20302          * @return {Object} The style property of the element
20303          * @deprecated use Roo.getDom instead
20304          * @static
20305          */
20306         getCss: function(id) {
20307             var el = Roo.getDom(id);
20308             return (el) ? el.style : null;
20309         },
20310
20311         /**
20312          * Inner class for cached elements
20313          * @class DragDropMgr.ElementWrapper
20314          * @for DragDropMgr
20315          * @private
20316          * @deprecated
20317          */
20318         ElementWrapper: function(el) {
20319                 /**
20320                  * The element
20321                  * @property el
20322                  */
20323                 this.el = el || null;
20324                 /**
20325                  * The element id
20326                  * @property id
20327                  */
20328                 this.id = this.el && el.id;
20329                 /**
20330                  * A reference to the style property
20331                  * @property css
20332                  */
20333                 this.css = this.el && el.style;
20334             },
20335
20336         /**
20337          * Returns the X position of an html element
20338          * @method getPosX
20339          * @param el the element for which to get the position
20340          * @return {int} the X coordinate
20341          * @for DragDropMgr
20342          * @deprecated use Roo.lib.Dom.getX instead
20343          * @static
20344          */
20345         getPosX: function(el) {
20346             return Roo.lib.Dom.getX(el);
20347         },
20348
20349         /**
20350          * Returns the Y position of an html element
20351          * @method getPosY
20352          * @param el the element for which to get the position
20353          * @return {int} the Y coordinate
20354          * @deprecated use Roo.lib.Dom.getY instead
20355          * @static
20356          */
20357         getPosY: function(el) {
20358             return Roo.lib.Dom.getY(el);
20359         },
20360
20361         /**
20362          * Swap two nodes.  In IE, we use the native method, for others we
20363          * emulate the IE behavior
20364          * @method swapNode
20365          * @param n1 the first node to swap
20366          * @param n2 the other node to swap
20367          * @static
20368          */
20369         swapNode: function(n1, n2) {
20370             if (n1.swapNode) {
20371                 n1.swapNode(n2);
20372             } else {
20373                 var p = n2.parentNode;
20374                 var s = n2.nextSibling;
20375
20376                 if (s == n1) {
20377                     p.insertBefore(n1, n2);
20378                 } else if (n2 == n1.nextSibling) {
20379                     p.insertBefore(n2, n1);
20380                 } else {
20381                     n1.parentNode.replaceChild(n2, n1);
20382                     p.insertBefore(n1, s);
20383                 }
20384             }
20385         },
20386
20387         /**
20388          * Returns the current scroll position
20389          * @method getScroll
20390          * @private
20391          * @static
20392          */
20393         getScroll: function () {
20394             var t, l, dde=document.documentElement, db=document.body;
20395             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20396                 t = dde.scrollTop;
20397                 l = dde.scrollLeft;
20398             } else if (db) {
20399                 t = db.scrollTop;
20400                 l = db.scrollLeft;
20401             } else {
20402
20403             }
20404             return { top: t, left: l };
20405         },
20406
20407         /**
20408          * Returns the specified element style property
20409          * @method getStyle
20410          * @param {HTMLElement} el          the element
20411          * @param {string}      styleProp   the style property
20412          * @return {string} The value of the style property
20413          * @deprecated use Roo.lib.Dom.getStyle
20414          * @static
20415          */
20416         getStyle: function(el, styleProp) {
20417             return Roo.fly(el).getStyle(styleProp);
20418         },
20419
20420         /**
20421          * Gets the scrollTop
20422          * @method getScrollTop
20423          * @return {int} the document's scrollTop
20424          * @static
20425          */
20426         getScrollTop: function () { return this.getScroll().top; },
20427
20428         /**
20429          * Gets the scrollLeft
20430          * @method getScrollLeft
20431          * @return {int} the document's scrollTop
20432          * @static
20433          */
20434         getScrollLeft: function () { return this.getScroll().left; },
20435
20436         /**
20437          * Sets the x/y position of an element to the location of the
20438          * target element.
20439          * @method moveToEl
20440          * @param {HTMLElement} moveEl      The element to move
20441          * @param {HTMLElement} targetEl    The position reference element
20442          * @static
20443          */
20444         moveToEl: function (moveEl, targetEl) {
20445             var aCoord = Roo.lib.Dom.getXY(targetEl);
20446             Roo.lib.Dom.setXY(moveEl, aCoord);
20447         },
20448
20449         /**
20450          * Numeric array sort function
20451          * @method numericSort
20452          * @static
20453          */
20454         numericSort: function(a, b) { return (a - b); },
20455
20456         /**
20457          * Internal counter
20458          * @property _timeoutCount
20459          * @private
20460          * @static
20461          */
20462         _timeoutCount: 0,
20463
20464         /**
20465          * Trying to make the load order less important.  Without this we get
20466          * an error if this file is loaded before the Event Utility.
20467          * @method _addListeners
20468          * @private
20469          * @static
20470          */
20471         _addListeners: function() {
20472             var DDM = Roo.dd.DDM;
20473             if ( Roo.lib.Event && document ) {
20474                 DDM._onLoad();
20475             } else {
20476                 if (DDM._timeoutCount > 2000) {
20477                 } else {
20478                     setTimeout(DDM._addListeners, 10);
20479                     if (document && document.body) {
20480                         DDM._timeoutCount += 1;
20481                     }
20482                 }
20483             }
20484         },
20485
20486         /**
20487          * Recursively searches the immediate parent and all child nodes for
20488          * the handle element in order to determine wheter or not it was
20489          * clicked.
20490          * @method handleWasClicked
20491          * @param node the html element to inspect
20492          * @static
20493          */
20494         handleWasClicked: function(node, id) {
20495             if (this.isHandle(id, node.id)) {
20496                 return true;
20497             } else {
20498                 // check to see if this is a text node child of the one we want
20499                 var p = node.parentNode;
20500
20501                 while (p) {
20502                     if (this.isHandle(id, p.id)) {
20503                         return true;
20504                     } else {
20505                         p = p.parentNode;
20506                     }
20507                 }
20508             }
20509
20510             return false;
20511         }
20512
20513     };
20514
20515 }();
20516
20517 // shorter alias, save a few bytes
20518 Roo.dd.DDM = Roo.dd.DragDropMgr;
20519 Roo.dd.DDM._addListeners();
20520
20521 }/*
20522  * Based on:
20523  * Ext JS Library 1.1.1
20524  * Copyright(c) 2006-2007, Ext JS, LLC.
20525  *
20526  * Originally Released Under LGPL - original licence link has changed is not relivant.
20527  *
20528  * Fork - LGPL
20529  * <script type="text/javascript">
20530  */
20531
20532 /**
20533  * @class Roo.dd.DD
20534  * A DragDrop implementation where the linked element follows the
20535  * mouse cursor during a drag.
20536  * @extends Roo.dd.DragDrop
20537  * @constructor
20538  * @param {String} id the id of the linked element
20539  * @param {String} sGroup the group of related DragDrop items
20540  * @param {object} config an object containing configurable attributes
20541  *                Valid properties for DD:
20542  *                    scroll
20543  */
20544 Roo.dd.DD = function(id, sGroup, config) {
20545     if (id) {
20546         this.init(id, sGroup, config);
20547     }
20548 };
20549
20550 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20551
20552     /**
20553      * When set to true, the utility automatically tries to scroll the browser
20554      * window wehn a drag and drop element is dragged near the viewport boundary.
20555      * Defaults to true.
20556      * @property scroll
20557      * @type boolean
20558      */
20559     scroll: true,
20560
20561     /**
20562      * Sets the pointer offset to the distance between the linked element's top
20563      * left corner and the location the element was clicked
20564      * @method autoOffset
20565      * @param {int} iPageX the X coordinate of the click
20566      * @param {int} iPageY the Y coordinate of the click
20567      */
20568     autoOffset: function(iPageX, iPageY) {
20569         var x = iPageX - this.startPageX;
20570         var y = iPageY - this.startPageY;
20571         this.setDelta(x, y);
20572     },
20573
20574     /**
20575      * Sets the pointer offset.  You can call this directly to force the
20576      * offset to be in a particular location (e.g., pass in 0,0 to set it
20577      * to the center of the object)
20578      * @method setDelta
20579      * @param {int} iDeltaX the distance from the left
20580      * @param {int} iDeltaY the distance from the top
20581      */
20582     setDelta: function(iDeltaX, iDeltaY) {
20583         this.deltaX = iDeltaX;
20584         this.deltaY = iDeltaY;
20585     },
20586
20587     /**
20588      * Sets the drag element to the location of the mousedown or click event,
20589      * maintaining the cursor location relative to the location on the element
20590      * that was clicked.  Override this if you want to place the element in a
20591      * location other than where the cursor is.
20592      * @method setDragElPos
20593      * @param {int} iPageX the X coordinate of the mousedown or drag event
20594      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20595      */
20596     setDragElPos: function(iPageX, iPageY) {
20597         // the first time we do this, we are going to check to make sure
20598         // the element has css positioning
20599
20600         var el = this.getDragEl();
20601         this.alignElWithMouse(el, iPageX, iPageY);
20602     },
20603
20604     /**
20605      * Sets the element to the location of the mousedown or click event,
20606      * maintaining the cursor location relative to the location on the element
20607      * that was clicked.  Override this if you want to place the element in a
20608      * location other than where the cursor is.
20609      * @method alignElWithMouse
20610      * @param {HTMLElement} el the element to move
20611      * @param {int} iPageX the X coordinate of the mousedown or drag event
20612      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20613      */
20614     alignElWithMouse: function(el, iPageX, iPageY) {
20615         var oCoord = this.getTargetCoord(iPageX, iPageY);
20616         var fly = el.dom ? el : Roo.fly(el);
20617         if (!this.deltaSetXY) {
20618             var aCoord = [oCoord.x, oCoord.y];
20619             fly.setXY(aCoord);
20620             var newLeft = fly.getLeft(true);
20621             var newTop  = fly.getTop(true);
20622             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20623         } else {
20624             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20625         }
20626
20627         this.cachePosition(oCoord.x, oCoord.y);
20628         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20629         return oCoord;
20630     },
20631
20632     /**
20633      * Saves the most recent position so that we can reset the constraints and
20634      * tick marks on-demand.  We need to know this so that we can calculate the
20635      * number of pixels the element is offset from its original position.
20636      * @method cachePosition
20637      * @param iPageX the current x position (optional, this just makes it so we
20638      * don't have to look it up again)
20639      * @param iPageY the current y position (optional, this just makes it so we
20640      * don't have to look it up again)
20641      */
20642     cachePosition: function(iPageX, iPageY) {
20643         if (iPageX) {
20644             this.lastPageX = iPageX;
20645             this.lastPageY = iPageY;
20646         } else {
20647             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20648             this.lastPageX = aCoord[0];
20649             this.lastPageY = aCoord[1];
20650         }
20651     },
20652
20653     /**
20654      * Auto-scroll the window if the dragged object has been moved beyond the
20655      * visible window boundary.
20656      * @method autoScroll
20657      * @param {int} x the drag element's x position
20658      * @param {int} y the drag element's y position
20659      * @param {int} h the height of the drag element
20660      * @param {int} w the width of the drag element
20661      * @private
20662      */
20663     autoScroll: function(x, y, h, w) {
20664
20665         if (this.scroll) {
20666             // The client height
20667             var clientH = Roo.lib.Dom.getViewWidth();
20668
20669             // The client width
20670             var clientW = Roo.lib.Dom.getViewHeight();
20671
20672             // The amt scrolled down
20673             var st = this.DDM.getScrollTop();
20674
20675             // The amt scrolled right
20676             var sl = this.DDM.getScrollLeft();
20677
20678             // Location of the bottom of the element
20679             var bot = h + y;
20680
20681             // Location of the right of the element
20682             var right = w + x;
20683
20684             // The distance from the cursor to the bottom of the visible area,
20685             // adjusted so that we don't scroll if the cursor is beyond the
20686             // element drag constraints
20687             var toBot = (clientH + st - y - this.deltaY);
20688
20689             // The distance from the cursor to the right of the visible area
20690             var toRight = (clientW + sl - x - this.deltaX);
20691
20692
20693             // How close to the edge the cursor must be before we scroll
20694             // var thresh = (document.all) ? 100 : 40;
20695             var thresh = 40;
20696
20697             // How many pixels to scroll per autoscroll op.  This helps to reduce
20698             // clunky scrolling. IE is more sensitive about this ... it needs this
20699             // value to be higher.
20700             var scrAmt = (document.all) ? 80 : 30;
20701
20702             // Scroll down if we are near the bottom of the visible page and the
20703             // obj extends below the crease
20704             if ( bot > clientH && toBot < thresh ) {
20705                 window.scrollTo(sl, st + scrAmt);
20706             }
20707
20708             // Scroll up if the window is scrolled down and the top of the object
20709             // goes above the top border
20710             if ( y < st && st > 0 && y - st < thresh ) {
20711                 window.scrollTo(sl, st - scrAmt);
20712             }
20713
20714             // Scroll right if the obj is beyond the right border and the cursor is
20715             // near the border.
20716             if ( right > clientW && toRight < thresh ) {
20717                 window.scrollTo(sl + scrAmt, st);
20718             }
20719
20720             // Scroll left if the window has been scrolled to the right and the obj
20721             // extends past the left border
20722             if ( x < sl && sl > 0 && x - sl < thresh ) {
20723                 window.scrollTo(sl - scrAmt, st);
20724             }
20725         }
20726     },
20727
20728     /**
20729      * Finds the location the element should be placed if we want to move
20730      * it to where the mouse location less the click offset would place us.
20731      * @method getTargetCoord
20732      * @param {int} iPageX the X coordinate of the click
20733      * @param {int} iPageY the Y coordinate of the click
20734      * @return an object that contains the coordinates (Object.x and Object.y)
20735      * @private
20736      */
20737     getTargetCoord: function(iPageX, iPageY) {
20738
20739
20740         var x = iPageX - this.deltaX;
20741         var y = iPageY - this.deltaY;
20742
20743         if (this.constrainX) {
20744             if (x < this.minX) { x = this.minX; }
20745             if (x > this.maxX) { x = this.maxX; }
20746         }
20747
20748         if (this.constrainY) {
20749             if (y < this.minY) { y = this.minY; }
20750             if (y > this.maxY) { y = this.maxY; }
20751         }
20752
20753         x = this.getTick(x, this.xTicks);
20754         y = this.getTick(y, this.yTicks);
20755
20756
20757         return {x:x, y:y};
20758     },
20759
20760     /*
20761      * Sets up config options specific to this class. Overrides
20762      * Roo.dd.DragDrop, but all versions of this method through the
20763      * inheritance chain are called
20764      */
20765     applyConfig: function() {
20766         Roo.dd.DD.superclass.applyConfig.call(this);
20767         this.scroll = (this.config.scroll !== false);
20768     },
20769
20770     /*
20771      * Event that fires prior to the onMouseDown event.  Overrides
20772      * Roo.dd.DragDrop.
20773      */
20774     b4MouseDown: function(e) {
20775         // this.resetConstraints();
20776         this.autoOffset(e.getPageX(),
20777                             e.getPageY());
20778     },
20779
20780     /*
20781      * Event that fires prior to the onDrag event.  Overrides
20782      * Roo.dd.DragDrop.
20783      */
20784     b4Drag: function(e) {
20785         this.setDragElPos(e.getPageX(),
20786                             e.getPageY());
20787     },
20788
20789     toString: function() {
20790         return ("DD " + this.id);
20791     }
20792
20793     //////////////////////////////////////////////////////////////////////////
20794     // Debugging ygDragDrop events that can be overridden
20795     //////////////////////////////////////////////////////////////////////////
20796     /*
20797     startDrag: function(x, y) {
20798     },
20799
20800     onDrag: function(e) {
20801     },
20802
20803     onDragEnter: function(e, id) {
20804     },
20805
20806     onDragOver: function(e, id) {
20807     },
20808
20809     onDragOut: function(e, id) {
20810     },
20811
20812     onDragDrop: function(e, id) {
20813     },
20814
20815     endDrag: function(e) {
20816     }
20817
20818     */
20819
20820 });/*
20821  * Based on:
20822  * Ext JS Library 1.1.1
20823  * Copyright(c) 2006-2007, Ext JS, LLC.
20824  *
20825  * Originally Released Under LGPL - original licence link has changed is not relivant.
20826  *
20827  * Fork - LGPL
20828  * <script type="text/javascript">
20829  */
20830
20831 /**
20832  * @class Roo.dd.DDProxy
20833  * A DragDrop implementation that inserts an empty, bordered div into
20834  * the document that follows the cursor during drag operations.  At the time of
20835  * the click, the frame div is resized to the dimensions of the linked html
20836  * element, and moved to the exact location of the linked element.
20837  *
20838  * References to the "frame" element refer to the single proxy element that
20839  * was created to be dragged in place of all DDProxy elements on the
20840  * page.
20841  *
20842  * @extends Roo.dd.DD
20843  * @constructor
20844  * @param {String} id the id of the linked html element
20845  * @param {String} sGroup the group of related DragDrop objects
20846  * @param {object} config an object containing configurable attributes
20847  *                Valid properties for DDProxy in addition to those in DragDrop:
20848  *                   resizeFrame, centerFrame, dragElId
20849  */
20850 Roo.dd.DDProxy = function(id, sGroup, config) {
20851     if (id) {
20852         this.init(id, sGroup, config);
20853         this.initFrame();
20854     }
20855 };
20856
20857 /**
20858  * The default drag frame div id
20859  * @property Roo.dd.DDProxy.dragElId
20860  * @type String
20861  * @static
20862  */
20863 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20864
20865 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20866
20867     /**
20868      * By default we resize the drag frame to be the same size as the element
20869      * we want to drag (this is to get the frame effect).  We can turn it off
20870      * if we want a different behavior.
20871      * @property resizeFrame
20872      * @type boolean
20873      */
20874     resizeFrame: true,
20875
20876     /**
20877      * By default the frame is positioned exactly where the drag element is, so
20878      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20879      * you do not have constraints on the obj is to have the drag frame centered
20880      * around the cursor.  Set centerFrame to true for this effect.
20881      * @property centerFrame
20882      * @type boolean
20883      */
20884     centerFrame: false,
20885
20886     /**
20887      * Creates the proxy element if it does not yet exist
20888      * @method createFrame
20889      */
20890     createFrame: function() {
20891         var self = this;
20892         var body = document.body;
20893
20894         if (!body || !body.firstChild) {
20895             setTimeout( function() { self.createFrame(); }, 50 );
20896             return;
20897         }
20898
20899         var div = this.getDragEl();
20900
20901         if (!div) {
20902             div    = document.createElement("div");
20903             div.id = this.dragElId;
20904             var s  = div.style;
20905
20906             s.position   = "absolute";
20907             s.visibility = "hidden";
20908             s.cursor     = "move";
20909             s.border     = "2px solid #aaa";
20910             s.zIndex     = 999;
20911
20912             // appendChild can blow up IE if invoked prior to the window load event
20913             // while rendering a table.  It is possible there are other scenarios
20914             // that would cause this to happen as well.
20915             body.insertBefore(div, body.firstChild);
20916         }
20917     },
20918
20919     /**
20920      * Initialization for the drag frame element.  Must be called in the
20921      * constructor of all subclasses
20922      * @method initFrame
20923      */
20924     initFrame: function() {
20925         this.createFrame();
20926     },
20927
20928     applyConfig: function() {
20929         Roo.dd.DDProxy.superclass.applyConfig.call(this);
20930
20931         this.resizeFrame = (this.config.resizeFrame !== false);
20932         this.centerFrame = (this.config.centerFrame);
20933         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
20934     },
20935
20936     /**
20937      * Resizes the drag frame to the dimensions of the clicked object, positions
20938      * it over the object, and finally displays it
20939      * @method showFrame
20940      * @param {int} iPageX X click position
20941      * @param {int} iPageY Y click position
20942      * @private
20943      */
20944     showFrame: function(iPageX, iPageY) {
20945         var el = this.getEl();
20946         var dragEl = this.getDragEl();
20947         var s = dragEl.style;
20948
20949         this._resizeProxy();
20950
20951         if (this.centerFrame) {
20952             this.setDelta( Math.round(parseInt(s.width,  10)/2),
20953                            Math.round(parseInt(s.height, 10)/2) );
20954         }
20955
20956         this.setDragElPos(iPageX, iPageY);
20957
20958         Roo.fly(dragEl).show();
20959     },
20960
20961     /**
20962      * The proxy is automatically resized to the dimensions of the linked
20963      * element when a drag is initiated, unless resizeFrame is set to false
20964      * @method _resizeProxy
20965      * @private
20966      */
20967     _resizeProxy: function() {
20968         if (this.resizeFrame) {
20969             var el = this.getEl();
20970             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
20971         }
20972     },
20973
20974     // overrides Roo.dd.DragDrop
20975     b4MouseDown: function(e) {
20976         var x = e.getPageX();
20977         var y = e.getPageY();
20978         this.autoOffset(x, y);
20979         this.setDragElPos(x, y);
20980     },
20981
20982     // overrides Roo.dd.DragDrop
20983     b4StartDrag: function(x, y) {
20984         // show the drag frame
20985         this.showFrame(x, y);
20986     },
20987
20988     // overrides Roo.dd.DragDrop
20989     b4EndDrag: function(e) {
20990         Roo.fly(this.getDragEl()).hide();
20991     },
20992
20993     // overrides Roo.dd.DragDrop
20994     // By default we try to move the element to the last location of the frame.
20995     // This is so that the default behavior mirrors that of Roo.dd.DD.
20996     endDrag: function(e) {
20997
20998         var lel = this.getEl();
20999         var del = this.getDragEl();
21000
21001         // Show the drag frame briefly so we can get its position
21002         del.style.visibility = "";
21003
21004         this.beforeMove();
21005         // Hide the linked element before the move to get around a Safari
21006         // rendering bug.
21007         lel.style.visibility = "hidden";
21008         Roo.dd.DDM.moveToEl(lel, del);
21009         del.style.visibility = "hidden";
21010         lel.style.visibility = "";
21011
21012         this.afterDrag();
21013     },
21014
21015     beforeMove : function(){
21016
21017     },
21018
21019     afterDrag : function(){
21020
21021     },
21022
21023     toString: function() {
21024         return ("DDProxy " + this.id);
21025     }
21026
21027 });
21028 /*
21029  * Based on:
21030  * Ext JS Library 1.1.1
21031  * Copyright(c) 2006-2007, Ext JS, LLC.
21032  *
21033  * Originally Released Under LGPL - original licence link has changed is not relivant.
21034  *
21035  * Fork - LGPL
21036  * <script type="text/javascript">
21037  */
21038
21039  /**
21040  * @class Roo.dd.DDTarget
21041  * A DragDrop implementation that does not move, but can be a drop
21042  * target.  You would get the same result by simply omitting implementation
21043  * for the event callbacks, but this way we reduce the processing cost of the
21044  * event listener and the callbacks.
21045  * @extends Roo.dd.DragDrop
21046  * @constructor
21047  * @param {String} id the id of the element that is a drop target
21048  * @param {String} sGroup the group of related DragDrop objects
21049  * @param {object} config an object containing configurable attributes
21050  *                 Valid properties for DDTarget in addition to those in
21051  *                 DragDrop:
21052  *                    none
21053  */
21054 Roo.dd.DDTarget = function(id, sGroup, config) {
21055     if (id) {
21056         this.initTarget(id, sGroup, config);
21057     }
21058     if (config.listeners || config.events) { 
21059        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21060             listeners : config.listeners || {}, 
21061             events : config.events || {} 
21062         });    
21063     }
21064 };
21065
21066 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21067 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21068     toString: function() {
21069         return ("DDTarget " + this.id);
21070     }
21071 });
21072 /*
21073  * Based on:
21074  * Ext JS Library 1.1.1
21075  * Copyright(c) 2006-2007, Ext JS, LLC.
21076  *
21077  * Originally Released Under LGPL - original licence link has changed is not relivant.
21078  *
21079  * Fork - LGPL
21080  * <script type="text/javascript">
21081  */
21082  
21083
21084 /**
21085  * @class Roo.dd.ScrollManager
21086  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21087  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21088  * @singleton
21089  */
21090 Roo.dd.ScrollManager = function(){
21091     var ddm = Roo.dd.DragDropMgr;
21092     var els = {};
21093     var dragEl = null;
21094     var proc = {};
21095     
21096     
21097     
21098     var onStop = function(e){
21099         dragEl = null;
21100         clearProc();
21101     };
21102     
21103     var triggerRefresh = function(){
21104         if(ddm.dragCurrent){
21105              ddm.refreshCache(ddm.dragCurrent.groups);
21106         }
21107     };
21108     
21109     var doScroll = function(){
21110         if(ddm.dragCurrent){
21111             var dds = Roo.dd.ScrollManager;
21112             if(!dds.animate){
21113                 if(proc.el.scroll(proc.dir, dds.increment)){
21114                     triggerRefresh();
21115                 }
21116             }else{
21117                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21118             }
21119         }
21120     };
21121     
21122     var clearProc = function(){
21123         if(proc.id){
21124             clearInterval(proc.id);
21125         }
21126         proc.id = 0;
21127         proc.el = null;
21128         proc.dir = "";
21129     };
21130     
21131     var startProc = function(el, dir){
21132          Roo.log('scroll startproc');
21133         clearProc();
21134         proc.el = el;
21135         proc.dir = dir;
21136         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21137     };
21138     
21139     var onFire = function(e, isDrop){
21140        
21141         if(isDrop || !ddm.dragCurrent){ return; }
21142         var dds = Roo.dd.ScrollManager;
21143         if(!dragEl || dragEl != ddm.dragCurrent){
21144             dragEl = ddm.dragCurrent;
21145             // refresh regions on drag start
21146             dds.refreshCache();
21147         }
21148         
21149         var xy = Roo.lib.Event.getXY(e);
21150         var pt = new Roo.lib.Point(xy[0], xy[1]);
21151         for(var id in els){
21152             var el = els[id], r = el._region;
21153             if(r && r.contains(pt) && el.isScrollable()){
21154                 if(r.bottom - pt.y <= dds.thresh){
21155                     if(proc.el != el){
21156                         startProc(el, "down");
21157                     }
21158                     return;
21159                 }else if(r.right - pt.x <= dds.thresh){
21160                     if(proc.el != el){
21161                         startProc(el, "left");
21162                     }
21163                     return;
21164                 }else if(pt.y - r.top <= dds.thresh){
21165                     if(proc.el != el){
21166                         startProc(el, "up");
21167                     }
21168                     return;
21169                 }else if(pt.x - r.left <= dds.thresh){
21170                     if(proc.el != el){
21171                         startProc(el, "right");
21172                     }
21173                     return;
21174                 }
21175             }
21176         }
21177         clearProc();
21178     };
21179     
21180     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21181     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21182     
21183     return {
21184         /**
21185          * Registers new overflow element(s) to auto scroll
21186          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21187          */
21188         register : function(el){
21189             if(el instanceof Array){
21190                 for(var i = 0, len = el.length; i < len; i++) {
21191                         this.register(el[i]);
21192                 }
21193             }else{
21194                 el = Roo.get(el);
21195                 els[el.id] = el;
21196             }
21197             Roo.dd.ScrollManager.els = els;
21198         },
21199         
21200         /**
21201          * Unregisters overflow element(s) so they are no longer scrolled
21202          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21203          */
21204         unregister : function(el){
21205             if(el instanceof Array){
21206                 for(var i = 0, len = el.length; i < len; i++) {
21207                         this.unregister(el[i]);
21208                 }
21209             }else{
21210                 el = Roo.get(el);
21211                 delete els[el.id];
21212             }
21213         },
21214         
21215         /**
21216          * The number of pixels from the edge of a container the pointer needs to be to 
21217          * trigger scrolling (defaults to 25)
21218          * @type Number
21219          */
21220         thresh : 25,
21221         
21222         /**
21223          * The number of pixels to scroll in each scroll increment (defaults to 50)
21224          * @type Number
21225          */
21226         increment : 100,
21227         
21228         /**
21229          * The frequency of scrolls in milliseconds (defaults to 500)
21230          * @type Number
21231          */
21232         frequency : 500,
21233         
21234         /**
21235          * True to animate the scroll (defaults to true)
21236          * @type Boolean
21237          */
21238         animate: true,
21239         
21240         /**
21241          * The animation duration in seconds - 
21242          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21243          * @type Number
21244          */
21245         animDuration: .4,
21246         
21247         /**
21248          * Manually trigger a cache refresh.
21249          */
21250         refreshCache : function(){
21251             for(var id in els){
21252                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21253                     els[id]._region = els[id].getRegion();
21254                 }
21255             }
21256         }
21257     };
21258 }();/*
21259  * Based on:
21260  * Ext JS Library 1.1.1
21261  * Copyright(c) 2006-2007, Ext JS, LLC.
21262  *
21263  * Originally Released Under LGPL - original licence link has changed is not relivant.
21264  *
21265  * Fork - LGPL
21266  * <script type="text/javascript">
21267  */
21268  
21269
21270 /**
21271  * @class Roo.dd.Registry
21272  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21273  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21274  * @singleton
21275  */
21276 Roo.dd.Registry = function(){
21277     var elements = {}; 
21278     var handles = {}; 
21279     var autoIdSeed = 0;
21280
21281     var getId = function(el, autogen){
21282         if(typeof el == "string"){
21283             return el;
21284         }
21285         var id = el.id;
21286         if(!id && autogen !== false){
21287             id = "roodd-" + (++autoIdSeed);
21288             el.id = id;
21289         }
21290         return id;
21291     };
21292     
21293     return {
21294     /**
21295      * Register a drag drop element
21296      * @param {String|HTMLElement} element The id or DOM node to register
21297      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21298      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21299      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21300      * populated in the data object (if applicable):
21301      * <pre>
21302 Value      Description<br />
21303 ---------  ------------------------------------------<br />
21304 handles    Array of DOM nodes that trigger dragging<br />
21305            for the element being registered<br />
21306 isHandle   True if the element passed in triggers<br />
21307            dragging itself, else false
21308 </pre>
21309      */
21310         register : function(el, data){
21311             data = data || {};
21312             if(typeof el == "string"){
21313                 el = document.getElementById(el);
21314             }
21315             data.ddel = el;
21316             elements[getId(el)] = data;
21317             if(data.isHandle !== false){
21318                 handles[data.ddel.id] = data;
21319             }
21320             if(data.handles){
21321                 var hs = data.handles;
21322                 for(var i = 0, len = hs.length; i < len; i++){
21323                         handles[getId(hs[i])] = data;
21324                 }
21325             }
21326         },
21327
21328     /**
21329      * Unregister a drag drop element
21330      * @param {String|HTMLElement}  element The id or DOM node to unregister
21331      */
21332         unregister : function(el){
21333             var id = getId(el, false);
21334             var data = elements[id];
21335             if(data){
21336                 delete elements[id];
21337                 if(data.handles){
21338                     var hs = data.handles;
21339                     for(var i = 0, len = hs.length; i < len; i++){
21340                         delete handles[getId(hs[i], false)];
21341                     }
21342                 }
21343             }
21344         },
21345
21346     /**
21347      * Returns the handle registered for a DOM Node by id
21348      * @param {String|HTMLElement} id The DOM node or id to look up
21349      * @return {Object} handle The custom handle data
21350      */
21351         getHandle : function(id){
21352             if(typeof id != "string"){ // must be element?
21353                 id = id.id;
21354             }
21355             return handles[id];
21356         },
21357
21358     /**
21359      * Returns the handle that is registered for the DOM node that is the target of the event
21360      * @param {Event} e The event
21361      * @return {Object} handle The custom handle data
21362      */
21363         getHandleFromEvent : function(e){
21364             var t = Roo.lib.Event.getTarget(e);
21365             return t ? handles[t.id] : null;
21366         },
21367
21368     /**
21369      * Returns a custom data object that is registered for a DOM node by id
21370      * @param {String|HTMLElement} id The DOM node or id to look up
21371      * @return {Object} data The custom data
21372      */
21373         getTarget : function(id){
21374             if(typeof id != "string"){ // must be element?
21375                 id = id.id;
21376             }
21377             return elements[id];
21378         },
21379
21380     /**
21381      * Returns a custom data object that is registered for the DOM node that is the target of the event
21382      * @param {Event} e The event
21383      * @return {Object} data The custom data
21384      */
21385         getTargetFromEvent : function(e){
21386             var t = Roo.lib.Event.getTarget(e);
21387             return t ? elements[t.id] || handles[t.id] : null;
21388         }
21389     };
21390 }();/*
21391  * Based on:
21392  * Ext JS Library 1.1.1
21393  * Copyright(c) 2006-2007, Ext JS, LLC.
21394  *
21395  * Originally Released Under LGPL - original licence link has changed is not relivant.
21396  *
21397  * Fork - LGPL
21398  * <script type="text/javascript">
21399  */
21400  
21401
21402 /**
21403  * @class Roo.dd.StatusProxy
21404  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21405  * default drag proxy used by all Roo.dd components.
21406  * @constructor
21407  * @param {Object} config
21408  */
21409 Roo.dd.StatusProxy = function(config){
21410     Roo.apply(this, config);
21411     this.id = this.id || Roo.id();
21412     this.el = new Roo.Layer({
21413         dh: {
21414             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21415                 {tag: "div", cls: "x-dd-drop-icon"},
21416                 {tag: "div", cls: "x-dd-drag-ghost"}
21417             ]
21418         }, 
21419         shadow: !config || config.shadow !== false
21420     });
21421     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21422     this.dropStatus = this.dropNotAllowed;
21423 };
21424
21425 Roo.dd.StatusProxy.prototype = {
21426     /**
21427      * @cfg {String} dropAllowed
21428      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21429      */
21430     dropAllowed : "x-dd-drop-ok",
21431     /**
21432      * @cfg {String} dropNotAllowed
21433      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21434      */
21435     dropNotAllowed : "x-dd-drop-nodrop",
21436
21437     /**
21438      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21439      * over the current target element.
21440      * @param {String} cssClass The css class for the new drop status indicator image
21441      */
21442     setStatus : function(cssClass){
21443         cssClass = cssClass || this.dropNotAllowed;
21444         if(this.dropStatus != cssClass){
21445             this.el.replaceClass(this.dropStatus, cssClass);
21446             this.dropStatus = cssClass;
21447         }
21448     },
21449
21450     /**
21451      * Resets the status indicator to the default dropNotAllowed value
21452      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21453      */
21454     reset : function(clearGhost){
21455         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21456         this.dropStatus = this.dropNotAllowed;
21457         if(clearGhost){
21458             this.ghost.update("");
21459         }
21460     },
21461
21462     /**
21463      * Updates the contents of the ghost element
21464      * @param {String} html The html that will replace the current innerHTML of the ghost element
21465      */
21466     update : function(html){
21467         if(typeof html == "string"){
21468             this.ghost.update(html);
21469         }else{
21470             this.ghost.update("");
21471             html.style.margin = "0";
21472             this.ghost.dom.appendChild(html);
21473         }
21474         // ensure float = none set?? cant remember why though.
21475         var el = this.ghost.dom.firstChild;
21476                 if(el){
21477                         Roo.fly(el).setStyle('float', 'none');
21478                 }
21479     },
21480     
21481     /**
21482      * Returns the underlying proxy {@link Roo.Layer}
21483      * @return {Roo.Layer} el
21484     */
21485     getEl : function(){
21486         return this.el;
21487     },
21488
21489     /**
21490      * Returns the ghost element
21491      * @return {Roo.Element} el
21492      */
21493     getGhost : function(){
21494         return this.ghost;
21495     },
21496
21497     /**
21498      * Hides the proxy
21499      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21500      */
21501     hide : function(clear){
21502         this.el.hide();
21503         if(clear){
21504             this.reset(true);
21505         }
21506     },
21507
21508     /**
21509      * Stops the repair animation if it's currently running
21510      */
21511     stop : function(){
21512         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21513             this.anim.stop();
21514         }
21515     },
21516
21517     /**
21518      * Displays this proxy
21519      */
21520     show : function(){
21521         this.el.show();
21522     },
21523
21524     /**
21525      * Force the Layer to sync its shadow and shim positions to the element
21526      */
21527     sync : function(){
21528         this.el.sync();
21529     },
21530
21531     /**
21532      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21533      * invalid drop operation by the item being dragged.
21534      * @param {Array} xy The XY position of the element ([x, y])
21535      * @param {Function} callback The function to call after the repair is complete
21536      * @param {Object} scope The scope in which to execute the callback
21537      */
21538     repair : function(xy, callback, scope){
21539         this.callback = callback;
21540         this.scope = scope;
21541         if(xy && this.animRepair !== false){
21542             this.el.addClass("x-dd-drag-repair");
21543             this.el.hideUnders(true);
21544             this.anim = this.el.shift({
21545                 duration: this.repairDuration || .5,
21546                 easing: 'easeOut',
21547                 xy: xy,
21548                 stopFx: true,
21549                 callback: this.afterRepair,
21550                 scope: this
21551             });
21552         }else{
21553             this.afterRepair();
21554         }
21555     },
21556
21557     // private
21558     afterRepair : function(){
21559         this.hide(true);
21560         if(typeof this.callback == "function"){
21561             this.callback.call(this.scope || this);
21562         }
21563         this.callback = null;
21564         this.scope = null;
21565     }
21566 };/*
21567  * Based on:
21568  * Ext JS Library 1.1.1
21569  * Copyright(c) 2006-2007, Ext JS, LLC.
21570  *
21571  * Originally Released Under LGPL - original licence link has changed is not relivant.
21572  *
21573  * Fork - LGPL
21574  * <script type="text/javascript">
21575  */
21576
21577 /**
21578  * @class Roo.dd.DragSource
21579  * @extends Roo.dd.DDProxy
21580  * A simple class that provides the basic implementation needed to make any element draggable.
21581  * @constructor
21582  * @param {String/HTMLElement/Element} el The container element
21583  * @param {Object} config
21584  */
21585 Roo.dd.DragSource = function(el, config){
21586     this.el = Roo.get(el);
21587     this.dragData = {};
21588     
21589     Roo.apply(this, config);
21590     
21591     if(!this.proxy){
21592         this.proxy = new Roo.dd.StatusProxy();
21593     }
21594
21595     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21596           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21597     
21598     this.dragging = false;
21599 };
21600
21601 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21602     /**
21603      * @cfg {String} dropAllowed
21604      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21605      */
21606     dropAllowed : "x-dd-drop-ok",
21607     /**
21608      * @cfg {String} dropNotAllowed
21609      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21610      */
21611     dropNotAllowed : "x-dd-drop-nodrop",
21612
21613     /**
21614      * Returns the data object associated with this drag source
21615      * @return {Object} data An object containing arbitrary data
21616      */
21617     getDragData : function(e){
21618         return this.dragData;
21619     },
21620
21621     // private
21622     onDragEnter : function(e, id){
21623         var target = Roo.dd.DragDropMgr.getDDById(id);
21624         this.cachedTarget = target;
21625         if(this.beforeDragEnter(target, e, id) !== false){
21626             if(target.isNotifyTarget){
21627                 var status = target.notifyEnter(this, e, this.dragData);
21628                 this.proxy.setStatus(status);
21629             }else{
21630                 this.proxy.setStatus(this.dropAllowed);
21631             }
21632             
21633             if(this.afterDragEnter){
21634                 /**
21635                  * An empty function by default, but provided so that you can perform a custom action
21636                  * when the dragged item enters the drop target by providing an implementation.
21637                  * @param {Roo.dd.DragDrop} target The drop target
21638                  * @param {Event} e The event object
21639                  * @param {String} id The id of the dragged element
21640                  * @method afterDragEnter
21641                  */
21642                 this.afterDragEnter(target, e, id);
21643             }
21644         }
21645     },
21646
21647     /**
21648      * An empty function by default, but provided so that you can perform a custom action
21649      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21650      * @param {Roo.dd.DragDrop} target The drop target
21651      * @param {Event} e The event object
21652      * @param {String} id The id of the dragged element
21653      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21654      */
21655     beforeDragEnter : function(target, e, id){
21656         return true;
21657     },
21658
21659     // private
21660     alignElWithMouse: function() {
21661         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21662         this.proxy.sync();
21663     },
21664
21665     // private
21666     onDragOver : function(e, id){
21667         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21668         if(this.beforeDragOver(target, e, id) !== false){
21669             if(target.isNotifyTarget){
21670                 var status = target.notifyOver(this, e, this.dragData);
21671                 this.proxy.setStatus(status);
21672             }
21673
21674             if(this.afterDragOver){
21675                 /**
21676                  * An empty function by default, but provided so that you can perform a custom action
21677                  * while the dragged item is over the drop target by providing an implementation.
21678                  * @param {Roo.dd.DragDrop} target The drop target
21679                  * @param {Event} e The event object
21680                  * @param {String} id The id of the dragged element
21681                  * @method afterDragOver
21682                  */
21683                 this.afterDragOver(target, e, id);
21684             }
21685         }
21686     },
21687
21688     /**
21689      * An empty function by default, but provided so that you can perform a custom action
21690      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21691      * @param {Roo.dd.DragDrop} target The drop target
21692      * @param {Event} e The event object
21693      * @param {String} id The id of the dragged element
21694      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21695      */
21696     beforeDragOver : function(target, e, id){
21697         return true;
21698     },
21699
21700     // private
21701     onDragOut : function(e, id){
21702         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21703         if(this.beforeDragOut(target, e, id) !== false){
21704             if(target.isNotifyTarget){
21705                 target.notifyOut(this, e, this.dragData);
21706             }
21707             this.proxy.reset();
21708             if(this.afterDragOut){
21709                 /**
21710                  * An empty function by default, but provided so that you can perform a custom action
21711                  * after the dragged item is dragged out of the target without dropping.
21712                  * @param {Roo.dd.DragDrop} target The drop target
21713                  * @param {Event} e The event object
21714                  * @param {String} id The id of the dragged element
21715                  * @method afterDragOut
21716                  */
21717                 this.afterDragOut(target, e, id);
21718             }
21719         }
21720         this.cachedTarget = null;
21721     },
21722
21723     /**
21724      * An empty function by default, but provided so that you can perform a custom action before the dragged
21725      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21726      * @param {Roo.dd.DragDrop} target The drop target
21727      * @param {Event} e The event object
21728      * @param {String} id The id of the dragged element
21729      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21730      */
21731     beforeDragOut : function(target, e, id){
21732         return true;
21733     },
21734     
21735     // private
21736     onDragDrop : function(e, id){
21737         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21738         if(this.beforeDragDrop(target, e, id) !== false){
21739             if(target.isNotifyTarget){
21740                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21741                     this.onValidDrop(target, e, id);
21742                 }else{
21743                     this.onInvalidDrop(target, e, id);
21744                 }
21745             }else{
21746                 this.onValidDrop(target, e, id);
21747             }
21748             
21749             if(this.afterDragDrop){
21750                 /**
21751                  * An empty function by default, but provided so that you can perform a custom action
21752                  * after a valid drag drop has occurred by providing an implementation.
21753                  * @param {Roo.dd.DragDrop} target The drop target
21754                  * @param {Event} e The event object
21755                  * @param {String} id The id of the dropped element
21756                  * @method afterDragDrop
21757                  */
21758                 this.afterDragDrop(target, e, id);
21759             }
21760         }
21761         delete this.cachedTarget;
21762     },
21763
21764     /**
21765      * An empty function by default, but provided so that you can perform a custom action before the dragged
21766      * item is dropped onto the target and optionally cancel the onDragDrop.
21767      * @param {Roo.dd.DragDrop} target The drop target
21768      * @param {Event} e The event object
21769      * @param {String} id The id of the dragged element
21770      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21771      */
21772     beforeDragDrop : function(target, e, id){
21773         return true;
21774     },
21775
21776     // private
21777     onValidDrop : function(target, e, id){
21778         this.hideProxy();
21779         if(this.afterValidDrop){
21780             /**
21781              * An empty function by default, but provided so that you can perform a custom action
21782              * after a valid drop has occurred by providing an implementation.
21783              * @param {Object} target The target DD 
21784              * @param {Event} e The event object
21785              * @param {String} id The id of the dropped element
21786              * @method afterInvalidDrop
21787              */
21788             this.afterValidDrop(target, e, id);
21789         }
21790     },
21791
21792     // private
21793     getRepairXY : function(e, data){
21794         return this.el.getXY();  
21795     },
21796
21797     // private
21798     onInvalidDrop : function(target, e, id){
21799         this.beforeInvalidDrop(target, e, id);
21800         if(this.cachedTarget){
21801             if(this.cachedTarget.isNotifyTarget){
21802                 this.cachedTarget.notifyOut(this, e, this.dragData);
21803             }
21804             this.cacheTarget = null;
21805         }
21806         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21807
21808         if(this.afterInvalidDrop){
21809             /**
21810              * An empty function by default, but provided so that you can perform a custom action
21811              * after an invalid drop has occurred by providing an implementation.
21812              * @param {Event} e The event object
21813              * @param {String} id The id of the dropped element
21814              * @method afterInvalidDrop
21815              */
21816             this.afterInvalidDrop(e, id);
21817         }
21818     },
21819
21820     // private
21821     afterRepair : function(){
21822         if(Roo.enableFx){
21823             this.el.highlight(this.hlColor || "c3daf9");
21824         }
21825         this.dragging = false;
21826     },
21827
21828     /**
21829      * An empty function by default, but provided so that you can perform a custom action after an invalid
21830      * drop has occurred.
21831      * @param {Roo.dd.DragDrop} target The drop target
21832      * @param {Event} e The event object
21833      * @param {String} id The id of the dragged element
21834      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21835      */
21836     beforeInvalidDrop : function(target, e, id){
21837         return true;
21838     },
21839
21840     // private
21841     handleMouseDown : function(e){
21842         if(this.dragging) {
21843             return;
21844         }
21845         var data = this.getDragData(e);
21846         if(data && this.onBeforeDrag(data, e) !== false){
21847             this.dragData = data;
21848             this.proxy.stop();
21849             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21850         } 
21851     },
21852
21853     /**
21854      * An empty function by default, but provided so that you can perform a custom action before the initial
21855      * drag event begins and optionally cancel it.
21856      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21857      * @param {Event} e The event object
21858      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21859      */
21860     onBeforeDrag : function(data, e){
21861         return true;
21862     },
21863
21864     /**
21865      * An empty function by default, but provided so that you can perform a custom action once the initial
21866      * drag event has begun.  The drag cannot be canceled from this function.
21867      * @param {Number} x The x position of the click on the dragged object
21868      * @param {Number} y The y position of the click on the dragged object
21869      */
21870     onStartDrag : Roo.emptyFn,
21871
21872     // private - YUI override
21873     startDrag : function(x, y){
21874         this.proxy.reset();
21875         this.dragging = true;
21876         this.proxy.update("");
21877         this.onInitDrag(x, y);
21878         this.proxy.show();
21879     },
21880
21881     // private
21882     onInitDrag : function(x, y){
21883         var clone = this.el.dom.cloneNode(true);
21884         clone.id = Roo.id(); // prevent duplicate ids
21885         this.proxy.update(clone);
21886         this.onStartDrag(x, y);
21887         return true;
21888     },
21889
21890     /**
21891      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21892      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21893      */
21894     getProxy : function(){
21895         return this.proxy;  
21896     },
21897
21898     /**
21899      * Hides the drag source's {@link Roo.dd.StatusProxy}
21900      */
21901     hideProxy : function(){
21902         this.proxy.hide();  
21903         this.proxy.reset(true);
21904         this.dragging = false;
21905     },
21906
21907     // private
21908     triggerCacheRefresh : function(){
21909         Roo.dd.DDM.refreshCache(this.groups);
21910     },
21911
21912     // private - override to prevent hiding
21913     b4EndDrag: function(e) {
21914     },
21915
21916     // private - override to prevent moving
21917     endDrag : function(e){
21918         this.onEndDrag(this.dragData, e);
21919     },
21920
21921     // private
21922     onEndDrag : function(data, e){
21923     },
21924     
21925     // private - pin to cursor
21926     autoOffset : function(x, y) {
21927         this.setDelta(-12, -20);
21928     }    
21929 });/*
21930  * Based on:
21931  * Ext JS Library 1.1.1
21932  * Copyright(c) 2006-2007, Ext JS, LLC.
21933  *
21934  * Originally Released Under LGPL - original licence link has changed is not relivant.
21935  *
21936  * Fork - LGPL
21937  * <script type="text/javascript">
21938  */
21939
21940
21941 /**
21942  * @class Roo.dd.DropTarget
21943  * @extends Roo.dd.DDTarget
21944  * A simple class that provides the basic implementation needed to make any element a drop target that can have
21945  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
21946  * @constructor
21947  * @param {String/HTMLElement/Element} el The container element
21948  * @param {Object} config
21949  */
21950 Roo.dd.DropTarget = function(el, config){
21951     this.el = Roo.get(el);
21952     
21953     var listeners = false; ;
21954     if (config && config.listeners) {
21955         listeners= config.listeners;
21956         delete config.listeners;
21957     }
21958     Roo.apply(this, config);
21959     
21960     if(this.containerScroll){
21961         Roo.dd.ScrollManager.register(this.el);
21962     }
21963     this.addEvents( {
21964          /**
21965          * @scope Roo.dd.DropTarget
21966          */
21967          
21968          /**
21969          * @event enter
21970          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
21971          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
21972          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
21973          * 
21974          * IMPORTANT : it should set this.overClass and this.dropAllowed
21975          * 
21976          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21977          * @param {Event} e The event
21978          * @param {Object} data An object containing arbitrary data supplied by the drag source
21979          */
21980         "enter" : true,
21981         
21982          /**
21983          * @event over
21984          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
21985          * This method will be called on every mouse movement while the drag source is over the drop target.
21986          * This default implementation simply returns the dropAllowed config value.
21987          * 
21988          * IMPORTANT : it should set this.dropAllowed
21989          * 
21990          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21991          * @param {Event} e The event
21992          * @param {Object} data An object containing arbitrary data supplied by the drag source
21993          
21994          */
21995         "over" : true,
21996         /**
21997          * @event out
21998          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
21999          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22000          * overClass (if any) from the drop element.
22001          * 
22002          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22003          * @param {Event} e The event
22004          * @param {Object} data An object containing arbitrary data supplied by the drag source
22005          */
22006          "out" : true,
22007          
22008         /**
22009          * @event drop
22010          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22011          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22012          * implementation that does something to process the drop event and returns true so that the drag source's
22013          * repair action does not run.
22014          * 
22015          * IMPORTANT : it should set this.success
22016          * 
22017          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22018          * @param {Event} e The event
22019          * @param {Object} data An object containing arbitrary data supplied by the drag source
22020         */
22021          "drop" : true
22022     });
22023             
22024      
22025     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22026         this.el.dom, 
22027         this.ddGroup || this.group,
22028         {
22029             isTarget: true,
22030             listeners : listeners || {} 
22031            
22032         
22033         }
22034     );
22035
22036 };
22037
22038 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22039     /**
22040      * @cfg {String} overClass
22041      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22042      */
22043      /**
22044      * @cfg {String} ddGroup
22045      * The drag drop group to handle drop events for
22046      */
22047      
22048     /**
22049      * @cfg {String} dropAllowed
22050      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22051      */
22052     dropAllowed : "x-dd-drop-ok",
22053     /**
22054      * @cfg {String} dropNotAllowed
22055      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22056      */
22057     dropNotAllowed : "x-dd-drop-nodrop",
22058     /**
22059      * @cfg {boolean} success
22060      * set this after drop listener.. 
22061      */
22062     success : false,
22063     /**
22064      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22065      * if the drop point is valid for over/enter..
22066      */
22067     valid : false,
22068     // private
22069     isTarget : true,
22070
22071     // private
22072     isNotifyTarget : true,
22073     
22074     /**
22075      * @hide
22076      */
22077     notifyEnter : function(dd, e, data)
22078     {
22079         this.valid = true;
22080         this.fireEvent('enter', dd, e, data);
22081         if(this.overClass){
22082             this.el.addClass(this.overClass);
22083         }
22084         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22085             this.valid ? this.dropAllowed : this.dropNotAllowed
22086         );
22087     },
22088
22089     /**
22090      * @hide
22091      */
22092     notifyOver : function(dd, e, data)
22093     {
22094         this.valid = true;
22095         this.fireEvent('over', dd, e, data);
22096         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22097             this.valid ? this.dropAllowed : this.dropNotAllowed
22098         );
22099     },
22100
22101     /**
22102      * @hide
22103      */
22104     notifyOut : function(dd, e, data)
22105     {
22106         this.fireEvent('out', dd, e, data);
22107         if(this.overClass){
22108             this.el.removeClass(this.overClass);
22109         }
22110     },
22111
22112     /**
22113      * @hide
22114      */
22115     notifyDrop : function(dd, e, data)
22116     {
22117         this.success = false;
22118         this.fireEvent('drop', dd, e, data);
22119         return this.success;
22120     }
22121 });/*
22122  * Based on:
22123  * Ext JS Library 1.1.1
22124  * Copyright(c) 2006-2007, Ext JS, LLC.
22125  *
22126  * Originally Released Under LGPL - original licence link has changed is not relivant.
22127  *
22128  * Fork - LGPL
22129  * <script type="text/javascript">
22130  */
22131
22132
22133 /**
22134  * @class Roo.dd.DragZone
22135  * @extends Roo.dd.DragSource
22136  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22137  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22138  * @constructor
22139  * @param {String/HTMLElement/Element} el The container element
22140  * @param {Object} config
22141  */
22142 Roo.dd.DragZone = function(el, config){
22143     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22144     if(this.containerScroll){
22145         Roo.dd.ScrollManager.register(this.el);
22146     }
22147 };
22148
22149 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22150     /**
22151      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22152      * for auto scrolling during drag operations.
22153      */
22154     /**
22155      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22156      * method after a failed drop (defaults to "c3daf9" - light blue)
22157      */
22158
22159     /**
22160      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22161      * for a valid target to drag based on the mouse down. Override this method
22162      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22163      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22164      * @param {EventObject} e The mouse down event
22165      * @return {Object} The dragData
22166      */
22167     getDragData : function(e){
22168         return Roo.dd.Registry.getHandleFromEvent(e);
22169     },
22170     
22171     /**
22172      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22173      * this.dragData.ddel
22174      * @param {Number} x The x position of the click on the dragged object
22175      * @param {Number} y The y position of the click on the dragged object
22176      * @return {Boolean} true to continue the drag, false to cancel
22177      */
22178     onInitDrag : function(x, y){
22179         this.proxy.update(this.dragData.ddel.cloneNode(true));
22180         this.onStartDrag(x, y);
22181         return true;
22182     },
22183     
22184     /**
22185      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22186      */
22187     afterRepair : function(){
22188         if(Roo.enableFx){
22189             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22190         }
22191         this.dragging = false;
22192     },
22193
22194     /**
22195      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22196      * the XY of this.dragData.ddel
22197      * @param {EventObject} e The mouse up event
22198      * @return {Array} The xy location (e.g. [100, 200])
22199      */
22200     getRepairXY : function(e){
22201         return Roo.Element.fly(this.dragData.ddel).getXY();  
22202     }
22203 });/*
22204  * Based on:
22205  * Ext JS Library 1.1.1
22206  * Copyright(c) 2006-2007, Ext JS, LLC.
22207  *
22208  * Originally Released Under LGPL - original licence link has changed is not relivant.
22209  *
22210  * Fork - LGPL
22211  * <script type="text/javascript">
22212  */
22213 /**
22214  * @class Roo.dd.DropZone
22215  * @extends Roo.dd.DropTarget
22216  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22217  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22218  * @constructor
22219  * @param {String/HTMLElement/Element} el The container element
22220  * @param {Object} config
22221  */
22222 Roo.dd.DropZone = function(el, config){
22223     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22224 };
22225
22226 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22227     /**
22228      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22229      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22230      * provide your own custom lookup.
22231      * @param {Event} e The event
22232      * @return {Object} data The custom data
22233      */
22234     getTargetFromEvent : function(e){
22235         return Roo.dd.Registry.getTargetFromEvent(e);
22236     },
22237
22238     /**
22239      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22240      * that it has registered.  This method has no default implementation and should be overridden to provide
22241      * node-specific processing if necessary.
22242      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22243      * {@link #getTargetFromEvent} for this node)
22244      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22245      * @param {Event} e The event
22246      * @param {Object} data An object containing arbitrary data supplied by the drag source
22247      */
22248     onNodeEnter : function(n, dd, e, data){
22249         
22250     },
22251
22252     /**
22253      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22254      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22255      * overridden to provide the proper feedback.
22256      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22257      * {@link #getTargetFromEvent} for this node)
22258      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22259      * @param {Event} e The event
22260      * @param {Object} data An object containing arbitrary data supplied by the drag source
22261      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22262      * underlying {@link Roo.dd.StatusProxy} can be updated
22263      */
22264     onNodeOver : function(n, dd, e, data){
22265         return this.dropAllowed;
22266     },
22267
22268     /**
22269      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22270      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22271      * node-specific processing if necessary.
22272      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22273      * {@link #getTargetFromEvent} for this node)
22274      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22275      * @param {Event} e The event
22276      * @param {Object} data An object containing arbitrary data supplied by the drag source
22277      */
22278     onNodeOut : function(n, dd, e, data){
22279         
22280     },
22281
22282     /**
22283      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22284      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22285      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22286      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22287      * {@link #getTargetFromEvent} for this node)
22288      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22289      * @param {Event} e The event
22290      * @param {Object} data An object containing arbitrary data supplied by the drag source
22291      * @return {Boolean} True if the drop was valid, else false
22292      */
22293     onNodeDrop : function(n, dd, e, data){
22294         return false;
22295     },
22296
22297     /**
22298      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22299      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22300      * it should be overridden to provide the proper feedback if necessary.
22301      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22302      * @param {Event} e The event
22303      * @param {Object} data An object containing arbitrary data supplied by the drag source
22304      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22305      * underlying {@link Roo.dd.StatusProxy} can be updated
22306      */
22307     onContainerOver : function(dd, e, data){
22308         return this.dropNotAllowed;
22309     },
22310
22311     /**
22312      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22313      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22314      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22315      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22316      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22317      * @param {Event} e The event
22318      * @param {Object} data An object containing arbitrary data supplied by the drag source
22319      * @return {Boolean} True if the drop was valid, else false
22320      */
22321     onContainerDrop : function(dd, e, data){
22322         return false;
22323     },
22324
22325     /**
22326      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22327      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22328      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22329      * you should override this method and provide a custom implementation.
22330      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22331      * @param {Event} e The event
22332      * @param {Object} data An object containing arbitrary data supplied by the drag source
22333      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22334      * underlying {@link Roo.dd.StatusProxy} can be updated
22335      */
22336     notifyEnter : function(dd, e, data){
22337         return this.dropNotAllowed;
22338     },
22339
22340     /**
22341      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22342      * This method will be called on every mouse movement while the drag source is over the drop zone.
22343      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22344      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22345      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22346      * registered node, it will call {@link #onContainerOver}.
22347      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22348      * @param {Event} e The event
22349      * @param {Object} data An object containing arbitrary data supplied by the drag source
22350      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22351      * underlying {@link Roo.dd.StatusProxy} can be updated
22352      */
22353     notifyOver : function(dd, e, data){
22354         var n = this.getTargetFromEvent(e);
22355         if(!n){ // not over valid drop target
22356             if(this.lastOverNode){
22357                 this.onNodeOut(this.lastOverNode, dd, e, data);
22358                 this.lastOverNode = null;
22359             }
22360             return this.onContainerOver(dd, e, data);
22361         }
22362         if(this.lastOverNode != n){
22363             if(this.lastOverNode){
22364                 this.onNodeOut(this.lastOverNode, dd, e, data);
22365             }
22366             this.onNodeEnter(n, dd, e, data);
22367             this.lastOverNode = n;
22368         }
22369         return this.onNodeOver(n, dd, e, data);
22370     },
22371
22372     /**
22373      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22374      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22375      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22376      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22377      * @param {Event} e The event
22378      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22379      */
22380     notifyOut : function(dd, e, data){
22381         if(this.lastOverNode){
22382             this.onNodeOut(this.lastOverNode, dd, e, data);
22383             this.lastOverNode = null;
22384         }
22385     },
22386
22387     /**
22388      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22389      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22390      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22391      * otherwise it will call {@link #onContainerDrop}.
22392      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22393      * @param {Event} e The event
22394      * @param {Object} data An object containing arbitrary data supplied by the drag source
22395      * @return {Boolean} True if the drop was valid, else false
22396      */
22397     notifyDrop : function(dd, e, data){
22398         if(this.lastOverNode){
22399             this.onNodeOut(this.lastOverNode, dd, e, data);
22400             this.lastOverNode = null;
22401         }
22402         var n = this.getTargetFromEvent(e);
22403         return n ?
22404             this.onNodeDrop(n, dd, e, data) :
22405             this.onContainerDrop(dd, e, data);
22406     },
22407
22408     // private
22409     triggerCacheRefresh : function(){
22410         Roo.dd.DDM.refreshCache(this.groups);
22411     }  
22412 });/*
22413  * Based on:
22414  * Ext JS Library 1.1.1
22415  * Copyright(c) 2006-2007, Ext JS, LLC.
22416  *
22417  * Originally Released Under LGPL - original licence link has changed is not relivant.
22418  *
22419  * Fork - LGPL
22420  * <script type="text/javascript">
22421  */
22422
22423
22424 /**
22425  * @class Roo.data.SortTypes
22426  * @singleton
22427  * Defines the default sorting (casting?) comparison functions used when sorting data.
22428  */
22429 Roo.data.SortTypes = {
22430     /**
22431      * Default sort that does nothing
22432      * @param {Mixed} s The value being converted
22433      * @return {Mixed} The comparison value
22434      */
22435     none : function(s){
22436         return s;
22437     },
22438     
22439     /**
22440      * The regular expression used to strip tags
22441      * @type {RegExp}
22442      * @property
22443      */
22444     stripTagsRE : /<\/?[^>]+>/gi,
22445     
22446     /**
22447      * Strips all HTML tags to sort on text only
22448      * @param {Mixed} s The value being converted
22449      * @return {String} The comparison value
22450      */
22451     asText : function(s){
22452         return String(s).replace(this.stripTagsRE, "");
22453     },
22454     
22455     /**
22456      * Strips all HTML tags to sort on text only - Case insensitive
22457      * @param {Mixed} s The value being converted
22458      * @return {String} The comparison value
22459      */
22460     asUCText : function(s){
22461         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22462     },
22463     
22464     /**
22465      * Case insensitive string
22466      * @param {Mixed} s The value being converted
22467      * @return {String} The comparison value
22468      */
22469     asUCString : function(s) {
22470         return String(s).toUpperCase();
22471     },
22472     
22473     /**
22474      * Date sorting
22475      * @param {Mixed} s The value being converted
22476      * @return {Number} The comparison value
22477      */
22478     asDate : function(s) {
22479         if(!s){
22480             return 0;
22481         }
22482         if(s instanceof Date){
22483             return s.getTime();
22484         }
22485         return Date.parse(String(s));
22486     },
22487     
22488     /**
22489      * Float sorting
22490      * @param {Mixed} s The value being converted
22491      * @return {Float} The comparison value
22492      */
22493     asFloat : function(s) {
22494         var val = parseFloat(String(s).replace(/,/g, ""));
22495         if(isNaN(val)) {
22496             val = 0;
22497         }
22498         return val;
22499     },
22500     
22501     /**
22502      * Integer sorting
22503      * @param {Mixed} s The value being converted
22504      * @return {Number} The comparison value
22505      */
22506     asInt : function(s) {
22507         var val = parseInt(String(s).replace(/,/g, ""));
22508         if(isNaN(val)) {
22509             val = 0;
22510         }
22511         return val;
22512     }
22513 };/*
22514  * Based on:
22515  * Ext JS Library 1.1.1
22516  * Copyright(c) 2006-2007, Ext JS, LLC.
22517  *
22518  * Originally Released Under LGPL - original licence link has changed is not relivant.
22519  *
22520  * Fork - LGPL
22521  * <script type="text/javascript">
22522  */
22523
22524 /**
22525 * @class Roo.data.Record
22526  * Instances of this class encapsulate both record <em>definition</em> information, and record
22527  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22528  * to access Records cached in an {@link Roo.data.Store} object.<br>
22529  * <p>
22530  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22531  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22532  * objects.<br>
22533  * <p>
22534  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22535  * @constructor
22536  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22537  * {@link #create}. The parameters are the same.
22538  * @param {Array} data An associative Array of data values keyed by the field name.
22539  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22540  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22541  * not specified an integer id is generated.
22542  */
22543 Roo.data.Record = function(data, id){
22544     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22545     this.data = data;
22546 };
22547
22548 /**
22549  * Generate a constructor for a specific record layout.
22550  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22551  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22552  * Each field definition object may contain the following properties: <ul>
22553  * <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,
22554  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22555  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22556  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22557  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22558  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22559  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22560  * this may be omitted.</p></li>
22561  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22562  * <ul><li>auto (Default, implies no conversion)</li>
22563  * <li>string</li>
22564  * <li>int</li>
22565  * <li>float</li>
22566  * <li>boolean</li>
22567  * <li>date</li></ul></p></li>
22568  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22569  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22570  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22571  * by the Reader into an object that will be stored in the Record. It is passed the
22572  * following parameters:<ul>
22573  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22574  * </ul></p></li>
22575  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22576  * </ul>
22577  * <br>usage:<br><pre><code>
22578 var TopicRecord = Roo.data.Record.create(
22579     {name: 'title', mapping: 'topic_title'},
22580     {name: 'author', mapping: 'username'},
22581     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22582     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22583     {name: 'lastPoster', mapping: 'user2'},
22584     {name: 'excerpt', mapping: 'post_text'}
22585 );
22586
22587 var myNewRecord = new TopicRecord({
22588     title: 'Do my job please',
22589     author: 'noobie',
22590     totalPosts: 1,
22591     lastPost: new Date(),
22592     lastPoster: 'Animal',
22593     excerpt: 'No way dude!'
22594 });
22595 myStore.add(myNewRecord);
22596 </code></pre>
22597  * @method create
22598  * @static
22599  */
22600 Roo.data.Record.create = function(o){
22601     var f = function(){
22602         f.superclass.constructor.apply(this, arguments);
22603     };
22604     Roo.extend(f, Roo.data.Record);
22605     var p = f.prototype;
22606     p.fields = new Roo.util.MixedCollection(false, function(field){
22607         return field.name;
22608     });
22609     for(var i = 0, len = o.length; i < len; i++){
22610         p.fields.add(new Roo.data.Field(o[i]));
22611     }
22612     f.getField = function(name){
22613         return p.fields.get(name);  
22614     };
22615     return f;
22616 };
22617
22618 Roo.data.Record.AUTO_ID = 1000;
22619 Roo.data.Record.EDIT = 'edit';
22620 Roo.data.Record.REJECT = 'reject';
22621 Roo.data.Record.COMMIT = 'commit';
22622
22623 Roo.data.Record.prototype = {
22624     /**
22625      * Readonly flag - true if this record has been modified.
22626      * @type Boolean
22627      */
22628     dirty : false,
22629     editing : false,
22630     error: null,
22631     modified: null,
22632
22633     // private
22634     join : function(store){
22635         this.store = store;
22636     },
22637
22638     /**
22639      * Set the named field to the specified value.
22640      * @param {String} name The name of the field to set.
22641      * @param {Object} value The value to set the field to.
22642      */
22643     set : function(name, value){
22644         if(this.data[name] == value){
22645             return;
22646         }
22647         this.dirty = true;
22648         if(!this.modified){
22649             this.modified = {};
22650         }
22651         if(typeof this.modified[name] == 'undefined'){
22652             this.modified[name] = this.data[name];
22653         }
22654         this.data[name] = value;
22655         if(!this.editing && this.store){
22656             this.store.afterEdit(this);
22657         }       
22658     },
22659
22660     /**
22661      * Get the value of the named field.
22662      * @param {String} name The name of the field to get the value of.
22663      * @return {Object} The value of the field.
22664      */
22665     get : function(name){
22666         return this.data[name]; 
22667     },
22668
22669     // private
22670     beginEdit : function(){
22671         this.editing = true;
22672         this.modified = {}; 
22673     },
22674
22675     // private
22676     cancelEdit : function(){
22677         this.editing = false;
22678         delete this.modified;
22679     },
22680
22681     // private
22682     endEdit : function(){
22683         this.editing = false;
22684         if(this.dirty && this.store){
22685             this.store.afterEdit(this);
22686         }
22687     },
22688
22689     /**
22690      * Usually called by the {@link Roo.data.Store} which owns the Record.
22691      * Rejects all changes made to the Record since either creation, or the last commit operation.
22692      * Modified fields are reverted to their original values.
22693      * <p>
22694      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22695      * of reject operations.
22696      */
22697     reject : function(){
22698         var m = this.modified;
22699         for(var n in m){
22700             if(typeof m[n] != "function"){
22701                 this.data[n] = m[n];
22702             }
22703         }
22704         this.dirty = false;
22705         delete this.modified;
22706         this.editing = false;
22707         if(this.store){
22708             this.store.afterReject(this);
22709         }
22710     },
22711
22712     /**
22713      * Usually called by the {@link Roo.data.Store} which owns the Record.
22714      * Commits all changes made to the Record since either creation, or the last commit operation.
22715      * <p>
22716      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22717      * of commit operations.
22718      */
22719     commit : function(){
22720         this.dirty = false;
22721         delete this.modified;
22722         this.editing = false;
22723         if(this.store){
22724             this.store.afterCommit(this);
22725         }
22726     },
22727
22728     // private
22729     hasError : function(){
22730         return this.error != null;
22731     },
22732
22733     // private
22734     clearError : function(){
22735         this.error = null;
22736     },
22737
22738     /**
22739      * Creates a copy of this record.
22740      * @param {String} id (optional) A new record id if you don't want to use this record's id
22741      * @return {Record}
22742      */
22743     copy : function(newId) {
22744         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22745     }
22746 };/*
22747  * Based on:
22748  * Ext JS Library 1.1.1
22749  * Copyright(c) 2006-2007, Ext JS, LLC.
22750  *
22751  * Originally Released Under LGPL - original licence link has changed is not relivant.
22752  *
22753  * Fork - LGPL
22754  * <script type="text/javascript">
22755  */
22756
22757
22758
22759 /**
22760  * @class Roo.data.Store
22761  * @extends Roo.util.Observable
22762  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22763  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22764  * <p>
22765  * 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
22766  * has no knowledge of the format of the data returned by the Proxy.<br>
22767  * <p>
22768  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22769  * instances from the data object. These records are cached and made available through accessor functions.
22770  * @constructor
22771  * Creates a new Store.
22772  * @param {Object} config A config object containing the objects needed for the Store to access data,
22773  * and read the data into Records.
22774  */
22775 Roo.data.Store = function(config){
22776     this.data = new Roo.util.MixedCollection(false);
22777     this.data.getKey = function(o){
22778         return o.id;
22779     };
22780     this.baseParams = {};
22781     // private
22782     this.paramNames = {
22783         "start" : "start",
22784         "limit" : "limit",
22785         "sort" : "sort",
22786         "dir" : "dir",
22787         "multisort" : "_multisort"
22788     };
22789
22790     if(config && config.data){
22791         this.inlineData = config.data;
22792         delete config.data;
22793     }
22794
22795     Roo.apply(this, config);
22796     
22797     if(this.reader){ // reader passed
22798         this.reader = Roo.factory(this.reader, Roo.data);
22799         this.reader.xmodule = this.xmodule || false;
22800         if(!this.recordType){
22801             this.recordType = this.reader.recordType;
22802         }
22803         if(this.reader.onMetaChange){
22804             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22805         }
22806     }
22807
22808     if(this.recordType){
22809         this.fields = this.recordType.prototype.fields;
22810     }
22811     this.modified = [];
22812
22813     this.addEvents({
22814         /**
22815          * @event datachanged
22816          * Fires when the data cache has changed, and a widget which is using this Store
22817          * as a Record cache should refresh its view.
22818          * @param {Store} this
22819          */
22820         datachanged : true,
22821         /**
22822          * @event metachange
22823          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22824          * @param {Store} this
22825          * @param {Object} meta The JSON metadata
22826          */
22827         metachange : true,
22828         /**
22829          * @event add
22830          * Fires when Records have been added to the Store
22831          * @param {Store} this
22832          * @param {Roo.data.Record[]} records The array of Records added
22833          * @param {Number} index The index at which the record(s) were added
22834          */
22835         add : true,
22836         /**
22837          * @event remove
22838          * Fires when a Record has been removed from the Store
22839          * @param {Store} this
22840          * @param {Roo.data.Record} record The Record that was removed
22841          * @param {Number} index The index at which the record was removed
22842          */
22843         remove : true,
22844         /**
22845          * @event update
22846          * Fires when a Record has been updated
22847          * @param {Store} this
22848          * @param {Roo.data.Record} record The Record that was updated
22849          * @param {String} operation The update operation being performed.  Value may be one of:
22850          * <pre><code>
22851  Roo.data.Record.EDIT
22852  Roo.data.Record.REJECT
22853  Roo.data.Record.COMMIT
22854          * </code></pre>
22855          */
22856         update : true,
22857         /**
22858          * @event clear
22859          * Fires when the data cache has been cleared.
22860          * @param {Store} this
22861          */
22862         clear : true,
22863         /**
22864          * @event beforeload
22865          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22866          * the load action will be canceled.
22867          * @param {Store} this
22868          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22869          */
22870         beforeload : true,
22871         /**
22872          * @event beforeloadadd
22873          * Fires after a new set of Records has been loaded.
22874          * @param {Store} this
22875          * @param {Roo.data.Record[]} records The Records that were loaded
22876          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22877          */
22878         beforeloadadd : true,
22879         /**
22880          * @event load
22881          * Fires after a new set of Records has been loaded, before they are added to the store.
22882          * @param {Store} this
22883          * @param {Roo.data.Record[]} records The Records that were loaded
22884          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22885          * @params {Object} return from reader
22886          */
22887         load : true,
22888         /**
22889          * @event loadexception
22890          * Fires if an exception occurs in the Proxy during loading.
22891          * Called with the signature of the Proxy's "loadexception" event.
22892          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22893          * 
22894          * @param {Proxy} 
22895          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22896          * @param {Object} load options 
22897          * @param {Object} jsonData from your request (normally this contains the Exception)
22898          */
22899         loadexception : true
22900     });
22901     
22902     if(this.proxy){
22903         this.proxy = Roo.factory(this.proxy, Roo.data);
22904         this.proxy.xmodule = this.xmodule || false;
22905         this.relayEvents(this.proxy,  ["loadexception"]);
22906     }
22907     this.sortToggle = {};
22908     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22909
22910     Roo.data.Store.superclass.constructor.call(this);
22911
22912     if(this.inlineData){
22913         this.loadData(this.inlineData);
22914         delete this.inlineData;
22915     }
22916 };
22917
22918 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22919      /**
22920     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22921     * without a remote query - used by combo/forms at present.
22922     */
22923     
22924     /**
22925     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
22926     */
22927     /**
22928     * @cfg {Array} data Inline data to be loaded when the store is initialized.
22929     */
22930     /**
22931     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
22932     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
22933     */
22934     /**
22935     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
22936     * on any HTTP request
22937     */
22938     /**
22939     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
22940     */
22941     /**
22942     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
22943     */
22944     multiSort: false,
22945     /**
22946     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
22947     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
22948     */
22949     remoteSort : false,
22950
22951     /**
22952     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
22953      * loaded or when a record is removed. (defaults to false).
22954     */
22955     pruneModifiedRecords : false,
22956
22957     // private
22958     lastOptions : null,
22959
22960     /**
22961      * Add Records to the Store and fires the add event.
22962      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22963      */
22964     add : function(records){
22965         records = [].concat(records);
22966         for(var i = 0, len = records.length; i < len; i++){
22967             records[i].join(this);
22968         }
22969         var index = this.data.length;
22970         this.data.addAll(records);
22971         this.fireEvent("add", this, records, index);
22972     },
22973
22974     /**
22975      * Remove a Record from the Store and fires the remove event.
22976      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
22977      */
22978     remove : function(record){
22979         var index = this.data.indexOf(record);
22980         this.data.removeAt(index);
22981  
22982         if(this.pruneModifiedRecords){
22983             this.modified.remove(record);
22984         }
22985         this.fireEvent("remove", this, record, index);
22986     },
22987
22988     /**
22989      * Remove all Records from the Store and fires the clear event.
22990      */
22991     removeAll : function(){
22992         this.data.clear();
22993         if(this.pruneModifiedRecords){
22994             this.modified = [];
22995         }
22996         this.fireEvent("clear", this);
22997     },
22998
22999     /**
23000      * Inserts Records to the Store at the given index and fires the add event.
23001      * @param {Number} index The start index at which to insert the passed Records.
23002      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23003      */
23004     insert : function(index, records){
23005         records = [].concat(records);
23006         for(var i = 0, len = records.length; i < len; i++){
23007             this.data.insert(index, records[i]);
23008             records[i].join(this);
23009         }
23010         this.fireEvent("add", this, records, index);
23011     },
23012
23013     /**
23014      * Get the index within the cache of the passed Record.
23015      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23016      * @return {Number} The index of the passed Record. Returns -1 if not found.
23017      */
23018     indexOf : function(record){
23019         return this.data.indexOf(record);
23020     },
23021
23022     /**
23023      * Get the index within the cache of the Record with the passed id.
23024      * @param {String} id The id of the Record to find.
23025      * @return {Number} The index of the Record. Returns -1 if not found.
23026      */
23027     indexOfId : function(id){
23028         return this.data.indexOfKey(id);
23029     },
23030
23031     /**
23032      * Get the Record with the specified id.
23033      * @param {String} id The id of the Record to find.
23034      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23035      */
23036     getById : function(id){
23037         return this.data.key(id);
23038     },
23039
23040     /**
23041      * Get the Record at the specified index.
23042      * @param {Number} index The index of the Record to find.
23043      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23044      */
23045     getAt : function(index){
23046         return this.data.itemAt(index);
23047     },
23048
23049     /**
23050      * Returns a range of Records between specified indices.
23051      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23052      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23053      * @return {Roo.data.Record[]} An array of Records
23054      */
23055     getRange : function(start, end){
23056         return this.data.getRange(start, end);
23057     },
23058
23059     // private
23060     storeOptions : function(o){
23061         o = Roo.apply({}, o);
23062         delete o.callback;
23063         delete o.scope;
23064         this.lastOptions = o;
23065     },
23066
23067     /**
23068      * Loads the Record cache from the configured Proxy using the configured Reader.
23069      * <p>
23070      * If using remote paging, then the first load call must specify the <em>start</em>
23071      * and <em>limit</em> properties in the options.params property to establish the initial
23072      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23073      * <p>
23074      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23075      * and this call will return before the new data has been loaded. Perform any post-processing
23076      * in a callback function, or in a "load" event handler.</strong>
23077      * <p>
23078      * @param {Object} options An object containing properties which control loading options:<ul>
23079      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23080      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23081      * passed the following arguments:<ul>
23082      * <li>r : Roo.data.Record[]</li>
23083      * <li>options: Options object from the load call</li>
23084      * <li>success: Boolean success indicator</li></ul></li>
23085      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23086      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23087      * </ul>
23088      */
23089     load : function(options){
23090         options = options || {};
23091         if(this.fireEvent("beforeload", this, options) !== false){
23092             this.storeOptions(options);
23093             var p = Roo.apply(options.params || {}, this.baseParams);
23094             // if meta was not loaded from remote source.. try requesting it.
23095             if (!this.reader.metaFromRemote) {
23096                 p._requestMeta = 1;
23097             }
23098             if(this.sortInfo && this.remoteSort){
23099                 var pn = this.paramNames;
23100                 p[pn["sort"]] = this.sortInfo.field;
23101                 p[pn["dir"]] = this.sortInfo.direction;
23102             }
23103             if (this.multiSort) {
23104                 var pn = this.paramNames;
23105                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23106             }
23107             
23108             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23109         }
23110     },
23111
23112     /**
23113      * Reloads the Record cache from the configured Proxy using the configured Reader and
23114      * the options from the last load operation performed.
23115      * @param {Object} options (optional) An object containing properties which may override the options
23116      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23117      * the most recently used options are reused).
23118      */
23119     reload : function(options){
23120         this.load(Roo.applyIf(options||{}, this.lastOptions));
23121     },
23122
23123     // private
23124     // Called as a callback by the Reader during a load operation.
23125     loadRecords : function(o, options, success){
23126         if(!o || success === false){
23127             if(success !== false){
23128                 this.fireEvent("load", this, [], options, o);
23129             }
23130             if(options.callback){
23131                 options.callback.call(options.scope || this, [], options, false);
23132             }
23133             return;
23134         }
23135         // if data returned failure - throw an exception.
23136         if (o.success === false) {
23137             // show a message if no listener is registered.
23138             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23139                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23140             }
23141             // loadmask wil be hooked into this..
23142             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23143             return;
23144         }
23145         var r = o.records, t = o.totalRecords || r.length;
23146         
23147         this.fireEvent("beforeloadadd", this, r, options, o);
23148         
23149         if(!options || options.add !== true){
23150             if(this.pruneModifiedRecords){
23151                 this.modified = [];
23152             }
23153             for(var i = 0, len = r.length; i < len; i++){
23154                 r[i].join(this);
23155             }
23156             if(this.snapshot){
23157                 this.data = this.snapshot;
23158                 delete this.snapshot;
23159             }
23160             this.data.clear();
23161             this.data.addAll(r);
23162             this.totalLength = t;
23163             this.applySort();
23164             this.fireEvent("datachanged", this);
23165         }else{
23166             this.totalLength = Math.max(t, this.data.length+r.length);
23167             this.add(r);
23168         }
23169         
23170         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23171                 
23172             var e = new Roo.data.Record({});
23173
23174             e.set(this.parent.displayField, this.parent.emptyTitle);
23175             e.set(this.parent.valueField, '');
23176
23177             this.insert(0, e);
23178         }
23179             
23180         this.fireEvent("load", this, r, options, o);
23181         if(options.callback){
23182             options.callback.call(options.scope || this, r, options, true);
23183         }
23184     },
23185
23186
23187     /**
23188      * Loads data from a passed data block. A Reader which understands the format of the data
23189      * must have been configured in the constructor.
23190      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23191      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23192      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23193      */
23194     loadData : function(o, append){
23195         var r = this.reader.readRecords(o);
23196         this.loadRecords(r, {add: append}, true);
23197     },
23198
23199     /**
23200      * Gets the number of cached records.
23201      * <p>
23202      * <em>If using paging, this may not be the total size of the dataset. If the data object
23203      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23204      * the data set size</em>
23205      */
23206     getCount : function(){
23207         return this.data.length || 0;
23208     },
23209
23210     /**
23211      * Gets the total number of records in the dataset as returned by the server.
23212      * <p>
23213      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23214      * the dataset size</em>
23215      */
23216     getTotalCount : function(){
23217         return this.totalLength || 0;
23218     },
23219
23220     /**
23221      * Returns the sort state of the Store as an object with two properties:
23222      * <pre><code>
23223  field {String} The name of the field by which the Records are sorted
23224  direction {String} The sort order, "ASC" or "DESC"
23225      * </code></pre>
23226      */
23227     getSortState : function(){
23228         return this.sortInfo;
23229     },
23230
23231     // private
23232     applySort : function(){
23233         if(this.sortInfo && !this.remoteSort){
23234             var s = this.sortInfo, f = s.field;
23235             var st = this.fields.get(f).sortType;
23236             var fn = function(r1, r2){
23237                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23238                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23239             };
23240             this.data.sort(s.direction, fn);
23241             if(this.snapshot && this.snapshot != this.data){
23242                 this.snapshot.sort(s.direction, fn);
23243             }
23244         }
23245     },
23246
23247     /**
23248      * Sets the default sort column and order to be used by the next load operation.
23249      * @param {String} fieldName The name of the field to sort by.
23250      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23251      */
23252     setDefaultSort : function(field, dir){
23253         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23254     },
23255
23256     /**
23257      * Sort the Records.
23258      * If remote sorting is used, the sort is performed on the server, and the cache is
23259      * reloaded. If local sorting is used, the cache is sorted internally.
23260      * @param {String} fieldName The name of the field to sort by.
23261      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23262      */
23263     sort : function(fieldName, dir){
23264         var f = this.fields.get(fieldName);
23265         if(!dir){
23266             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23267             
23268             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23269                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23270             }else{
23271                 dir = f.sortDir;
23272             }
23273         }
23274         this.sortToggle[f.name] = dir;
23275         this.sortInfo = {field: f.name, direction: dir};
23276         if(!this.remoteSort){
23277             this.applySort();
23278             this.fireEvent("datachanged", this);
23279         }else{
23280             this.load(this.lastOptions);
23281         }
23282     },
23283
23284     /**
23285      * Calls the specified function for each of the Records in the cache.
23286      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23287      * Returning <em>false</em> aborts and exits the iteration.
23288      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23289      */
23290     each : function(fn, scope){
23291         this.data.each(fn, scope);
23292     },
23293
23294     /**
23295      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23296      * (e.g., during paging).
23297      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23298      */
23299     getModifiedRecords : function(){
23300         return this.modified;
23301     },
23302
23303     // private
23304     createFilterFn : function(property, value, anyMatch){
23305         if(!value.exec){ // not a regex
23306             value = String(value);
23307             if(value.length == 0){
23308                 return false;
23309             }
23310             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23311         }
23312         return function(r){
23313             return value.test(r.data[property]);
23314         };
23315     },
23316
23317     /**
23318      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23319      * @param {String} property A field on your records
23320      * @param {Number} start The record index to start at (defaults to 0)
23321      * @param {Number} end The last record index to include (defaults to length - 1)
23322      * @return {Number} The sum
23323      */
23324     sum : function(property, start, end){
23325         var rs = this.data.items, v = 0;
23326         start = start || 0;
23327         end = (end || end === 0) ? end : rs.length-1;
23328
23329         for(var i = start; i <= end; i++){
23330             v += (rs[i].data[property] || 0);
23331         }
23332         return v;
23333     },
23334
23335     /**
23336      * Filter the records by a specified property.
23337      * @param {String} field A field on your records
23338      * @param {String/RegExp} value Either a string that the field
23339      * should start with or a RegExp to test against the field
23340      * @param {Boolean} anyMatch True to match any part not just the beginning
23341      */
23342     filter : function(property, value, anyMatch){
23343         var fn = this.createFilterFn(property, value, anyMatch);
23344         return fn ? this.filterBy(fn) : this.clearFilter();
23345     },
23346
23347     /**
23348      * Filter by a function. The specified function will be called with each
23349      * record in this data source. If the function returns true the record is included,
23350      * otherwise it is filtered.
23351      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23352      * @param {Object} scope (optional) The scope of the function (defaults to this)
23353      */
23354     filterBy : function(fn, scope){
23355         this.snapshot = this.snapshot || this.data;
23356         this.data = this.queryBy(fn, scope||this);
23357         this.fireEvent("datachanged", this);
23358     },
23359
23360     /**
23361      * Query the records by a specified property.
23362      * @param {String} field A field on your records
23363      * @param {String/RegExp} value Either a string that the field
23364      * should start with or a RegExp to test against the field
23365      * @param {Boolean} anyMatch True to match any part not just the beginning
23366      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23367      */
23368     query : function(property, value, anyMatch){
23369         var fn = this.createFilterFn(property, value, anyMatch);
23370         return fn ? this.queryBy(fn) : this.data.clone();
23371     },
23372
23373     /**
23374      * Query by a function. The specified function will be called with each
23375      * record in this data source. If the function returns true the record is included
23376      * in the results.
23377      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23378      * @param {Object} scope (optional) The scope of the function (defaults to this)
23379       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23380      **/
23381     queryBy : function(fn, scope){
23382         var data = this.snapshot || this.data;
23383         return data.filterBy(fn, scope||this);
23384     },
23385
23386     /**
23387      * Collects unique values for a particular dataIndex from this store.
23388      * @param {String} dataIndex The property to collect
23389      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23390      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23391      * @return {Array} An array of the unique values
23392      **/
23393     collect : function(dataIndex, allowNull, bypassFilter){
23394         var d = (bypassFilter === true && this.snapshot) ?
23395                 this.snapshot.items : this.data.items;
23396         var v, sv, r = [], l = {};
23397         for(var i = 0, len = d.length; i < len; i++){
23398             v = d[i].data[dataIndex];
23399             sv = String(v);
23400             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23401                 l[sv] = true;
23402                 r[r.length] = v;
23403             }
23404         }
23405         return r;
23406     },
23407
23408     /**
23409      * Revert to a view of the Record cache with no filtering applied.
23410      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23411      */
23412     clearFilter : function(suppressEvent){
23413         if(this.snapshot && this.snapshot != this.data){
23414             this.data = this.snapshot;
23415             delete this.snapshot;
23416             if(suppressEvent !== true){
23417                 this.fireEvent("datachanged", this);
23418             }
23419         }
23420     },
23421
23422     // private
23423     afterEdit : function(record){
23424         if(this.modified.indexOf(record) == -1){
23425             this.modified.push(record);
23426         }
23427         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23428     },
23429     
23430     // private
23431     afterReject : function(record){
23432         this.modified.remove(record);
23433         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23434     },
23435
23436     // private
23437     afterCommit : function(record){
23438         this.modified.remove(record);
23439         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23440     },
23441
23442     /**
23443      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23444      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23445      */
23446     commitChanges : function(){
23447         var m = this.modified.slice(0);
23448         this.modified = [];
23449         for(var i = 0, len = m.length; i < len; i++){
23450             m[i].commit();
23451         }
23452     },
23453
23454     /**
23455      * Cancel outstanding changes on all changed records.
23456      */
23457     rejectChanges : function(){
23458         var m = this.modified.slice(0);
23459         this.modified = [];
23460         for(var i = 0, len = m.length; i < len; i++){
23461             m[i].reject();
23462         }
23463     },
23464
23465     onMetaChange : function(meta, rtype, o){
23466         this.recordType = rtype;
23467         this.fields = rtype.prototype.fields;
23468         delete this.snapshot;
23469         this.sortInfo = meta.sortInfo || this.sortInfo;
23470         this.modified = [];
23471         this.fireEvent('metachange', this, this.reader.meta);
23472     },
23473     
23474     moveIndex : function(data, type)
23475     {
23476         var index = this.indexOf(data);
23477         
23478         var newIndex = index + type;
23479         
23480         this.remove(data);
23481         
23482         this.insert(newIndex, data);
23483         
23484     }
23485 });/*
23486  * Based on:
23487  * Ext JS Library 1.1.1
23488  * Copyright(c) 2006-2007, Ext JS, LLC.
23489  *
23490  * Originally Released Under LGPL - original licence link has changed is not relivant.
23491  *
23492  * Fork - LGPL
23493  * <script type="text/javascript">
23494  */
23495
23496 /**
23497  * @class Roo.data.SimpleStore
23498  * @extends Roo.data.Store
23499  * Small helper class to make creating Stores from Array data easier.
23500  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23501  * @cfg {Array} fields An array of field definition objects, or field name strings.
23502  * @cfg {Array} data The multi-dimensional array of data
23503  * @constructor
23504  * @param {Object} config
23505  */
23506 Roo.data.SimpleStore = function(config){
23507     Roo.data.SimpleStore.superclass.constructor.call(this, {
23508         isLocal : true,
23509         reader: new Roo.data.ArrayReader({
23510                 id: config.id
23511             },
23512             Roo.data.Record.create(config.fields)
23513         ),
23514         proxy : new Roo.data.MemoryProxy(config.data)
23515     });
23516     this.load();
23517 };
23518 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23519  * Based on:
23520  * Ext JS Library 1.1.1
23521  * Copyright(c) 2006-2007, Ext JS, LLC.
23522  *
23523  * Originally Released Under LGPL - original licence link has changed is not relivant.
23524  *
23525  * Fork - LGPL
23526  * <script type="text/javascript">
23527  */
23528
23529 /**
23530 /**
23531  * @extends Roo.data.Store
23532  * @class Roo.data.JsonStore
23533  * Small helper class to make creating Stores for JSON data easier. <br/>
23534 <pre><code>
23535 var store = new Roo.data.JsonStore({
23536     url: 'get-images.php',
23537     root: 'images',
23538     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23539 });
23540 </code></pre>
23541  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23542  * JsonReader and HttpProxy (unless inline data is provided).</b>
23543  * @cfg {Array} fields An array of field definition objects, or field name strings.
23544  * @constructor
23545  * @param {Object} config
23546  */
23547 Roo.data.JsonStore = function(c){
23548     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23549         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23550         reader: new Roo.data.JsonReader(c, c.fields)
23551     }));
23552 };
23553 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23554  * Based on:
23555  * Ext JS Library 1.1.1
23556  * Copyright(c) 2006-2007, Ext JS, LLC.
23557  *
23558  * Originally Released Under LGPL - original licence link has changed is not relivant.
23559  *
23560  * Fork - LGPL
23561  * <script type="text/javascript">
23562  */
23563
23564  
23565 Roo.data.Field = function(config){
23566     if(typeof config == "string"){
23567         config = {name: config};
23568     }
23569     Roo.apply(this, config);
23570     
23571     if(!this.type){
23572         this.type = "auto";
23573     }
23574     
23575     var st = Roo.data.SortTypes;
23576     // named sortTypes are supported, here we look them up
23577     if(typeof this.sortType == "string"){
23578         this.sortType = st[this.sortType];
23579     }
23580     
23581     // set default sortType for strings and dates
23582     if(!this.sortType){
23583         switch(this.type){
23584             case "string":
23585                 this.sortType = st.asUCString;
23586                 break;
23587             case "date":
23588                 this.sortType = st.asDate;
23589                 break;
23590             default:
23591                 this.sortType = st.none;
23592         }
23593     }
23594
23595     // define once
23596     var stripRe = /[\$,%]/g;
23597
23598     // prebuilt conversion function for this field, instead of
23599     // switching every time we're reading a value
23600     if(!this.convert){
23601         var cv, dateFormat = this.dateFormat;
23602         switch(this.type){
23603             case "":
23604             case "auto":
23605             case undefined:
23606                 cv = function(v){ return v; };
23607                 break;
23608             case "string":
23609                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23610                 break;
23611             case "int":
23612                 cv = function(v){
23613                     return v !== undefined && v !== null && v !== '' ?
23614                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23615                     };
23616                 break;
23617             case "float":
23618                 cv = function(v){
23619                     return v !== undefined && v !== null && v !== '' ?
23620                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23621                     };
23622                 break;
23623             case "bool":
23624             case "boolean":
23625                 cv = function(v){ return v === true || v === "true" || v == 1; };
23626                 break;
23627             case "date":
23628                 cv = function(v){
23629                     if(!v){
23630                         return '';
23631                     }
23632                     if(v instanceof Date){
23633                         return v;
23634                     }
23635                     if(dateFormat){
23636                         if(dateFormat == "timestamp"){
23637                             return new Date(v*1000);
23638                         }
23639                         return Date.parseDate(v, dateFormat);
23640                     }
23641                     var parsed = Date.parse(v);
23642                     return parsed ? new Date(parsed) : null;
23643                 };
23644              break;
23645             
23646         }
23647         this.convert = cv;
23648     }
23649 };
23650
23651 Roo.data.Field.prototype = {
23652     dateFormat: null,
23653     defaultValue: "",
23654     mapping: null,
23655     sortType : null,
23656     sortDir : "ASC"
23657 };/*
23658  * Based on:
23659  * Ext JS Library 1.1.1
23660  * Copyright(c) 2006-2007, Ext JS, LLC.
23661  *
23662  * Originally Released Under LGPL - original licence link has changed is not relivant.
23663  *
23664  * Fork - LGPL
23665  * <script type="text/javascript">
23666  */
23667  
23668 // Base class for reading structured data from a data source.  This class is intended to be
23669 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23670
23671 /**
23672  * @class Roo.data.DataReader
23673  * Base class for reading structured data from a data source.  This class is intended to be
23674  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23675  */
23676
23677 Roo.data.DataReader = function(meta, recordType){
23678     
23679     this.meta = meta;
23680     
23681     this.recordType = recordType instanceof Array ? 
23682         Roo.data.Record.create(recordType) : recordType;
23683 };
23684
23685 Roo.data.DataReader.prototype = {
23686      /**
23687      * Create an empty record
23688      * @param {Object} data (optional) - overlay some values
23689      * @return {Roo.data.Record} record created.
23690      */
23691     newRow :  function(d) {
23692         var da =  {};
23693         this.recordType.prototype.fields.each(function(c) {
23694             switch( c.type) {
23695                 case 'int' : da[c.name] = 0; break;
23696                 case 'date' : da[c.name] = new Date(); break;
23697                 case 'float' : da[c.name] = 0.0; break;
23698                 case 'boolean' : da[c.name] = false; break;
23699                 default : da[c.name] = ""; break;
23700             }
23701             
23702         });
23703         return new this.recordType(Roo.apply(da, d));
23704     }
23705     
23706 };/*
23707  * Based on:
23708  * Ext JS Library 1.1.1
23709  * Copyright(c) 2006-2007, Ext JS, LLC.
23710  *
23711  * Originally Released Under LGPL - original licence link has changed is not relivant.
23712  *
23713  * Fork - LGPL
23714  * <script type="text/javascript">
23715  */
23716
23717 /**
23718  * @class Roo.data.DataProxy
23719  * @extends Roo.data.Observable
23720  * This class is an abstract base class for implementations which provide retrieval of
23721  * unformatted data objects.<br>
23722  * <p>
23723  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23724  * (of the appropriate type which knows how to parse the data object) to provide a block of
23725  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23726  * <p>
23727  * Custom implementations must implement the load method as described in
23728  * {@link Roo.data.HttpProxy#load}.
23729  */
23730 Roo.data.DataProxy = function(){
23731     this.addEvents({
23732         /**
23733          * @event beforeload
23734          * Fires before a network request is made to retrieve a data object.
23735          * @param {Object} This DataProxy object.
23736          * @param {Object} params The params parameter to the load function.
23737          */
23738         beforeload : true,
23739         /**
23740          * @event load
23741          * Fires before the load method's callback is called.
23742          * @param {Object} This DataProxy object.
23743          * @param {Object} o The data object.
23744          * @param {Object} arg The callback argument object passed to the load function.
23745          */
23746         load : true,
23747         /**
23748          * @event loadexception
23749          * Fires if an Exception occurs during data retrieval.
23750          * @param {Object} This DataProxy object.
23751          * @param {Object} o The data object.
23752          * @param {Object} arg The callback argument object passed to the load function.
23753          * @param {Object} e The Exception.
23754          */
23755         loadexception : true
23756     });
23757     Roo.data.DataProxy.superclass.constructor.call(this);
23758 };
23759
23760 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23761
23762     /**
23763      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23764      */
23765 /*
23766  * Based on:
23767  * Ext JS Library 1.1.1
23768  * Copyright(c) 2006-2007, Ext JS, LLC.
23769  *
23770  * Originally Released Under LGPL - original licence link has changed is not relivant.
23771  *
23772  * Fork - LGPL
23773  * <script type="text/javascript">
23774  */
23775 /**
23776  * @class Roo.data.MemoryProxy
23777  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23778  * to the Reader when its load method is called.
23779  * @constructor
23780  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23781  */
23782 Roo.data.MemoryProxy = function(data){
23783     if (data.data) {
23784         data = data.data;
23785     }
23786     Roo.data.MemoryProxy.superclass.constructor.call(this);
23787     this.data = data;
23788 };
23789
23790 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23791     
23792     /**
23793      * Load data from the requested source (in this case an in-memory
23794      * data object passed to the constructor), read the data object into
23795      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23796      * process that block using the passed callback.
23797      * @param {Object} params This parameter is not used by the MemoryProxy class.
23798      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23799      * object into a block of Roo.data.Records.
23800      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23801      * The function must be passed <ul>
23802      * <li>The Record block object</li>
23803      * <li>The "arg" argument from the load function</li>
23804      * <li>A boolean success indicator</li>
23805      * </ul>
23806      * @param {Object} scope The scope in which to call the callback
23807      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23808      */
23809     load : function(params, reader, callback, scope, arg){
23810         params = params || {};
23811         var result;
23812         try {
23813             result = reader.readRecords(this.data);
23814         }catch(e){
23815             this.fireEvent("loadexception", this, arg, null, e);
23816             callback.call(scope, null, arg, false);
23817             return;
23818         }
23819         callback.call(scope, result, arg, true);
23820     },
23821     
23822     // private
23823     update : function(params, records){
23824         
23825     }
23826 });/*
23827  * Based on:
23828  * Ext JS Library 1.1.1
23829  * Copyright(c) 2006-2007, Ext JS, LLC.
23830  *
23831  * Originally Released Under LGPL - original licence link has changed is not relivant.
23832  *
23833  * Fork - LGPL
23834  * <script type="text/javascript">
23835  */
23836 /**
23837  * @class Roo.data.HttpProxy
23838  * @extends Roo.data.DataProxy
23839  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23840  * configured to reference a certain URL.<br><br>
23841  * <p>
23842  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23843  * from which the running page was served.<br><br>
23844  * <p>
23845  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23846  * <p>
23847  * Be aware that to enable the browser to parse an XML document, the server must set
23848  * the Content-Type header in the HTTP response to "text/xml".
23849  * @constructor
23850  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23851  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23852  * will be used to make the request.
23853  */
23854 Roo.data.HttpProxy = function(conn){
23855     Roo.data.HttpProxy.superclass.constructor.call(this);
23856     // is conn a conn config or a real conn?
23857     this.conn = conn;
23858     this.useAjax = !conn || !conn.events;
23859   
23860 };
23861
23862 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23863     // thse are take from connection...
23864     
23865     /**
23866      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23867      */
23868     /**
23869      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23870      * extra parameters to each request made by this object. (defaults to undefined)
23871      */
23872     /**
23873      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23874      *  to each request made by this object. (defaults to undefined)
23875      */
23876     /**
23877      * @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)
23878      */
23879     /**
23880      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23881      */
23882      /**
23883      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23884      * @type Boolean
23885      */
23886   
23887
23888     /**
23889      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23890      * @type Boolean
23891      */
23892     /**
23893      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23894      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23895      * a finer-grained basis than the DataProxy events.
23896      */
23897     getConnection : function(){
23898         return this.useAjax ? Roo.Ajax : this.conn;
23899     },
23900
23901     /**
23902      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23903      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23904      * process that block using the passed callback.
23905      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23906      * for the request to the remote server.
23907      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23908      * object into a block of Roo.data.Records.
23909      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23910      * The function must be passed <ul>
23911      * <li>The Record block object</li>
23912      * <li>The "arg" argument from the load function</li>
23913      * <li>A boolean success indicator</li>
23914      * </ul>
23915      * @param {Object} scope The scope in which to call the callback
23916      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23917      */
23918     load : function(params, reader, callback, scope, arg){
23919         if(this.fireEvent("beforeload", this, params) !== false){
23920             var  o = {
23921                 params : params || {},
23922                 request: {
23923                     callback : callback,
23924                     scope : scope,
23925                     arg : arg
23926                 },
23927                 reader: reader,
23928                 callback : this.loadResponse,
23929                 scope: this
23930             };
23931             if(this.useAjax){
23932                 Roo.applyIf(o, this.conn);
23933                 if(this.activeRequest){
23934                     Roo.Ajax.abort(this.activeRequest);
23935                 }
23936                 this.activeRequest = Roo.Ajax.request(o);
23937             }else{
23938                 this.conn.request(o);
23939             }
23940         }else{
23941             callback.call(scope||this, null, arg, false);
23942         }
23943     },
23944
23945     // private
23946     loadResponse : function(o, success, response){
23947         delete this.activeRequest;
23948         if(!success){
23949             this.fireEvent("loadexception", this, o, response);
23950             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23951             return;
23952         }
23953         var result;
23954         try {
23955             result = o.reader.read(response);
23956         }catch(e){
23957             this.fireEvent("loadexception", this, o, response, e);
23958             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23959             return;
23960         }
23961         
23962         this.fireEvent("load", this, o, o.request.arg);
23963         o.request.callback.call(o.request.scope, result, o.request.arg, true);
23964     },
23965
23966     // private
23967     update : function(dataSet){
23968
23969     },
23970
23971     // private
23972     updateResponse : function(dataSet){
23973
23974     }
23975 });/*
23976  * Based on:
23977  * Ext JS Library 1.1.1
23978  * Copyright(c) 2006-2007, Ext JS, LLC.
23979  *
23980  * Originally Released Under LGPL - original licence link has changed is not relivant.
23981  *
23982  * Fork - LGPL
23983  * <script type="text/javascript">
23984  */
23985
23986 /**
23987  * @class Roo.data.ScriptTagProxy
23988  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
23989  * other than the originating domain of the running page.<br><br>
23990  * <p>
23991  * <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
23992  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
23993  * <p>
23994  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
23995  * source code that is used as the source inside a &lt;script> tag.<br><br>
23996  * <p>
23997  * In order for the browser to process the returned data, the server must wrap the data object
23998  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
23999  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24000  * depending on whether the callback name was passed:
24001  * <p>
24002  * <pre><code>
24003 boolean scriptTag = false;
24004 String cb = request.getParameter("callback");
24005 if (cb != null) {
24006     scriptTag = true;
24007     response.setContentType("text/javascript");
24008 } else {
24009     response.setContentType("application/x-json");
24010 }
24011 Writer out = response.getWriter();
24012 if (scriptTag) {
24013     out.write(cb + "(");
24014 }
24015 out.print(dataBlock.toJsonString());
24016 if (scriptTag) {
24017     out.write(");");
24018 }
24019 </pre></code>
24020  *
24021  * @constructor
24022  * @param {Object} config A configuration object.
24023  */
24024 Roo.data.ScriptTagProxy = function(config){
24025     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24026     Roo.apply(this, config);
24027     this.head = document.getElementsByTagName("head")[0];
24028 };
24029
24030 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24031
24032 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24033     /**
24034      * @cfg {String} url The URL from which to request the data object.
24035      */
24036     /**
24037      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24038      */
24039     timeout : 30000,
24040     /**
24041      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24042      * the server the name of the callback function set up by the load call to process the returned data object.
24043      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24044      * javascript output which calls this named function passing the data object as its only parameter.
24045      */
24046     callbackParam : "callback",
24047     /**
24048      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24049      * name to the request.
24050      */
24051     nocache : true,
24052
24053     /**
24054      * Load data from the configured URL, read the data object into
24055      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24056      * process that block using the passed callback.
24057      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24058      * for the request to the remote server.
24059      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24060      * object into a block of Roo.data.Records.
24061      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24062      * The function must be passed <ul>
24063      * <li>The Record block object</li>
24064      * <li>The "arg" argument from the load function</li>
24065      * <li>A boolean success indicator</li>
24066      * </ul>
24067      * @param {Object} scope The scope in which to call the callback
24068      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24069      */
24070     load : function(params, reader, callback, scope, arg){
24071         if(this.fireEvent("beforeload", this, params) !== false){
24072
24073             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24074
24075             var url = this.url;
24076             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24077             if(this.nocache){
24078                 url += "&_dc=" + (new Date().getTime());
24079             }
24080             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24081             var trans = {
24082                 id : transId,
24083                 cb : "stcCallback"+transId,
24084                 scriptId : "stcScript"+transId,
24085                 params : params,
24086                 arg : arg,
24087                 url : url,
24088                 callback : callback,
24089                 scope : scope,
24090                 reader : reader
24091             };
24092             var conn = this;
24093
24094             window[trans.cb] = function(o){
24095                 conn.handleResponse(o, trans);
24096             };
24097
24098             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24099
24100             if(this.autoAbort !== false){
24101                 this.abort();
24102             }
24103
24104             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24105
24106             var script = document.createElement("script");
24107             script.setAttribute("src", url);
24108             script.setAttribute("type", "text/javascript");
24109             script.setAttribute("id", trans.scriptId);
24110             this.head.appendChild(script);
24111
24112             this.trans = trans;
24113         }else{
24114             callback.call(scope||this, null, arg, false);
24115         }
24116     },
24117
24118     // private
24119     isLoading : function(){
24120         return this.trans ? true : false;
24121     },
24122
24123     /**
24124      * Abort the current server request.
24125      */
24126     abort : function(){
24127         if(this.isLoading()){
24128             this.destroyTrans(this.trans);
24129         }
24130     },
24131
24132     // private
24133     destroyTrans : function(trans, isLoaded){
24134         this.head.removeChild(document.getElementById(trans.scriptId));
24135         clearTimeout(trans.timeoutId);
24136         if(isLoaded){
24137             window[trans.cb] = undefined;
24138             try{
24139                 delete window[trans.cb];
24140             }catch(e){}
24141         }else{
24142             // if hasn't been loaded, wait for load to remove it to prevent script error
24143             window[trans.cb] = function(){
24144                 window[trans.cb] = undefined;
24145                 try{
24146                     delete window[trans.cb];
24147                 }catch(e){}
24148             };
24149         }
24150     },
24151
24152     // private
24153     handleResponse : function(o, trans){
24154         this.trans = false;
24155         this.destroyTrans(trans, true);
24156         var result;
24157         try {
24158             result = trans.reader.readRecords(o);
24159         }catch(e){
24160             this.fireEvent("loadexception", this, o, trans.arg, e);
24161             trans.callback.call(trans.scope||window, null, trans.arg, false);
24162             return;
24163         }
24164         this.fireEvent("load", this, o, trans.arg);
24165         trans.callback.call(trans.scope||window, result, trans.arg, true);
24166     },
24167
24168     // private
24169     handleFailure : function(trans){
24170         this.trans = false;
24171         this.destroyTrans(trans, false);
24172         this.fireEvent("loadexception", this, null, trans.arg);
24173         trans.callback.call(trans.scope||window, null, trans.arg, false);
24174     }
24175 });/*
24176  * Based on:
24177  * Ext JS Library 1.1.1
24178  * Copyright(c) 2006-2007, Ext JS, LLC.
24179  *
24180  * Originally Released Under LGPL - original licence link has changed is not relivant.
24181  *
24182  * Fork - LGPL
24183  * <script type="text/javascript">
24184  */
24185
24186 /**
24187  * @class Roo.data.JsonReader
24188  * @extends Roo.data.DataReader
24189  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24190  * based on mappings in a provided Roo.data.Record constructor.
24191  * 
24192  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24193  * in the reply previously. 
24194  * 
24195  * <p>
24196  * Example code:
24197  * <pre><code>
24198 var RecordDef = Roo.data.Record.create([
24199     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24200     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24201 ]);
24202 var myReader = new Roo.data.JsonReader({
24203     totalProperty: "results",    // The property which contains the total dataset size (optional)
24204     root: "rows",                // The property which contains an Array of row objects
24205     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24206 }, RecordDef);
24207 </code></pre>
24208  * <p>
24209  * This would consume a JSON file like this:
24210  * <pre><code>
24211 { 'results': 2, 'rows': [
24212     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24213     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24214 }
24215 </code></pre>
24216  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24217  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24218  * paged from the remote server.
24219  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24220  * @cfg {String} root name of the property which contains the Array of row objects.
24221  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24222  * @cfg {Array} fields Array of field definition objects
24223  * @constructor
24224  * Create a new JsonReader
24225  * @param {Object} meta Metadata configuration options
24226  * @param {Object} recordType Either an Array of field definition objects,
24227  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24228  */
24229 Roo.data.JsonReader = function(meta, recordType){
24230     
24231     meta = meta || {};
24232     // set some defaults:
24233     Roo.applyIf(meta, {
24234         totalProperty: 'total',
24235         successProperty : 'success',
24236         root : 'data',
24237         id : 'id'
24238     });
24239     
24240     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24241 };
24242 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24243     
24244     /**
24245      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24246      * Used by Store query builder to append _requestMeta to params.
24247      * 
24248      */
24249     metaFromRemote : false,
24250     /**
24251      * This method is only used by a DataProxy which has retrieved data from a remote server.
24252      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24253      * @return {Object} data A data block which is used by an Roo.data.Store object as
24254      * a cache of Roo.data.Records.
24255      */
24256     read : function(response){
24257         var json = response.responseText;
24258        
24259         var o = /* eval:var:o */ eval("("+json+")");
24260         if(!o) {
24261             throw {message: "JsonReader.read: Json object not found"};
24262         }
24263         
24264         if(o.metaData){
24265             
24266             delete this.ef;
24267             this.metaFromRemote = true;
24268             this.meta = o.metaData;
24269             this.recordType = Roo.data.Record.create(o.metaData.fields);
24270             this.onMetaChange(this.meta, this.recordType, o);
24271         }
24272         return this.readRecords(o);
24273     },
24274
24275     // private function a store will implement
24276     onMetaChange : function(meta, recordType, o){
24277
24278     },
24279
24280     /**
24281          * @ignore
24282          */
24283     simpleAccess: function(obj, subsc) {
24284         return obj[subsc];
24285     },
24286
24287         /**
24288          * @ignore
24289          */
24290     getJsonAccessor: function(){
24291         var re = /[\[\.]/;
24292         return function(expr) {
24293             try {
24294                 return(re.test(expr))
24295                     ? new Function("obj", "return obj." + expr)
24296                     : function(obj){
24297                         return obj[expr];
24298                     };
24299             } catch(e){}
24300             return Roo.emptyFn;
24301         };
24302     }(),
24303
24304     /**
24305      * Create a data block containing Roo.data.Records from an XML document.
24306      * @param {Object} o An object which contains an Array of row objects in the property specified
24307      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24308      * which contains the total size of the dataset.
24309      * @return {Object} data A data block which is used by an Roo.data.Store object as
24310      * a cache of Roo.data.Records.
24311      */
24312     readRecords : function(o){
24313         /**
24314          * After any data loads, the raw JSON data is available for further custom processing.
24315          * @type Object
24316          */
24317         this.o = o;
24318         var s = this.meta, Record = this.recordType,
24319             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24320
24321 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24322         if (!this.ef) {
24323             if(s.totalProperty) {
24324                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24325                 }
24326                 if(s.successProperty) {
24327                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24328                 }
24329                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24330                 if (s.id) {
24331                         var g = this.getJsonAccessor(s.id);
24332                         this.getId = function(rec) {
24333                                 var r = g(rec);  
24334                                 return (r === undefined || r === "") ? null : r;
24335                         };
24336                 } else {
24337                         this.getId = function(){return null;};
24338                 }
24339             this.ef = [];
24340             for(var jj = 0; jj < fl; jj++){
24341                 f = fi[jj];
24342                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24343                 this.ef[jj] = this.getJsonAccessor(map);
24344             }
24345         }
24346
24347         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24348         if(s.totalProperty){
24349             var vt = parseInt(this.getTotal(o), 10);
24350             if(!isNaN(vt)){
24351                 totalRecords = vt;
24352             }
24353         }
24354         if(s.successProperty){
24355             var vs = this.getSuccess(o);
24356             if(vs === false || vs === 'false'){
24357                 success = false;
24358             }
24359         }
24360         var records = [];
24361         for(var i = 0; i < c; i++){
24362                 var n = root[i];
24363             var values = {};
24364             var id = this.getId(n);
24365             for(var j = 0; j < fl; j++){
24366                 f = fi[j];
24367             var v = this.ef[j](n);
24368             if (!f.convert) {
24369                 Roo.log('missing convert for ' + f.name);
24370                 Roo.log(f);
24371                 continue;
24372             }
24373             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24374             }
24375             var record = new Record(values, id);
24376             record.json = n;
24377             records[i] = record;
24378         }
24379         return {
24380             raw : o,
24381             success : success,
24382             records : records,
24383             totalRecords : totalRecords
24384         };
24385     }
24386 });/*
24387  * Based on:
24388  * Ext JS Library 1.1.1
24389  * Copyright(c) 2006-2007, Ext JS, LLC.
24390  *
24391  * Originally Released Under LGPL - original licence link has changed is not relivant.
24392  *
24393  * Fork - LGPL
24394  * <script type="text/javascript">
24395  */
24396
24397 /**
24398  * @class Roo.data.XmlReader
24399  * @extends Roo.data.DataReader
24400  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24401  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24402  * <p>
24403  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24404  * header in the HTTP response must be set to "text/xml".</em>
24405  * <p>
24406  * Example code:
24407  * <pre><code>
24408 var RecordDef = Roo.data.Record.create([
24409    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24410    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24411 ]);
24412 var myReader = new Roo.data.XmlReader({
24413    totalRecords: "results", // The element which contains the total dataset size (optional)
24414    record: "row",           // The repeated element which contains row information
24415    id: "id"                 // The element within the row that provides an ID for the record (optional)
24416 }, RecordDef);
24417 </code></pre>
24418  * <p>
24419  * This would consume an XML file like this:
24420  * <pre><code>
24421 &lt;?xml?>
24422 &lt;dataset>
24423  &lt;results>2&lt;/results>
24424  &lt;row>
24425    &lt;id>1&lt;/id>
24426    &lt;name>Bill&lt;/name>
24427    &lt;occupation>Gardener&lt;/occupation>
24428  &lt;/row>
24429  &lt;row>
24430    &lt;id>2&lt;/id>
24431    &lt;name>Ben&lt;/name>
24432    &lt;occupation>Horticulturalist&lt;/occupation>
24433  &lt;/row>
24434 &lt;/dataset>
24435 </code></pre>
24436  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24437  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24438  * paged from the remote server.
24439  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24440  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24441  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24442  * a record identifier value.
24443  * @constructor
24444  * Create a new XmlReader
24445  * @param {Object} meta Metadata configuration options
24446  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24447  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24448  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24449  */
24450 Roo.data.XmlReader = function(meta, recordType){
24451     meta = meta || {};
24452     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24453 };
24454 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24455     /**
24456      * This method is only used by a DataProxy which has retrieved data from a remote server.
24457          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24458          * to contain a method called 'responseXML' that returns an XML document object.
24459      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24460      * a cache of Roo.data.Records.
24461      */
24462     read : function(response){
24463         var doc = response.responseXML;
24464         if(!doc) {
24465             throw {message: "XmlReader.read: XML Document not available"};
24466         }
24467         return this.readRecords(doc);
24468     },
24469
24470     /**
24471      * Create a data block containing Roo.data.Records from an XML document.
24472          * @param {Object} doc A parsed XML document.
24473      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24474      * a cache of Roo.data.Records.
24475      */
24476     readRecords : function(doc){
24477         /**
24478          * After any data loads/reads, the raw XML Document is available for further custom processing.
24479          * @type XMLDocument
24480          */
24481         this.xmlData = doc;
24482         var root = doc.documentElement || doc;
24483         var q = Roo.DomQuery;
24484         var recordType = this.recordType, fields = recordType.prototype.fields;
24485         var sid = this.meta.id;
24486         var totalRecords = 0, success = true;
24487         if(this.meta.totalRecords){
24488             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24489         }
24490         
24491         if(this.meta.success){
24492             var sv = q.selectValue(this.meta.success, root, true);
24493             success = sv !== false && sv !== 'false';
24494         }
24495         var records = [];
24496         var ns = q.select(this.meta.record, root);
24497         for(var i = 0, len = ns.length; i < len; i++) {
24498                 var n = ns[i];
24499                 var values = {};
24500                 var id = sid ? q.selectValue(sid, n) : undefined;
24501                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24502                     var f = fields.items[j];
24503                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24504                     v = f.convert(v);
24505                     values[f.name] = v;
24506                 }
24507                 var record = new recordType(values, id);
24508                 record.node = n;
24509                 records[records.length] = record;
24510             }
24511
24512             return {
24513                 success : success,
24514                 records : records,
24515                 totalRecords : totalRecords || records.length
24516             };
24517     }
24518 });/*
24519  * Based on:
24520  * Ext JS Library 1.1.1
24521  * Copyright(c) 2006-2007, Ext JS, LLC.
24522  *
24523  * Originally Released Under LGPL - original licence link has changed is not relivant.
24524  *
24525  * Fork - LGPL
24526  * <script type="text/javascript">
24527  */
24528
24529 /**
24530  * @class Roo.data.ArrayReader
24531  * @extends Roo.data.DataReader
24532  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24533  * Each element of that Array represents a row of data fields. The
24534  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24535  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24536  * <p>
24537  * Example code:.
24538  * <pre><code>
24539 var RecordDef = Roo.data.Record.create([
24540     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24541     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24542 ]);
24543 var myReader = new Roo.data.ArrayReader({
24544     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24545 }, RecordDef);
24546 </code></pre>
24547  * <p>
24548  * This would consume an Array like this:
24549  * <pre><code>
24550 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24551   </code></pre>
24552  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
24553  * @constructor
24554  * Create a new JsonReader
24555  * @param {Object} meta Metadata configuration options.
24556  * @param {Object} recordType Either an Array of field definition objects
24557  * as specified to {@link Roo.data.Record#create},
24558  * or an {@link Roo.data.Record} object
24559  * created using {@link Roo.data.Record#create}.
24560  */
24561 Roo.data.ArrayReader = function(meta, recordType){
24562     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
24563 };
24564
24565 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24566     /**
24567      * Create a data block containing Roo.data.Records from an XML document.
24568      * @param {Object} o An Array of row objects which represents the dataset.
24569      * @return {Object} data A data block which is used by an Roo.data.Store object as
24570      * a cache of Roo.data.Records.
24571      */
24572     readRecords : function(o){
24573         var sid = this.meta ? this.meta.id : null;
24574         var recordType = this.recordType, fields = recordType.prototype.fields;
24575         var records = [];
24576         var root = o;
24577             for(var i = 0; i < root.length; i++){
24578                     var n = root[i];
24579                 var values = {};
24580                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24581                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24582                 var f = fields.items[j];
24583                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24584                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24585                 v = f.convert(v);
24586                 values[f.name] = v;
24587             }
24588                 var record = new recordType(values, id);
24589                 record.json = n;
24590                 records[records.length] = record;
24591             }
24592             return {
24593                 records : records,
24594                 totalRecords : records.length
24595             };
24596     }
24597 });/*
24598  * Based on:
24599  * Ext JS Library 1.1.1
24600  * Copyright(c) 2006-2007, Ext JS, LLC.
24601  *
24602  * Originally Released Under LGPL - original licence link has changed is not relivant.
24603  *
24604  * Fork - LGPL
24605  * <script type="text/javascript">
24606  */
24607
24608
24609 /**
24610  * @class Roo.data.Tree
24611  * @extends Roo.util.Observable
24612  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24613  * in the tree have most standard DOM functionality.
24614  * @constructor
24615  * @param {Node} root (optional) The root node
24616  */
24617 Roo.data.Tree = function(root){
24618    this.nodeHash = {};
24619    /**
24620     * The root node for this tree
24621     * @type Node
24622     */
24623    this.root = null;
24624    if(root){
24625        this.setRootNode(root);
24626    }
24627    this.addEvents({
24628        /**
24629         * @event append
24630         * Fires when a new child node is appended to a node in this tree.
24631         * @param {Tree} tree The owner tree
24632         * @param {Node} parent The parent node
24633         * @param {Node} node The newly appended node
24634         * @param {Number} index The index of the newly appended node
24635         */
24636        "append" : true,
24637        /**
24638         * @event remove
24639         * Fires when a child node is removed from a node in this tree.
24640         * @param {Tree} tree The owner tree
24641         * @param {Node} parent The parent node
24642         * @param {Node} node The child node removed
24643         */
24644        "remove" : true,
24645        /**
24646         * @event move
24647         * Fires when a node is moved to a new location in the tree
24648         * @param {Tree} tree The owner tree
24649         * @param {Node} node The node moved
24650         * @param {Node} oldParent The old parent of this node
24651         * @param {Node} newParent The new parent of this node
24652         * @param {Number} index The index it was moved to
24653         */
24654        "move" : true,
24655        /**
24656         * @event insert
24657         * Fires when a new child node is inserted in a node in this tree.
24658         * @param {Tree} tree The owner tree
24659         * @param {Node} parent The parent node
24660         * @param {Node} node The child node inserted
24661         * @param {Node} refNode The child node the node was inserted before
24662         */
24663        "insert" : true,
24664        /**
24665         * @event beforeappend
24666         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24667         * @param {Tree} tree The owner tree
24668         * @param {Node} parent The parent node
24669         * @param {Node} node The child node to be appended
24670         */
24671        "beforeappend" : true,
24672        /**
24673         * @event beforeremove
24674         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24675         * @param {Tree} tree The owner tree
24676         * @param {Node} parent The parent node
24677         * @param {Node} node The child node to be removed
24678         */
24679        "beforeremove" : true,
24680        /**
24681         * @event beforemove
24682         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24683         * @param {Tree} tree The owner tree
24684         * @param {Node} node The node being moved
24685         * @param {Node} oldParent The parent of the node
24686         * @param {Node} newParent The new parent the node is moving to
24687         * @param {Number} index The index it is being moved to
24688         */
24689        "beforemove" : true,
24690        /**
24691         * @event beforeinsert
24692         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24693         * @param {Tree} tree The owner tree
24694         * @param {Node} parent The parent node
24695         * @param {Node} node The child node to be inserted
24696         * @param {Node} refNode The child node the node is being inserted before
24697         */
24698        "beforeinsert" : true
24699    });
24700
24701     Roo.data.Tree.superclass.constructor.call(this);
24702 };
24703
24704 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24705     pathSeparator: "/",
24706
24707     proxyNodeEvent : function(){
24708         return this.fireEvent.apply(this, arguments);
24709     },
24710
24711     /**
24712      * Returns the root node for this tree.
24713      * @return {Node}
24714      */
24715     getRootNode : function(){
24716         return this.root;
24717     },
24718
24719     /**
24720      * Sets the root node for this tree.
24721      * @param {Node} node
24722      * @return {Node}
24723      */
24724     setRootNode : function(node){
24725         this.root = node;
24726         node.ownerTree = this;
24727         node.isRoot = true;
24728         this.registerNode(node);
24729         return node;
24730     },
24731
24732     /**
24733      * Gets a node in this tree by its id.
24734      * @param {String} id
24735      * @return {Node}
24736      */
24737     getNodeById : function(id){
24738         return this.nodeHash[id];
24739     },
24740
24741     registerNode : function(node){
24742         this.nodeHash[node.id] = node;
24743     },
24744
24745     unregisterNode : function(node){
24746         delete this.nodeHash[node.id];
24747     },
24748
24749     toString : function(){
24750         return "[Tree"+(this.id?" "+this.id:"")+"]";
24751     }
24752 });
24753
24754 /**
24755  * @class Roo.data.Node
24756  * @extends Roo.util.Observable
24757  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24758  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24759  * @constructor
24760  * @param {Object} attributes The attributes/config for the node
24761  */
24762 Roo.data.Node = function(attributes){
24763     /**
24764      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24765      * @type {Object}
24766      */
24767     this.attributes = attributes || {};
24768     this.leaf = this.attributes.leaf;
24769     /**
24770      * The node id. @type String
24771      */
24772     this.id = this.attributes.id;
24773     if(!this.id){
24774         this.id = Roo.id(null, "ynode-");
24775         this.attributes.id = this.id;
24776     }
24777      
24778     
24779     /**
24780      * All child nodes of this node. @type Array
24781      */
24782     this.childNodes = [];
24783     if(!this.childNodes.indexOf){ // indexOf is a must
24784         this.childNodes.indexOf = function(o){
24785             for(var i = 0, len = this.length; i < len; i++){
24786                 if(this[i] == o) {
24787                     return i;
24788                 }
24789             }
24790             return -1;
24791         };
24792     }
24793     /**
24794      * The parent node for this node. @type Node
24795      */
24796     this.parentNode = null;
24797     /**
24798      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24799      */
24800     this.firstChild = null;
24801     /**
24802      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24803      */
24804     this.lastChild = null;
24805     /**
24806      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24807      */
24808     this.previousSibling = null;
24809     /**
24810      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24811      */
24812     this.nextSibling = null;
24813
24814     this.addEvents({
24815        /**
24816         * @event append
24817         * Fires when a new child node is appended
24818         * @param {Tree} tree The owner tree
24819         * @param {Node} this This node
24820         * @param {Node} node The newly appended node
24821         * @param {Number} index The index of the newly appended node
24822         */
24823        "append" : true,
24824        /**
24825         * @event remove
24826         * Fires when a child node is removed
24827         * @param {Tree} tree The owner tree
24828         * @param {Node} this This node
24829         * @param {Node} node The removed node
24830         */
24831        "remove" : true,
24832        /**
24833         * @event move
24834         * Fires when this node is moved to a new location in the tree
24835         * @param {Tree} tree The owner tree
24836         * @param {Node} this This node
24837         * @param {Node} oldParent The old parent of this node
24838         * @param {Node} newParent The new parent of this node
24839         * @param {Number} index The index it was moved to
24840         */
24841        "move" : true,
24842        /**
24843         * @event insert
24844         * Fires when a new child node is inserted.
24845         * @param {Tree} tree The owner tree
24846         * @param {Node} this This node
24847         * @param {Node} node The child node inserted
24848         * @param {Node} refNode The child node the node was inserted before
24849         */
24850        "insert" : true,
24851        /**
24852         * @event beforeappend
24853         * Fires before a new child is appended, return false to cancel the append.
24854         * @param {Tree} tree The owner tree
24855         * @param {Node} this This node
24856         * @param {Node} node The child node to be appended
24857         */
24858        "beforeappend" : true,
24859        /**
24860         * @event beforeremove
24861         * Fires before a child is removed, return false to cancel the remove.
24862         * @param {Tree} tree The owner tree
24863         * @param {Node} this This node
24864         * @param {Node} node The child node to be removed
24865         */
24866        "beforeremove" : true,
24867        /**
24868         * @event beforemove
24869         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24870         * @param {Tree} tree The owner tree
24871         * @param {Node} this This node
24872         * @param {Node} oldParent The parent of this node
24873         * @param {Node} newParent The new parent this node is moving to
24874         * @param {Number} index The index it is being moved to
24875         */
24876        "beforemove" : true,
24877        /**
24878         * @event beforeinsert
24879         * Fires before a new child is inserted, return false to cancel the insert.
24880         * @param {Tree} tree The owner tree
24881         * @param {Node} this This node
24882         * @param {Node} node The child node to be inserted
24883         * @param {Node} refNode The child node the node is being inserted before
24884         */
24885        "beforeinsert" : true
24886    });
24887     this.listeners = this.attributes.listeners;
24888     Roo.data.Node.superclass.constructor.call(this);
24889 };
24890
24891 Roo.extend(Roo.data.Node, Roo.util.Observable, {
24892     fireEvent : function(evtName){
24893         // first do standard event for this node
24894         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
24895             return false;
24896         }
24897         // then bubble it up to the tree if the event wasn't cancelled
24898         var ot = this.getOwnerTree();
24899         if(ot){
24900             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
24901                 return false;
24902             }
24903         }
24904         return true;
24905     },
24906
24907     /**
24908      * Returns true if this node is a leaf
24909      * @return {Boolean}
24910      */
24911     isLeaf : function(){
24912         return this.leaf === true;
24913     },
24914
24915     // private
24916     setFirstChild : function(node){
24917         this.firstChild = node;
24918     },
24919
24920     //private
24921     setLastChild : function(node){
24922         this.lastChild = node;
24923     },
24924
24925
24926     /**
24927      * Returns true if this node is the last child of its parent
24928      * @return {Boolean}
24929      */
24930     isLast : function(){
24931        return (!this.parentNode ? true : this.parentNode.lastChild == this);
24932     },
24933
24934     /**
24935      * Returns true if this node is the first child of its parent
24936      * @return {Boolean}
24937      */
24938     isFirst : function(){
24939        return (!this.parentNode ? true : this.parentNode.firstChild == this);
24940     },
24941
24942     hasChildNodes : function(){
24943         return !this.isLeaf() && this.childNodes.length > 0;
24944     },
24945
24946     /**
24947      * Insert node(s) as the last child node of this node.
24948      * @param {Node/Array} node The node or Array of nodes to append
24949      * @return {Node} The appended node if single append, or null if an array was passed
24950      */
24951     appendChild : function(node){
24952         var multi = false;
24953         if(node instanceof Array){
24954             multi = node;
24955         }else if(arguments.length > 1){
24956             multi = arguments;
24957         }
24958         // if passed an array or multiple args do them one by one
24959         if(multi){
24960             for(var i = 0, len = multi.length; i < len; i++) {
24961                 this.appendChild(multi[i]);
24962             }
24963         }else{
24964             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
24965                 return false;
24966             }
24967             var index = this.childNodes.length;
24968             var oldParent = node.parentNode;
24969             // it's a move, make sure we move it cleanly
24970             if(oldParent){
24971                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
24972                     return false;
24973                 }
24974                 oldParent.removeChild(node);
24975             }
24976             index = this.childNodes.length;
24977             if(index == 0){
24978                 this.setFirstChild(node);
24979             }
24980             this.childNodes.push(node);
24981             node.parentNode = this;
24982             var ps = this.childNodes[index-1];
24983             if(ps){
24984                 node.previousSibling = ps;
24985                 ps.nextSibling = node;
24986             }else{
24987                 node.previousSibling = null;
24988             }
24989             node.nextSibling = null;
24990             this.setLastChild(node);
24991             node.setOwnerTree(this.getOwnerTree());
24992             this.fireEvent("append", this.ownerTree, this, node, index);
24993             if(oldParent){
24994                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
24995             }
24996             return node;
24997         }
24998     },
24999
25000     /**
25001      * Removes a child node from this node.
25002      * @param {Node} node The node to remove
25003      * @return {Node} The removed node
25004      */
25005     removeChild : function(node){
25006         var index = this.childNodes.indexOf(node);
25007         if(index == -1){
25008             return false;
25009         }
25010         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25011             return false;
25012         }
25013
25014         // remove it from childNodes collection
25015         this.childNodes.splice(index, 1);
25016
25017         // update siblings
25018         if(node.previousSibling){
25019             node.previousSibling.nextSibling = node.nextSibling;
25020         }
25021         if(node.nextSibling){
25022             node.nextSibling.previousSibling = node.previousSibling;
25023         }
25024
25025         // update child refs
25026         if(this.firstChild == node){
25027             this.setFirstChild(node.nextSibling);
25028         }
25029         if(this.lastChild == node){
25030             this.setLastChild(node.previousSibling);
25031         }
25032
25033         node.setOwnerTree(null);
25034         // clear any references from the node
25035         node.parentNode = null;
25036         node.previousSibling = null;
25037         node.nextSibling = null;
25038         this.fireEvent("remove", this.ownerTree, this, node);
25039         return node;
25040     },
25041
25042     /**
25043      * Inserts the first node before the second node in this nodes childNodes collection.
25044      * @param {Node} node The node to insert
25045      * @param {Node} refNode The node to insert before (if null the node is appended)
25046      * @return {Node} The inserted node
25047      */
25048     insertBefore : function(node, refNode){
25049         if(!refNode){ // like standard Dom, refNode can be null for append
25050             return this.appendChild(node);
25051         }
25052         // nothing to do
25053         if(node == refNode){
25054             return false;
25055         }
25056
25057         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25058             return false;
25059         }
25060         var index = this.childNodes.indexOf(refNode);
25061         var oldParent = node.parentNode;
25062         var refIndex = index;
25063
25064         // when moving internally, indexes will change after remove
25065         if(oldParent == this && this.childNodes.indexOf(node) < index){
25066             refIndex--;
25067         }
25068
25069         // it's a move, make sure we move it cleanly
25070         if(oldParent){
25071             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25072                 return false;
25073             }
25074             oldParent.removeChild(node);
25075         }
25076         if(refIndex == 0){
25077             this.setFirstChild(node);
25078         }
25079         this.childNodes.splice(refIndex, 0, node);
25080         node.parentNode = this;
25081         var ps = this.childNodes[refIndex-1];
25082         if(ps){
25083             node.previousSibling = ps;
25084             ps.nextSibling = node;
25085         }else{
25086             node.previousSibling = null;
25087         }
25088         node.nextSibling = refNode;
25089         refNode.previousSibling = node;
25090         node.setOwnerTree(this.getOwnerTree());
25091         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25092         if(oldParent){
25093             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25094         }
25095         return node;
25096     },
25097
25098     /**
25099      * Returns the child node at the specified index.
25100      * @param {Number} index
25101      * @return {Node}
25102      */
25103     item : function(index){
25104         return this.childNodes[index];
25105     },
25106
25107     /**
25108      * Replaces one child node in this node with another.
25109      * @param {Node} newChild The replacement node
25110      * @param {Node} oldChild The node to replace
25111      * @return {Node} The replaced node
25112      */
25113     replaceChild : function(newChild, oldChild){
25114         this.insertBefore(newChild, oldChild);
25115         this.removeChild(oldChild);
25116         return oldChild;
25117     },
25118
25119     /**
25120      * Returns the index of a child node
25121      * @param {Node} node
25122      * @return {Number} The index of the node or -1 if it was not found
25123      */
25124     indexOf : function(child){
25125         return this.childNodes.indexOf(child);
25126     },
25127
25128     /**
25129      * Returns the tree this node is in.
25130      * @return {Tree}
25131      */
25132     getOwnerTree : function(){
25133         // if it doesn't have one, look for one
25134         if(!this.ownerTree){
25135             var p = this;
25136             while(p){
25137                 if(p.ownerTree){
25138                     this.ownerTree = p.ownerTree;
25139                     break;
25140                 }
25141                 p = p.parentNode;
25142             }
25143         }
25144         return this.ownerTree;
25145     },
25146
25147     /**
25148      * Returns depth of this node (the root node has a depth of 0)
25149      * @return {Number}
25150      */
25151     getDepth : function(){
25152         var depth = 0;
25153         var p = this;
25154         while(p.parentNode){
25155             ++depth;
25156             p = p.parentNode;
25157         }
25158         return depth;
25159     },
25160
25161     // private
25162     setOwnerTree : function(tree){
25163         // if it's move, we need to update everyone
25164         if(tree != this.ownerTree){
25165             if(this.ownerTree){
25166                 this.ownerTree.unregisterNode(this);
25167             }
25168             this.ownerTree = tree;
25169             var cs = this.childNodes;
25170             for(var i = 0, len = cs.length; i < len; i++) {
25171                 cs[i].setOwnerTree(tree);
25172             }
25173             if(tree){
25174                 tree.registerNode(this);
25175             }
25176         }
25177     },
25178
25179     /**
25180      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25181      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25182      * @return {String} The path
25183      */
25184     getPath : function(attr){
25185         attr = attr || "id";
25186         var p = this.parentNode;
25187         var b = [this.attributes[attr]];
25188         while(p){
25189             b.unshift(p.attributes[attr]);
25190             p = p.parentNode;
25191         }
25192         var sep = this.getOwnerTree().pathSeparator;
25193         return sep + b.join(sep);
25194     },
25195
25196     /**
25197      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25198      * function call will be the scope provided or the current node. The arguments to the function
25199      * will be the args provided or the current node. If the function returns false at any point,
25200      * the bubble is stopped.
25201      * @param {Function} fn The function to call
25202      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25203      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25204      */
25205     bubble : function(fn, scope, args){
25206         var p = this;
25207         while(p){
25208             if(fn.call(scope || p, args || p) === false){
25209                 break;
25210             }
25211             p = p.parentNode;
25212         }
25213     },
25214
25215     /**
25216      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25217      * function call will be the scope provided or the current node. The arguments to the function
25218      * will be the args provided or the current node. If the function returns false at any point,
25219      * the cascade is stopped on that branch.
25220      * @param {Function} fn The function to call
25221      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25222      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25223      */
25224     cascade : function(fn, scope, args){
25225         if(fn.call(scope || this, args || this) !== false){
25226             var cs = this.childNodes;
25227             for(var i = 0, len = cs.length; i < len; i++) {
25228                 cs[i].cascade(fn, scope, args);
25229             }
25230         }
25231     },
25232
25233     /**
25234      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25235      * function call will be the scope provided or the current node. The arguments to the function
25236      * will be the args provided or the current node. If the function returns false at any point,
25237      * the iteration stops.
25238      * @param {Function} fn The function to call
25239      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25240      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25241      */
25242     eachChild : function(fn, scope, args){
25243         var cs = this.childNodes;
25244         for(var i = 0, len = cs.length; i < len; i++) {
25245                 if(fn.call(scope || this, args || cs[i]) === false){
25246                     break;
25247                 }
25248         }
25249     },
25250
25251     /**
25252      * Finds the first child that has the attribute with the specified value.
25253      * @param {String} attribute The attribute name
25254      * @param {Mixed} value The value to search for
25255      * @return {Node} The found child or null if none was found
25256      */
25257     findChild : function(attribute, value){
25258         var cs = this.childNodes;
25259         for(var i = 0, len = cs.length; i < len; i++) {
25260                 if(cs[i].attributes[attribute] == value){
25261                     return cs[i];
25262                 }
25263         }
25264         return null;
25265     },
25266
25267     /**
25268      * Finds the first child by a custom function. The child matches if the function passed
25269      * returns true.
25270      * @param {Function} fn
25271      * @param {Object} scope (optional)
25272      * @return {Node} The found child or null if none was found
25273      */
25274     findChildBy : function(fn, scope){
25275         var cs = this.childNodes;
25276         for(var i = 0, len = cs.length; i < len; i++) {
25277                 if(fn.call(scope||cs[i], cs[i]) === true){
25278                     return cs[i];
25279                 }
25280         }
25281         return null;
25282     },
25283
25284     /**
25285      * Sorts this nodes children using the supplied sort function
25286      * @param {Function} fn
25287      * @param {Object} scope (optional)
25288      */
25289     sort : function(fn, scope){
25290         var cs = this.childNodes;
25291         var len = cs.length;
25292         if(len > 0){
25293             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25294             cs.sort(sortFn);
25295             for(var i = 0; i < len; i++){
25296                 var n = cs[i];
25297                 n.previousSibling = cs[i-1];
25298                 n.nextSibling = cs[i+1];
25299                 if(i == 0){
25300                     this.setFirstChild(n);
25301                 }
25302                 if(i == len-1){
25303                     this.setLastChild(n);
25304                 }
25305             }
25306         }
25307     },
25308
25309     /**
25310      * Returns true if this node is an ancestor (at any point) of the passed node.
25311      * @param {Node} node
25312      * @return {Boolean}
25313      */
25314     contains : function(node){
25315         return node.isAncestor(this);
25316     },
25317
25318     /**
25319      * Returns true if the passed node is an ancestor (at any point) of this node.
25320      * @param {Node} node
25321      * @return {Boolean}
25322      */
25323     isAncestor : function(node){
25324         var p = this.parentNode;
25325         while(p){
25326             if(p == node){
25327                 return true;
25328             }
25329             p = p.parentNode;
25330         }
25331         return false;
25332     },
25333
25334     toString : function(){
25335         return "[Node"+(this.id?" "+this.id:"")+"]";
25336     }
25337 });/*
25338  * Based on:
25339  * Ext JS Library 1.1.1
25340  * Copyright(c) 2006-2007, Ext JS, LLC.
25341  *
25342  * Originally Released Under LGPL - original licence link has changed is not relivant.
25343  *
25344  * Fork - LGPL
25345  * <script type="text/javascript">
25346  */
25347  (function(){ 
25348 /**
25349  * @class Roo.Layer
25350  * @extends Roo.Element
25351  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25352  * automatic maintaining of shadow/shim positions.
25353  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25354  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25355  * you can pass a string with a CSS class name. False turns off the shadow.
25356  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25357  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25358  * @cfg {String} cls CSS class to add to the element
25359  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25360  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25361  * @constructor
25362  * @param {Object} config An object with config options.
25363  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25364  */
25365
25366 Roo.Layer = function(config, existingEl){
25367     config = config || {};
25368     var dh = Roo.DomHelper;
25369     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25370     if(existingEl){
25371         this.dom = Roo.getDom(existingEl);
25372     }
25373     if(!this.dom){
25374         var o = config.dh || {tag: "div", cls: "x-layer"};
25375         this.dom = dh.append(pel, o);
25376     }
25377     if(config.cls){
25378         this.addClass(config.cls);
25379     }
25380     this.constrain = config.constrain !== false;
25381     this.visibilityMode = Roo.Element.VISIBILITY;
25382     if(config.id){
25383         this.id = this.dom.id = config.id;
25384     }else{
25385         this.id = Roo.id(this.dom);
25386     }
25387     this.zindex = config.zindex || this.getZIndex();
25388     this.position("absolute", this.zindex);
25389     if(config.shadow){
25390         this.shadowOffset = config.shadowOffset || 4;
25391         this.shadow = new Roo.Shadow({
25392             offset : this.shadowOffset,
25393             mode : config.shadow
25394         });
25395     }else{
25396         this.shadowOffset = 0;
25397     }
25398     this.useShim = config.shim !== false && Roo.useShims;
25399     this.useDisplay = config.useDisplay;
25400     this.hide();
25401 };
25402
25403 var supr = Roo.Element.prototype;
25404
25405 // shims are shared among layer to keep from having 100 iframes
25406 var shims = [];
25407
25408 Roo.extend(Roo.Layer, Roo.Element, {
25409
25410     getZIndex : function(){
25411         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25412     },
25413
25414     getShim : function(){
25415         if(!this.useShim){
25416             return null;
25417         }
25418         if(this.shim){
25419             return this.shim;
25420         }
25421         var shim = shims.shift();
25422         if(!shim){
25423             shim = this.createShim();
25424             shim.enableDisplayMode('block');
25425             shim.dom.style.display = 'none';
25426             shim.dom.style.visibility = 'visible';
25427         }
25428         var pn = this.dom.parentNode;
25429         if(shim.dom.parentNode != pn){
25430             pn.insertBefore(shim.dom, this.dom);
25431         }
25432         shim.setStyle('z-index', this.getZIndex()-2);
25433         this.shim = shim;
25434         return shim;
25435     },
25436
25437     hideShim : function(){
25438         if(this.shim){
25439             this.shim.setDisplayed(false);
25440             shims.push(this.shim);
25441             delete this.shim;
25442         }
25443     },
25444
25445     disableShadow : function(){
25446         if(this.shadow){
25447             this.shadowDisabled = true;
25448             this.shadow.hide();
25449             this.lastShadowOffset = this.shadowOffset;
25450             this.shadowOffset = 0;
25451         }
25452     },
25453
25454     enableShadow : function(show){
25455         if(this.shadow){
25456             this.shadowDisabled = false;
25457             this.shadowOffset = this.lastShadowOffset;
25458             delete this.lastShadowOffset;
25459             if(show){
25460                 this.sync(true);
25461             }
25462         }
25463     },
25464
25465     // private
25466     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25467     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25468     sync : function(doShow){
25469         var sw = this.shadow;
25470         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25471             var sh = this.getShim();
25472
25473             var w = this.getWidth(),
25474                 h = this.getHeight();
25475
25476             var l = this.getLeft(true),
25477                 t = this.getTop(true);
25478
25479             if(sw && !this.shadowDisabled){
25480                 if(doShow && !sw.isVisible()){
25481                     sw.show(this);
25482                 }else{
25483                     sw.realign(l, t, w, h);
25484                 }
25485                 if(sh){
25486                     if(doShow){
25487                        sh.show();
25488                     }
25489                     // fit the shim behind the shadow, so it is shimmed too
25490                     var a = sw.adjusts, s = sh.dom.style;
25491                     s.left = (Math.min(l, l+a.l))+"px";
25492                     s.top = (Math.min(t, t+a.t))+"px";
25493                     s.width = (w+a.w)+"px";
25494                     s.height = (h+a.h)+"px";
25495                 }
25496             }else if(sh){
25497                 if(doShow){
25498                    sh.show();
25499                 }
25500                 sh.setSize(w, h);
25501                 sh.setLeftTop(l, t);
25502             }
25503             
25504         }
25505     },
25506
25507     // private
25508     destroy : function(){
25509         this.hideShim();
25510         if(this.shadow){
25511             this.shadow.hide();
25512         }
25513         this.removeAllListeners();
25514         var pn = this.dom.parentNode;
25515         if(pn){
25516             pn.removeChild(this.dom);
25517         }
25518         Roo.Element.uncache(this.id);
25519     },
25520
25521     remove : function(){
25522         this.destroy();
25523     },
25524
25525     // private
25526     beginUpdate : function(){
25527         this.updating = true;
25528     },
25529
25530     // private
25531     endUpdate : function(){
25532         this.updating = false;
25533         this.sync(true);
25534     },
25535
25536     // private
25537     hideUnders : function(negOffset){
25538         if(this.shadow){
25539             this.shadow.hide();
25540         }
25541         this.hideShim();
25542     },
25543
25544     // private
25545     constrainXY : function(){
25546         if(this.constrain){
25547             var vw = Roo.lib.Dom.getViewWidth(),
25548                 vh = Roo.lib.Dom.getViewHeight();
25549             var s = Roo.get(document).getScroll();
25550
25551             var xy = this.getXY();
25552             var x = xy[0], y = xy[1];   
25553             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25554             // only move it if it needs it
25555             var moved = false;
25556             // first validate right/bottom
25557             if((x + w) > vw+s.left){
25558                 x = vw - w - this.shadowOffset;
25559                 moved = true;
25560             }
25561             if((y + h) > vh+s.top){
25562                 y = vh - h - this.shadowOffset;
25563                 moved = true;
25564             }
25565             // then make sure top/left isn't negative
25566             if(x < s.left){
25567                 x = s.left;
25568                 moved = true;
25569             }
25570             if(y < s.top){
25571                 y = s.top;
25572                 moved = true;
25573             }
25574             if(moved){
25575                 if(this.avoidY){
25576                     var ay = this.avoidY;
25577                     if(y <= ay && (y+h) >= ay){
25578                         y = ay-h-5;   
25579                     }
25580                 }
25581                 xy = [x, y];
25582                 this.storeXY(xy);
25583                 supr.setXY.call(this, xy);
25584                 this.sync();
25585             }
25586         }
25587     },
25588
25589     isVisible : function(){
25590         return this.visible;    
25591     },
25592
25593     // private
25594     showAction : function(){
25595         this.visible = true; // track visibility to prevent getStyle calls
25596         if(this.useDisplay === true){
25597             this.setDisplayed("");
25598         }else if(this.lastXY){
25599             supr.setXY.call(this, this.lastXY);
25600         }else if(this.lastLT){
25601             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25602         }
25603     },
25604
25605     // private
25606     hideAction : function(){
25607         this.visible = false;
25608         if(this.useDisplay === true){
25609             this.setDisplayed(false);
25610         }else{
25611             this.setLeftTop(-10000,-10000);
25612         }
25613     },
25614
25615     // overridden Element method
25616     setVisible : function(v, a, d, c, e){
25617         if(v){
25618             this.showAction();
25619         }
25620         if(a && v){
25621             var cb = function(){
25622                 this.sync(true);
25623                 if(c){
25624                     c();
25625                 }
25626             }.createDelegate(this);
25627             supr.setVisible.call(this, true, true, d, cb, e);
25628         }else{
25629             if(!v){
25630                 this.hideUnders(true);
25631             }
25632             var cb = c;
25633             if(a){
25634                 cb = function(){
25635                     this.hideAction();
25636                     if(c){
25637                         c();
25638                     }
25639                 }.createDelegate(this);
25640             }
25641             supr.setVisible.call(this, v, a, d, cb, e);
25642             if(v){
25643                 this.sync(true);
25644             }else if(!a){
25645                 this.hideAction();
25646             }
25647         }
25648     },
25649
25650     storeXY : function(xy){
25651         delete this.lastLT;
25652         this.lastXY = xy;
25653     },
25654
25655     storeLeftTop : function(left, top){
25656         delete this.lastXY;
25657         this.lastLT = [left, top];
25658     },
25659
25660     // private
25661     beforeFx : function(){
25662         this.beforeAction();
25663         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25664     },
25665
25666     // private
25667     afterFx : function(){
25668         Roo.Layer.superclass.afterFx.apply(this, arguments);
25669         this.sync(this.isVisible());
25670     },
25671
25672     // private
25673     beforeAction : function(){
25674         if(!this.updating && this.shadow){
25675             this.shadow.hide();
25676         }
25677     },
25678
25679     // overridden Element method
25680     setLeft : function(left){
25681         this.storeLeftTop(left, this.getTop(true));
25682         supr.setLeft.apply(this, arguments);
25683         this.sync();
25684     },
25685
25686     setTop : function(top){
25687         this.storeLeftTop(this.getLeft(true), top);
25688         supr.setTop.apply(this, arguments);
25689         this.sync();
25690     },
25691
25692     setLeftTop : function(left, top){
25693         this.storeLeftTop(left, top);
25694         supr.setLeftTop.apply(this, arguments);
25695         this.sync();
25696     },
25697
25698     setXY : function(xy, a, d, c, e){
25699         this.fixDisplay();
25700         this.beforeAction();
25701         this.storeXY(xy);
25702         var cb = this.createCB(c);
25703         supr.setXY.call(this, xy, a, d, cb, e);
25704         if(!a){
25705             cb();
25706         }
25707     },
25708
25709     // private
25710     createCB : function(c){
25711         var el = this;
25712         return function(){
25713             el.constrainXY();
25714             el.sync(true);
25715             if(c){
25716                 c();
25717             }
25718         };
25719     },
25720
25721     // overridden Element method
25722     setX : function(x, a, d, c, e){
25723         this.setXY([x, this.getY()], a, d, c, e);
25724     },
25725
25726     // overridden Element method
25727     setY : function(y, a, d, c, e){
25728         this.setXY([this.getX(), y], a, d, c, e);
25729     },
25730
25731     // overridden Element method
25732     setSize : function(w, h, a, d, c, e){
25733         this.beforeAction();
25734         var cb = this.createCB(c);
25735         supr.setSize.call(this, w, h, a, d, cb, e);
25736         if(!a){
25737             cb();
25738         }
25739     },
25740
25741     // overridden Element method
25742     setWidth : function(w, a, d, c, e){
25743         this.beforeAction();
25744         var cb = this.createCB(c);
25745         supr.setWidth.call(this, w, a, d, cb, e);
25746         if(!a){
25747             cb();
25748         }
25749     },
25750
25751     // overridden Element method
25752     setHeight : function(h, a, d, c, e){
25753         this.beforeAction();
25754         var cb = this.createCB(c);
25755         supr.setHeight.call(this, h, a, d, cb, e);
25756         if(!a){
25757             cb();
25758         }
25759     },
25760
25761     // overridden Element method
25762     setBounds : function(x, y, w, h, a, d, c, e){
25763         this.beforeAction();
25764         var cb = this.createCB(c);
25765         if(!a){
25766             this.storeXY([x, y]);
25767             supr.setXY.call(this, [x, y]);
25768             supr.setSize.call(this, w, h, a, d, cb, e);
25769             cb();
25770         }else{
25771             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25772         }
25773         return this;
25774     },
25775     
25776     /**
25777      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25778      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25779      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25780      * @param {Number} zindex The new z-index to set
25781      * @return {this} The Layer
25782      */
25783     setZIndex : function(zindex){
25784         this.zindex = zindex;
25785         this.setStyle("z-index", zindex + 2);
25786         if(this.shadow){
25787             this.shadow.setZIndex(zindex + 1);
25788         }
25789         if(this.shim){
25790             this.shim.setStyle("z-index", zindex);
25791         }
25792     }
25793 });
25794 })();/*
25795  * Based on:
25796  * Ext JS Library 1.1.1
25797  * Copyright(c) 2006-2007, Ext JS, LLC.
25798  *
25799  * Originally Released Under LGPL - original licence link has changed is not relivant.
25800  *
25801  * Fork - LGPL
25802  * <script type="text/javascript">
25803  */
25804
25805
25806 /**
25807  * @class Roo.Shadow
25808  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25809  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25810  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25811  * @constructor
25812  * Create a new Shadow
25813  * @param {Object} config The config object
25814  */
25815 Roo.Shadow = function(config){
25816     Roo.apply(this, config);
25817     if(typeof this.mode != "string"){
25818         this.mode = this.defaultMode;
25819     }
25820     var o = this.offset, a = {h: 0};
25821     var rad = Math.floor(this.offset/2);
25822     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25823         case "drop":
25824             a.w = 0;
25825             a.l = a.t = o;
25826             a.t -= 1;
25827             if(Roo.isIE){
25828                 a.l -= this.offset + rad;
25829                 a.t -= this.offset + rad;
25830                 a.w -= rad;
25831                 a.h -= rad;
25832                 a.t += 1;
25833             }
25834         break;
25835         case "sides":
25836             a.w = (o*2);
25837             a.l = -o;
25838             a.t = o-1;
25839             if(Roo.isIE){
25840                 a.l -= (this.offset - rad);
25841                 a.t -= this.offset + rad;
25842                 a.l += 1;
25843                 a.w -= (this.offset - rad)*2;
25844                 a.w -= rad + 1;
25845                 a.h -= 1;
25846             }
25847         break;
25848         case "frame":
25849             a.w = a.h = (o*2);
25850             a.l = a.t = -o;
25851             a.t += 1;
25852             a.h -= 2;
25853             if(Roo.isIE){
25854                 a.l -= (this.offset - rad);
25855                 a.t -= (this.offset - rad);
25856                 a.l += 1;
25857                 a.w -= (this.offset + rad + 1);
25858                 a.h -= (this.offset + rad);
25859                 a.h += 1;
25860             }
25861         break;
25862     };
25863
25864     this.adjusts = a;
25865 };
25866
25867 Roo.Shadow.prototype = {
25868     /**
25869      * @cfg {String} mode
25870      * The shadow display mode.  Supports the following options:<br />
25871      * sides: Shadow displays on both sides and bottom only<br />
25872      * frame: Shadow displays equally on all four sides<br />
25873      * drop: Traditional bottom-right drop shadow (default)
25874      */
25875     /**
25876      * @cfg {String} offset
25877      * The number of pixels to offset the shadow from the element (defaults to 4)
25878      */
25879     offset: 4,
25880
25881     // private
25882     defaultMode: "drop",
25883
25884     /**
25885      * Displays the shadow under the target element
25886      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25887      */
25888     show : function(target){
25889         target = Roo.get(target);
25890         if(!this.el){
25891             this.el = Roo.Shadow.Pool.pull();
25892             if(this.el.dom.nextSibling != target.dom){
25893                 this.el.insertBefore(target);
25894             }
25895         }
25896         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25897         if(Roo.isIE){
25898             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25899         }
25900         this.realign(
25901             target.getLeft(true),
25902             target.getTop(true),
25903             target.getWidth(),
25904             target.getHeight()
25905         );
25906         this.el.dom.style.display = "block";
25907     },
25908
25909     /**
25910      * Returns true if the shadow is visible, else false
25911      */
25912     isVisible : function(){
25913         return this.el ? true : false;  
25914     },
25915
25916     /**
25917      * Direct alignment when values are already available. Show must be called at least once before
25918      * calling this method to ensure it is initialized.
25919      * @param {Number} left The target element left position
25920      * @param {Number} top The target element top position
25921      * @param {Number} width The target element width
25922      * @param {Number} height The target element height
25923      */
25924     realign : function(l, t, w, h){
25925         if(!this.el){
25926             return;
25927         }
25928         var a = this.adjusts, d = this.el.dom, s = d.style;
25929         var iea = 0;
25930         s.left = (l+a.l)+"px";
25931         s.top = (t+a.t)+"px";
25932         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
25933  
25934         if(s.width != sws || s.height != shs){
25935             s.width = sws;
25936             s.height = shs;
25937             if(!Roo.isIE){
25938                 var cn = d.childNodes;
25939                 var sww = Math.max(0, (sw-12))+"px";
25940                 cn[0].childNodes[1].style.width = sww;
25941                 cn[1].childNodes[1].style.width = sww;
25942                 cn[2].childNodes[1].style.width = sww;
25943                 cn[1].style.height = Math.max(0, (sh-12))+"px";
25944             }
25945         }
25946     },
25947
25948     /**
25949      * Hides this shadow
25950      */
25951     hide : function(){
25952         if(this.el){
25953             this.el.dom.style.display = "none";
25954             Roo.Shadow.Pool.push(this.el);
25955             delete this.el;
25956         }
25957     },
25958
25959     /**
25960      * Adjust the z-index of this shadow
25961      * @param {Number} zindex The new z-index
25962      */
25963     setZIndex : function(z){
25964         this.zIndex = z;
25965         if(this.el){
25966             this.el.setStyle("z-index", z);
25967         }
25968     }
25969 };
25970
25971 // Private utility class that manages the internal Shadow cache
25972 Roo.Shadow.Pool = function(){
25973     var p = [];
25974     var markup = Roo.isIE ?
25975                  '<div class="x-ie-shadow"></div>' :
25976                  '<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>';
25977     return {
25978         pull : function(){
25979             var sh = p.shift();
25980             if(!sh){
25981                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
25982                 sh.autoBoxAdjust = false;
25983             }
25984             return sh;
25985         },
25986
25987         push : function(sh){
25988             p.push(sh);
25989         }
25990     };
25991 }();/*
25992  * Based on:
25993  * Ext JS Library 1.1.1
25994  * Copyright(c) 2006-2007, Ext JS, LLC.
25995  *
25996  * Originally Released Under LGPL - original licence link has changed is not relivant.
25997  *
25998  * Fork - LGPL
25999  * <script type="text/javascript">
26000  */
26001
26002
26003 /**
26004  * @class Roo.SplitBar
26005  * @extends Roo.util.Observable
26006  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26007  * <br><br>
26008  * Usage:
26009  * <pre><code>
26010 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26011                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26012 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26013 split.minSize = 100;
26014 split.maxSize = 600;
26015 split.animate = true;
26016 split.on('moved', splitterMoved);
26017 </code></pre>
26018  * @constructor
26019  * Create a new SplitBar
26020  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26021  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26022  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26023  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26024                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26025                         position of the SplitBar).
26026  */
26027 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26028     
26029     /** @private */
26030     this.el = Roo.get(dragElement, true);
26031     this.el.dom.unselectable = "on";
26032     /** @private */
26033     this.resizingEl = Roo.get(resizingElement, true);
26034
26035     /**
26036      * @private
26037      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26038      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26039      * @type Number
26040      */
26041     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26042     
26043     /**
26044      * The minimum size of the resizing element. (Defaults to 0)
26045      * @type Number
26046      */
26047     this.minSize = 0;
26048     
26049     /**
26050      * The maximum size of the resizing element. (Defaults to 2000)
26051      * @type Number
26052      */
26053     this.maxSize = 2000;
26054     
26055     /**
26056      * Whether to animate the transition to the new size
26057      * @type Boolean
26058      */
26059     this.animate = false;
26060     
26061     /**
26062      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26063      * @type Boolean
26064      */
26065     this.useShim = false;
26066     
26067     /** @private */
26068     this.shim = null;
26069     
26070     if(!existingProxy){
26071         /** @private */
26072         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26073     }else{
26074         this.proxy = Roo.get(existingProxy).dom;
26075     }
26076     /** @private */
26077     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26078     
26079     /** @private */
26080     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26081     
26082     /** @private */
26083     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26084     
26085     /** @private */
26086     this.dragSpecs = {};
26087     
26088     /**
26089      * @private The adapter to use to positon and resize elements
26090      */
26091     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26092     this.adapter.init(this);
26093     
26094     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26095         /** @private */
26096         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26097         this.el.addClass("x-splitbar-h");
26098     }else{
26099         /** @private */
26100         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26101         this.el.addClass("x-splitbar-v");
26102     }
26103     
26104     this.addEvents({
26105         /**
26106          * @event resize
26107          * Fires when the splitter is moved (alias for {@link #event-moved})
26108          * @param {Roo.SplitBar} this
26109          * @param {Number} newSize the new width or height
26110          */
26111         "resize" : true,
26112         /**
26113          * @event moved
26114          * Fires when the splitter is moved
26115          * @param {Roo.SplitBar} this
26116          * @param {Number} newSize the new width or height
26117          */
26118         "moved" : true,
26119         /**
26120          * @event beforeresize
26121          * Fires before the splitter is dragged
26122          * @param {Roo.SplitBar} this
26123          */
26124         "beforeresize" : true,
26125
26126         "beforeapply" : true
26127     });
26128
26129     Roo.util.Observable.call(this);
26130 };
26131
26132 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26133     onStartProxyDrag : function(x, y){
26134         this.fireEvent("beforeresize", this);
26135         if(!this.overlay){
26136             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26137             o.unselectable();
26138             o.enableDisplayMode("block");
26139             // all splitbars share the same overlay
26140             Roo.SplitBar.prototype.overlay = o;
26141         }
26142         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26143         this.overlay.show();
26144         Roo.get(this.proxy).setDisplayed("block");
26145         var size = this.adapter.getElementSize(this);
26146         this.activeMinSize = this.getMinimumSize();;
26147         this.activeMaxSize = this.getMaximumSize();;
26148         var c1 = size - this.activeMinSize;
26149         var c2 = Math.max(this.activeMaxSize - size, 0);
26150         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26151             this.dd.resetConstraints();
26152             this.dd.setXConstraint(
26153                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26154                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26155             );
26156             this.dd.setYConstraint(0, 0);
26157         }else{
26158             this.dd.resetConstraints();
26159             this.dd.setXConstraint(0, 0);
26160             this.dd.setYConstraint(
26161                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26162                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26163             );
26164          }
26165         this.dragSpecs.startSize = size;
26166         this.dragSpecs.startPoint = [x, y];
26167         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26168     },
26169     
26170     /** 
26171      * @private Called after the drag operation by the DDProxy
26172      */
26173     onEndProxyDrag : function(e){
26174         Roo.get(this.proxy).setDisplayed(false);
26175         var endPoint = Roo.lib.Event.getXY(e);
26176         if(this.overlay){
26177             this.overlay.hide();
26178         }
26179         var newSize;
26180         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26181             newSize = this.dragSpecs.startSize + 
26182                 (this.placement == Roo.SplitBar.LEFT ?
26183                     endPoint[0] - this.dragSpecs.startPoint[0] :
26184                     this.dragSpecs.startPoint[0] - endPoint[0]
26185                 );
26186         }else{
26187             newSize = this.dragSpecs.startSize + 
26188                 (this.placement == Roo.SplitBar.TOP ?
26189                     endPoint[1] - this.dragSpecs.startPoint[1] :
26190                     this.dragSpecs.startPoint[1] - endPoint[1]
26191                 );
26192         }
26193         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26194         if(newSize != this.dragSpecs.startSize){
26195             if(this.fireEvent('beforeapply', this, newSize) !== false){
26196                 this.adapter.setElementSize(this, newSize);
26197                 this.fireEvent("moved", this, newSize);
26198                 this.fireEvent("resize", this, newSize);
26199             }
26200         }
26201     },
26202     
26203     /**
26204      * Get the adapter this SplitBar uses
26205      * @return The adapter object
26206      */
26207     getAdapter : function(){
26208         return this.adapter;
26209     },
26210     
26211     /**
26212      * Set the adapter this SplitBar uses
26213      * @param {Object} adapter A SplitBar adapter object
26214      */
26215     setAdapter : function(adapter){
26216         this.adapter = adapter;
26217         this.adapter.init(this);
26218     },
26219     
26220     /**
26221      * Gets the minimum size for the resizing element
26222      * @return {Number} The minimum size
26223      */
26224     getMinimumSize : function(){
26225         return this.minSize;
26226     },
26227     
26228     /**
26229      * Sets the minimum size for the resizing element
26230      * @param {Number} minSize The minimum size
26231      */
26232     setMinimumSize : function(minSize){
26233         this.minSize = minSize;
26234     },
26235     
26236     /**
26237      * Gets the maximum size for the resizing element
26238      * @return {Number} The maximum size
26239      */
26240     getMaximumSize : function(){
26241         return this.maxSize;
26242     },
26243     
26244     /**
26245      * Sets the maximum size for the resizing element
26246      * @param {Number} maxSize The maximum size
26247      */
26248     setMaximumSize : function(maxSize){
26249         this.maxSize = maxSize;
26250     },
26251     
26252     /**
26253      * Sets the initialize size for the resizing element
26254      * @param {Number} size The initial size
26255      */
26256     setCurrentSize : function(size){
26257         var oldAnimate = this.animate;
26258         this.animate = false;
26259         this.adapter.setElementSize(this, size);
26260         this.animate = oldAnimate;
26261     },
26262     
26263     /**
26264      * Destroy this splitbar. 
26265      * @param {Boolean} removeEl True to remove the element
26266      */
26267     destroy : function(removeEl){
26268         if(this.shim){
26269             this.shim.remove();
26270         }
26271         this.dd.unreg();
26272         this.proxy.parentNode.removeChild(this.proxy);
26273         if(removeEl){
26274             this.el.remove();
26275         }
26276     }
26277 });
26278
26279 /**
26280  * @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.
26281  */
26282 Roo.SplitBar.createProxy = function(dir){
26283     var proxy = new Roo.Element(document.createElement("div"));
26284     proxy.unselectable();
26285     var cls = 'x-splitbar-proxy';
26286     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26287     document.body.appendChild(proxy.dom);
26288     return proxy.dom;
26289 };
26290
26291 /** 
26292  * @class Roo.SplitBar.BasicLayoutAdapter
26293  * Default Adapter. It assumes the splitter and resizing element are not positioned
26294  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26295  */
26296 Roo.SplitBar.BasicLayoutAdapter = function(){
26297 };
26298
26299 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26300     // do nothing for now
26301     init : function(s){
26302     
26303     },
26304     /**
26305      * Called before drag operations to get the current size of the resizing element. 
26306      * @param {Roo.SplitBar} s The SplitBar using this adapter
26307      */
26308      getElementSize : function(s){
26309         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26310             return s.resizingEl.getWidth();
26311         }else{
26312             return s.resizingEl.getHeight();
26313         }
26314     },
26315     
26316     /**
26317      * Called after drag operations to set the size of the resizing element.
26318      * @param {Roo.SplitBar} s The SplitBar using this adapter
26319      * @param {Number} newSize The new size to set
26320      * @param {Function} onComplete A function to be invoked when resizing is complete
26321      */
26322     setElementSize : function(s, newSize, onComplete){
26323         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26324             if(!s.animate){
26325                 s.resizingEl.setWidth(newSize);
26326                 if(onComplete){
26327                     onComplete(s, newSize);
26328                 }
26329             }else{
26330                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26331             }
26332         }else{
26333             
26334             if(!s.animate){
26335                 s.resizingEl.setHeight(newSize);
26336                 if(onComplete){
26337                     onComplete(s, newSize);
26338                 }
26339             }else{
26340                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26341             }
26342         }
26343     }
26344 };
26345
26346 /** 
26347  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26348  * @extends Roo.SplitBar.BasicLayoutAdapter
26349  * Adapter that  moves the splitter element to align with the resized sizing element. 
26350  * Used with an absolute positioned SplitBar.
26351  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26352  * document.body, make sure you assign an id to the body element.
26353  */
26354 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26355     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26356     this.container = Roo.get(container);
26357 };
26358
26359 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26360     init : function(s){
26361         this.basic.init(s);
26362     },
26363     
26364     getElementSize : function(s){
26365         return this.basic.getElementSize(s);
26366     },
26367     
26368     setElementSize : function(s, newSize, onComplete){
26369         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26370     },
26371     
26372     moveSplitter : function(s){
26373         var yes = Roo.SplitBar;
26374         switch(s.placement){
26375             case yes.LEFT:
26376                 s.el.setX(s.resizingEl.getRight());
26377                 break;
26378             case yes.RIGHT:
26379                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26380                 break;
26381             case yes.TOP:
26382                 s.el.setY(s.resizingEl.getBottom());
26383                 break;
26384             case yes.BOTTOM:
26385                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26386                 break;
26387         }
26388     }
26389 };
26390
26391 /**
26392  * Orientation constant - Create a vertical SplitBar
26393  * @static
26394  * @type Number
26395  */
26396 Roo.SplitBar.VERTICAL = 1;
26397
26398 /**
26399  * Orientation constant - Create a horizontal SplitBar
26400  * @static
26401  * @type Number
26402  */
26403 Roo.SplitBar.HORIZONTAL = 2;
26404
26405 /**
26406  * Placement constant - The resizing element is to the left of the splitter element
26407  * @static
26408  * @type Number
26409  */
26410 Roo.SplitBar.LEFT = 1;
26411
26412 /**
26413  * Placement constant - The resizing element is to the right of the splitter element
26414  * @static
26415  * @type Number
26416  */
26417 Roo.SplitBar.RIGHT = 2;
26418
26419 /**
26420  * Placement constant - The resizing element is positioned above the splitter element
26421  * @static
26422  * @type Number
26423  */
26424 Roo.SplitBar.TOP = 3;
26425
26426 /**
26427  * Placement constant - The resizing element is positioned under splitter element
26428  * @static
26429  * @type Number
26430  */
26431 Roo.SplitBar.BOTTOM = 4;
26432 /*
26433  * Based on:
26434  * Ext JS Library 1.1.1
26435  * Copyright(c) 2006-2007, Ext JS, LLC.
26436  *
26437  * Originally Released Under LGPL - original licence link has changed is not relivant.
26438  *
26439  * Fork - LGPL
26440  * <script type="text/javascript">
26441  */
26442
26443 /**
26444  * @class Roo.View
26445  * @extends Roo.util.Observable
26446  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26447  * This class also supports single and multi selection modes. <br>
26448  * Create a data model bound view:
26449  <pre><code>
26450  var store = new Roo.data.Store(...);
26451
26452  var view = new Roo.View({
26453     el : "my-element",
26454     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26455  
26456     singleSelect: true,
26457     selectedClass: "ydataview-selected",
26458     store: store
26459  });
26460
26461  // listen for node click?
26462  view.on("click", function(vw, index, node, e){
26463  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26464  });
26465
26466  // load XML data
26467  dataModel.load("foobar.xml");
26468  </code></pre>
26469  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26470  * <br><br>
26471  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26472  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26473  * 
26474  * Note: old style constructor is still suported (container, template, config)
26475  * 
26476  * @constructor
26477  * Create a new View
26478  * @param {Object} config The config object
26479  * 
26480  */
26481 Roo.View = function(config, depreciated_tpl, depreciated_config){
26482     
26483     this.parent = false;
26484     
26485     if (typeof(depreciated_tpl) == 'undefined') {
26486         // new way.. - universal constructor.
26487         Roo.apply(this, config);
26488         this.el  = Roo.get(this.el);
26489     } else {
26490         // old format..
26491         this.el  = Roo.get(config);
26492         this.tpl = depreciated_tpl;
26493         Roo.apply(this, depreciated_config);
26494     }
26495     this.wrapEl  = this.el.wrap().wrap();
26496     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26497     
26498     
26499     if(typeof(this.tpl) == "string"){
26500         this.tpl = new Roo.Template(this.tpl);
26501     } else {
26502         // support xtype ctors..
26503         this.tpl = new Roo.factory(this.tpl, Roo);
26504     }
26505     
26506     
26507     this.tpl.compile();
26508     
26509     /** @private */
26510     this.addEvents({
26511         /**
26512          * @event beforeclick
26513          * Fires before a click is processed. Returns false to cancel the default action.
26514          * @param {Roo.View} this
26515          * @param {Number} index The index of the target node
26516          * @param {HTMLElement} node The target node
26517          * @param {Roo.EventObject} e The raw event object
26518          */
26519             "beforeclick" : true,
26520         /**
26521          * @event click
26522          * Fires when a template node is clicked.
26523          * @param {Roo.View} this
26524          * @param {Number} index The index of the target node
26525          * @param {HTMLElement} node The target node
26526          * @param {Roo.EventObject} e The raw event object
26527          */
26528             "click" : true,
26529         /**
26530          * @event dblclick
26531          * Fires when a template node is double clicked.
26532          * @param {Roo.View} this
26533          * @param {Number} index The index of the target node
26534          * @param {HTMLElement} node The target node
26535          * @param {Roo.EventObject} e The raw event object
26536          */
26537             "dblclick" : true,
26538         /**
26539          * @event contextmenu
26540          * Fires when a template node is right clicked.
26541          * @param {Roo.View} this
26542          * @param {Number} index The index of the target node
26543          * @param {HTMLElement} node The target node
26544          * @param {Roo.EventObject} e The raw event object
26545          */
26546             "contextmenu" : true,
26547         /**
26548          * @event selectionchange
26549          * Fires when the selected nodes change.
26550          * @param {Roo.View} this
26551          * @param {Array} selections Array of the selected nodes
26552          */
26553             "selectionchange" : true,
26554     
26555         /**
26556          * @event beforeselect
26557          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26558          * @param {Roo.View} this
26559          * @param {HTMLElement} node The node to be selected
26560          * @param {Array} selections Array of currently selected nodes
26561          */
26562             "beforeselect" : true,
26563         /**
26564          * @event preparedata
26565          * Fires on every row to render, to allow you to change the data.
26566          * @param {Roo.View} this
26567          * @param {Object} data to be rendered (change this)
26568          */
26569           "preparedata" : true
26570           
26571           
26572         });
26573
26574
26575
26576     this.el.on({
26577         "click": this.onClick,
26578         "dblclick": this.onDblClick,
26579         "contextmenu": this.onContextMenu,
26580         scope:this
26581     });
26582
26583     this.selections = [];
26584     this.nodes = [];
26585     this.cmp = new Roo.CompositeElementLite([]);
26586     if(this.store){
26587         this.store = Roo.factory(this.store, Roo.data);
26588         this.setStore(this.store, true);
26589     }
26590     
26591     if ( this.footer && this.footer.xtype) {
26592            
26593          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26594         
26595         this.footer.dataSource = this.store;
26596         this.footer.container = fctr;
26597         this.footer = Roo.factory(this.footer, Roo);
26598         fctr.insertFirst(this.el);
26599         
26600         // this is a bit insane - as the paging toolbar seems to detach the el..
26601 //        dom.parentNode.parentNode.parentNode
26602          // they get detached?
26603     }
26604     
26605     
26606     Roo.View.superclass.constructor.call(this);
26607     
26608     
26609 };
26610
26611 Roo.extend(Roo.View, Roo.util.Observable, {
26612     
26613      /**
26614      * @cfg {Roo.data.Store} store Data store to load data from.
26615      */
26616     store : false,
26617     
26618     /**
26619      * @cfg {String|Roo.Element} el The container element.
26620      */
26621     el : '',
26622     
26623     /**
26624      * @cfg {String|Roo.Template} tpl The template used by this View 
26625      */
26626     tpl : false,
26627     /**
26628      * @cfg {String} dataName the named area of the template to use as the data area
26629      *                          Works with domtemplates roo-name="name"
26630      */
26631     dataName: false,
26632     /**
26633      * @cfg {String} selectedClass The css class to add to selected nodes
26634      */
26635     selectedClass : "x-view-selected",
26636      /**
26637      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26638      */
26639     emptyText : "",
26640     
26641     /**
26642      * @cfg {String} text to display on mask (default Loading)
26643      */
26644     mask : false,
26645     /**
26646      * @cfg {Boolean} multiSelect Allow multiple selection
26647      */
26648     multiSelect : false,
26649     /**
26650      * @cfg {Boolean} singleSelect Allow single selection
26651      */
26652     singleSelect:  false,
26653     
26654     /**
26655      * @cfg {Boolean} toggleSelect - selecting 
26656      */
26657     toggleSelect : false,
26658     
26659     /**
26660      * @cfg {Boolean} tickable - selecting 
26661      */
26662     tickable : false,
26663     
26664     /**
26665      * Returns the element this view is bound to.
26666      * @return {Roo.Element}
26667      */
26668     getEl : function(){
26669         return this.wrapEl;
26670     },
26671     
26672     
26673
26674     /**
26675      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26676      */
26677     refresh : function(){
26678         //Roo.log('refresh');
26679         var t = this.tpl;
26680         
26681         // if we are using something like 'domtemplate', then
26682         // the what gets used is:
26683         // t.applySubtemplate(NAME, data, wrapping data..)
26684         // the outer template then get' applied with
26685         //     the store 'extra data'
26686         // and the body get's added to the
26687         //      roo-name="data" node?
26688         //      <span class='roo-tpl-{name}'></span> ?????
26689         
26690         
26691         
26692         this.clearSelections();
26693         this.el.update("");
26694         var html = [];
26695         var records = this.store.getRange();
26696         if(records.length < 1) {
26697             
26698             // is this valid??  = should it render a template??
26699             
26700             this.el.update(this.emptyText);
26701             return;
26702         }
26703         var el = this.el;
26704         if (this.dataName) {
26705             this.el.update(t.apply(this.store.meta)); //????
26706             el = this.el.child('.roo-tpl-' + this.dataName);
26707         }
26708         
26709         for(var i = 0, len = records.length; i < len; i++){
26710             var data = this.prepareData(records[i].data, i, records[i]);
26711             this.fireEvent("preparedata", this, data, i, records[i]);
26712             
26713             var d = Roo.apply({}, data);
26714             
26715             if(this.tickable){
26716                 Roo.apply(d, {'roo-id' : Roo.id()});
26717                 
26718                 var _this = this;
26719             
26720                 Roo.each(this.parent.item, function(item){
26721                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26722                         return;
26723                     }
26724                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26725                 });
26726             }
26727             
26728             html[html.length] = Roo.util.Format.trim(
26729                 this.dataName ?
26730                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26731                     t.apply(d)
26732             );
26733         }
26734         
26735         
26736         
26737         el.update(html.join(""));
26738         this.nodes = el.dom.childNodes;
26739         this.updateIndexes(0);
26740     },
26741     
26742
26743     /**
26744      * Function to override to reformat the data that is sent to
26745      * the template for each node.
26746      * DEPRICATED - use the preparedata event handler.
26747      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26748      * a JSON object for an UpdateManager bound view).
26749      */
26750     prepareData : function(data, index, record)
26751     {
26752         this.fireEvent("preparedata", this, data, index, record);
26753         return data;
26754     },
26755
26756     onUpdate : function(ds, record){
26757         // Roo.log('on update');   
26758         this.clearSelections();
26759         var index = this.store.indexOf(record);
26760         var n = this.nodes[index];
26761         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26762         n.parentNode.removeChild(n);
26763         this.updateIndexes(index, index);
26764     },
26765
26766     
26767     
26768 // --------- FIXME     
26769     onAdd : function(ds, records, index)
26770     {
26771         //Roo.log(['on Add', ds, records, index] );        
26772         this.clearSelections();
26773         if(this.nodes.length == 0){
26774             this.refresh();
26775             return;
26776         }
26777         var n = this.nodes[index];
26778         for(var i = 0, len = records.length; i < len; i++){
26779             var d = this.prepareData(records[i].data, i, records[i]);
26780             if(n){
26781                 this.tpl.insertBefore(n, d);
26782             }else{
26783                 
26784                 this.tpl.append(this.el, d);
26785             }
26786         }
26787         this.updateIndexes(index);
26788     },
26789
26790     onRemove : function(ds, record, index){
26791        // Roo.log('onRemove');
26792         this.clearSelections();
26793         var el = this.dataName  ?
26794             this.el.child('.roo-tpl-' + this.dataName) :
26795             this.el; 
26796         
26797         el.dom.removeChild(this.nodes[index]);
26798         this.updateIndexes(index);
26799     },
26800
26801     /**
26802      * Refresh an individual node.
26803      * @param {Number} index
26804      */
26805     refreshNode : function(index){
26806         this.onUpdate(this.store, this.store.getAt(index));
26807     },
26808
26809     updateIndexes : function(startIndex, endIndex){
26810         var ns = this.nodes;
26811         startIndex = startIndex || 0;
26812         endIndex = endIndex || ns.length - 1;
26813         for(var i = startIndex; i <= endIndex; i++){
26814             ns[i].nodeIndex = i;
26815         }
26816     },
26817
26818     /**
26819      * Changes the data store this view uses and refresh the view.
26820      * @param {Store} store
26821      */
26822     setStore : function(store, initial){
26823         if(!initial && this.store){
26824             this.store.un("datachanged", this.refresh);
26825             this.store.un("add", this.onAdd);
26826             this.store.un("remove", this.onRemove);
26827             this.store.un("update", this.onUpdate);
26828             this.store.un("clear", this.refresh);
26829             this.store.un("beforeload", this.onBeforeLoad);
26830             this.store.un("load", this.onLoad);
26831             this.store.un("loadexception", this.onLoad);
26832         }
26833         if(store){
26834           
26835             store.on("datachanged", this.refresh, this);
26836             store.on("add", this.onAdd, this);
26837             store.on("remove", this.onRemove, this);
26838             store.on("update", this.onUpdate, this);
26839             store.on("clear", this.refresh, this);
26840             store.on("beforeload", this.onBeforeLoad, this);
26841             store.on("load", this.onLoad, this);
26842             store.on("loadexception", this.onLoad, this);
26843         }
26844         
26845         if(store){
26846             this.refresh();
26847         }
26848     },
26849     /**
26850      * onbeforeLoad - masks the loading area.
26851      *
26852      */
26853     onBeforeLoad : function(store,opts)
26854     {
26855          //Roo.log('onBeforeLoad');   
26856         if (!opts.add) {
26857             this.el.update("");
26858         }
26859         this.el.mask(this.mask ? this.mask : "Loading" ); 
26860     },
26861     onLoad : function ()
26862     {
26863         this.el.unmask();
26864     },
26865     
26866
26867     /**
26868      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26869      * @param {HTMLElement} node
26870      * @return {HTMLElement} The template node
26871      */
26872     findItemFromChild : function(node){
26873         var el = this.dataName  ?
26874             this.el.child('.roo-tpl-' + this.dataName,true) :
26875             this.el.dom; 
26876         
26877         if(!node || node.parentNode == el){
26878                     return node;
26879             }
26880             var p = node.parentNode;
26881             while(p && p != el){
26882             if(p.parentNode == el){
26883                 return p;
26884             }
26885             p = p.parentNode;
26886         }
26887             return null;
26888     },
26889
26890     /** @ignore */
26891     onClick : function(e){
26892         var item = this.findItemFromChild(e.getTarget());
26893         if(item){
26894             var index = this.indexOf(item);
26895             if(this.onItemClick(item, index, e) !== false){
26896                 this.fireEvent("click", this, index, item, e);
26897             }
26898         }else{
26899             this.clearSelections();
26900         }
26901     },
26902
26903     /** @ignore */
26904     onContextMenu : function(e){
26905         var item = this.findItemFromChild(e.getTarget());
26906         if(item){
26907             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
26908         }
26909     },
26910
26911     /** @ignore */
26912     onDblClick : function(e){
26913         var item = this.findItemFromChild(e.getTarget());
26914         if(item){
26915             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
26916         }
26917     },
26918
26919     onItemClick : function(item, index, e)
26920     {
26921         if(this.fireEvent("beforeclick", this, index, item, e) === false){
26922             return false;
26923         }
26924         if (this.toggleSelect) {
26925             var m = this.isSelected(item) ? 'unselect' : 'select';
26926             //Roo.log(m);
26927             var _t = this;
26928             _t[m](item, true, false);
26929             return true;
26930         }
26931         if(this.multiSelect || this.singleSelect){
26932             if(this.multiSelect && e.shiftKey && this.lastSelection){
26933                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
26934             }else{
26935                 this.select(item, this.multiSelect && e.ctrlKey);
26936                 this.lastSelection = item;
26937             }
26938             
26939             if(!this.tickable){
26940                 e.preventDefault();
26941             }
26942             
26943         }
26944         return true;
26945     },
26946
26947     /**
26948      * Get the number of selected nodes.
26949      * @return {Number}
26950      */
26951     getSelectionCount : function(){
26952         return this.selections.length;
26953     },
26954
26955     /**
26956      * Get the currently selected nodes.
26957      * @return {Array} An array of HTMLElements
26958      */
26959     getSelectedNodes : function(){
26960         return this.selections;
26961     },
26962
26963     /**
26964      * Get the indexes of the selected nodes.
26965      * @return {Array}
26966      */
26967     getSelectedIndexes : function(){
26968         var indexes = [], s = this.selections;
26969         for(var i = 0, len = s.length; i < len; i++){
26970             indexes.push(s[i].nodeIndex);
26971         }
26972         return indexes;
26973     },
26974
26975     /**
26976      * Clear all selections
26977      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
26978      */
26979     clearSelections : function(suppressEvent){
26980         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
26981             this.cmp.elements = this.selections;
26982             this.cmp.removeClass(this.selectedClass);
26983             this.selections = [];
26984             if(!suppressEvent){
26985                 this.fireEvent("selectionchange", this, this.selections);
26986             }
26987         }
26988     },
26989
26990     /**
26991      * Returns true if the passed node is selected
26992      * @param {HTMLElement/Number} node The node or node index
26993      * @return {Boolean}
26994      */
26995     isSelected : function(node){
26996         var s = this.selections;
26997         if(s.length < 1){
26998             return false;
26999         }
27000         node = this.getNode(node);
27001         return s.indexOf(node) !== -1;
27002     },
27003
27004     /**
27005      * Selects nodes.
27006      * @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
27007      * @param {Boolean} keepExisting (optional) true to keep existing selections
27008      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27009      */
27010     select : function(nodeInfo, keepExisting, suppressEvent){
27011         if(nodeInfo instanceof Array){
27012             if(!keepExisting){
27013                 this.clearSelections(true);
27014             }
27015             for(var i = 0, len = nodeInfo.length; i < len; i++){
27016                 this.select(nodeInfo[i], true, true);
27017             }
27018             return;
27019         } 
27020         var node = this.getNode(nodeInfo);
27021         if(!node || this.isSelected(node)){
27022             return; // already selected.
27023         }
27024         if(!keepExisting){
27025             this.clearSelections(true);
27026         }
27027         
27028         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27029             Roo.fly(node).addClass(this.selectedClass);
27030             this.selections.push(node);
27031             if(!suppressEvent){
27032                 this.fireEvent("selectionchange", this, this.selections);
27033             }
27034         }
27035         
27036         
27037     },
27038       /**
27039      * Unselects nodes.
27040      * @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
27041      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27042      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27043      */
27044     unselect : function(nodeInfo, keepExisting, suppressEvent)
27045     {
27046         if(nodeInfo instanceof Array){
27047             Roo.each(this.selections, function(s) {
27048                 this.unselect(s, nodeInfo);
27049             }, this);
27050             return;
27051         }
27052         var node = this.getNode(nodeInfo);
27053         if(!node || !this.isSelected(node)){
27054             //Roo.log("not selected");
27055             return; // not selected.
27056         }
27057         // fireevent???
27058         var ns = [];
27059         Roo.each(this.selections, function(s) {
27060             if (s == node ) {
27061                 Roo.fly(node).removeClass(this.selectedClass);
27062
27063                 return;
27064             }
27065             ns.push(s);
27066         },this);
27067         
27068         this.selections= ns;
27069         this.fireEvent("selectionchange", this, this.selections);
27070     },
27071
27072     /**
27073      * Gets a template node.
27074      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27075      * @return {HTMLElement} The node or null if it wasn't found
27076      */
27077     getNode : function(nodeInfo){
27078         if(typeof nodeInfo == "string"){
27079             return document.getElementById(nodeInfo);
27080         }else if(typeof nodeInfo == "number"){
27081             return this.nodes[nodeInfo];
27082         }
27083         return nodeInfo;
27084     },
27085
27086     /**
27087      * Gets a range template nodes.
27088      * @param {Number} startIndex
27089      * @param {Number} endIndex
27090      * @return {Array} An array of nodes
27091      */
27092     getNodes : function(start, end){
27093         var ns = this.nodes;
27094         start = start || 0;
27095         end = typeof end == "undefined" ? ns.length - 1 : end;
27096         var nodes = [];
27097         if(start <= end){
27098             for(var i = start; i <= end; i++){
27099                 nodes.push(ns[i]);
27100             }
27101         } else{
27102             for(var i = start; i >= end; i--){
27103                 nodes.push(ns[i]);
27104             }
27105         }
27106         return nodes;
27107     },
27108
27109     /**
27110      * Finds the index of the passed node
27111      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27112      * @return {Number} The index of the node or -1
27113      */
27114     indexOf : function(node){
27115         node = this.getNode(node);
27116         if(typeof node.nodeIndex == "number"){
27117             return node.nodeIndex;
27118         }
27119         var ns = this.nodes;
27120         for(var i = 0, len = ns.length; i < len; i++){
27121             if(ns[i] == node){
27122                 return i;
27123             }
27124         }
27125         return -1;
27126     }
27127 });
27128 /*
27129  * Based on:
27130  * Ext JS Library 1.1.1
27131  * Copyright(c) 2006-2007, Ext JS, LLC.
27132  *
27133  * Originally Released Under LGPL - original licence link has changed is not relivant.
27134  *
27135  * Fork - LGPL
27136  * <script type="text/javascript">
27137  */
27138
27139 /**
27140  * @class Roo.JsonView
27141  * @extends Roo.View
27142  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27143 <pre><code>
27144 var view = new Roo.JsonView({
27145     container: "my-element",
27146     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27147     multiSelect: true, 
27148     jsonRoot: "data" 
27149 });
27150
27151 // listen for node click?
27152 view.on("click", function(vw, index, node, e){
27153     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27154 });
27155
27156 // direct load of JSON data
27157 view.load("foobar.php");
27158
27159 // Example from my blog list
27160 var tpl = new Roo.Template(
27161     '&lt;div class="entry"&gt;' +
27162     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27163     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27164     "&lt;/div&gt;&lt;hr /&gt;"
27165 );
27166
27167 var moreView = new Roo.JsonView({
27168     container :  "entry-list", 
27169     template : tpl,
27170     jsonRoot: "posts"
27171 });
27172 moreView.on("beforerender", this.sortEntries, this);
27173 moreView.load({
27174     url: "/blog/get-posts.php",
27175     params: "allposts=true",
27176     text: "Loading Blog Entries..."
27177 });
27178 </code></pre>
27179
27180 * Note: old code is supported with arguments : (container, template, config)
27181
27182
27183  * @constructor
27184  * Create a new JsonView
27185  * 
27186  * @param {Object} config The config object
27187  * 
27188  */
27189 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27190     
27191     
27192     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27193
27194     var um = this.el.getUpdateManager();
27195     um.setRenderer(this);
27196     um.on("update", this.onLoad, this);
27197     um.on("failure", this.onLoadException, this);
27198
27199     /**
27200      * @event beforerender
27201      * Fires before rendering of the downloaded JSON data.
27202      * @param {Roo.JsonView} this
27203      * @param {Object} data The JSON data loaded
27204      */
27205     /**
27206      * @event load
27207      * Fires when data is loaded.
27208      * @param {Roo.JsonView} this
27209      * @param {Object} data The JSON data loaded
27210      * @param {Object} response The raw Connect response object
27211      */
27212     /**
27213      * @event loadexception
27214      * Fires when loading fails.
27215      * @param {Roo.JsonView} this
27216      * @param {Object} response The raw Connect response object
27217      */
27218     this.addEvents({
27219         'beforerender' : true,
27220         'load' : true,
27221         'loadexception' : true
27222     });
27223 };
27224 Roo.extend(Roo.JsonView, Roo.View, {
27225     /**
27226      * @type {String} The root property in the loaded JSON object that contains the data
27227      */
27228     jsonRoot : "",
27229
27230     /**
27231      * Refreshes the view.
27232      */
27233     refresh : function(){
27234         this.clearSelections();
27235         this.el.update("");
27236         var html = [];
27237         var o = this.jsonData;
27238         if(o && o.length > 0){
27239             for(var i = 0, len = o.length; i < len; i++){
27240                 var data = this.prepareData(o[i], i, o);
27241                 html[html.length] = this.tpl.apply(data);
27242             }
27243         }else{
27244             html.push(this.emptyText);
27245         }
27246         this.el.update(html.join(""));
27247         this.nodes = this.el.dom.childNodes;
27248         this.updateIndexes(0);
27249     },
27250
27251     /**
27252      * 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.
27253      * @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:
27254      <pre><code>
27255      view.load({
27256          url: "your-url.php",
27257          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27258          callback: yourFunction,
27259          scope: yourObject, //(optional scope)
27260          discardUrl: false,
27261          nocache: false,
27262          text: "Loading...",
27263          timeout: 30,
27264          scripts: false
27265      });
27266      </code></pre>
27267      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27268      * 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.
27269      * @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}
27270      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27271      * @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.
27272      */
27273     load : function(){
27274         var um = this.el.getUpdateManager();
27275         um.update.apply(um, arguments);
27276     },
27277
27278     // note - render is a standard framework call...
27279     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27280     render : function(el, response){
27281         
27282         this.clearSelections();
27283         this.el.update("");
27284         var o;
27285         try{
27286             if (response != '') {
27287                 o = Roo.util.JSON.decode(response.responseText);
27288                 if(this.jsonRoot){
27289                     
27290                     o = o[this.jsonRoot];
27291                 }
27292             }
27293         } catch(e){
27294         }
27295         /**
27296          * The current JSON data or null
27297          */
27298         this.jsonData = o;
27299         this.beforeRender();
27300         this.refresh();
27301     },
27302
27303 /**
27304  * Get the number of records in the current JSON dataset
27305  * @return {Number}
27306  */
27307     getCount : function(){
27308         return this.jsonData ? this.jsonData.length : 0;
27309     },
27310
27311 /**
27312  * Returns the JSON object for the specified node(s)
27313  * @param {HTMLElement/Array} node The node or an array of nodes
27314  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27315  * you get the JSON object for the node
27316  */
27317     getNodeData : function(node){
27318         if(node instanceof Array){
27319             var data = [];
27320             for(var i = 0, len = node.length; i < len; i++){
27321                 data.push(this.getNodeData(node[i]));
27322             }
27323             return data;
27324         }
27325         return this.jsonData[this.indexOf(node)] || null;
27326     },
27327
27328     beforeRender : function(){
27329         this.snapshot = this.jsonData;
27330         if(this.sortInfo){
27331             this.sort.apply(this, this.sortInfo);
27332         }
27333         this.fireEvent("beforerender", this, this.jsonData);
27334     },
27335
27336     onLoad : function(el, o){
27337         this.fireEvent("load", this, this.jsonData, o);
27338     },
27339
27340     onLoadException : function(el, o){
27341         this.fireEvent("loadexception", this, o);
27342     },
27343
27344 /**
27345  * Filter the data by a specific property.
27346  * @param {String} property A property on your JSON objects
27347  * @param {String/RegExp} value Either string that the property values
27348  * should start with, or a RegExp to test against the property
27349  */
27350     filter : function(property, value){
27351         if(this.jsonData){
27352             var data = [];
27353             var ss = this.snapshot;
27354             if(typeof value == "string"){
27355                 var vlen = value.length;
27356                 if(vlen == 0){
27357                     this.clearFilter();
27358                     return;
27359                 }
27360                 value = value.toLowerCase();
27361                 for(var i = 0, len = ss.length; i < len; i++){
27362                     var o = ss[i];
27363                     if(o[property].substr(0, vlen).toLowerCase() == value){
27364                         data.push(o);
27365                     }
27366                 }
27367             } else if(value.exec){ // regex?
27368                 for(var i = 0, len = ss.length; i < len; i++){
27369                     var o = ss[i];
27370                     if(value.test(o[property])){
27371                         data.push(o);
27372                     }
27373                 }
27374             } else{
27375                 return;
27376             }
27377             this.jsonData = data;
27378             this.refresh();
27379         }
27380     },
27381
27382 /**
27383  * Filter by a function. The passed function will be called with each
27384  * object in the current dataset. If the function returns true the value is kept,
27385  * otherwise it is filtered.
27386  * @param {Function} fn
27387  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27388  */
27389     filterBy : function(fn, scope){
27390         if(this.jsonData){
27391             var data = [];
27392             var ss = this.snapshot;
27393             for(var i = 0, len = ss.length; i < len; i++){
27394                 var o = ss[i];
27395                 if(fn.call(scope || this, o)){
27396                     data.push(o);
27397                 }
27398             }
27399             this.jsonData = data;
27400             this.refresh();
27401         }
27402     },
27403
27404 /**
27405  * Clears the current filter.
27406  */
27407     clearFilter : function(){
27408         if(this.snapshot && this.jsonData != this.snapshot){
27409             this.jsonData = this.snapshot;
27410             this.refresh();
27411         }
27412     },
27413
27414
27415 /**
27416  * Sorts the data for this view and refreshes it.
27417  * @param {String} property A property on your JSON objects to sort on
27418  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27419  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27420  */
27421     sort : function(property, dir, sortType){
27422         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27423         if(this.jsonData){
27424             var p = property;
27425             var dsc = dir && dir.toLowerCase() == "desc";
27426             var f = function(o1, o2){
27427                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27428                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27429                 ;
27430                 if(v1 < v2){
27431                     return dsc ? +1 : -1;
27432                 } else if(v1 > v2){
27433                     return dsc ? -1 : +1;
27434                 } else{
27435                     return 0;
27436                 }
27437             };
27438             this.jsonData.sort(f);
27439             this.refresh();
27440             if(this.jsonData != this.snapshot){
27441                 this.snapshot.sort(f);
27442             }
27443         }
27444     }
27445 });/*
27446  * Based on:
27447  * Ext JS Library 1.1.1
27448  * Copyright(c) 2006-2007, Ext JS, LLC.
27449  *
27450  * Originally Released Under LGPL - original licence link has changed is not relivant.
27451  *
27452  * Fork - LGPL
27453  * <script type="text/javascript">
27454  */
27455  
27456
27457 /**
27458  * @class Roo.ColorPalette
27459  * @extends Roo.Component
27460  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27461  * Here's an example of typical usage:
27462  * <pre><code>
27463 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27464 cp.render('my-div');
27465
27466 cp.on('select', function(palette, selColor){
27467     // do something with selColor
27468 });
27469 </code></pre>
27470  * @constructor
27471  * Create a new ColorPalette
27472  * @param {Object} config The config object
27473  */
27474 Roo.ColorPalette = function(config){
27475     Roo.ColorPalette.superclass.constructor.call(this, config);
27476     this.addEvents({
27477         /**
27478              * @event select
27479              * Fires when a color is selected
27480              * @param {ColorPalette} this
27481              * @param {String} color The 6-digit color hex code (without the # symbol)
27482              */
27483         select: true
27484     });
27485
27486     if(this.handler){
27487         this.on("select", this.handler, this.scope, true);
27488     }
27489 };
27490 Roo.extend(Roo.ColorPalette, Roo.Component, {
27491     /**
27492      * @cfg {String} itemCls
27493      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27494      */
27495     itemCls : "x-color-palette",
27496     /**
27497      * @cfg {String} value
27498      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27499      * the hex codes are case-sensitive.
27500      */
27501     value : null,
27502     clickEvent:'click',
27503     // private
27504     ctype: "Roo.ColorPalette",
27505
27506     /**
27507      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27508      */
27509     allowReselect : false,
27510
27511     /**
27512      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27513      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27514      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27515      * of colors with the width setting until the box is symmetrical.</p>
27516      * <p>You can override individual colors if needed:</p>
27517      * <pre><code>
27518 var cp = new Roo.ColorPalette();
27519 cp.colors[0] = "FF0000";  // change the first box to red
27520 </code></pre>
27521
27522 Or you can provide a custom array of your own for complete control:
27523 <pre><code>
27524 var cp = new Roo.ColorPalette();
27525 cp.colors = ["000000", "993300", "333300"];
27526 </code></pre>
27527      * @type Array
27528      */
27529     colors : [
27530         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27531         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27532         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27533         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27534         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27535     ],
27536
27537     // private
27538     onRender : function(container, position){
27539         var t = new Roo.MasterTemplate(
27540             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27541         );
27542         var c = this.colors;
27543         for(var i = 0, len = c.length; i < len; i++){
27544             t.add([c[i]]);
27545         }
27546         var el = document.createElement("div");
27547         el.className = this.itemCls;
27548         t.overwrite(el);
27549         container.dom.insertBefore(el, position);
27550         this.el = Roo.get(el);
27551         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27552         if(this.clickEvent != 'click'){
27553             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27554         }
27555     },
27556
27557     // private
27558     afterRender : function(){
27559         Roo.ColorPalette.superclass.afterRender.call(this);
27560         if(this.value){
27561             var s = this.value;
27562             this.value = null;
27563             this.select(s);
27564         }
27565     },
27566
27567     // private
27568     handleClick : function(e, t){
27569         e.preventDefault();
27570         if(!this.disabled){
27571             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27572             this.select(c.toUpperCase());
27573         }
27574     },
27575
27576     /**
27577      * Selects the specified color in the palette (fires the select event)
27578      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27579      */
27580     select : function(color){
27581         color = color.replace("#", "");
27582         if(color != this.value || this.allowReselect){
27583             var el = this.el;
27584             if(this.value){
27585                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27586             }
27587             el.child("a.color-"+color).addClass("x-color-palette-sel");
27588             this.value = color;
27589             this.fireEvent("select", this, color);
27590         }
27591     }
27592 });/*
27593  * Based on:
27594  * Ext JS Library 1.1.1
27595  * Copyright(c) 2006-2007, Ext JS, LLC.
27596  *
27597  * Originally Released Under LGPL - original licence link has changed is not relivant.
27598  *
27599  * Fork - LGPL
27600  * <script type="text/javascript">
27601  */
27602  
27603 /**
27604  * @class Roo.DatePicker
27605  * @extends Roo.Component
27606  * Simple date picker class.
27607  * @constructor
27608  * Create a new DatePicker
27609  * @param {Object} config The config object
27610  */
27611 Roo.DatePicker = function(config){
27612     Roo.DatePicker.superclass.constructor.call(this, config);
27613
27614     this.value = config && config.value ?
27615                  config.value.clearTime() : new Date().clearTime();
27616
27617     this.addEvents({
27618         /**
27619              * @event select
27620              * Fires when a date is selected
27621              * @param {DatePicker} this
27622              * @param {Date} date The selected date
27623              */
27624         'select': true,
27625         /**
27626              * @event monthchange
27627              * Fires when the displayed month changes 
27628              * @param {DatePicker} this
27629              * @param {Date} date The selected month
27630              */
27631         'monthchange': true
27632     });
27633
27634     if(this.handler){
27635         this.on("select", this.handler,  this.scope || this);
27636     }
27637     // build the disabledDatesRE
27638     if(!this.disabledDatesRE && this.disabledDates){
27639         var dd = this.disabledDates;
27640         var re = "(?:";
27641         for(var i = 0; i < dd.length; i++){
27642             re += dd[i];
27643             if(i != dd.length-1) {
27644                 re += "|";
27645             }
27646         }
27647         this.disabledDatesRE = new RegExp(re + ")");
27648     }
27649 };
27650
27651 Roo.extend(Roo.DatePicker, Roo.Component, {
27652     /**
27653      * @cfg {String} todayText
27654      * The text to display on the button that selects the current date (defaults to "Today")
27655      */
27656     todayText : "Today",
27657     /**
27658      * @cfg {String} okText
27659      * The text to display on the ok button
27660      */
27661     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27662     /**
27663      * @cfg {String} cancelText
27664      * The text to display on the cancel button
27665      */
27666     cancelText : "Cancel",
27667     /**
27668      * @cfg {String} todayTip
27669      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27670      */
27671     todayTip : "{0} (Spacebar)",
27672     /**
27673      * @cfg {Date} minDate
27674      * Minimum allowable date (JavaScript date object, defaults to null)
27675      */
27676     minDate : null,
27677     /**
27678      * @cfg {Date} maxDate
27679      * Maximum allowable date (JavaScript date object, defaults to null)
27680      */
27681     maxDate : null,
27682     /**
27683      * @cfg {String} minText
27684      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27685      */
27686     minText : "This date is before the minimum date",
27687     /**
27688      * @cfg {String} maxText
27689      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27690      */
27691     maxText : "This date is after the maximum date",
27692     /**
27693      * @cfg {String} format
27694      * The default date format string which can be overriden for localization support.  The format must be
27695      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27696      */
27697     format : "m/d/y",
27698     /**
27699      * @cfg {Array} disabledDays
27700      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27701      */
27702     disabledDays : null,
27703     /**
27704      * @cfg {String} disabledDaysText
27705      * The tooltip to display when the date falls on a disabled day (defaults to "")
27706      */
27707     disabledDaysText : "",
27708     /**
27709      * @cfg {RegExp} disabledDatesRE
27710      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27711      */
27712     disabledDatesRE : null,
27713     /**
27714      * @cfg {String} disabledDatesText
27715      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27716      */
27717     disabledDatesText : "",
27718     /**
27719      * @cfg {Boolean} constrainToViewport
27720      * True to constrain the date picker to the viewport (defaults to true)
27721      */
27722     constrainToViewport : true,
27723     /**
27724      * @cfg {Array} monthNames
27725      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27726      */
27727     monthNames : Date.monthNames,
27728     /**
27729      * @cfg {Array} dayNames
27730      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27731      */
27732     dayNames : Date.dayNames,
27733     /**
27734      * @cfg {String} nextText
27735      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27736      */
27737     nextText: 'Next Month (Control+Right)',
27738     /**
27739      * @cfg {String} prevText
27740      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27741      */
27742     prevText: 'Previous Month (Control+Left)',
27743     /**
27744      * @cfg {String} monthYearText
27745      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27746      */
27747     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27748     /**
27749      * @cfg {Number} startDay
27750      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27751      */
27752     startDay : 0,
27753     /**
27754      * @cfg {Bool} showClear
27755      * Show a clear button (usefull for date form elements that can be blank.)
27756      */
27757     
27758     showClear: false,
27759     
27760     /**
27761      * Sets the value of the date field
27762      * @param {Date} value The date to set
27763      */
27764     setValue : function(value){
27765         var old = this.value;
27766         
27767         if (typeof(value) == 'string') {
27768          
27769             value = Date.parseDate(value, this.format);
27770         }
27771         if (!value) {
27772             value = new Date();
27773         }
27774         
27775         this.value = value.clearTime(true);
27776         if(this.el){
27777             this.update(this.value);
27778         }
27779     },
27780
27781     /**
27782      * Gets the current selected value of the date field
27783      * @return {Date} The selected date
27784      */
27785     getValue : function(){
27786         return this.value;
27787     },
27788
27789     // private
27790     focus : function(){
27791         if(this.el){
27792             this.update(this.activeDate);
27793         }
27794     },
27795
27796     // privateval
27797     onRender : function(container, position){
27798         
27799         var m = [
27800              '<table cellspacing="0">',
27801                 '<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>',
27802                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27803         var dn = this.dayNames;
27804         for(var i = 0; i < 7; i++){
27805             var d = this.startDay+i;
27806             if(d > 6){
27807                 d = d-7;
27808             }
27809             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27810         }
27811         m[m.length] = "</tr></thead><tbody><tr>";
27812         for(var i = 0; i < 42; i++) {
27813             if(i % 7 == 0 && i != 0){
27814                 m[m.length] = "</tr><tr>";
27815             }
27816             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27817         }
27818         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27819             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27820
27821         var el = document.createElement("div");
27822         el.className = "x-date-picker";
27823         el.innerHTML = m.join("");
27824
27825         container.dom.insertBefore(el, position);
27826
27827         this.el = Roo.get(el);
27828         this.eventEl = Roo.get(el.firstChild);
27829
27830         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27831             handler: this.showPrevMonth,
27832             scope: this,
27833             preventDefault:true,
27834             stopDefault:true
27835         });
27836
27837         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27838             handler: this.showNextMonth,
27839             scope: this,
27840             preventDefault:true,
27841             stopDefault:true
27842         });
27843
27844         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27845
27846         this.monthPicker = this.el.down('div.x-date-mp');
27847         this.monthPicker.enableDisplayMode('block');
27848         
27849         var kn = new Roo.KeyNav(this.eventEl, {
27850             "left" : function(e){
27851                 e.ctrlKey ?
27852                     this.showPrevMonth() :
27853                     this.update(this.activeDate.add("d", -1));
27854             },
27855
27856             "right" : function(e){
27857                 e.ctrlKey ?
27858                     this.showNextMonth() :
27859                     this.update(this.activeDate.add("d", 1));
27860             },
27861
27862             "up" : function(e){
27863                 e.ctrlKey ?
27864                     this.showNextYear() :
27865                     this.update(this.activeDate.add("d", -7));
27866             },
27867
27868             "down" : function(e){
27869                 e.ctrlKey ?
27870                     this.showPrevYear() :
27871                     this.update(this.activeDate.add("d", 7));
27872             },
27873
27874             "pageUp" : function(e){
27875                 this.showNextMonth();
27876             },
27877
27878             "pageDown" : function(e){
27879                 this.showPrevMonth();
27880             },
27881
27882             "enter" : function(e){
27883                 e.stopPropagation();
27884                 return true;
27885             },
27886
27887             scope : this
27888         });
27889
27890         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27891
27892         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27893
27894         this.el.unselectable();
27895         
27896         this.cells = this.el.select("table.x-date-inner tbody td");
27897         this.textNodes = this.el.query("table.x-date-inner tbody span");
27898
27899         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27900             text: "&#160;",
27901             tooltip: this.monthYearText
27902         });
27903
27904         this.mbtn.on('click', this.showMonthPicker, this);
27905         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27906
27907
27908         var today = (new Date()).dateFormat(this.format);
27909         
27910         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
27911         if (this.showClear) {
27912             baseTb.add( new Roo.Toolbar.Fill());
27913         }
27914         baseTb.add({
27915             text: String.format(this.todayText, today),
27916             tooltip: String.format(this.todayTip, today),
27917             handler: this.selectToday,
27918             scope: this
27919         });
27920         
27921         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
27922             
27923         //});
27924         if (this.showClear) {
27925             
27926             baseTb.add( new Roo.Toolbar.Fill());
27927             baseTb.add({
27928                 text: '&#160;',
27929                 cls: 'x-btn-icon x-btn-clear',
27930                 handler: function() {
27931                     //this.value = '';
27932                     this.fireEvent("select", this, '');
27933                 },
27934                 scope: this
27935             });
27936         }
27937         
27938         
27939         if(Roo.isIE){
27940             this.el.repaint();
27941         }
27942         this.update(this.value);
27943     },
27944
27945     createMonthPicker : function(){
27946         if(!this.monthPicker.dom.firstChild){
27947             var buf = ['<table border="0" cellspacing="0">'];
27948             for(var i = 0; i < 6; i++){
27949                 buf.push(
27950                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
27951                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
27952                     i == 0 ?
27953                     '<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>' :
27954                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
27955                 );
27956             }
27957             buf.push(
27958                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
27959                     this.okText,
27960                     '</button><button type="button" class="x-date-mp-cancel">',
27961                     this.cancelText,
27962                     '</button></td></tr>',
27963                 '</table>'
27964             );
27965             this.monthPicker.update(buf.join(''));
27966             this.monthPicker.on('click', this.onMonthClick, this);
27967             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
27968
27969             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
27970             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
27971
27972             this.mpMonths.each(function(m, a, i){
27973                 i += 1;
27974                 if((i%2) == 0){
27975                     m.dom.xmonth = 5 + Math.round(i * .5);
27976                 }else{
27977                     m.dom.xmonth = Math.round((i-1) * .5);
27978                 }
27979             });
27980         }
27981     },
27982
27983     showMonthPicker : function(){
27984         this.createMonthPicker();
27985         var size = this.el.getSize();
27986         this.monthPicker.setSize(size);
27987         this.monthPicker.child('table').setSize(size);
27988
27989         this.mpSelMonth = (this.activeDate || this.value).getMonth();
27990         this.updateMPMonth(this.mpSelMonth);
27991         this.mpSelYear = (this.activeDate || this.value).getFullYear();
27992         this.updateMPYear(this.mpSelYear);
27993
27994         this.monthPicker.slideIn('t', {duration:.2});
27995     },
27996
27997     updateMPYear : function(y){
27998         this.mpyear = y;
27999         var ys = this.mpYears.elements;
28000         for(var i = 1; i <= 10; i++){
28001             var td = ys[i-1], y2;
28002             if((i%2) == 0){
28003                 y2 = y + Math.round(i * .5);
28004                 td.firstChild.innerHTML = y2;
28005                 td.xyear = y2;
28006             }else{
28007                 y2 = y - (5-Math.round(i * .5));
28008                 td.firstChild.innerHTML = y2;
28009                 td.xyear = y2;
28010             }
28011             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28012         }
28013     },
28014
28015     updateMPMonth : function(sm){
28016         this.mpMonths.each(function(m, a, i){
28017             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28018         });
28019     },
28020
28021     selectMPMonth: function(m){
28022         
28023     },
28024
28025     onMonthClick : function(e, t){
28026         e.stopEvent();
28027         var el = new Roo.Element(t), pn;
28028         if(el.is('button.x-date-mp-cancel')){
28029             this.hideMonthPicker();
28030         }
28031         else if(el.is('button.x-date-mp-ok')){
28032             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28033             this.hideMonthPicker();
28034         }
28035         else if(pn = el.up('td.x-date-mp-month', 2)){
28036             this.mpMonths.removeClass('x-date-mp-sel');
28037             pn.addClass('x-date-mp-sel');
28038             this.mpSelMonth = pn.dom.xmonth;
28039         }
28040         else if(pn = el.up('td.x-date-mp-year', 2)){
28041             this.mpYears.removeClass('x-date-mp-sel');
28042             pn.addClass('x-date-mp-sel');
28043             this.mpSelYear = pn.dom.xyear;
28044         }
28045         else if(el.is('a.x-date-mp-prev')){
28046             this.updateMPYear(this.mpyear-10);
28047         }
28048         else if(el.is('a.x-date-mp-next')){
28049             this.updateMPYear(this.mpyear+10);
28050         }
28051     },
28052
28053     onMonthDblClick : function(e, t){
28054         e.stopEvent();
28055         var el = new Roo.Element(t), pn;
28056         if(pn = el.up('td.x-date-mp-month', 2)){
28057             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28058             this.hideMonthPicker();
28059         }
28060         else if(pn = el.up('td.x-date-mp-year', 2)){
28061             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28062             this.hideMonthPicker();
28063         }
28064     },
28065
28066     hideMonthPicker : function(disableAnim){
28067         if(this.monthPicker){
28068             if(disableAnim === true){
28069                 this.monthPicker.hide();
28070             }else{
28071                 this.monthPicker.slideOut('t', {duration:.2});
28072             }
28073         }
28074     },
28075
28076     // private
28077     showPrevMonth : function(e){
28078         this.update(this.activeDate.add("mo", -1));
28079     },
28080
28081     // private
28082     showNextMonth : function(e){
28083         this.update(this.activeDate.add("mo", 1));
28084     },
28085
28086     // private
28087     showPrevYear : function(){
28088         this.update(this.activeDate.add("y", -1));
28089     },
28090
28091     // private
28092     showNextYear : function(){
28093         this.update(this.activeDate.add("y", 1));
28094     },
28095
28096     // private
28097     handleMouseWheel : function(e){
28098         var delta = e.getWheelDelta();
28099         if(delta > 0){
28100             this.showPrevMonth();
28101             e.stopEvent();
28102         } else if(delta < 0){
28103             this.showNextMonth();
28104             e.stopEvent();
28105         }
28106     },
28107
28108     // private
28109     handleDateClick : function(e, t){
28110         e.stopEvent();
28111         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28112             this.setValue(new Date(t.dateValue));
28113             this.fireEvent("select", this, this.value);
28114         }
28115     },
28116
28117     // private
28118     selectToday : function(){
28119         this.setValue(new Date().clearTime());
28120         this.fireEvent("select", this, this.value);
28121     },
28122
28123     // private
28124     update : function(date)
28125     {
28126         var vd = this.activeDate;
28127         this.activeDate = date;
28128         if(vd && this.el){
28129             var t = date.getTime();
28130             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28131                 this.cells.removeClass("x-date-selected");
28132                 this.cells.each(function(c){
28133                    if(c.dom.firstChild.dateValue == t){
28134                        c.addClass("x-date-selected");
28135                        setTimeout(function(){
28136                             try{c.dom.firstChild.focus();}catch(e){}
28137                        }, 50);
28138                        return false;
28139                    }
28140                 });
28141                 return;
28142             }
28143         }
28144         
28145         var days = date.getDaysInMonth();
28146         var firstOfMonth = date.getFirstDateOfMonth();
28147         var startingPos = firstOfMonth.getDay()-this.startDay;
28148
28149         if(startingPos <= this.startDay){
28150             startingPos += 7;
28151         }
28152
28153         var pm = date.add("mo", -1);
28154         var prevStart = pm.getDaysInMonth()-startingPos;
28155
28156         var cells = this.cells.elements;
28157         var textEls = this.textNodes;
28158         days += startingPos;
28159
28160         // convert everything to numbers so it's fast
28161         var day = 86400000;
28162         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28163         var today = new Date().clearTime().getTime();
28164         var sel = date.clearTime().getTime();
28165         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28166         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28167         var ddMatch = this.disabledDatesRE;
28168         var ddText = this.disabledDatesText;
28169         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28170         var ddaysText = this.disabledDaysText;
28171         var format = this.format;
28172
28173         var setCellClass = function(cal, cell){
28174             cell.title = "";
28175             var t = d.getTime();
28176             cell.firstChild.dateValue = t;
28177             if(t == today){
28178                 cell.className += " x-date-today";
28179                 cell.title = cal.todayText;
28180             }
28181             if(t == sel){
28182                 cell.className += " x-date-selected";
28183                 setTimeout(function(){
28184                     try{cell.firstChild.focus();}catch(e){}
28185                 }, 50);
28186             }
28187             // disabling
28188             if(t < min) {
28189                 cell.className = " x-date-disabled";
28190                 cell.title = cal.minText;
28191                 return;
28192             }
28193             if(t > max) {
28194                 cell.className = " x-date-disabled";
28195                 cell.title = cal.maxText;
28196                 return;
28197             }
28198             if(ddays){
28199                 if(ddays.indexOf(d.getDay()) != -1){
28200                     cell.title = ddaysText;
28201                     cell.className = " x-date-disabled";
28202                 }
28203             }
28204             if(ddMatch && format){
28205                 var fvalue = d.dateFormat(format);
28206                 if(ddMatch.test(fvalue)){
28207                     cell.title = ddText.replace("%0", fvalue);
28208                     cell.className = " x-date-disabled";
28209                 }
28210             }
28211         };
28212
28213         var i = 0;
28214         for(; i < startingPos; i++) {
28215             textEls[i].innerHTML = (++prevStart);
28216             d.setDate(d.getDate()+1);
28217             cells[i].className = "x-date-prevday";
28218             setCellClass(this, cells[i]);
28219         }
28220         for(; i < days; i++){
28221             intDay = i - startingPos + 1;
28222             textEls[i].innerHTML = (intDay);
28223             d.setDate(d.getDate()+1);
28224             cells[i].className = "x-date-active";
28225             setCellClass(this, cells[i]);
28226         }
28227         var extraDays = 0;
28228         for(; i < 42; i++) {
28229              textEls[i].innerHTML = (++extraDays);
28230              d.setDate(d.getDate()+1);
28231              cells[i].className = "x-date-nextday";
28232              setCellClass(this, cells[i]);
28233         }
28234
28235         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28236         this.fireEvent('monthchange', this, date);
28237         
28238         if(!this.internalRender){
28239             var main = this.el.dom.firstChild;
28240             var w = main.offsetWidth;
28241             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28242             Roo.fly(main).setWidth(w);
28243             this.internalRender = true;
28244             // opera does not respect the auto grow header center column
28245             // then, after it gets a width opera refuses to recalculate
28246             // without a second pass
28247             if(Roo.isOpera && !this.secondPass){
28248                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28249                 this.secondPass = true;
28250                 this.update.defer(10, this, [date]);
28251             }
28252         }
28253         
28254         
28255     }
28256 });        /*
28257  * Based on:
28258  * Ext JS Library 1.1.1
28259  * Copyright(c) 2006-2007, Ext JS, LLC.
28260  *
28261  * Originally Released Under LGPL - original licence link has changed is not relivant.
28262  *
28263  * Fork - LGPL
28264  * <script type="text/javascript">
28265  */
28266 /**
28267  * @class Roo.TabPanel
28268  * @extends Roo.util.Observable
28269  * A lightweight tab container.
28270  * <br><br>
28271  * Usage:
28272  * <pre><code>
28273 // basic tabs 1, built from existing content
28274 var tabs = new Roo.TabPanel("tabs1");
28275 tabs.addTab("script", "View Script");
28276 tabs.addTab("markup", "View Markup");
28277 tabs.activate("script");
28278
28279 // more advanced tabs, built from javascript
28280 var jtabs = new Roo.TabPanel("jtabs");
28281 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28282
28283 // set up the UpdateManager
28284 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28285 var updater = tab2.getUpdateManager();
28286 updater.setDefaultUrl("ajax1.htm");
28287 tab2.on('activate', updater.refresh, updater, true);
28288
28289 // Use setUrl for Ajax loading
28290 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28291 tab3.setUrl("ajax2.htm", null, true);
28292
28293 // Disabled tab
28294 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28295 tab4.disable();
28296
28297 jtabs.activate("jtabs-1");
28298  * </code></pre>
28299  * @constructor
28300  * Create a new TabPanel.
28301  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28302  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28303  */
28304 Roo.TabPanel = function(container, config){
28305     /**
28306     * The container element for this TabPanel.
28307     * @type Roo.Element
28308     */
28309     this.el = Roo.get(container, true);
28310     if(config){
28311         if(typeof config == "boolean"){
28312             this.tabPosition = config ? "bottom" : "top";
28313         }else{
28314             Roo.apply(this, config);
28315         }
28316     }
28317     if(this.tabPosition == "bottom"){
28318         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28319         this.el.addClass("x-tabs-bottom");
28320     }
28321     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28322     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28323     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28324     if(Roo.isIE){
28325         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28326     }
28327     if(this.tabPosition != "bottom"){
28328         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28329          * @type Roo.Element
28330          */
28331         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28332         this.el.addClass("x-tabs-top");
28333     }
28334     this.items = [];
28335
28336     this.bodyEl.setStyle("position", "relative");
28337
28338     this.active = null;
28339     this.activateDelegate = this.activate.createDelegate(this);
28340
28341     this.addEvents({
28342         /**
28343          * @event tabchange
28344          * Fires when the active tab changes
28345          * @param {Roo.TabPanel} this
28346          * @param {Roo.TabPanelItem} activePanel The new active tab
28347          */
28348         "tabchange": true,
28349         /**
28350          * @event beforetabchange
28351          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28352          * @param {Roo.TabPanel} this
28353          * @param {Object} e Set cancel to true on this object to cancel the tab change
28354          * @param {Roo.TabPanelItem} tab The tab being changed to
28355          */
28356         "beforetabchange" : true
28357     });
28358
28359     Roo.EventManager.onWindowResize(this.onResize, this);
28360     this.cpad = this.el.getPadding("lr");
28361     this.hiddenCount = 0;
28362
28363
28364     // toolbar on the tabbar support...
28365     if (this.toolbar) {
28366         var tcfg = this.toolbar;
28367         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28368         this.toolbar = new Roo.Toolbar(tcfg);
28369         if (Roo.isSafari) {
28370             var tbl = tcfg.container.child('table', true);
28371             tbl.setAttribute('width', '100%');
28372         }
28373         
28374     }
28375    
28376
28377
28378     Roo.TabPanel.superclass.constructor.call(this);
28379 };
28380
28381 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28382     /*
28383      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28384      */
28385     tabPosition : "top",
28386     /*
28387      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28388      */
28389     currentTabWidth : 0,
28390     /*
28391      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28392      */
28393     minTabWidth : 40,
28394     /*
28395      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28396      */
28397     maxTabWidth : 250,
28398     /*
28399      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28400      */
28401     preferredTabWidth : 175,
28402     /*
28403      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28404      */
28405     resizeTabs : false,
28406     /*
28407      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28408      */
28409     monitorResize : true,
28410     /*
28411      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28412      */
28413     toolbar : false,
28414
28415     /**
28416      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28417      * @param {String} id The id of the div to use <b>or create</b>
28418      * @param {String} text The text for the tab
28419      * @param {String} content (optional) Content to put in the TabPanelItem body
28420      * @param {Boolean} closable (optional) True to create a close icon on the tab
28421      * @return {Roo.TabPanelItem} The created TabPanelItem
28422      */
28423     addTab : function(id, text, content, closable){
28424         var item = new Roo.TabPanelItem(this, id, text, closable);
28425         this.addTabItem(item);
28426         if(content){
28427             item.setContent(content);
28428         }
28429         return item;
28430     },
28431
28432     /**
28433      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28434      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28435      * @return {Roo.TabPanelItem}
28436      */
28437     getTab : function(id){
28438         return this.items[id];
28439     },
28440
28441     /**
28442      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28443      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28444      */
28445     hideTab : function(id){
28446         var t = this.items[id];
28447         if(!t.isHidden()){
28448            t.setHidden(true);
28449            this.hiddenCount++;
28450            this.autoSizeTabs();
28451         }
28452     },
28453
28454     /**
28455      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28456      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28457      */
28458     unhideTab : function(id){
28459         var t = this.items[id];
28460         if(t.isHidden()){
28461            t.setHidden(false);
28462            this.hiddenCount--;
28463            this.autoSizeTabs();
28464         }
28465     },
28466
28467     /**
28468      * Adds an existing {@link Roo.TabPanelItem}.
28469      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28470      */
28471     addTabItem : function(item){
28472         this.items[item.id] = item;
28473         this.items.push(item);
28474         if(this.resizeTabs){
28475            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28476            this.autoSizeTabs();
28477         }else{
28478             item.autoSize();
28479         }
28480     },
28481
28482     /**
28483      * Removes a {@link Roo.TabPanelItem}.
28484      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28485      */
28486     removeTab : function(id){
28487         var items = this.items;
28488         var tab = items[id];
28489         if(!tab) { return; }
28490         var index = items.indexOf(tab);
28491         if(this.active == tab && items.length > 1){
28492             var newTab = this.getNextAvailable(index);
28493             if(newTab) {
28494                 newTab.activate();
28495             }
28496         }
28497         this.stripEl.dom.removeChild(tab.pnode.dom);
28498         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28499             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28500         }
28501         items.splice(index, 1);
28502         delete this.items[tab.id];
28503         tab.fireEvent("close", tab);
28504         tab.purgeListeners();
28505         this.autoSizeTabs();
28506     },
28507
28508     getNextAvailable : function(start){
28509         var items = this.items;
28510         var index = start;
28511         // look for a next tab that will slide over to
28512         // replace the one being removed
28513         while(index < items.length){
28514             var item = items[++index];
28515             if(item && !item.isHidden()){
28516                 return item;
28517             }
28518         }
28519         // if one isn't found select the previous tab (on the left)
28520         index = start;
28521         while(index >= 0){
28522             var item = items[--index];
28523             if(item && !item.isHidden()){
28524                 return item;
28525             }
28526         }
28527         return null;
28528     },
28529
28530     /**
28531      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28532      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28533      */
28534     disableTab : function(id){
28535         var tab = this.items[id];
28536         if(tab && this.active != tab){
28537             tab.disable();
28538         }
28539     },
28540
28541     /**
28542      * Enables a {@link Roo.TabPanelItem} that is disabled.
28543      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28544      */
28545     enableTab : function(id){
28546         var tab = this.items[id];
28547         tab.enable();
28548     },
28549
28550     /**
28551      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28552      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28553      * @return {Roo.TabPanelItem} The TabPanelItem.
28554      */
28555     activate : function(id){
28556         var tab = this.items[id];
28557         if(!tab){
28558             return null;
28559         }
28560         if(tab == this.active || tab.disabled){
28561             return tab;
28562         }
28563         var e = {};
28564         this.fireEvent("beforetabchange", this, e, tab);
28565         if(e.cancel !== true && !tab.disabled){
28566             if(this.active){
28567                 this.active.hide();
28568             }
28569             this.active = this.items[id];
28570             this.active.show();
28571             this.fireEvent("tabchange", this, this.active);
28572         }
28573         return tab;
28574     },
28575
28576     /**
28577      * Gets the active {@link Roo.TabPanelItem}.
28578      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28579      */
28580     getActiveTab : function(){
28581         return this.active;
28582     },
28583
28584     /**
28585      * Updates the tab body element to fit the height of the container element
28586      * for overflow scrolling
28587      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28588      */
28589     syncHeight : function(targetHeight){
28590         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28591         var bm = this.bodyEl.getMargins();
28592         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28593         this.bodyEl.setHeight(newHeight);
28594         return newHeight;
28595     },
28596
28597     onResize : function(){
28598         if(this.monitorResize){
28599             this.autoSizeTabs();
28600         }
28601     },
28602
28603     /**
28604      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28605      */
28606     beginUpdate : function(){
28607         this.updating = true;
28608     },
28609
28610     /**
28611      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28612      */
28613     endUpdate : function(){
28614         this.updating = false;
28615         this.autoSizeTabs();
28616     },
28617
28618     /**
28619      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28620      */
28621     autoSizeTabs : function(){
28622         var count = this.items.length;
28623         var vcount = count - this.hiddenCount;
28624         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28625             return;
28626         }
28627         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28628         var availWidth = Math.floor(w / vcount);
28629         var b = this.stripBody;
28630         if(b.getWidth() > w){
28631             var tabs = this.items;
28632             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28633             if(availWidth < this.minTabWidth){
28634                 /*if(!this.sleft){    // incomplete scrolling code
28635                     this.createScrollButtons();
28636                 }
28637                 this.showScroll();
28638                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28639             }
28640         }else{
28641             if(this.currentTabWidth < this.preferredTabWidth){
28642                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28643             }
28644         }
28645     },
28646
28647     /**
28648      * Returns the number of tabs in this TabPanel.
28649      * @return {Number}
28650      */
28651      getCount : function(){
28652          return this.items.length;
28653      },
28654
28655     /**
28656      * Resizes all the tabs to the passed width
28657      * @param {Number} The new width
28658      */
28659     setTabWidth : function(width){
28660         this.currentTabWidth = width;
28661         for(var i = 0, len = this.items.length; i < len; i++) {
28662                 if(!this.items[i].isHidden()) {
28663                 this.items[i].setWidth(width);
28664             }
28665         }
28666     },
28667
28668     /**
28669      * Destroys this TabPanel
28670      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28671      */
28672     destroy : function(removeEl){
28673         Roo.EventManager.removeResizeListener(this.onResize, this);
28674         for(var i = 0, len = this.items.length; i < len; i++){
28675             this.items[i].purgeListeners();
28676         }
28677         if(removeEl === true){
28678             this.el.update("");
28679             this.el.remove();
28680         }
28681     }
28682 });
28683
28684 /**
28685  * @class Roo.TabPanelItem
28686  * @extends Roo.util.Observable
28687  * Represents an individual item (tab plus body) in a TabPanel.
28688  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28689  * @param {String} id The id of this TabPanelItem
28690  * @param {String} text The text for the tab of this TabPanelItem
28691  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28692  */
28693 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28694     /**
28695      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28696      * @type Roo.TabPanel
28697      */
28698     this.tabPanel = tabPanel;
28699     /**
28700      * The id for this TabPanelItem
28701      * @type String
28702      */
28703     this.id = id;
28704     /** @private */
28705     this.disabled = false;
28706     /** @private */
28707     this.text = text;
28708     /** @private */
28709     this.loaded = false;
28710     this.closable = closable;
28711
28712     /**
28713      * The body element for this TabPanelItem.
28714      * @type Roo.Element
28715      */
28716     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28717     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28718     this.bodyEl.setStyle("display", "block");
28719     this.bodyEl.setStyle("zoom", "1");
28720     this.hideAction();
28721
28722     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28723     /** @private */
28724     this.el = Roo.get(els.el, true);
28725     this.inner = Roo.get(els.inner, true);
28726     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28727     this.pnode = Roo.get(els.el.parentNode, true);
28728     this.el.on("mousedown", this.onTabMouseDown, this);
28729     this.el.on("click", this.onTabClick, this);
28730     /** @private */
28731     if(closable){
28732         var c = Roo.get(els.close, true);
28733         c.dom.title = this.closeText;
28734         c.addClassOnOver("close-over");
28735         c.on("click", this.closeClick, this);
28736      }
28737
28738     this.addEvents({
28739          /**
28740          * @event activate
28741          * Fires when this tab becomes the active tab.
28742          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28743          * @param {Roo.TabPanelItem} this
28744          */
28745         "activate": true,
28746         /**
28747          * @event beforeclose
28748          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28749          * @param {Roo.TabPanelItem} this
28750          * @param {Object} e Set cancel to true on this object to cancel the close.
28751          */
28752         "beforeclose": true,
28753         /**
28754          * @event close
28755          * Fires when this tab is closed.
28756          * @param {Roo.TabPanelItem} this
28757          */
28758          "close": true,
28759         /**
28760          * @event deactivate
28761          * Fires when this tab is no longer the active tab.
28762          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28763          * @param {Roo.TabPanelItem} this
28764          */
28765          "deactivate" : true
28766     });
28767     this.hidden = false;
28768
28769     Roo.TabPanelItem.superclass.constructor.call(this);
28770 };
28771
28772 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28773     purgeListeners : function(){
28774        Roo.util.Observable.prototype.purgeListeners.call(this);
28775        this.el.removeAllListeners();
28776     },
28777     /**
28778      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28779      */
28780     show : function(){
28781         this.pnode.addClass("on");
28782         this.showAction();
28783         if(Roo.isOpera){
28784             this.tabPanel.stripWrap.repaint();
28785         }
28786         this.fireEvent("activate", this.tabPanel, this);
28787     },
28788
28789     /**
28790      * Returns true if this tab is the active tab.
28791      * @return {Boolean}
28792      */
28793     isActive : function(){
28794         return this.tabPanel.getActiveTab() == this;
28795     },
28796
28797     /**
28798      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28799      */
28800     hide : function(){
28801         this.pnode.removeClass("on");
28802         this.hideAction();
28803         this.fireEvent("deactivate", this.tabPanel, this);
28804     },
28805
28806     hideAction : function(){
28807         this.bodyEl.hide();
28808         this.bodyEl.setStyle("position", "absolute");
28809         this.bodyEl.setLeft("-20000px");
28810         this.bodyEl.setTop("-20000px");
28811     },
28812
28813     showAction : function(){
28814         this.bodyEl.setStyle("position", "relative");
28815         this.bodyEl.setTop("");
28816         this.bodyEl.setLeft("");
28817         this.bodyEl.show();
28818     },
28819
28820     /**
28821      * Set the tooltip for the tab.
28822      * @param {String} tooltip The tab's tooltip
28823      */
28824     setTooltip : function(text){
28825         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28826             this.textEl.dom.qtip = text;
28827             this.textEl.dom.removeAttribute('title');
28828         }else{
28829             this.textEl.dom.title = text;
28830         }
28831     },
28832
28833     onTabClick : function(e){
28834         e.preventDefault();
28835         this.tabPanel.activate(this.id);
28836     },
28837
28838     onTabMouseDown : function(e){
28839         e.preventDefault();
28840         this.tabPanel.activate(this.id);
28841     },
28842
28843     getWidth : function(){
28844         return this.inner.getWidth();
28845     },
28846
28847     setWidth : function(width){
28848         var iwidth = width - this.pnode.getPadding("lr");
28849         this.inner.setWidth(iwidth);
28850         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28851         this.pnode.setWidth(width);
28852     },
28853
28854     /**
28855      * Show or hide the tab
28856      * @param {Boolean} hidden True to hide or false to show.
28857      */
28858     setHidden : function(hidden){
28859         this.hidden = hidden;
28860         this.pnode.setStyle("display", hidden ? "none" : "");
28861     },
28862
28863     /**
28864      * Returns true if this tab is "hidden"
28865      * @return {Boolean}
28866      */
28867     isHidden : function(){
28868         return this.hidden;
28869     },
28870
28871     /**
28872      * Returns the text for this tab
28873      * @return {String}
28874      */
28875     getText : function(){
28876         return this.text;
28877     },
28878
28879     autoSize : function(){
28880         //this.el.beginMeasure();
28881         this.textEl.setWidth(1);
28882         /*
28883          *  #2804 [new] Tabs in Roojs
28884          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28885          */
28886         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28887         //this.el.endMeasure();
28888     },
28889
28890     /**
28891      * Sets the text for the tab (Note: this also sets the tooltip text)
28892      * @param {String} text The tab's text and tooltip
28893      */
28894     setText : function(text){
28895         this.text = text;
28896         this.textEl.update(text);
28897         this.setTooltip(text);
28898         if(!this.tabPanel.resizeTabs){
28899             this.autoSize();
28900         }
28901     },
28902     /**
28903      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28904      */
28905     activate : function(){
28906         this.tabPanel.activate(this.id);
28907     },
28908
28909     /**
28910      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
28911      */
28912     disable : function(){
28913         if(this.tabPanel.active != this){
28914             this.disabled = true;
28915             this.pnode.addClass("disabled");
28916         }
28917     },
28918
28919     /**
28920      * Enables this TabPanelItem if it was previously disabled.
28921      */
28922     enable : function(){
28923         this.disabled = false;
28924         this.pnode.removeClass("disabled");
28925     },
28926
28927     /**
28928      * Sets the content for this TabPanelItem.
28929      * @param {String} content The content
28930      * @param {Boolean} loadScripts true to look for and load scripts
28931      */
28932     setContent : function(content, loadScripts){
28933         this.bodyEl.update(content, loadScripts);
28934     },
28935
28936     /**
28937      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
28938      * @return {Roo.UpdateManager} The UpdateManager
28939      */
28940     getUpdateManager : function(){
28941         return this.bodyEl.getUpdateManager();
28942     },
28943
28944     /**
28945      * Set a URL to be used to load the content for this TabPanelItem.
28946      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
28947      * @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)
28948      * @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)
28949      * @return {Roo.UpdateManager} The UpdateManager
28950      */
28951     setUrl : function(url, params, loadOnce){
28952         if(this.refreshDelegate){
28953             this.un('activate', this.refreshDelegate);
28954         }
28955         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
28956         this.on("activate", this.refreshDelegate);
28957         return this.bodyEl.getUpdateManager();
28958     },
28959
28960     /** @private */
28961     _handleRefresh : function(url, params, loadOnce){
28962         if(!loadOnce || !this.loaded){
28963             var updater = this.bodyEl.getUpdateManager();
28964             updater.update(url, params, this._setLoaded.createDelegate(this));
28965         }
28966     },
28967
28968     /**
28969      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
28970      *   Will fail silently if the setUrl method has not been called.
28971      *   This does not activate the panel, just updates its content.
28972      */
28973     refresh : function(){
28974         if(this.refreshDelegate){
28975            this.loaded = false;
28976            this.refreshDelegate();
28977         }
28978     },
28979
28980     /** @private */
28981     _setLoaded : function(){
28982         this.loaded = true;
28983     },
28984
28985     /** @private */
28986     closeClick : function(e){
28987         var o = {};
28988         e.stopEvent();
28989         this.fireEvent("beforeclose", this, o);
28990         if(o.cancel !== true){
28991             this.tabPanel.removeTab(this.id);
28992         }
28993     },
28994     /**
28995      * The text displayed in the tooltip for the close icon.
28996      * @type String
28997      */
28998     closeText : "Close this tab"
28999 });
29000
29001 /** @private */
29002 Roo.TabPanel.prototype.createStrip = function(container){
29003     var strip = document.createElement("div");
29004     strip.className = "x-tabs-wrap";
29005     container.appendChild(strip);
29006     return strip;
29007 };
29008 /** @private */
29009 Roo.TabPanel.prototype.createStripList = function(strip){
29010     // div wrapper for retard IE
29011     // returns the "tr" element.
29012     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29013         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29014         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29015     return strip.firstChild.firstChild.firstChild.firstChild;
29016 };
29017 /** @private */
29018 Roo.TabPanel.prototype.createBody = function(container){
29019     var body = document.createElement("div");
29020     Roo.id(body, "tab-body");
29021     Roo.fly(body).addClass("x-tabs-body");
29022     container.appendChild(body);
29023     return body;
29024 };
29025 /** @private */
29026 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29027     var body = Roo.getDom(id);
29028     if(!body){
29029         body = document.createElement("div");
29030         body.id = id;
29031     }
29032     Roo.fly(body).addClass("x-tabs-item-body");
29033     bodyEl.insertBefore(body, bodyEl.firstChild);
29034     return body;
29035 };
29036 /** @private */
29037 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29038     var td = document.createElement("td");
29039     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29040     //stripEl.appendChild(td);
29041     if(closable){
29042         td.className = "x-tabs-closable";
29043         if(!this.closeTpl){
29044             this.closeTpl = new Roo.Template(
29045                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29046                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29047                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29048             );
29049         }
29050         var el = this.closeTpl.overwrite(td, {"text": text});
29051         var close = el.getElementsByTagName("div")[0];
29052         var inner = el.getElementsByTagName("em")[0];
29053         return {"el": el, "close": close, "inner": inner};
29054     } else {
29055         if(!this.tabTpl){
29056             this.tabTpl = new Roo.Template(
29057                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29058                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29059             );
29060         }
29061         var el = this.tabTpl.overwrite(td, {"text": text});
29062         var inner = el.getElementsByTagName("em")[0];
29063         return {"el": el, "inner": inner};
29064     }
29065 };/*
29066  * Based on:
29067  * Ext JS Library 1.1.1
29068  * Copyright(c) 2006-2007, Ext JS, LLC.
29069  *
29070  * Originally Released Under LGPL - original licence link has changed is not relivant.
29071  *
29072  * Fork - LGPL
29073  * <script type="text/javascript">
29074  */
29075
29076 /**
29077  * @class Roo.Button
29078  * @extends Roo.util.Observable
29079  * Simple Button class
29080  * @cfg {String} text The button text
29081  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29082  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29083  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29084  * @cfg {Object} scope The scope of the handler
29085  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29086  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29087  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29088  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29089  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29090  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29091    applies if enableToggle = true)
29092  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29093  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29094   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29095  * @constructor
29096  * Create a new button
29097  * @param {Object} config The config object
29098  */
29099 Roo.Button = function(renderTo, config)
29100 {
29101     if (!config) {
29102         config = renderTo;
29103         renderTo = config.renderTo || false;
29104     }
29105     
29106     Roo.apply(this, config);
29107     this.addEvents({
29108         /**
29109              * @event click
29110              * Fires when this button is clicked
29111              * @param {Button} this
29112              * @param {EventObject} e The click event
29113              */
29114             "click" : true,
29115         /**
29116              * @event toggle
29117              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29118              * @param {Button} this
29119              * @param {Boolean} pressed
29120              */
29121             "toggle" : true,
29122         /**
29123              * @event mouseover
29124              * Fires when the mouse hovers over the button
29125              * @param {Button} this
29126              * @param {Event} e The event object
29127              */
29128         'mouseover' : true,
29129         /**
29130              * @event mouseout
29131              * Fires when the mouse exits the button
29132              * @param {Button} this
29133              * @param {Event} e The event object
29134              */
29135         'mouseout': true,
29136          /**
29137              * @event render
29138              * Fires when the button is rendered
29139              * @param {Button} this
29140              */
29141         'render': true
29142     });
29143     if(this.menu){
29144         this.menu = Roo.menu.MenuMgr.get(this.menu);
29145     }
29146     // register listeners first!!  - so render can be captured..
29147     Roo.util.Observable.call(this);
29148     if(renderTo){
29149         this.render(renderTo);
29150     }
29151     
29152   
29153 };
29154
29155 Roo.extend(Roo.Button, Roo.util.Observable, {
29156     /**
29157      * 
29158      */
29159     
29160     /**
29161      * Read-only. True if this button is hidden
29162      * @type Boolean
29163      */
29164     hidden : false,
29165     /**
29166      * Read-only. True if this button is disabled
29167      * @type Boolean
29168      */
29169     disabled : false,
29170     /**
29171      * Read-only. True if this button is pressed (only if enableToggle = true)
29172      * @type Boolean
29173      */
29174     pressed : false,
29175
29176     /**
29177      * @cfg {Number} tabIndex 
29178      * The DOM tabIndex for this button (defaults to undefined)
29179      */
29180     tabIndex : undefined,
29181
29182     /**
29183      * @cfg {Boolean} enableToggle
29184      * True to enable pressed/not pressed toggling (defaults to false)
29185      */
29186     enableToggle: false,
29187     /**
29188      * @cfg {Mixed} menu
29189      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29190      */
29191     menu : undefined,
29192     /**
29193      * @cfg {String} menuAlign
29194      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29195      */
29196     menuAlign : "tl-bl?",
29197
29198     /**
29199      * @cfg {String} iconCls
29200      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29201      */
29202     iconCls : undefined,
29203     /**
29204      * @cfg {String} type
29205      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29206      */
29207     type : 'button',
29208
29209     // private
29210     menuClassTarget: 'tr',
29211
29212     /**
29213      * @cfg {String} clickEvent
29214      * The type of event to map to the button's event handler (defaults to 'click')
29215      */
29216     clickEvent : 'click',
29217
29218     /**
29219      * @cfg {Boolean} handleMouseEvents
29220      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29221      */
29222     handleMouseEvents : true,
29223
29224     /**
29225      * @cfg {String} tooltipType
29226      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29227      */
29228     tooltipType : 'qtip',
29229
29230     /**
29231      * @cfg {String} cls
29232      * A CSS class to apply to the button's main element.
29233      */
29234     
29235     /**
29236      * @cfg {Roo.Template} template (Optional)
29237      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29238      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29239      * require code modifications if required elements (e.g. a button) aren't present.
29240      */
29241
29242     // private
29243     render : function(renderTo){
29244         var btn;
29245         if(this.hideParent){
29246             this.parentEl = Roo.get(renderTo);
29247         }
29248         if(!this.dhconfig){
29249             if(!this.template){
29250                 if(!Roo.Button.buttonTemplate){
29251                     // hideous table template
29252                     Roo.Button.buttonTemplate = new Roo.Template(
29253                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29254                         '<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>',
29255                         "</tr></tbody></table>");
29256                 }
29257                 this.template = Roo.Button.buttonTemplate;
29258             }
29259             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29260             var btnEl = btn.child("button:first");
29261             btnEl.on('focus', this.onFocus, this);
29262             btnEl.on('blur', this.onBlur, this);
29263             if(this.cls){
29264                 btn.addClass(this.cls);
29265             }
29266             if(this.icon){
29267                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29268             }
29269             if(this.iconCls){
29270                 btnEl.addClass(this.iconCls);
29271                 if(!this.cls){
29272                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29273                 }
29274             }
29275             if(this.tabIndex !== undefined){
29276                 btnEl.dom.tabIndex = this.tabIndex;
29277             }
29278             if(this.tooltip){
29279                 if(typeof this.tooltip == 'object'){
29280                     Roo.QuickTips.tips(Roo.apply({
29281                           target: btnEl.id
29282                     }, this.tooltip));
29283                 } else {
29284                     btnEl.dom[this.tooltipType] = this.tooltip;
29285                 }
29286             }
29287         }else{
29288             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29289         }
29290         this.el = btn;
29291         if(this.id){
29292             this.el.dom.id = this.el.id = this.id;
29293         }
29294         if(this.menu){
29295             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29296             this.menu.on("show", this.onMenuShow, this);
29297             this.menu.on("hide", this.onMenuHide, this);
29298         }
29299         btn.addClass("x-btn");
29300         if(Roo.isIE && !Roo.isIE7){
29301             this.autoWidth.defer(1, this);
29302         }else{
29303             this.autoWidth();
29304         }
29305         if(this.handleMouseEvents){
29306             btn.on("mouseover", this.onMouseOver, this);
29307             btn.on("mouseout", this.onMouseOut, this);
29308             btn.on("mousedown", this.onMouseDown, this);
29309         }
29310         btn.on(this.clickEvent, this.onClick, this);
29311         //btn.on("mouseup", this.onMouseUp, this);
29312         if(this.hidden){
29313             this.hide();
29314         }
29315         if(this.disabled){
29316             this.disable();
29317         }
29318         Roo.ButtonToggleMgr.register(this);
29319         if(this.pressed){
29320             this.el.addClass("x-btn-pressed");
29321         }
29322         if(this.repeat){
29323             var repeater = new Roo.util.ClickRepeater(btn,
29324                 typeof this.repeat == "object" ? this.repeat : {}
29325             );
29326             repeater.on("click", this.onClick,  this);
29327         }
29328         
29329         this.fireEvent('render', this);
29330         
29331     },
29332     /**
29333      * Returns the button's underlying element
29334      * @return {Roo.Element} The element
29335      */
29336     getEl : function(){
29337         return this.el;  
29338     },
29339     
29340     /**
29341      * Destroys this Button and removes any listeners.
29342      */
29343     destroy : function(){
29344         Roo.ButtonToggleMgr.unregister(this);
29345         this.el.removeAllListeners();
29346         this.purgeListeners();
29347         this.el.remove();
29348     },
29349
29350     // private
29351     autoWidth : function(){
29352         if(this.el){
29353             this.el.setWidth("auto");
29354             if(Roo.isIE7 && Roo.isStrict){
29355                 var ib = this.el.child('button');
29356                 if(ib && ib.getWidth() > 20){
29357                     ib.clip();
29358                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29359                 }
29360             }
29361             if(this.minWidth){
29362                 if(this.hidden){
29363                     this.el.beginMeasure();
29364                 }
29365                 if(this.el.getWidth() < this.minWidth){
29366                     this.el.setWidth(this.minWidth);
29367                 }
29368                 if(this.hidden){
29369                     this.el.endMeasure();
29370                 }
29371             }
29372         }
29373     },
29374
29375     /**
29376      * Assigns this button's click handler
29377      * @param {Function} handler The function to call when the button is clicked
29378      * @param {Object} scope (optional) Scope for the function passed in
29379      */
29380     setHandler : function(handler, scope){
29381         this.handler = handler;
29382         this.scope = scope;  
29383     },
29384     
29385     /**
29386      * Sets this button's text
29387      * @param {String} text The button text
29388      */
29389     setText : function(text){
29390         this.text = text;
29391         if(this.el){
29392             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29393         }
29394         this.autoWidth();
29395     },
29396     
29397     /**
29398      * Gets the text for this button
29399      * @return {String} The button text
29400      */
29401     getText : function(){
29402         return this.text;  
29403     },
29404     
29405     /**
29406      * Show this button
29407      */
29408     show: function(){
29409         this.hidden = false;
29410         if(this.el){
29411             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29412         }
29413     },
29414     
29415     /**
29416      * Hide this button
29417      */
29418     hide: function(){
29419         this.hidden = true;
29420         if(this.el){
29421             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29422         }
29423     },
29424     
29425     /**
29426      * Convenience function for boolean show/hide
29427      * @param {Boolean} visible True to show, false to hide
29428      */
29429     setVisible: function(visible){
29430         if(visible) {
29431             this.show();
29432         }else{
29433             this.hide();
29434         }
29435     },
29436     
29437     /**
29438      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29439      * @param {Boolean} state (optional) Force a particular state
29440      */
29441     toggle : function(state){
29442         state = state === undefined ? !this.pressed : state;
29443         if(state != this.pressed){
29444             if(state){
29445                 this.el.addClass("x-btn-pressed");
29446                 this.pressed = true;
29447                 this.fireEvent("toggle", this, true);
29448             }else{
29449                 this.el.removeClass("x-btn-pressed");
29450                 this.pressed = false;
29451                 this.fireEvent("toggle", this, false);
29452             }
29453             if(this.toggleHandler){
29454                 this.toggleHandler.call(this.scope || this, this, state);
29455             }
29456         }
29457     },
29458     
29459     /**
29460      * Focus the button
29461      */
29462     focus : function(){
29463         this.el.child('button:first').focus();
29464     },
29465     
29466     /**
29467      * Disable this button
29468      */
29469     disable : function(){
29470         if(this.el){
29471             this.el.addClass("x-btn-disabled");
29472         }
29473         this.disabled = true;
29474     },
29475     
29476     /**
29477      * Enable this button
29478      */
29479     enable : function(){
29480         if(this.el){
29481             this.el.removeClass("x-btn-disabled");
29482         }
29483         this.disabled = false;
29484     },
29485
29486     /**
29487      * Convenience function for boolean enable/disable
29488      * @param {Boolean} enabled True to enable, false to disable
29489      */
29490     setDisabled : function(v){
29491         this[v !== true ? "enable" : "disable"]();
29492     },
29493
29494     // private
29495     onClick : function(e)
29496     {
29497         if(e){
29498             e.preventDefault();
29499         }
29500         if(e.button != 0){
29501             return;
29502         }
29503         if(!this.disabled){
29504             if(this.enableToggle){
29505                 this.toggle();
29506             }
29507             if(this.menu && !this.menu.isVisible()){
29508                 this.menu.show(this.el, this.menuAlign);
29509             }
29510             this.fireEvent("click", this, e);
29511             if(this.handler){
29512                 this.el.removeClass("x-btn-over");
29513                 this.handler.call(this.scope || this, this, e);
29514             }
29515         }
29516     },
29517     // private
29518     onMouseOver : function(e){
29519         if(!this.disabled){
29520             this.el.addClass("x-btn-over");
29521             this.fireEvent('mouseover', this, e);
29522         }
29523     },
29524     // private
29525     onMouseOut : function(e){
29526         if(!e.within(this.el,  true)){
29527             this.el.removeClass("x-btn-over");
29528             this.fireEvent('mouseout', this, e);
29529         }
29530     },
29531     // private
29532     onFocus : function(e){
29533         if(!this.disabled){
29534             this.el.addClass("x-btn-focus");
29535         }
29536     },
29537     // private
29538     onBlur : function(e){
29539         this.el.removeClass("x-btn-focus");
29540     },
29541     // private
29542     onMouseDown : function(e){
29543         if(!this.disabled && e.button == 0){
29544             this.el.addClass("x-btn-click");
29545             Roo.get(document).on('mouseup', this.onMouseUp, this);
29546         }
29547     },
29548     // private
29549     onMouseUp : function(e){
29550         if(e.button == 0){
29551             this.el.removeClass("x-btn-click");
29552             Roo.get(document).un('mouseup', this.onMouseUp, this);
29553         }
29554     },
29555     // private
29556     onMenuShow : function(e){
29557         this.el.addClass("x-btn-menu-active");
29558     },
29559     // private
29560     onMenuHide : function(e){
29561         this.el.removeClass("x-btn-menu-active");
29562     }   
29563 });
29564
29565 // Private utility class used by Button
29566 Roo.ButtonToggleMgr = function(){
29567    var groups = {};
29568    
29569    function toggleGroup(btn, state){
29570        if(state){
29571            var g = groups[btn.toggleGroup];
29572            for(var i = 0, l = g.length; i < l; i++){
29573                if(g[i] != btn){
29574                    g[i].toggle(false);
29575                }
29576            }
29577        }
29578    }
29579    
29580    return {
29581        register : function(btn){
29582            if(!btn.toggleGroup){
29583                return;
29584            }
29585            var g = groups[btn.toggleGroup];
29586            if(!g){
29587                g = groups[btn.toggleGroup] = [];
29588            }
29589            g.push(btn);
29590            btn.on("toggle", toggleGroup);
29591        },
29592        
29593        unregister : function(btn){
29594            if(!btn.toggleGroup){
29595                return;
29596            }
29597            var g = groups[btn.toggleGroup];
29598            if(g){
29599                g.remove(btn);
29600                btn.un("toggle", toggleGroup);
29601            }
29602        }
29603    };
29604 }();/*
29605  * Based on:
29606  * Ext JS Library 1.1.1
29607  * Copyright(c) 2006-2007, Ext JS, LLC.
29608  *
29609  * Originally Released Under LGPL - original licence link has changed is not relivant.
29610  *
29611  * Fork - LGPL
29612  * <script type="text/javascript">
29613  */
29614  
29615 /**
29616  * @class Roo.SplitButton
29617  * @extends Roo.Button
29618  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29619  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29620  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29621  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29622  * @cfg {String} arrowTooltip The title attribute of the arrow
29623  * @constructor
29624  * Create a new menu button
29625  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29626  * @param {Object} config The config object
29627  */
29628 Roo.SplitButton = function(renderTo, config){
29629     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29630     /**
29631      * @event arrowclick
29632      * Fires when this button's arrow is clicked
29633      * @param {SplitButton} this
29634      * @param {EventObject} e The click event
29635      */
29636     this.addEvents({"arrowclick":true});
29637 };
29638
29639 Roo.extend(Roo.SplitButton, Roo.Button, {
29640     render : function(renderTo){
29641         // this is one sweet looking template!
29642         var tpl = new Roo.Template(
29643             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29644             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29645             '<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>',
29646             "</tbody></table></td><td>",
29647             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29648             '<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>',
29649             "</tbody></table></td></tr></table>"
29650         );
29651         var btn = tpl.append(renderTo, [this.text, this.type], true);
29652         var btnEl = btn.child("button");
29653         if(this.cls){
29654             btn.addClass(this.cls);
29655         }
29656         if(this.icon){
29657             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29658         }
29659         if(this.iconCls){
29660             btnEl.addClass(this.iconCls);
29661             if(!this.cls){
29662                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29663             }
29664         }
29665         this.el = btn;
29666         if(this.handleMouseEvents){
29667             btn.on("mouseover", this.onMouseOver, this);
29668             btn.on("mouseout", this.onMouseOut, this);
29669             btn.on("mousedown", this.onMouseDown, this);
29670             btn.on("mouseup", this.onMouseUp, this);
29671         }
29672         btn.on(this.clickEvent, this.onClick, this);
29673         if(this.tooltip){
29674             if(typeof this.tooltip == 'object'){
29675                 Roo.QuickTips.tips(Roo.apply({
29676                       target: btnEl.id
29677                 }, this.tooltip));
29678             } else {
29679                 btnEl.dom[this.tooltipType] = this.tooltip;
29680             }
29681         }
29682         if(this.arrowTooltip){
29683             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29684         }
29685         if(this.hidden){
29686             this.hide();
29687         }
29688         if(this.disabled){
29689             this.disable();
29690         }
29691         if(this.pressed){
29692             this.el.addClass("x-btn-pressed");
29693         }
29694         if(Roo.isIE && !Roo.isIE7){
29695             this.autoWidth.defer(1, this);
29696         }else{
29697             this.autoWidth();
29698         }
29699         if(this.menu){
29700             this.menu.on("show", this.onMenuShow, this);
29701             this.menu.on("hide", this.onMenuHide, this);
29702         }
29703         this.fireEvent('render', this);
29704     },
29705
29706     // private
29707     autoWidth : function(){
29708         if(this.el){
29709             var tbl = this.el.child("table:first");
29710             var tbl2 = this.el.child("table:last");
29711             this.el.setWidth("auto");
29712             tbl.setWidth("auto");
29713             if(Roo.isIE7 && Roo.isStrict){
29714                 var ib = this.el.child('button:first');
29715                 if(ib && ib.getWidth() > 20){
29716                     ib.clip();
29717                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29718                 }
29719             }
29720             if(this.minWidth){
29721                 if(this.hidden){
29722                     this.el.beginMeasure();
29723                 }
29724                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29725                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29726                 }
29727                 if(this.hidden){
29728                     this.el.endMeasure();
29729                 }
29730             }
29731             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29732         } 
29733     },
29734     /**
29735      * Sets this button's click handler
29736      * @param {Function} handler The function to call when the button is clicked
29737      * @param {Object} scope (optional) Scope for the function passed above
29738      */
29739     setHandler : function(handler, scope){
29740         this.handler = handler;
29741         this.scope = scope;  
29742     },
29743     
29744     /**
29745      * Sets this button's arrow click handler
29746      * @param {Function} handler The function to call when the arrow is clicked
29747      * @param {Object} scope (optional) Scope for the function passed above
29748      */
29749     setArrowHandler : function(handler, scope){
29750         this.arrowHandler = handler;
29751         this.scope = scope;  
29752     },
29753     
29754     /**
29755      * Focus the button
29756      */
29757     focus : function(){
29758         if(this.el){
29759             this.el.child("button:first").focus();
29760         }
29761     },
29762
29763     // private
29764     onClick : function(e){
29765         e.preventDefault();
29766         if(!this.disabled){
29767             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29768                 if(this.menu && !this.menu.isVisible()){
29769                     this.menu.show(this.el, this.menuAlign);
29770                 }
29771                 this.fireEvent("arrowclick", this, e);
29772                 if(this.arrowHandler){
29773                     this.arrowHandler.call(this.scope || this, this, e);
29774                 }
29775             }else{
29776                 this.fireEvent("click", this, e);
29777                 if(this.handler){
29778                     this.handler.call(this.scope || this, this, e);
29779                 }
29780             }
29781         }
29782     },
29783     // private
29784     onMouseDown : function(e){
29785         if(!this.disabled){
29786             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29787         }
29788     },
29789     // private
29790     onMouseUp : function(e){
29791         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29792     }   
29793 });
29794
29795
29796 // backwards compat
29797 Roo.MenuButton = Roo.SplitButton;/*
29798  * Based on:
29799  * Ext JS Library 1.1.1
29800  * Copyright(c) 2006-2007, Ext JS, LLC.
29801  *
29802  * Originally Released Under LGPL - original licence link has changed is not relivant.
29803  *
29804  * Fork - LGPL
29805  * <script type="text/javascript">
29806  */
29807
29808 /**
29809  * @class Roo.Toolbar
29810  * Basic Toolbar class.
29811  * @constructor
29812  * Creates a new Toolbar
29813  * @param {Object} container The config object
29814  */ 
29815 Roo.Toolbar = function(container, buttons, config)
29816 {
29817     /// old consturctor format still supported..
29818     if(container instanceof Array){ // omit the container for later rendering
29819         buttons = container;
29820         config = buttons;
29821         container = null;
29822     }
29823     if (typeof(container) == 'object' && container.xtype) {
29824         config = container;
29825         container = config.container;
29826         buttons = config.buttons || []; // not really - use items!!
29827     }
29828     var xitems = [];
29829     if (config && config.items) {
29830         xitems = config.items;
29831         delete config.items;
29832     }
29833     Roo.apply(this, config);
29834     this.buttons = buttons;
29835     
29836     if(container){
29837         this.render(container);
29838     }
29839     this.xitems = xitems;
29840     Roo.each(xitems, function(b) {
29841         this.add(b);
29842     }, this);
29843     
29844 };
29845
29846 Roo.Toolbar.prototype = {
29847     /**
29848      * @cfg {Array} items
29849      * array of button configs or elements to add (will be converted to a MixedCollection)
29850      */
29851     
29852     /**
29853      * @cfg {String/HTMLElement/Element} container
29854      * The id or element that will contain the toolbar
29855      */
29856     // private
29857     render : function(ct){
29858         this.el = Roo.get(ct);
29859         if(this.cls){
29860             this.el.addClass(this.cls);
29861         }
29862         // using a table allows for vertical alignment
29863         // 100% width is needed by Safari...
29864         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29865         this.tr = this.el.child("tr", true);
29866         var autoId = 0;
29867         this.items = new Roo.util.MixedCollection(false, function(o){
29868             return o.id || ("item" + (++autoId));
29869         });
29870         if(this.buttons){
29871             this.add.apply(this, this.buttons);
29872             delete this.buttons;
29873         }
29874     },
29875
29876     /**
29877      * Adds element(s) to the toolbar -- this function takes a variable number of 
29878      * arguments of mixed type and adds them to the toolbar.
29879      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29880      * <ul>
29881      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29882      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29883      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29884      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29885      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29886      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29887      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29888      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29889      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29890      * </ul>
29891      * @param {Mixed} arg2
29892      * @param {Mixed} etc.
29893      */
29894     add : function(){
29895         var a = arguments, l = a.length;
29896         for(var i = 0; i < l; i++){
29897             this._add(a[i]);
29898         }
29899     },
29900     // private..
29901     _add : function(el) {
29902         
29903         if (el.xtype) {
29904             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29905         }
29906         
29907         if (el.applyTo){ // some kind of form field
29908             return this.addField(el);
29909         } 
29910         if (el.render){ // some kind of Toolbar.Item
29911             return this.addItem(el);
29912         }
29913         if (typeof el == "string"){ // string
29914             if(el == "separator" || el == "-"){
29915                 return this.addSeparator();
29916             }
29917             if (el == " "){
29918                 return this.addSpacer();
29919             }
29920             if(el == "->"){
29921                 return this.addFill();
29922             }
29923             return this.addText(el);
29924             
29925         }
29926         if(el.tagName){ // element
29927             return this.addElement(el);
29928         }
29929         if(typeof el == "object"){ // must be button config?
29930             return this.addButton(el);
29931         }
29932         // and now what?!?!
29933         return false;
29934         
29935     },
29936     
29937     /**
29938      * Add an Xtype element
29939      * @param {Object} xtype Xtype Object
29940      * @return {Object} created Object
29941      */
29942     addxtype : function(e){
29943         return this.add(e);  
29944     },
29945     
29946     /**
29947      * Returns the Element for this toolbar.
29948      * @return {Roo.Element}
29949      */
29950     getEl : function(){
29951         return this.el;  
29952     },
29953     
29954     /**
29955      * Adds a separator
29956      * @return {Roo.Toolbar.Item} The separator item
29957      */
29958     addSeparator : function(){
29959         return this.addItem(new Roo.Toolbar.Separator());
29960     },
29961
29962     /**
29963      * Adds a spacer element
29964      * @return {Roo.Toolbar.Spacer} The spacer item
29965      */
29966     addSpacer : function(){
29967         return this.addItem(new Roo.Toolbar.Spacer());
29968     },
29969
29970     /**
29971      * Adds a fill element that forces subsequent additions to the right side of the toolbar
29972      * @return {Roo.Toolbar.Fill} The fill item
29973      */
29974     addFill : function(){
29975         return this.addItem(new Roo.Toolbar.Fill());
29976     },
29977
29978     /**
29979      * Adds any standard HTML element to the toolbar
29980      * @param {String/HTMLElement/Element} el The element or id of the element to add
29981      * @return {Roo.Toolbar.Item} The element's item
29982      */
29983     addElement : function(el){
29984         return this.addItem(new Roo.Toolbar.Item(el));
29985     },
29986     /**
29987      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
29988      * @type Roo.util.MixedCollection  
29989      */
29990     items : false,
29991      
29992     /**
29993      * Adds any Toolbar.Item or subclass
29994      * @param {Roo.Toolbar.Item} item
29995      * @return {Roo.Toolbar.Item} The item
29996      */
29997     addItem : function(item){
29998         var td = this.nextBlock();
29999         item.render(td);
30000         this.items.add(item);
30001         return item;
30002     },
30003     
30004     /**
30005      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30006      * @param {Object/Array} config A button config or array of configs
30007      * @return {Roo.Toolbar.Button/Array}
30008      */
30009     addButton : function(config){
30010         if(config instanceof Array){
30011             var buttons = [];
30012             for(var i = 0, len = config.length; i < len; i++) {
30013                 buttons.push(this.addButton(config[i]));
30014             }
30015             return buttons;
30016         }
30017         var b = config;
30018         if(!(config instanceof Roo.Toolbar.Button)){
30019             b = config.split ?
30020                 new Roo.Toolbar.SplitButton(config) :
30021                 new Roo.Toolbar.Button(config);
30022         }
30023         var td = this.nextBlock();
30024         b.render(td);
30025         this.items.add(b);
30026         return b;
30027     },
30028     
30029     /**
30030      * Adds text to the toolbar
30031      * @param {String} text The text to add
30032      * @return {Roo.Toolbar.Item} The element's item
30033      */
30034     addText : function(text){
30035         return this.addItem(new Roo.Toolbar.TextItem(text));
30036     },
30037     
30038     /**
30039      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30040      * @param {Number} index The index where the item is to be inserted
30041      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30042      * @return {Roo.Toolbar.Button/Item}
30043      */
30044     insertButton : function(index, item){
30045         if(item instanceof Array){
30046             var buttons = [];
30047             for(var i = 0, len = item.length; i < len; i++) {
30048                buttons.push(this.insertButton(index + i, item[i]));
30049             }
30050             return buttons;
30051         }
30052         if (!(item instanceof Roo.Toolbar.Button)){
30053            item = new Roo.Toolbar.Button(item);
30054         }
30055         var td = document.createElement("td");
30056         this.tr.insertBefore(td, this.tr.childNodes[index]);
30057         item.render(td);
30058         this.items.insert(index, item);
30059         return item;
30060     },
30061     
30062     /**
30063      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30064      * @param {Object} config
30065      * @return {Roo.Toolbar.Item} The element's item
30066      */
30067     addDom : function(config, returnEl){
30068         var td = this.nextBlock();
30069         Roo.DomHelper.overwrite(td, config);
30070         var ti = new Roo.Toolbar.Item(td.firstChild);
30071         ti.render(td);
30072         this.items.add(ti);
30073         return ti;
30074     },
30075
30076     /**
30077      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30078      * @type Roo.util.MixedCollection  
30079      */
30080     fields : false,
30081     
30082     /**
30083      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30084      * Note: the field should not have been rendered yet. For a field that has already been
30085      * rendered, use {@link #addElement}.
30086      * @param {Roo.form.Field} field
30087      * @return {Roo.ToolbarItem}
30088      */
30089      
30090       
30091     addField : function(field) {
30092         if (!this.fields) {
30093             var autoId = 0;
30094             this.fields = new Roo.util.MixedCollection(false, function(o){
30095                 return o.id || ("item" + (++autoId));
30096             });
30097
30098         }
30099         
30100         var td = this.nextBlock();
30101         field.render(td);
30102         var ti = new Roo.Toolbar.Item(td.firstChild);
30103         ti.render(td);
30104         this.items.add(ti);
30105         this.fields.add(field);
30106         return ti;
30107     },
30108     /**
30109      * Hide the toolbar
30110      * @method hide
30111      */
30112      
30113       
30114     hide : function()
30115     {
30116         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30117         this.el.child('div').hide();
30118     },
30119     /**
30120      * Show the toolbar
30121      * @method show
30122      */
30123     show : function()
30124     {
30125         this.el.child('div').show();
30126     },
30127       
30128     // private
30129     nextBlock : function(){
30130         var td = document.createElement("td");
30131         this.tr.appendChild(td);
30132         return td;
30133     },
30134
30135     // private
30136     destroy : function(){
30137         if(this.items){ // rendered?
30138             Roo.destroy.apply(Roo, this.items.items);
30139         }
30140         if(this.fields){ // rendered?
30141             Roo.destroy.apply(Roo, this.fields.items);
30142         }
30143         Roo.Element.uncache(this.el, this.tr);
30144     }
30145 };
30146
30147 /**
30148  * @class Roo.Toolbar.Item
30149  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30150  * @constructor
30151  * Creates a new Item
30152  * @param {HTMLElement} el 
30153  */
30154 Roo.Toolbar.Item = function(el){
30155     var cfg = {};
30156     if (typeof (el.xtype) != 'undefined') {
30157         cfg = el;
30158         el = cfg.el;
30159     }
30160     
30161     this.el = Roo.getDom(el);
30162     this.id = Roo.id(this.el);
30163     this.hidden = false;
30164     
30165     this.addEvents({
30166          /**
30167              * @event render
30168              * Fires when the button is rendered
30169              * @param {Button} this
30170              */
30171         'render': true
30172     });
30173     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30174 };
30175 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30176 //Roo.Toolbar.Item.prototype = {
30177     
30178     /**
30179      * Get this item's HTML Element
30180      * @return {HTMLElement}
30181      */
30182     getEl : function(){
30183        return this.el;  
30184     },
30185
30186     // private
30187     render : function(td){
30188         
30189          this.td = td;
30190         td.appendChild(this.el);
30191         
30192         this.fireEvent('render', this);
30193     },
30194     
30195     /**
30196      * Removes and destroys this item.
30197      */
30198     destroy : function(){
30199         this.td.parentNode.removeChild(this.td);
30200     },
30201     
30202     /**
30203      * Shows this item.
30204      */
30205     show: function(){
30206         this.hidden = false;
30207         this.td.style.display = "";
30208     },
30209     
30210     /**
30211      * Hides this item.
30212      */
30213     hide: function(){
30214         this.hidden = true;
30215         this.td.style.display = "none";
30216     },
30217     
30218     /**
30219      * Convenience function for boolean show/hide.
30220      * @param {Boolean} visible true to show/false to hide
30221      */
30222     setVisible: function(visible){
30223         if(visible) {
30224             this.show();
30225         }else{
30226             this.hide();
30227         }
30228     },
30229     
30230     /**
30231      * Try to focus this item.
30232      */
30233     focus : function(){
30234         Roo.fly(this.el).focus();
30235     },
30236     
30237     /**
30238      * Disables this item.
30239      */
30240     disable : function(){
30241         Roo.fly(this.td).addClass("x-item-disabled");
30242         this.disabled = true;
30243         this.el.disabled = true;
30244     },
30245     
30246     /**
30247      * Enables this item.
30248      */
30249     enable : function(){
30250         Roo.fly(this.td).removeClass("x-item-disabled");
30251         this.disabled = false;
30252         this.el.disabled = false;
30253     }
30254 });
30255
30256
30257 /**
30258  * @class Roo.Toolbar.Separator
30259  * @extends Roo.Toolbar.Item
30260  * A simple toolbar separator class
30261  * @constructor
30262  * Creates a new Separator
30263  */
30264 Roo.Toolbar.Separator = function(cfg){
30265     
30266     var s = document.createElement("span");
30267     s.className = "ytb-sep";
30268     if (cfg) {
30269         cfg.el = s;
30270     }
30271     
30272     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30273 };
30274 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30275     enable:Roo.emptyFn,
30276     disable:Roo.emptyFn,
30277     focus:Roo.emptyFn
30278 });
30279
30280 /**
30281  * @class Roo.Toolbar.Spacer
30282  * @extends Roo.Toolbar.Item
30283  * A simple element that adds extra horizontal space to a toolbar.
30284  * @constructor
30285  * Creates a new Spacer
30286  */
30287 Roo.Toolbar.Spacer = function(cfg){
30288     var s = document.createElement("div");
30289     s.className = "ytb-spacer";
30290     if (cfg) {
30291         cfg.el = s;
30292     }
30293     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30294 };
30295 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30296     enable:Roo.emptyFn,
30297     disable:Roo.emptyFn,
30298     focus:Roo.emptyFn
30299 });
30300
30301 /**
30302  * @class Roo.Toolbar.Fill
30303  * @extends Roo.Toolbar.Spacer
30304  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30305  * @constructor
30306  * Creates a new Spacer
30307  */
30308 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30309     // private
30310     render : function(td){
30311         td.style.width = '100%';
30312         Roo.Toolbar.Fill.superclass.render.call(this, td);
30313     }
30314 });
30315
30316 /**
30317  * @class Roo.Toolbar.TextItem
30318  * @extends Roo.Toolbar.Item
30319  * A simple class that renders text directly into a toolbar.
30320  * @constructor
30321  * Creates a new TextItem
30322  * @param {String} text
30323  */
30324 Roo.Toolbar.TextItem = function(cfg){
30325     var  text = cfg || "";
30326     if (typeof(cfg) == 'object') {
30327         text = cfg.text || "";
30328     }  else {
30329         cfg = null;
30330     }
30331     var s = document.createElement("span");
30332     s.className = "ytb-text";
30333     s.innerHTML = text;
30334     if (cfg) {
30335         cfg.el  = s;
30336     }
30337     
30338     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30339 };
30340 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30341     
30342      
30343     enable:Roo.emptyFn,
30344     disable:Roo.emptyFn,
30345     focus:Roo.emptyFn
30346 });
30347
30348 /**
30349  * @class Roo.Toolbar.Button
30350  * @extends Roo.Button
30351  * A button that renders into a toolbar.
30352  * @constructor
30353  * Creates a new Button
30354  * @param {Object} config A standard {@link Roo.Button} config object
30355  */
30356 Roo.Toolbar.Button = function(config){
30357     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30358 };
30359 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30360     render : function(td){
30361         this.td = td;
30362         Roo.Toolbar.Button.superclass.render.call(this, td);
30363     },
30364     
30365     /**
30366      * Removes and destroys this button
30367      */
30368     destroy : function(){
30369         Roo.Toolbar.Button.superclass.destroy.call(this);
30370         this.td.parentNode.removeChild(this.td);
30371     },
30372     
30373     /**
30374      * Shows this button
30375      */
30376     show: function(){
30377         this.hidden = false;
30378         this.td.style.display = "";
30379     },
30380     
30381     /**
30382      * Hides this button
30383      */
30384     hide: function(){
30385         this.hidden = true;
30386         this.td.style.display = "none";
30387     },
30388
30389     /**
30390      * Disables this item
30391      */
30392     disable : function(){
30393         Roo.fly(this.td).addClass("x-item-disabled");
30394         this.disabled = true;
30395     },
30396
30397     /**
30398      * Enables this item
30399      */
30400     enable : function(){
30401         Roo.fly(this.td).removeClass("x-item-disabled");
30402         this.disabled = false;
30403     }
30404 });
30405 // backwards compat
30406 Roo.ToolbarButton = Roo.Toolbar.Button;
30407
30408 /**
30409  * @class Roo.Toolbar.SplitButton
30410  * @extends Roo.SplitButton
30411  * A menu button that renders into a toolbar.
30412  * @constructor
30413  * Creates a new SplitButton
30414  * @param {Object} config A standard {@link Roo.SplitButton} config object
30415  */
30416 Roo.Toolbar.SplitButton = function(config){
30417     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30418 };
30419 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30420     render : function(td){
30421         this.td = td;
30422         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30423     },
30424     
30425     /**
30426      * Removes and destroys this button
30427      */
30428     destroy : function(){
30429         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30430         this.td.parentNode.removeChild(this.td);
30431     },
30432     
30433     /**
30434      * Shows this button
30435      */
30436     show: function(){
30437         this.hidden = false;
30438         this.td.style.display = "";
30439     },
30440     
30441     /**
30442      * Hides this button
30443      */
30444     hide: function(){
30445         this.hidden = true;
30446         this.td.style.display = "none";
30447     }
30448 });
30449
30450 // backwards compat
30451 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30452  * Based on:
30453  * Ext JS Library 1.1.1
30454  * Copyright(c) 2006-2007, Ext JS, LLC.
30455  *
30456  * Originally Released Under LGPL - original licence link has changed is not relivant.
30457  *
30458  * Fork - LGPL
30459  * <script type="text/javascript">
30460  */
30461  
30462 /**
30463  * @class Roo.PagingToolbar
30464  * @extends Roo.Toolbar
30465  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30466  * @constructor
30467  * Create a new PagingToolbar
30468  * @param {Object} config The config object
30469  */
30470 Roo.PagingToolbar = function(el, ds, config)
30471 {
30472     // old args format still supported... - xtype is prefered..
30473     if (typeof(el) == 'object' && el.xtype) {
30474         // created from xtype...
30475         config = el;
30476         ds = el.dataSource;
30477         el = config.container;
30478     }
30479     var items = [];
30480     if (config.items) {
30481         items = config.items;
30482         config.items = [];
30483     }
30484     
30485     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30486     this.ds = ds;
30487     this.cursor = 0;
30488     this.renderButtons(this.el);
30489     this.bind(ds);
30490     
30491     // supprot items array.
30492    
30493     Roo.each(items, function(e) {
30494         this.add(Roo.factory(e));
30495     },this);
30496     
30497 };
30498
30499 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30500     /**
30501      * @cfg {Roo.data.Store} dataSource
30502      * The underlying data store providing the paged data
30503      */
30504     /**
30505      * @cfg {String/HTMLElement/Element} container
30506      * container The id or element that will contain the toolbar
30507      */
30508     /**
30509      * @cfg {Boolean} displayInfo
30510      * True to display the displayMsg (defaults to false)
30511      */
30512     /**
30513      * @cfg {Number} pageSize
30514      * The number of records to display per page (defaults to 20)
30515      */
30516     pageSize: 20,
30517     /**
30518      * @cfg {String} displayMsg
30519      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30520      */
30521     displayMsg : 'Displaying {0} - {1} of {2}',
30522     /**
30523      * @cfg {String} emptyMsg
30524      * The message to display when no records are found (defaults to "No data to display")
30525      */
30526     emptyMsg : 'No data to display',
30527     /**
30528      * Customizable piece of the default paging text (defaults to "Page")
30529      * @type String
30530      */
30531     beforePageText : "Page",
30532     /**
30533      * Customizable piece of the default paging text (defaults to "of %0")
30534      * @type String
30535      */
30536     afterPageText : "of {0}",
30537     /**
30538      * Customizable piece of the default paging text (defaults to "First Page")
30539      * @type String
30540      */
30541     firstText : "First Page",
30542     /**
30543      * Customizable piece of the default paging text (defaults to "Previous Page")
30544      * @type String
30545      */
30546     prevText : "Previous Page",
30547     /**
30548      * Customizable piece of the default paging text (defaults to "Next Page")
30549      * @type String
30550      */
30551     nextText : "Next Page",
30552     /**
30553      * Customizable piece of the default paging text (defaults to "Last Page")
30554      * @type String
30555      */
30556     lastText : "Last Page",
30557     /**
30558      * Customizable piece of the default paging text (defaults to "Refresh")
30559      * @type String
30560      */
30561     refreshText : "Refresh",
30562
30563     // private
30564     renderButtons : function(el){
30565         Roo.PagingToolbar.superclass.render.call(this, el);
30566         this.first = this.addButton({
30567             tooltip: this.firstText,
30568             cls: "x-btn-icon x-grid-page-first",
30569             disabled: true,
30570             handler: this.onClick.createDelegate(this, ["first"])
30571         });
30572         this.prev = this.addButton({
30573             tooltip: this.prevText,
30574             cls: "x-btn-icon x-grid-page-prev",
30575             disabled: true,
30576             handler: this.onClick.createDelegate(this, ["prev"])
30577         });
30578         //this.addSeparator();
30579         this.add(this.beforePageText);
30580         this.field = Roo.get(this.addDom({
30581            tag: "input",
30582            type: "text",
30583            size: "3",
30584            value: "1",
30585            cls: "x-grid-page-number"
30586         }).el);
30587         this.field.on("keydown", this.onPagingKeydown, this);
30588         this.field.on("focus", function(){this.dom.select();});
30589         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30590         this.field.setHeight(18);
30591         //this.addSeparator();
30592         this.next = this.addButton({
30593             tooltip: this.nextText,
30594             cls: "x-btn-icon x-grid-page-next",
30595             disabled: true,
30596             handler: this.onClick.createDelegate(this, ["next"])
30597         });
30598         this.last = this.addButton({
30599             tooltip: this.lastText,
30600             cls: "x-btn-icon x-grid-page-last",
30601             disabled: true,
30602             handler: this.onClick.createDelegate(this, ["last"])
30603         });
30604         //this.addSeparator();
30605         this.loading = this.addButton({
30606             tooltip: this.refreshText,
30607             cls: "x-btn-icon x-grid-loading",
30608             handler: this.onClick.createDelegate(this, ["refresh"])
30609         });
30610
30611         if(this.displayInfo){
30612             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30613         }
30614     },
30615
30616     // private
30617     updateInfo : function(){
30618         if(this.displayEl){
30619             var count = this.ds.getCount();
30620             var msg = count == 0 ?
30621                 this.emptyMsg :
30622                 String.format(
30623                     this.displayMsg,
30624                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30625                 );
30626             this.displayEl.update(msg);
30627         }
30628     },
30629
30630     // private
30631     onLoad : function(ds, r, o){
30632        this.cursor = o.params ? o.params.start : 0;
30633        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30634
30635        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30636        this.field.dom.value = ap;
30637        this.first.setDisabled(ap == 1);
30638        this.prev.setDisabled(ap == 1);
30639        this.next.setDisabled(ap == ps);
30640        this.last.setDisabled(ap == ps);
30641        this.loading.enable();
30642        this.updateInfo();
30643     },
30644
30645     // private
30646     getPageData : function(){
30647         var total = this.ds.getTotalCount();
30648         return {
30649             total : total,
30650             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30651             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30652         };
30653     },
30654
30655     // private
30656     onLoadError : function(){
30657         this.loading.enable();
30658     },
30659
30660     // private
30661     onPagingKeydown : function(e){
30662         var k = e.getKey();
30663         var d = this.getPageData();
30664         if(k == e.RETURN){
30665             var v = this.field.dom.value, pageNum;
30666             if(!v || isNaN(pageNum = parseInt(v, 10))){
30667                 this.field.dom.value = d.activePage;
30668                 return;
30669             }
30670             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30671             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30672             e.stopEvent();
30673         }
30674         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))
30675         {
30676           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30677           this.field.dom.value = pageNum;
30678           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30679           e.stopEvent();
30680         }
30681         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30682         {
30683           var v = this.field.dom.value, pageNum; 
30684           var increment = (e.shiftKey) ? 10 : 1;
30685           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30686             increment *= -1;
30687           }
30688           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30689             this.field.dom.value = d.activePage;
30690             return;
30691           }
30692           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30693           {
30694             this.field.dom.value = parseInt(v, 10) + increment;
30695             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30696             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30697           }
30698           e.stopEvent();
30699         }
30700     },
30701
30702     // private
30703     beforeLoad : function(){
30704         if(this.loading){
30705             this.loading.disable();
30706         }
30707     },
30708
30709     // private
30710     onClick : function(which){
30711         var ds = this.ds;
30712         switch(which){
30713             case "first":
30714                 ds.load({params:{start: 0, limit: this.pageSize}});
30715             break;
30716             case "prev":
30717                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30718             break;
30719             case "next":
30720                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30721             break;
30722             case "last":
30723                 var total = ds.getTotalCount();
30724                 var extra = total % this.pageSize;
30725                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30726                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30727             break;
30728             case "refresh":
30729                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30730             break;
30731         }
30732     },
30733
30734     /**
30735      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30736      * @param {Roo.data.Store} store The data store to unbind
30737      */
30738     unbind : function(ds){
30739         ds.un("beforeload", this.beforeLoad, this);
30740         ds.un("load", this.onLoad, this);
30741         ds.un("loadexception", this.onLoadError, this);
30742         ds.un("remove", this.updateInfo, this);
30743         ds.un("add", this.updateInfo, this);
30744         this.ds = undefined;
30745     },
30746
30747     /**
30748      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30749      * @param {Roo.data.Store} store The data store to bind
30750      */
30751     bind : function(ds){
30752         ds.on("beforeload", this.beforeLoad, this);
30753         ds.on("load", this.onLoad, this);
30754         ds.on("loadexception", this.onLoadError, this);
30755         ds.on("remove", this.updateInfo, this);
30756         ds.on("add", this.updateInfo, this);
30757         this.ds = ds;
30758     }
30759 });/*
30760  * Based on:
30761  * Ext JS Library 1.1.1
30762  * Copyright(c) 2006-2007, Ext JS, LLC.
30763  *
30764  * Originally Released Under LGPL - original licence link has changed is not relivant.
30765  *
30766  * Fork - LGPL
30767  * <script type="text/javascript">
30768  */
30769
30770 /**
30771  * @class Roo.Resizable
30772  * @extends Roo.util.Observable
30773  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30774  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30775  * 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
30776  * the element will be wrapped for you automatically.</p>
30777  * <p>Here is the list of valid resize handles:</p>
30778  * <pre>
30779 Value   Description
30780 ------  -------------------
30781  'n'     north
30782  's'     south
30783  'e'     east
30784  'w'     west
30785  'nw'    northwest
30786  'sw'    southwest
30787  'se'    southeast
30788  'ne'    northeast
30789  'hd'    horizontal drag
30790  'all'   all
30791 </pre>
30792  * <p>Here's an example showing the creation of a typical Resizable:</p>
30793  * <pre><code>
30794 var resizer = new Roo.Resizable("element-id", {
30795     handles: 'all',
30796     minWidth: 200,
30797     minHeight: 100,
30798     maxWidth: 500,
30799     maxHeight: 400,
30800     pinned: true
30801 });
30802 resizer.on("resize", myHandler);
30803 </code></pre>
30804  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30805  * resizer.east.setDisplayed(false);</p>
30806  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30807  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30808  * resize operation's new size (defaults to [0, 0])
30809  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30810  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30811  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30812  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30813  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30814  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30815  * @cfg {Number} width The width of the element in pixels (defaults to null)
30816  * @cfg {Number} height The height of the element in pixels (defaults to null)
30817  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30818  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30819  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30820  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30821  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30822  * in favor of the handles config option (defaults to false)
30823  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30824  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30825  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30826  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30827  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30828  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30829  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30830  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30831  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30832  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30833  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30834  * @constructor
30835  * Create a new resizable component
30836  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30837  * @param {Object} config configuration options
30838   */
30839 Roo.Resizable = function(el, config)
30840 {
30841     this.el = Roo.get(el);
30842
30843     if(config && config.wrap){
30844         config.resizeChild = this.el;
30845         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30846         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30847         this.el.setStyle("overflow", "hidden");
30848         this.el.setPositioning(config.resizeChild.getPositioning());
30849         config.resizeChild.clearPositioning();
30850         if(!config.width || !config.height){
30851             var csize = config.resizeChild.getSize();
30852             this.el.setSize(csize.width, csize.height);
30853         }
30854         if(config.pinned && !config.adjustments){
30855             config.adjustments = "auto";
30856         }
30857     }
30858
30859     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30860     this.proxy.unselectable();
30861     this.proxy.enableDisplayMode('block');
30862
30863     Roo.apply(this, config);
30864
30865     if(this.pinned){
30866         this.disableTrackOver = true;
30867         this.el.addClass("x-resizable-pinned");
30868     }
30869     // if the element isn't positioned, make it relative
30870     var position = this.el.getStyle("position");
30871     if(position != "absolute" && position != "fixed"){
30872         this.el.setStyle("position", "relative");
30873     }
30874     if(!this.handles){ // no handles passed, must be legacy style
30875         this.handles = 's,e,se';
30876         if(this.multiDirectional){
30877             this.handles += ',n,w';
30878         }
30879     }
30880     if(this.handles == "all"){
30881         this.handles = "n s e w ne nw se sw";
30882     }
30883     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30884     var ps = Roo.Resizable.positions;
30885     for(var i = 0, len = hs.length; i < len; i++){
30886         if(hs[i] && ps[hs[i]]){
30887             var pos = ps[hs[i]];
30888             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30889         }
30890     }
30891     // legacy
30892     this.corner = this.southeast;
30893     
30894     // updateBox = the box can move..
30895     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30896         this.updateBox = true;
30897     }
30898
30899     this.activeHandle = null;
30900
30901     if(this.resizeChild){
30902         if(typeof this.resizeChild == "boolean"){
30903             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30904         }else{
30905             this.resizeChild = Roo.get(this.resizeChild, true);
30906         }
30907     }
30908     
30909     if(this.adjustments == "auto"){
30910         var rc = this.resizeChild;
30911         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
30912         if(rc && (hw || hn)){
30913             rc.position("relative");
30914             rc.setLeft(hw ? hw.el.getWidth() : 0);
30915             rc.setTop(hn ? hn.el.getHeight() : 0);
30916         }
30917         this.adjustments = [
30918             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
30919             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
30920         ];
30921     }
30922
30923     if(this.draggable){
30924         this.dd = this.dynamic ?
30925             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
30926         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
30927     }
30928
30929     // public events
30930     this.addEvents({
30931         /**
30932          * @event beforeresize
30933          * Fired before resize is allowed. Set enabled to false to cancel resize.
30934          * @param {Roo.Resizable} this
30935          * @param {Roo.EventObject} e The mousedown event
30936          */
30937         "beforeresize" : true,
30938         /**
30939          * @event resizing
30940          * Fired a resizing.
30941          * @param {Roo.Resizable} this
30942          * @param {Number} x The new x position
30943          * @param {Number} y The new y position
30944          * @param {Number} w The new w width
30945          * @param {Number} h The new h hight
30946          * @param {Roo.EventObject} e The mouseup event
30947          */
30948         "resizing" : true,
30949         /**
30950          * @event resize
30951          * Fired after a resize.
30952          * @param {Roo.Resizable} this
30953          * @param {Number} width The new width
30954          * @param {Number} height The new height
30955          * @param {Roo.EventObject} e The mouseup event
30956          */
30957         "resize" : true
30958     });
30959
30960     if(this.width !== null && this.height !== null){
30961         this.resizeTo(this.width, this.height);
30962     }else{
30963         this.updateChildSize();
30964     }
30965     if(Roo.isIE){
30966         this.el.dom.style.zoom = 1;
30967     }
30968     Roo.Resizable.superclass.constructor.call(this);
30969 };
30970
30971 Roo.extend(Roo.Resizable, Roo.util.Observable, {
30972         resizeChild : false,
30973         adjustments : [0, 0],
30974         minWidth : 5,
30975         minHeight : 5,
30976         maxWidth : 10000,
30977         maxHeight : 10000,
30978         enabled : true,
30979         animate : false,
30980         duration : .35,
30981         dynamic : false,
30982         handles : false,
30983         multiDirectional : false,
30984         disableTrackOver : false,
30985         easing : 'easeOutStrong',
30986         widthIncrement : 0,
30987         heightIncrement : 0,
30988         pinned : false,
30989         width : null,
30990         height : null,
30991         preserveRatio : false,
30992         transparent: false,
30993         minX: 0,
30994         minY: 0,
30995         draggable: false,
30996
30997         /**
30998          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
30999          */
31000         constrainTo: undefined,
31001         /**
31002          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31003          */
31004         resizeRegion: undefined,
31005
31006
31007     /**
31008      * Perform a manual resize
31009      * @param {Number} width
31010      * @param {Number} height
31011      */
31012     resizeTo : function(width, height){
31013         this.el.setSize(width, height);
31014         this.updateChildSize();
31015         this.fireEvent("resize", this, width, height, null);
31016     },
31017
31018     // private
31019     startSizing : function(e, handle){
31020         this.fireEvent("beforeresize", this, e);
31021         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31022
31023             if(!this.overlay){
31024                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31025                 this.overlay.unselectable();
31026                 this.overlay.enableDisplayMode("block");
31027                 this.overlay.on("mousemove", this.onMouseMove, this);
31028                 this.overlay.on("mouseup", this.onMouseUp, this);
31029             }
31030             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31031
31032             this.resizing = true;
31033             this.startBox = this.el.getBox();
31034             this.startPoint = e.getXY();
31035             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31036                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31037
31038             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31039             this.overlay.show();
31040
31041             if(this.constrainTo) {
31042                 var ct = Roo.get(this.constrainTo);
31043                 this.resizeRegion = ct.getRegion().adjust(
31044                     ct.getFrameWidth('t'),
31045                     ct.getFrameWidth('l'),
31046                     -ct.getFrameWidth('b'),
31047                     -ct.getFrameWidth('r')
31048                 );
31049             }
31050
31051             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31052             this.proxy.show();
31053             this.proxy.setBox(this.startBox);
31054             if(!this.dynamic){
31055                 this.proxy.setStyle('visibility', 'visible');
31056             }
31057         }
31058     },
31059
31060     // private
31061     onMouseDown : function(handle, e){
31062         if(this.enabled){
31063             e.stopEvent();
31064             this.activeHandle = handle;
31065             this.startSizing(e, handle);
31066         }
31067     },
31068
31069     // private
31070     onMouseUp : function(e){
31071         var size = this.resizeElement();
31072         this.resizing = false;
31073         this.handleOut();
31074         this.overlay.hide();
31075         this.proxy.hide();
31076         this.fireEvent("resize", this, size.width, size.height, e);
31077     },
31078
31079     // private
31080     updateChildSize : function(){
31081         
31082         if(this.resizeChild){
31083             var el = this.el;
31084             var child = this.resizeChild;
31085             var adj = this.adjustments;
31086             if(el.dom.offsetWidth){
31087                 var b = el.getSize(true);
31088                 child.setSize(b.width+adj[0], b.height+adj[1]);
31089             }
31090             // Second call here for IE
31091             // The first call enables instant resizing and
31092             // the second call corrects scroll bars if they
31093             // exist
31094             if(Roo.isIE){
31095                 setTimeout(function(){
31096                     if(el.dom.offsetWidth){
31097                         var b = el.getSize(true);
31098                         child.setSize(b.width+adj[0], b.height+adj[1]);
31099                     }
31100                 }, 10);
31101             }
31102         }
31103     },
31104
31105     // private
31106     snap : function(value, inc, min){
31107         if(!inc || !value) {
31108             return value;
31109         }
31110         var newValue = value;
31111         var m = value % inc;
31112         if(m > 0){
31113             if(m > (inc/2)){
31114                 newValue = value + (inc-m);
31115             }else{
31116                 newValue = value - m;
31117             }
31118         }
31119         return Math.max(min, newValue);
31120     },
31121
31122     // private
31123     resizeElement : function(){
31124         var box = this.proxy.getBox();
31125         if(this.updateBox){
31126             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31127         }else{
31128             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31129         }
31130         this.updateChildSize();
31131         if(!this.dynamic){
31132             this.proxy.hide();
31133         }
31134         return box;
31135     },
31136
31137     // private
31138     constrain : function(v, diff, m, mx){
31139         if(v - diff < m){
31140             diff = v - m;
31141         }else if(v - diff > mx){
31142             diff = mx - v;
31143         }
31144         return diff;
31145     },
31146
31147     // private
31148     onMouseMove : function(e){
31149         
31150         if(this.enabled){
31151             try{// try catch so if something goes wrong the user doesn't get hung
31152
31153             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31154                 return;
31155             }
31156
31157             //var curXY = this.startPoint;
31158             var curSize = this.curSize || this.startBox;
31159             var x = this.startBox.x, y = this.startBox.y;
31160             var ox = x, oy = y;
31161             var w = curSize.width, h = curSize.height;
31162             var ow = w, oh = h;
31163             var mw = this.minWidth, mh = this.minHeight;
31164             var mxw = this.maxWidth, mxh = this.maxHeight;
31165             var wi = this.widthIncrement;
31166             var hi = this.heightIncrement;
31167
31168             var eventXY = e.getXY();
31169             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31170             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31171
31172             var pos = this.activeHandle.position;
31173
31174             switch(pos){
31175                 case "east":
31176                     w += diffX;
31177                     w = Math.min(Math.max(mw, w), mxw);
31178                     break;
31179              
31180                 case "south":
31181                     h += diffY;
31182                     h = Math.min(Math.max(mh, h), mxh);
31183                     break;
31184                 case "southeast":
31185                     w += diffX;
31186                     h += diffY;
31187                     w = Math.min(Math.max(mw, w), mxw);
31188                     h = Math.min(Math.max(mh, h), mxh);
31189                     break;
31190                 case "north":
31191                     diffY = this.constrain(h, diffY, mh, mxh);
31192                     y += diffY;
31193                     h -= diffY;
31194                     break;
31195                 case "hdrag":
31196                     
31197                     if (wi) {
31198                         var adiffX = Math.abs(diffX);
31199                         var sub = (adiffX % wi); // how much 
31200                         if (sub > (wi/2)) { // far enough to snap
31201                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31202                         } else {
31203                             // remove difference.. 
31204                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31205                         }
31206                     }
31207                     x += diffX;
31208                     x = Math.max(this.minX, x);
31209                     break;
31210                 case "west":
31211                     diffX = this.constrain(w, diffX, mw, mxw);
31212                     x += diffX;
31213                     w -= diffX;
31214                     break;
31215                 case "northeast":
31216                     w += diffX;
31217                     w = Math.min(Math.max(mw, w), mxw);
31218                     diffY = this.constrain(h, diffY, mh, mxh);
31219                     y += diffY;
31220                     h -= diffY;
31221                     break;
31222                 case "northwest":
31223                     diffX = this.constrain(w, diffX, mw, mxw);
31224                     diffY = this.constrain(h, diffY, mh, mxh);
31225                     y += diffY;
31226                     h -= diffY;
31227                     x += diffX;
31228                     w -= diffX;
31229                     break;
31230                case "southwest":
31231                     diffX = this.constrain(w, diffX, mw, mxw);
31232                     h += diffY;
31233                     h = Math.min(Math.max(mh, h), mxh);
31234                     x += diffX;
31235                     w -= diffX;
31236                     break;
31237             }
31238
31239             var sw = this.snap(w, wi, mw);
31240             var sh = this.snap(h, hi, mh);
31241             if(sw != w || sh != h){
31242                 switch(pos){
31243                     case "northeast":
31244                         y -= sh - h;
31245                     break;
31246                     case "north":
31247                         y -= sh - h;
31248                         break;
31249                     case "southwest":
31250                         x -= sw - w;
31251                     break;
31252                     case "west":
31253                         x -= sw - w;
31254                         break;
31255                     case "northwest":
31256                         x -= sw - w;
31257                         y -= sh - h;
31258                     break;
31259                 }
31260                 w = sw;
31261                 h = sh;
31262             }
31263
31264             if(this.preserveRatio){
31265                 switch(pos){
31266                     case "southeast":
31267                     case "east":
31268                         h = oh * (w/ow);
31269                         h = Math.min(Math.max(mh, h), mxh);
31270                         w = ow * (h/oh);
31271                        break;
31272                     case "south":
31273                         w = ow * (h/oh);
31274                         w = Math.min(Math.max(mw, w), mxw);
31275                         h = oh * (w/ow);
31276                         break;
31277                     case "northeast":
31278                         w = ow * (h/oh);
31279                         w = Math.min(Math.max(mw, w), mxw);
31280                         h = oh * (w/ow);
31281                     break;
31282                     case "north":
31283                         var tw = w;
31284                         w = ow * (h/oh);
31285                         w = Math.min(Math.max(mw, w), mxw);
31286                         h = oh * (w/ow);
31287                         x += (tw - w) / 2;
31288                         break;
31289                     case "southwest":
31290                         h = oh * (w/ow);
31291                         h = Math.min(Math.max(mh, h), mxh);
31292                         var tw = w;
31293                         w = ow * (h/oh);
31294                         x += tw - w;
31295                         break;
31296                     case "west":
31297                         var th = h;
31298                         h = oh * (w/ow);
31299                         h = Math.min(Math.max(mh, h), mxh);
31300                         y += (th - h) / 2;
31301                         var tw = w;
31302                         w = ow * (h/oh);
31303                         x += tw - w;
31304                        break;
31305                     case "northwest":
31306                         var tw = w;
31307                         var th = h;
31308                         h = oh * (w/ow);
31309                         h = Math.min(Math.max(mh, h), mxh);
31310                         w = ow * (h/oh);
31311                         y += th - h;
31312                         x += tw - w;
31313                        break;
31314
31315                 }
31316             }
31317             if (pos == 'hdrag') {
31318                 w = ow;
31319             }
31320             this.proxy.setBounds(x, y, w, h);
31321             if(this.dynamic){
31322                 this.resizeElement();
31323             }
31324             }catch(e){}
31325         }
31326         this.fireEvent("resizing", this, x, y, w, h, e);
31327     },
31328
31329     // private
31330     handleOver : function(){
31331         if(this.enabled){
31332             this.el.addClass("x-resizable-over");
31333         }
31334     },
31335
31336     // private
31337     handleOut : function(){
31338         if(!this.resizing){
31339             this.el.removeClass("x-resizable-over");
31340         }
31341     },
31342
31343     /**
31344      * Returns the element this component is bound to.
31345      * @return {Roo.Element}
31346      */
31347     getEl : function(){
31348         return this.el;
31349     },
31350
31351     /**
31352      * Returns the resizeChild element (or null).
31353      * @return {Roo.Element}
31354      */
31355     getResizeChild : function(){
31356         return this.resizeChild;
31357     },
31358     groupHandler : function()
31359     {
31360         
31361     },
31362     /**
31363      * Destroys this resizable. If the element was wrapped and
31364      * removeEl is not true then the element remains.
31365      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31366      */
31367     destroy : function(removeEl){
31368         this.proxy.remove();
31369         if(this.overlay){
31370             this.overlay.removeAllListeners();
31371             this.overlay.remove();
31372         }
31373         var ps = Roo.Resizable.positions;
31374         for(var k in ps){
31375             if(typeof ps[k] != "function" && this[ps[k]]){
31376                 var h = this[ps[k]];
31377                 h.el.removeAllListeners();
31378                 h.el.remove();
31379             }
31380         }
31381         if(removeEl){
31382             this.el.update("");
31383             this.el.remove();
31384         }
31385     }
31386 });
31387
31388 // private
31389 // hash to map config positions to true positions
31390 Roo.Resizable.positions = {
31391     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31392     hd: "hdrag"
31393 };
31394
31395 // private
31396 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31397     if(!this.tpl){
31398         // only initialize the template if resizable is used
31399         var tpl = Roo.DomHelper.createTemplate(
31400             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31401         );
31402         tpl.compile();
31403         Roo.Resizable.Handle.prototype.tpl = tpl;
31404     }
31405     this.position = pos;
31406     this.rz = rz;
31407     // show north drag fro topdra
31408     var handlepos = pos == 'hdrag' ? 'north' : pos;
31409     
31410     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31411     if (pos == 'hdrag') {
31412         this.el.setStyle('cursor', 'pointer');
31413     }
31414     this.el.unselectable();
31415     if(transparent){
31416         this.el.setOpacity(0);
31417     }
31418     this.el.on("mousedown", this.onMouseDown, this);
31419     if(!disableTrackOver){
31420         this.el.on("mouseover", this.onMouseOver, this);
31421         this.el.on("mouseout", this.onMouseOut, this);
31422     }
31423 };
31424
31425 // private
31426 Roo.Resizable.Handle.prototype = {
31427     afterResize : function(rz){
31428         Roo.log('after?');
31429         // do nothing
31430     },
31431     // private
31432     onMouseDown : function(e){
31433         this.rz.onMouseDown(this, e);
31434     },
31435     // private
31436     onMouseOver : function(e){
31437         this.rz.handleOver(this, e);
31438     },
31439     // private
31440     onMouseOut : function(e){
31441         this.rz.handleOut(this, e);
31442     }
31443 };/*
31444  * Based on:
31445  * Ext JS Library 1.1.1
31446  * Copyright(c) 2006-2007, Ext JS, LLC.
31447  *
31448  * Originally Released Under LGPL - original licence link has changed is not relivant.
31449  *
31450  * Fork - LGPL
31451  * <script type="text/javascript">
31452  */
31453
31454 /**
31455  * @class Roo.Editor
31456  * @extends Roo.Component
31457  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31458  * @constructor
31459  * Create a new Editor
31460  * @param {Roo.form.Field} field The Field object (or descendant)
31461  * @param {Object} config The config object
31462  */
31463 Roo.Editor = function(field, config){
31464     Roo.Editor.superclass.constructor.call(this, config);
31465     this.field = field;
31466     this.addEvents({
31467         /**
31468              * @event beforestartedit
31469              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31470              * false from the handler of this event.
31471              * @param {Editor} this
31472              * @param {Roo.Element} boundEl The underlying element bound to this editor
31473              * @param {Mixed} value The field value being set
31474              */
31475         "beforestartedit" : true,
31476         /**
31477              * @event startedit
31478              * Fires when this editor is displayed
31479              * @param {Roo.Element} boundEl The underlying element bound to this editor
31480              * @param {Mixed} value The starting field value
31481              */
31482         "startedit" : true,
31483         /**
31484              * @event beforecomplete
31485              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31486              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31487              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31488              * event will not fire since no edit actually occurred.
31489              * @param {Editor} this
31490              * @param {Mixed} value The current field value
31491              * @param {Mixed} startValue The original field value
31492              */
31493         "beforecomplete" : true,
31494         /**
31495              * @event complete
31496              * Fires after editing is complete and any changed value has been written to the underlying field.
31497              * @param {Editor} this
31498              * @param {Mixed} value The current field value
31499              * @param {Mixed} startValue The original field value
31500              */
31501         "complete" : true,
31502         /**
31503          * @event specialkey
31504          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31505          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31506          * @param {Roo.form.Field} this
31507          * @param {Roo.EventObject} e The event object
31508          */
31509         "specialkey" : true
31510     });
31511 };
31512
31513 Roo.extend(Roo.Editor, Roo.Component, {
31514     /**
31515      * @cfg {Boolean/String} autosize
31516      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31517      * or "height" to adopt the height only (defaults to false)
31518      */
31519     /**
31520      * @cfg {Boolean} revertInvalid
31521      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31522      * validation fails (defaults to true)
31523      */
31524     /**
31525      * @cfg {Boolean} ignoreNoChange
31526      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31527      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31528      * will never be ignored.
31529      */
31530     /**
31531      * @cfg {Boolean} hideEl
31532      * False to keep the bound element visible while the editor is displayed (defaults to true)
31533      */
31534     /**
31535      * @cfg {Mixed} value
31536      * The data value of the underlying field (defaults to "")
31537      */
31538     value : "",
31539     /**
31540      * @cfg {String} alignment
31541      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31542      */
31543     alignment: "c-c?",
31544     /**
31545      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31546      * for bottom-right shadow (defaults to "frame")
31547      */
31548     shadow : "frame",
31549     /**
31550      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31551      */
31552     constrain : false,
31553     /**
31554      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31555      */
31556     completeOnEnter : false,
31557     /**
31558      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31559      */
31560     cancelOnEsc : false,
31561     /**
31562      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31563      */
31564     updateEl : false,
31565
31566     // private
31567     onRender : function(ct, position){
31568         this.el = new Roo.Layer({
31569             shadow: this.shadow,
31570             cls: "x-editor",
31571             parentEl : ct,
31572             shim : this.shim,
31573             shadowOffset:4,
31574             id: this.id,
31575             constrain: this.constrain
31576         });
31577         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31578         if(this.field.msgTarget != 'title'){
31579             this.field.msgTarget = 'qtip';
31580         }
31581         this.field.render(this.el);
31582         if(Roo.isGecko){
31583             this.field.el.dom.setAttribute('autocomplete', 'off');
31584         }
31585         this.field.on("specialkey", this.onSpecialKey, this);
31586         if(this.swallowKeys){
31587             this.field.el.swallowEvent(['keydown','keypress']);
31588         }
31589         this.field.show();
31590         this.field.on("blur", this.onBlur, this);
31591         if(this.field.grow){
31592             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31593         }
31594     },
31595
31596     onSpecialKey : function(field, e)
31597     {
31598         //Roo.log('editor onSpecialKey');
31599         if(this.completeOnEnter && e.getKey() == e.ENTER){
31600             e.stopEvent();
31601             this.completeEdit();
31602             return;
31603         }
31604         // do not fire special key otherwise it might hide close the editor...
31605         if(e.getKey() == e.ENTER){    
31606             return;
31607         }
31608         if(this.cancelOnEsc && e.getKey() == e.ESC){
31609             this.cancelEdit();
31610             return;
31611         } 
31612         this.fireEvent('specialkey', field, e);
31613     
31614     },
31615
31616     /**
31617      * Starts the editing process and shows the editor.
31618      * @param {String/HTMLElement/Element} el The element to edit
31619      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31620       * to the innerHTML of el.
31621      */
31622     startEdit : function(el, value){
31623         if(this.editing){
31624             this.completeEdit();
31625         }
31626         this.boundEl = Roo.get(el);
31627         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31628         if(!this.rendered){
31629             this.render(this.parentEl || document.body);
31630         }
31631         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31632             return;
31633         }
31634         this.startValue = v;
31635         this.field.setValue(v);
31636         if(this.autoSize){
31637             var sz = this.boundEl.getSize();
31638             switch(this.autoSize){
31639                 case "width":
31640                 this.setSize(sz.width,  "");
31641                 break;
31642                 case "height":
31643                 this.setSize("",  sz.height);
31644                 break;
31645                 default:
31646                 this.setSize(sz.width,  sz.height);
31647             }
31648         }
31649         this.el.alignTo(this.boundEl, this.alignment);
31650         this.editing = true;
31651         if(Roo.QuickTips){
31652             Roo.QuickTips.disable();
31653         }
31654         this.show();
31655     },
31656
31657     /**
31658      * Sets the height and width of this editor.
31659      * @param {Number} width The new width
31660      * @param {Number} height The new height
31661      */
31662     setSize : function(w, h){
31663         this.field.setSize(w, h);
31664         if(this.el){
31665             this.el.sync();
31666         }
31667     },
31668
31669     /**
31670      * Realigns the editor to the bound field based on the current alignment config value.
31671      */
31672     realign : function(){
31673         this.el.alignTo(this.boundEl, this.alignment);
31674     },
31675
31676     /**
31677      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31678      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31679      */
31680     completeEdit : function(remainVisible){
31681         if(!this.editing){
31682             return;
31683         }
31684         var v = this.getValue();
31685         if(this.revertInvalid !== false && !this.field.isValid()){
31686             v = this.startValue;
31687             this.cancelEdit(true);
31688         }
31689         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31690             this.editing = false;
31691             this.hide();
31692             return;
31693         }
31694         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31695             this.editing = false;
31696             if(this.updateEl && this.boundEl){
31697                 this.boundEl.update(v);
31698             }
31699             if(remainVisible !== true){
31700                 this.hide();
31701             }
31702             this.fireEvent("complete", this, v, this.startValue);
31703         }
31704     },
31705
31706     // private
31707     onShow : function(){
31708         this.el.show();
31709         if(this.hideEl !== false){
31710             this.boundEl.hide();
31711         }
31712         this.field.show();
31713         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31714             this.fixIEFocus = true;
31715             this.deferredFocus.defer(50, this);
31716         }else{
31717             this.field.focus();
31718         }
31719         this.fireEvent("startedit", this.boundEl, this.startValue);
31720     },
31721
31722     deferredFocus : function(){
31723         if(this.editing){
31724             this.field.focus();
31725         }
31726     },
31727
31728     /**
31729      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31730      * reverted to the original starting value.
31731      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31732      * cancel (defaults to false)
31733      */
31734     cancelEdit : function(remainVisible){
31735         if(this.editing){
31736             this.setValue(this.startValue);
31737             if(remainVisible !== true){
31738                 this.hide();
31739             }
31740         }
31741     },
31742
31743     // private
31744     onBlur : function(){
31745         if(this.allowBlur !== true && this.editing){
31746             this.completeEdit();
31747         }
31748     },
31749
31750     // private
31751     onHide : function(){
31752         if(this.editing){
31753             this.completeEdit();
31754             return;
31755         }
31756         this.field.blur();
31757         if(this.field.collapse){
31758             this.field.collapse();
31759         }
31760         this.el.hide();
31761         if(this.hideEl !== false){
31762             this.boundEl.show();
31763         }
31764         if(Roo.QuickTips){
31765             Roo.QuickTips.enable();
31766         }
31767     },
31768
31769     /**
31770      * Sets the data value of the editor
31771      * @param {Mixed} value Any valid value supported by the underlying field
31772      */
31773     setValue : function(v){
31774         this.field.setValue(v);
31775     },
31776
31777     /**
31778      * Gets the data value of the editor
31779      * @return {Mixed} The data value
31780      */
31781     getValue : function(){
31782         return this.field.getValue();
31783     }
31784 });/*
31785  * Based on:
31786  * Ext JS Library 1.1.1
31787  * Copyright(c) 2006-2007, Ext JS, LLC.
31788  *
31789  * Originally Released Under LGPL - original licence link has changed is not relivant.
31790  *
31791  * Fork - LGPL
31792  * <script type="text/javascript">
31793  */
31794  
31795 /**
31796  * @class Roo.BasicDialog
31797  * @extends Roo.util.Observable
31798  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31799  * <pre><code>
31800 var dlg = new Roo.BasicDialog("my-dlg", {
31801     height: 200,
31802     width: 300,
31803     minHeight: 100,
31804     minWidth: 150,
31805     modal: true,
31806     proxyDrag: true,
31807     shadow: true
31808 });
31809 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31810 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31811 dlg.addButton('Cancel', dlg.hide, dlg);
31812 dlg.show();
31813 </code></pre>
31814   <b>A Dialog should always be a direct child of the body element.</b>
31815  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31816  * @cfg {String} title Default text to display in the title bar (defaults to null)
31817  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31818  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31819  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31820  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31821  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31822  * (defaults to null with no animation)
31823  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31824  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31825  * property for valid values (defaults to 'all')
31826  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31827  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31828  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31829  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31830  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31831  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31832  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31833  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31834  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31835  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31836  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31837  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31838  * draggable = true (defaults to false)
31839  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31840  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31841  * shadow (defaults to false)
31842  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31843  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31844  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31845  * @cfg {Array} buttons Array of buttons
31846  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31847  * @constructor
31848  * Create a new BasicDialog.
31849  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31850  * @param {Object} config Configuration options
31851  */
31852 Roo.BasicDialog = function(el, config){
31853     this.el = Roo.get(el);
31854     var dh = Roo.DomHelper;
31855     if(!this.el && config && config.autoCreate){
31856         if(typeof config.autoCreate == "object"){
31857             if(!config.autoCreate.id){
31858                 config.autoCreate.id = el;
31859             }
31860             this.el = dh.append(document.body,
31861                         config.autoCreate, true);
31862         }else{
31863             this.el = dh.append(document.body,
31864                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31865         }
31866     }
31867     el = this.el;
31868     el.setDisplayed(true);
31869     el.hide = this.hideAction;
31870     this.id = el.id;
31871     el.addClass("x-dlg");
31872
31873     Roo.apply(this, config);
31874
31875     this.proxy = el.createProxy("x-dlg-proxy");
31876     this.proxy.hide = this.hideAction;
31877     this.proxy.setOpacity(.5);
31878     this.proxy.hide();
31879
31880     if(config.width){
31881         el.setWidth(config.width);
31882     }
31883     if(config.height){
31884         el.setHeight(config.height);
31885     }
31886     this.size = el.getSize();
31887     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31888         this.xy = [config.x,config.y];
31889     }else{
31890         this.xy = el.getCenterXY(true);
31891     }
31892     /** The header element @type Roo.Element */
31893     this.header = el.child("> .x-dlg-hd");
31894     /** The body element @type Roo.Element */
31895     this.body = el.child("> .x-dlg-bd");
31896     /** The footer element @type Roo.Element */
31897     this.footer = el.child("> .x-dlg-ft");
31898
31899     if(!this.header){
31900         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31901     }
31902     if(!this.body){
31903         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31904     }
31905
31906     this.header.unselectable();
31907     if(this.title){
31908         this.header.update(this.title);
31909     }
31910     // this element allows the dialog to be focused for keyboard event
31911     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
31912     this.focusEl.swallowEvent("click", true);
31913
31914     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
31915
31916     // wrap the body and footer for special rendering
31917     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
31918     if(this.footer){
31919         this.bwrap.dom.appendChild(this.footer.dom);
31920     }
31921
31922     this.bg = this.el.createChild({
31923         tag: "div", cls:"x-dlg-bg",
31924         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
31925     });
31926     this.centerBg = this.bg.child("div.x-dlg-bg-center");
31927
31928
31929     if(this.autoScroll !== false && !this.autoTabs){
31930         this.body.setStyle("overflow", "auto");
31931     }
31932
31933     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
31934
31935     if(this.closable !== false){
31936         this.el.addClass("x-dlg-closable");
31937         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
31938         this.close.on("click", this.closeClick, this);
31939         this.close.addClassOnOver("x-dlg-close-over");
31940     }
31941     if(this.collapsible !== false){
31942         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
31943         this.collapseBtn.on("click", this.collapseClick, this);
31944         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
31945         this.header.on("dblclick", this.collapseClick, this);
31946     }
31947     if(this.resizable !== false){
31948         this.el.addClass("x-dlg-resizable");
31949         this.resizer = new Roo.Resizable(el, {
31950             minWidth: this.minWidth || 80,
31951             minHeight:this.minHeight || 80,
31952             handles: this.resizeHandles || "all",
31953             pinned: true
31954         });
31955         this.resizer.on("beforeresize", this.beforeResize, this);
31956         this.resizer.on("resize", this.onResize, this);
31957     }
31958     if(this.draggable !== false){
31959         el.addClass("x-dlg-draggable");
31960         if (!this.proxyDrag) {
31961             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
31962         }
31963         else {
31964             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
31965         }
31966         dd.setHandleElId(this.header.id);
31967         dd.endDrag = this.endMove.createDelegate(this);
31968         dd.startDrag = this.startMove.createDelegate(this);
31969         dd.onDrag = this.onDrag.createDelegate(this);
31970         dd.scroll = false;
31971         this.dd = dd;
31972     }
31973     if(this.modal){
31974         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
31975         this.mask.enableDisplayMode("block");
31976         this.mask.hide();
31977         this.el.addClass("x-dlg-modal");
31978     }
31979     if(this.shadow){
31980         this.shadow = new Roo.Shadow({
31981             mode : typeof this.shadow == "string" ? this.shadow : "sides",
31982             offset : this.shadowOffset
31983         });
31984     }else{
31985         this.shadowOffset = 0;
31986     }
31987     if(Roo.useShims && this.shim !== false){
31988         this.shim = this.el.createShim();
31989         this.shim.hide = this.hideAction;
31990         this.shim.hide();
31991     }else{
31992         this.shim = false;
31993     }
31994     if(this.autoTabs){
31995         this.initTabs();
31996     }
31997     if (this.buttons) { 
31998         var bts= this.buttons;
31999         this.buttons = [];
32000         Roo.each(bts, function(b) {
32001             this.addButton(b);
32002         }, this);
32003     }
32004     
32005     
32006     this.addEvents({
32007         /**
32008          * @event keydown
32009          * Fires when a key is pressed
32010          * @param {Roo.BasicDialog} this
32011          * @param {Roo.EventObject} e
32012          */
32013         "keydown" : true,
32014         /**
32015          * @event move
32016          * Fires when this dialog is moved by the user.
32017          * @param {Roo.BasicDialog} this
32018          * @param {Number} x The new page X
32019          * @param {Number} y The new page Y
32020          */
32021         "move" : true,
32022         /**
32023          * @event resize
32024          * Fires when this dialog is resized by the user.
32025          * @param {Roo.BasicDialog} this
32026          * @param {Number} width The new width
32027          * @param {Number} height The new height
32028          */
32029         "resize" : true,
32030         /**
32031          * @event beforehide
32032          * Fires before this dialog is hidden.
32033          * @param {Roo.BasicDialog} this
32034          */
32035         "beforehide" : true,
32036         /**
32037          * @event hide
32038          * Fires when this dialog is hidden.
32039          * @param {Roo.BasicDialog} this
32040          */
32041         "hide" : true,
32042         /**
32043          * @event beforeshow
32044          * Fires before this dialog is shown.
32045          * @param {Roo.BasicDialog} this
32046          */
32047         "beforeshow" : true,
32048         /**
32049          * @event show
32050          * Fires when this dialog is shown.
32051          * @param {Roo.BasicDialog} this
32052          */
32053         "show" : true
32054     });
32055     el.on("keydown", this.onKeyDown, this);
32056     el.on("mousedown", this.toFront, this);
32057     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32058     this.el.hide();
32059     Roo.DialogManager.register(this);
32060     Roo.BasicDialog.superclass.constructor.call(this);
32061 };
32062
32063 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32064     shadowOffset: Roo.isIE ? 6 : 5,
32065     minHeight: 80,
32066     minWidth: 200,
32067     minButtonWidth: 75,
32068     defaultButton: null,
32069     buttonAlign: "right",
32070     tabTag: 'div',
32071     firstShow: true,
32072
32073     /**
32074      * Sets the dialog title text
32075      * @param {String} text The title text to display
32076      * @return {Roo.BasicDialog} this
32077      */
32078     setTitle : function(text){
32079         this.header.update(text);
32080         return this;
32081     },
32082
32083     // private
32084     closeClick : function(){
32085         this.hide();
32086     },
32087
32088     // private
32089     collapseClick : function(){
32090         this[this.collapsed ? "expand" : "collapse"]();
32091     },
32092
32093     /**
32094      * Collapses the dialog to its minimized state (only the title bar is visible).
32095      * Equivalent to the user clicking the collapse dialog button.
32096      */
32097     collapse : function(){
32098         if(!this.collapsed){
32099             this.collapsed = true;
32100             this.el.addClass("x-dlg-collapsed");
32101             this.restoreHeight = this.el.getHeight();
32102             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32103         }
32104     },
32105
32106     /**
32107      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32108      * clicking the expand dialog button.
32109      */
32110     expand : function(){
32111         if(this.collapsed){
32112             this.collapsed = false;
32113             this.el.removeClass("x-dlg-collapsed");
32114             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32115         }
32116     },
32117
32118     /**
32119      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32120      * @return {Roo.TabPanel} The tabs component
32121      */
32122     initTabs : function(){
32123         var tabs = this.getTabs();
32124         while(tabs.getTab(0)){
32125             tabs.removeTab(0);
32126         }
32127         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32128             var dom = el.dom;
32129             tabs.addTab(Roo.id(dom), dom.title);
32130             dom.title = "";
32131         });
32132         tabs.activate(0);
32133         return tabs;
32134     },
32135
32136     // private
32137     beforeResize : function(){
32138         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32139     },
32140
32141     // private
32142     onResize : function(){
32143         this.refreshSize();
32144         this.syncBodyHeight();
32145         this.adjustAssets();
32146         this.focus();
32147         this.fireEvent("resize", this, this.size.width, this.size.height);
32148     },
32149
32150     // private
32151     onKeyDown : function(e){
32152         if(this.isVisible()){
32153             this.fireEvent("keydown", this, e);
32154         }
32155     },
32156
32157     /**
32158      * Resizes the dialog.
32159      * @param {Number} width
32160      * @param {Number} height
32161      * @return {Roo.BasicDialog} this
32162      */
32163     resizeTo : function(width, height){
32164         this.el.setSize(width, height);
32165         this.size = {width: width, height: height};
32166         this.syncBodyHeight();
32167         if(this.fixedcenter){
32168             this.center();
32169         }
32170         if(this.isVisible()){
32171             this.constrainXY();
32172             this.adjustAssets();
32173         }
32174         this.fireEvent("resize", this, width, height);
32175         return this;
32176     },
32177
32178
32179     /**
32180      * Resizes the dialog to fit the specified content size.
32181      * @param {Number} width
32182      * @param {Number} height
32183      * @return {Roo.BasicDialog} this
32184      */
32185     setContentSize : function(w, h){
32186         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32187         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32188         //if(!this.el.isBorderBox()){
32189             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32190             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32191         //}
32192         if(this.tabs){
32193             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32194             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32195         }
32196         this.resizeTo(w, h);
32197         return this;
32198     },
32199
32200     /**
32201      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32202      * executed in response to a particular key being pressed while the dialog is active.
32203      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32204      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32205      * @param {Function} fn The function to call
32206      * @param {Object} scope (optional) The scope of the function
32207      * @return {Roo.BasicDialog} this
32208      */
32209     addKeyListener : function(key, fn, scope){
32210         var keyCode, shift, ctrl, alt;
32211         if(typeof key == "object" && !(key instanceof Array)){
32212             keyCode = key["key"];
32213             shift = key["shift"];
32214             ctrl = key["ctrl"];
32215             alt = key["alt"];
32216         }else{
32217             keyCode = key;
32218         }
32219         var handler = function(dlg, e){
32220             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32221                 var k = e.getKey();
32222                 if(keyCode instanceof Array){
32223                     for(var i = 0, len = keyCode.length; i < len; i++){
32224                         if(keyCode[i] == k){
32225                           fn.call(scope || window, dlg, k, e);
32226                           return;
32227                         }
32228                     }
32229                 }else{
32230                     if(k == keyCode){
32231                         fn.call(scope || window, dlg, k, e);
32232                     }
32233                 }
32234             }
32235         };
32236         this.on("keydown", handler);
32237         return this;
32238     },
32239
32240     /**
32241      * Returns the TabPanel component (creates it if it doesn't exist).
32242      * Note: If you wish to simply check for the existence of tabs without creating them,
32243      * check for a null 'tabs' property.
32244      * @return {Roo.TabPanel} The tabs component
32245      */
32246     getTabs : function(){
32247         if(!this.tabs){
32248             this.el.addClass("x-dlg-auto-tabs");
32249             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32250             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32251         }
32252         return this.tabs;
32253     },
32254
32255     /**
32256      * Adds a button to the footer section of the dialog.
32257      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32258      * object or a valid Roo.DomHelper element config
32259      * @param {Function} handler The function called when the button is clicked
32260      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32261      * @return {Roo.Button} The new button
32262      */
32263     addButton : function(config, handler, scope){
32264         var dh = Roo.DomHelper;
32265         if(!this.footer){
32266             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32267         }
32268         if(!this.btnContainer){
32269             var tb = this.footer.createChild({
32270
32271                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32272                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32273             }, null, true);
32274             this.btnContainer = tb.firstChild.firstChild.firstChild;
32275         }
32276         var bconfig = {
32277             handler: handler,
32278             scope: scope,
32279             minWidth: this.minButtonWidth,
32280             hideParent:true
32281         };
32282         if(typeof config == "string"){
32283             bconfig.text = config;
32284         }else{
32285             if(config.tag){
32286                 bconfig.dhconfig = config;
32287             }else{
32288                 Roo.apply(bconfig, config);
32289             }
32290         }
32291         var fc = false;
32292         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32293             bconfig.position = Math.max(0, bconfig.position);
32294             fc = this.btnContainer.childNodes[bconfig.position];
32295         }
32296          
32297         var btn = new Roo.Button(
32298             fc ? 
32299                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32300                 : this.btnContainer.appendChild(document.createElement("td")),
32301             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32302             bconfig
32303         );
32304         this.syncBodyHeight();
32305         if(!this.buttons){
32306             /**
32307              * Array of all the buttons that have been added to this dialog via addButton
32308              * @type Array
32309              */
32310             this.buttons = [];
32311         }
32312         this.buttons.push(btn);
32313         return btn;
32314     },
32315
32316     /**
32317      * Sets the default button to be focused when the dialog is displayed.
32318      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32319      * @return {Roo.BasicDialog} this
32320      */
32321     setDefaultButton : function(btn){
32322         this.defaultButton = btn;
32323         return this;
32324     },
32325
32326     // private
32327     getHeaderFooterHeight : function(safe){
32328         var height = 0;
32329         if(this.header){
32330            height += this.header.getHeight();
32331         }
32332         if(this.footer){
32333            var fm = this.footer.getMargins();
32334             height += (this.footer.getHeight()+fm.top+fm.bottom);
32335         }
32336         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32337         height += this.centerBg.getPadding("tb");
32338         return height;
32339     },
32340
32341     // private
32342     syncBodyHeight : function()
32343     {
32344         var bd = this.body, // the text
32345             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32346             bw = this.bwrap;
32347         var height = this.size.height - this.getHeaderFooterHeight(false);
32348         bd.setHeight(height-bd.getMargins("tb"));
32349         var hh = this.header.getHeight();
32350         var h = this.size.height-hh;
32351         cb.setHeight(h);
32352         
32353         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32354         bw.setHeight(h-cb.getPadding("tb"));
32355         
32356         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32357         bd.setWidth(bw.getWidth(true));
32358         if(this.tabs){
32359             this.tabs.syncHeight();
32360             if(Roo.isIE){
32361                 this.tabs.el.repaint();
32362             }
32363         }
32364     },
32365
32366     /**
32367      * Restores the previous state of the dialog if Roo.state is configured.
32368      * @return {Roo.BasicDialog} this
32369      */
32370     restoreState : function(){
32371         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32372         if(box && box.width){
32373             this.xy = [box.x, box.y];
32374             this.resizeTo(box.width, box.height);
32375         }
32376         return this;
32377     },
32378
32379     // private
32380     beforeShow : function(){
32381         this.expand();
32382         if(this.fixedcenter){
32383             this.xy = this.el.getCenterXY(true);
32384         }
32385         if(this.modal){
32386             Roo.get(document.body).addClass("x-body-masked");
32387             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32388             this.mask.show();
32389         }
32390         this.constrainXY();
32391     },
32392
32393     // private
32394     animShow : function(){
32395         var b = Roo.get(this.animateTarget).getBox();
32396         this.proxy.setSize(b.width, b.height);
32397         this.proxy.setLocation(b.x, b.y);
32398         this.proxy.show();
32399         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32400                     true, .35, this.showEl.createDelegate(this));
32401     },
32402
32403     /**
32404      * Shows the dialog.
32405      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32406      * @return {Roo.BasicDialog} this
32407      */
32408     show : function(animateTarget){
32409         if (this.fireEvent("beforeshow", this) === false){
32410             return;
32411         }
32412         if(this.syncHeightBeforeShow){
32413             this.syncBodyHeight();
32414         }else if(this.firstShow){
32415             this.firstShow = false;
32416             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32417         }
32418         this.animateTarget = animateTarget || this.animateTarget;
32419         if(!this.el.isVisible()){
32420             this.beforeShow();
32421             if(this.animateTarget && Roo.get(this.animateTarget)){
32422                 this.animShow();
32423             }else{
32424                 this.showEl();
32425             }
32426         }
32427         return this;
32428     },
32429
32430     // private
32431     showEl : function(){
32432         this.proxy.hide();
32433         this.el.setXY(this.xy);
32434         this.el.show();
32435         this.adjustAssets(true);
32436         this.toFront();
32437         this.focus();
32438         // IE peekaboo bug - fix found by Dave Fenwick
32439         if(Roo.isIE){
32440             this.el.repaint();
32441         }
32442         this.fireEvent("show", this);
32443     },
32444
32445     /**
32446      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32447      * dialog itself will receive focus.
32448      */
32449     focus : function(){
32450         if(this.defaultButton){
32451             this.defaultButton.focus();
32452         }else{
32453             this.focusEl.focus();
32454         }
32455     },
32456
32457     // private
32458     constrainXY : function(){
32459         if(this.constraintoviewport !== false){
32460             if(!this.viewSize){
32461                 if(this.container){
32462                     var s = this.container.getSize();
32463                     this.viewSize = [s.width, s.height];
32464                 }else{
32465                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32466                 }
32467             }
32468             var s = Roo.get(this.container||document).getScroll();
32469
32470             var x = this.xy[0], y = this.xy[1];
32471             var w = this.size.width, h = this.size.height;
32472             var vw = this.viewSize[0], vh = this.viewSize[1];
32473             // only move it if it needs it
32474             var moved = false;
32475             // first validate right/bottom
32476             if(x + w > vw+s.left){
32477                 x = vw - w;
32478                 moved = true;
32479             }
32480             if(y + h > vh+s.top){
32481                 y = vh - h;
32482                 moved = true;
32483             }
32484             // then make sure top/left isn't negative
32485             if(x < s.left){
32486                 x = s.left;
32487                 moved = true;
32488             }
32489             if(y < s.top){
32490                 y = s.top;
32491                 moved = true;
32492             }
32493             if(moved){
32494                 // cache xy
32495                 this.xy = [x, y];
32496                 if(this.isVisible()){
32497                     this.el.setLocation(x, y);
32498                     this.adjustAssets();
32499                 }
32500             }
32501         }
32502     },
32503
32504     // private
32505     onDrag : function(){
32506         if(!this.proxyDrag){
32507             this.xy = this.el.getXY();
32508             this.adjustAssets();
32509         }
32510     },
32511
32512     // private
32513     adjustAssets : function(doShow){
32514         var x = this.xy[0], y = this.xy[1];
32515         var w = this.size.width, h = this.size.height;
32516         if(doShow === true){
32517             if(this.shadow){
32518                 this.shadow.show(this.el);
32519             }
32520             if(this.shim){
32521                 this.shim.show();
32522             }
32523         }
32524         if(this.shadow && this.shadow.isVisible()){
32525             this.shadow.show(this.el);
32526         }
32527         if(this.shim && this.shim.isVisible()){
32528             this.shim.setBounds(x, y, w, h);
32529         }
32530     },
32531
32532     // private
32533     adjustViewport : function(w, h){
32534         if(!w || !h){
32535             w = Roo.lib.Dom.getViewWidth();
32536             h = Roo.lib.Dom.getViewHeight();
32537         }
32538         // cache the size
32539         this.viewSize = [w, h];
32540         if(this.modal && this.mask.isVisible()){
32541             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32542             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32543         }
32544         if(this.isVisible()){
32545             this.constrainXY();
32546         }
32547     },
32548
32549     /**
32550      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32551      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32552      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32553      */
32554     destroy : function(removeEl){
32555         if(this.isVisible()){
32556             this.animateTarget = null;
32557             this.hide();
32558         }
32559         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32560         if(this.tabs){
32561             this.tabs.destroy(removeEl);
32562         }
32563         Roo.destroy(
32564              this.shim,
32565              this.proxy,
32566              this.resizer,
32567              this.close,
32568              this.mask
32569         );
32570         if(this.dd){
32571             this.dd.unreg();
32572         }
32573         if(this.buttons){
32574            for(var i = 0, len = this.buttons.length; i < len; i++){
32575                this.buttons[i].destroy();
32576            }
32577         }
32578         this.el.removeAllListeners();
32579         if(removeEl === true){
32580             this.el.update("");
32581             this.el.remove();
32582         }
32583         Roo.DialogManager.unregister(this);
32584     },
32585
32586     // private
32587     startMove : function(){
32588         if(this.proxyDrag){
32589             this.proxy.show();
32590         }
32591         if(this.constraintoviewport !== false){
32592             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32593         }
32594     },
32595
32596     // private
32597     endMove : function(){
32598         if(!this.proxyDrag){
32599             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32600         }else{
32601             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32602             this.proxy.hide();
32603         }
32604         this.refreshSize();
32605         this.adjustAssets();
32606         this.focus();
32607         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32608     },
32609
32610     /**
32611      * Brings this dialog to the front of any other visible dialogs
32612      * @return {Roo.BasicDialog} this
32613      */
32614     toFront : function(){
32615         Roo.DialogManager.bringToFront(this);
32616         return this;
32617     },
32618
32619     /**
32620      * Sends this dialog to the back (under) of any other visible dialogs
32621      * @return {Roo.BasicDialog} this
32622      */
32623     toBack : function(){
32624         Roo.DialogManager.sendToBack(this);
32625         return this;
32626     },
32627
32628     /**
32629      * Centers this dialog in the viewport
32630      * @return {Roo.BasicDialog} this
32631      */
32632     center : function(){
32633         var xy = this.el.getCenterXY(true);
32634         this.moveTo(xy[0], xy[1]);
32635         return this;
32636     },
32637
32638     /**
32639      * Moves the dialog's top-left corner to the specified point
32640      * @param {Number} x
32641      * @param {Number} y
32642      * @return {Roo.BasicDialog} this
32643      */
32644     moveTo : function(x, y){
32645         this.xy = [x,y];
32646         if(this.isVisible()){
32647             this.el.setXY(this.xy);
32648             this.adjustAssets();
32649         }
32650         return this;
32651     },
32652
32653     /**
32654      * Aligns the dialog to the specified element
32655      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32656      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32657      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32658      * @return {Roo.BasicDialog} this
32659      */
32660     alignTo : function(element, position, offsets){
32661         this.xy = this.el.getAlignToXY(element, position, offsets);
32662         if(this.isVisible()){
32663             this.el.setXY(this.xy);
32664             this.adjustAssets();
32665         }
32666         return this;
32667     },
32668
32669     /**
32670      * Anchors an element to another element and realigns it when the window is resized.
32671      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32672      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32673      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32674      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32675      * is a number, it is used as the buffer delay (defaults to 50ms).
32676      * @return {Roo.BasicDialog} this
32677      */
32678     anchorTo : function(el, alignment, offsets, monitorScroll){
32679         var action = function(){
32680             this.alignTo(el, alignment, offsets);
32681         };
32682         Roo.EventManager.onWindowResize(action, this);
32683         var tm = typeof monitorScroll;
32684         if(tm != 'undefined'){
32685             Roo.EventManager.on(window, 'scroll', action, this,
32686                 {buffer: tm == 'number' ? monitorScroll : 50});
32687         }
32688         action.call(this);
32689         return this;
32690     },
32691
32692     /**
32693      * Returns true if the dialog is visible
32694      * @return {Boolean}
32695      */
32696     isVisible : function(){
32697         return this.el.isVisible();
32698     },
32699
32700     // private
32701     animHide : function(callback){
32702         var b = Roo.get(this.animateTarget).getBox();
32703         this.proxy.show();
32704         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32705         this.el.hide();
32706         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32707                     this.hideEl.createDelegate(this, [callback]));
32708     },
32709
32710     /**
32711      * Hides the dialog.
32712      * @param {Function} callback (optional) Function to call when the dialog is hidden
32713      * @return {Roo.BasicDialog} this
32714      */
32715     hide : function(callback){
32716         if (this.fireEvent("beforehide", this) === false){
32717             return;
32718         }
32719         if(this.shadow){
32720             this.shadow.hide();
32721         }
32722         if(this.shim) {
32723           this.shim.hide();
32724         }
32725         // sometimes animateTarget seems to get set.. causing problems...
32726         // this just double checks..
32727         if(this.animateTarget && Roo.get(this.animateTarget)) {
32728            this.animHide(callback);
32729         }else{
32730             this.el.hide();
32731             this.hideEl(callback);
32732         }
32733         return this;
32734     },
32735
32736     // private
32737     hideEl : function(callback){
32738         this.proxy.hide();
32739         if(this.modal){
32740             this.mask.hide();
32741             Roo.get(document.body).removeClass("x-body-masked");
32742         }
32743         this.fireEvent("hide", this);
32744         if(typeof callback == "function"){
32745             callback();
32746         }
32747     },
32748
32749     // private
32750     hideAction : function(){
32751         this.setLeft("-10000px");
32752         this.setTop("-10000px");
32753         this.setStyle("visibility", "hidden");
32754     },
32755
32756     // private
32757     refreshSize : function(){
32758         this.size = this.el.getSize();
32759         this.xy = this.el.getXY();
32760         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32761     },
32762
32763     // private
32764     // z-index is managed by the DialogManager and may be overwritten at any time
32765     setZIndex : function(index){
32766         if(this.modal){
32767             this.mask.setStyle("z-index", index);
32768         }
32769         if(this.shim){
32770             this.shim.setStyle("z-index", ++index);
32771         }
32772         if(this.shadow){
32773             this.shadow.setZIndex(++index);
32774         }
32775         this.el.setStyle("z-index", ++index);
32776         if(this.proxy){
32777             this.proxy.setStyle("z-index", ++index);
32778         }
32779         if(this.resizer){
32780             this.resizer.proxy.setStyle("z-index", ++index);
32781         }
32782
32783         this.lastZIndex = index;
32784     },
32785
32786     /**
32787      * Returns the element for this dialog
32788      * @return {Roo.Element} The underlying dialog Element
32789      */
32790     getEl : function(){
32791         return this.el;
32792     }
32793 });
32794
32795 /**
32796  * @class Roo.DialogManager
32797  * Provides global access to BasicDialogs that have been created and
32798  * support for z-indexing (layering) multiple open dialogs.
32799  */
32800 Roo.DialogManager = function(){
32801     var list = {};
32802     var accessList = [];
32803     var front = null;
32804
32805     // private
32806     var sortDialogs = function(d1, d2){
32807         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32808     };
32809
32810     // private
32811     var orderDialogs = function(){
32812         accessList.sort(sortDialogs);
32813         var seed = Roo.DialogManager.zseed;
32814         for(var i = 0, len = accessList.length; i < len; i++){
32815             var dlg = accessList[i];
32816             if(dlg){
32817                 dlg.setZIndex(seed + (i*10));
32818             }
32819         }
32820     };
32821
32822     return {
32823         /**
32824          * The starting z-index for BasicDialogs (defaults to 9000)
32825          * @type Number The z-index value
32826          */
32827         zseed : 9000,
32828
32829         // private
32830         register : function(dlg){
32831             list[dlg.id] = dlg;
32832             accessList.push(dlg);
32833         },
32834
32835         // private
32836         unregister : function(dlg){
32837             delete list[dlg.id];
32838             var i=0;
32839             var len=0;
32840             if(!accessList.indexOf){
32841                 for(  i = 0, len = accessList.length; i < len; i++){
32842                     if(accessList[i] == dlg){
32843                         accessList.splice(i, 1);
32844                         return;
32845                     }
32846                 }
32847             }else{
32848                  i = accessList.indexOf(dlg);
32849                 if(i != -1){
32850                     accessList.splice(i, 1);
32851                 }
32852             }
32853         },
32854
32855         /**
32856          * Gets a registered dialog by id
32857          * @param {String/Object} id The id of the dialog or a dialog
32858          * @return {Roo.BasicDialog} this
32859          */
32860         get : function(id){
32861             return typeof id == "object" ? id : list[id];
32862         },
32863
32864         /**
32865          * Brings the specified dialog to the front
32866          * @param {String/Object} dlg The id of the dialog or a dialog
32867          * @return {Roo.BasicDialog} this
32868          */
32869         bringToFront : function(dlg){
32870             dlg = this.get(dlg);
32871             if(dlg != front){
32872                 front = dlg;
32873                 dlg._lastAccess = new Date().getTime();
32874                 orderDialogs();
32875             }
32876             return dlg;
32877         },
32878
32879         /**
32880          * Sends the specified dialog to the back
32881          * @param {String/Object} dlg The id of the dialog or a dialog
32882          * @return {Roo.BasicDialog} this
32883          */
32884         sendToBack : function(dlg){
32885             dlg = this.get(dlg);
32886             dlg._lastAccess = -(new Date().getTime());
32887             orderDialogs();
32888             return dlg;
32889         },
32890
32891         /**
32892          * Hides all dialogs
32893          */
32894         hideAll : function(){
32895             for(var id in list){
32896                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32897                     list[id].hide();
32898                 }
32899             }
32900         }
32901     };
32902 }();
32903
32904 /**
32905  * @class Roo.LayoutDialog
32906  * @extends Roo.BasicDialog
32907  * Dialog which provides adjustments for working with a layout in a Dialog.
32908  * Add your necessary layout config options to the dialog's config.<br>
32909  * Example usage (including a nested layout):
32910  * <pre><code>
32911 if(!dialog){
32912     dialog = new Roo.LayoutDialog("download-dlg", {
32913         modal: true,
32914         width:600,
32915         height:450,
32916         shadow:true,
32917         minWidth:500,
32918         minHeight:350,
32919         autoTabs:true,
32920         proxyDrag:true,
32921         // layout config merges with the dialog config
32922         center:{
32923             tabPosition: "top",
32924             alwaysShowTabs: true
32925         }
32926     });
32927     dialog.addKeyListener(27, dialog.hide, dialog);
32928     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
32929     dialog.addButton("Build It!", this.getDownload, this);
32930
32931     // we can even add nested layouts
32932     var innerLayout = new Roo.BorderLayout("dl-inner", {
32933         east: {
32934             initialSize: 200,
32935             autoScroll:true,
32936             split:true
32937         },
32938         center: {
32939             autoScroll:true
32940         }
32941     });
32942     innerLayout.beginUpdate();
32943     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
32944     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
32945     innerLayout.endUpdate(true);
32946
32947     var layout = dialog.getLayout();
32948     layout.beginUpdate();
32949     layout.add("center", new Roo.ContentPanel("standard-panel",
32950                         {title: "Download the Source", fitToFrame:true}));
32951     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
32952                {title: "Build your own roo.js"}));
32953     layout.getRegion("center").showPanel(sp);
32954     layout.endUpdate();
32955 }
32956 </code></pre>
32957     * @constructor
32958     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
32959     * @param {Object} config configuration options
32960   */
32961 Roo.LayoutDialog = function(el, cfg){
32962     
32963     var config=  cfg;
32964     if (typeof(cfg) == 'undefined') {
32965         config = Roo.apply({}, el);
32966         // not sure why we use documentElement here.. - it should always be body.
32967         // IE7 borks horribly if we use documentElement.
32968         // webkit also does not like documentElement - it creates a body element...
32969         el = Roo.get( document.body || document.documentElement ).createChild();
32970         //config.autoCreate = true;
32971     }
32972     
32973     
32974     config.autoTabs = false;
32975     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
32976     this.body.setStyle({overflow:"hidden", position:"relative"});
32977     this.layout = new Roo.BorderLayout(this.body.dom, config);
32978     this.layout.monitorWindowResize = false;
32979     this.el.addClass("x-dlg-auto-layout");
32980     // fix case when center region overwrites center function
32981     this.center = Roo.BasicDialog.prototype.center;
32982     this.on("show", this.layout.layout, this.layout, true);
32983     if (config.items) {
32984         var xitems = config.items;
32985         delete config.items;
32986         Roo.each(xitems, this.addxtype, this);
32987     }
32988     
32989     
32990 };
32991 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
32992     /**
32993      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
32994      * @deprecated
32995      */
32996     endUpdate : function(){
32997         this.layout.endUpdate();
32998     },
32999
33000     /**
33001      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33002      *  @deprecated
33003      */
33004     beginUpdate : function(){
33005         this.layout.beginUpdate();
33006     },
33007
33008     /**
33009      * Get the BorderLayout for this dialog
33010      * @return {Roo.BorderLayout}
33011      */
33012     getLayout : function(){
33013         return this.layout;
33014     },
33015
33016     showEl : function(){
33017         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33018         if(Roo.isIE7){
33019             this.layout.layout();
33020         }
33021     },
33022
33023     // private
33024     // Use the syncHeightBeforeShow config option to control this automatically
33025     syncBodyHeight : function(){
33026         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33027         if(this.layout){this.layout.layout();}
33028     },
33029     
33030       /**
33031      * Add an xtype element (actually adds to the layout.)
33032      * @return {Object} xdata xtype object data.
33033      */
33034     
33035     addxtype : function(c) {
33036         return this.layout.addxtype(c);
33037     }
33038 });/*
33039  * Based on:
33040  * Ext JS Library 1.1.1
33041  * Copyright(c) 2006-2007, Ext JS, LLC.
33042  *
33043  * Originally Released Under LGPL - original licence link has changed is not relivant.
33044  *
33045  * Fork - LGPL
33046  * <script type="text/javascript">
33047  */
33048  
33049 /**
33050  * @class Roo.MessageBox
33051  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33052  * Example usage:
33053  *<pre><code>
33054 // Basic alert:
33055 Roo.Msg.alert('Status', 'Changes saved successfully.');
33056
33057 // Prompt for user data:
33058 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33059     if (btn == 'ok'){
33060         // process text value...
33061     }
33062 });
33063
33064 // Show a dialog using config options:
33065 Roo.Msg.show({
33066    title:'Save Changes?',
33067    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33068    buttons: Roo.Msg.YESNOCANCEL,
33069    fn: processResult,
33070    animEl: 'elId'
33071 });
33072 </code></pre>
33073  * @singleton
33074  */
33075 Roo.MessageBox = function(){
33076     var dlg, opt, mask, waitTimer;
33077     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33078     var buttons, activeTextEl, bwidth;
33079
33080     // private
33081     var handleButton = function(button){
33082         dlg.hide();
33083         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33084     };
33085
33086     // private
33087     var handleHide = function(){
33088         if(opt && opt.cls){
33089             dlg.el.removeClass(opt.cls);
33090         }
33091         if(waitTimer){
33092             Roo.TaskMgr.stop(waitTimer);
33093             waitTimer = null;
33094         }
33095     };
33096
33097     // private
33098     var updateButtons = function(b){
33099         var width = 0;
33100         if(!b){
33101             buttons["ok"].hide();
33102             buttons["cancel"].hide();
33103             buttons["yes"].hide();
33104             buttons["no"].hide();
33105             dlg.footer.dom.style.display = 'none';
33106             return width;
33107         }
33108         dlg.footer.dom.style.display = '';
33109         for(var k in buttons){
33110             if(typeof buttons[k] != "function"){
33111                 if(b[k]){
33112                     buttons[k].show();
33113                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33114                     width += buttons[k].el.getWidth()+15;
33115                 }else{
33116                     buttons[k].hide();
33117                 }
33118             }
33119         }
33120         return width;
33121     };
33122
33123     // private
33124     var handleEsc = function(d, k, e){
33125         if(opt && opt.closable !== false){
33126             dlg.hide();
33127         }
33128         if(e){
33129             e.stopEvent();
33130         }
33131     };
33132
33133     return {
33134         /**
33135          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33136          * @return {Roo.BasicDialog} The BasicDialog element
33137          */
33138         getDialog : function(){
33139            if(!dlg){
33140                 dlg = new Roo.BasicDialog("x-msg-box", {
33141                     autoCreate : true,
33142                     shadow: true,
33143                     draggable: true,
33144                     resizable:false,
33145                     constraintoviewport:false,
33146                     fixedcenter:true,
33147                     collapsible : false,
33148                     shim:true,
33149                     modal: true,
33150                     width:400, height:100,
33151                     buttonAlign:"center",
33152                     closeClick : function(){
33153                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33154                             handleButton("no");
33155                         }else{
33156                             handleButton("cancel");
33157                         }
33158                     }
33159                 });
33160                 dlg.on("hide", handleHide);
33161                 mask = dlg.mask;
33162                 dlg.addKeyListener(27, handleEsc);
33163                 buttons = {};
33164                 var bt = this.buttonText;
33165                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33166                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33167                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33168                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33169                 bodyEl = dlg.body.createChild({
33170
33171                     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>'
33172                 });
33173                 msgEl = bodyEl.dom.firstChild;
33174                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33175                 textboxEl.enableDisplayMode();
33176                 textboxEl.addKeyListener([10,13], function(){
33177                     if(dlg.isVisible() && opt && opt.buttons){
33178                         if(opt.buttons.ok){
33179                             handleButton("ok");
33180                         }else if(opt.buttons.yes){
33181                             handleButton("yes");
33182                         }
33183                     }
33184                 });
33185                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33186                 textareaEl.enableDisplayMode();
33187                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33188                 progressEl.enableDisplayMode();
33189                 var pf = progressEl.dom.firstChild;
33190                 if (pf) {
33191                     pp = Roo.get(pf.firstChild);
33192                     pp.setHeight(pf.offsetHeight);
33193                 }
33194                 
33195             }
33196             return dlg;
33197         },
33198
33199         /**
33200          * Updates the message box body text
33201          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33202          * the XHTML-compliant non-breaking space character '&amp;#160;')
33203          * @return {Roo.MessageBox} This message box
33204          */
33205         updateText : function(text){
33206             if(!dlg.isVisible() && !opt.width){
33207                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33208             }
33209             msgEl.innerHTML = text || '&#160;';
33210       
33211             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33212             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33213             var w = Math.max(
33214                     Math.min(opt.width || cw , this.maxWidth), 
33215                     Math.max(opt.minWidth || this.minWidth, bwidth)
33216             );
33217             if(opt.prompt){
33218                 activeTextEl.setWidth(w);
33219             }
33220             if(dlg.isVisible()){
33221                 dlg.fixedcenter = false;
33222             }
33223             // to big, make it scroll. = But as usual stupid IE does not support
33224             // !important..
33225             
33226             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33227                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33228                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33229             } else {
33230                 bodyEl.dom.style.height = '';
33231                 bodyEl.dom.style.overflowY = '';
33232             }
33233             if (cw > w) {
33234                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33235             } else {
33236                 bodyEl.dom.style.overflowX = '';
33237             }
33238             
33239             dlg.setContentSize(w, bodyEl.getHeight());
33240             if(dlg.isVisible()){
33241                 dlg.fixedcenter = true;
33242             }
33243             return this;
33244         },
33245
33246         /**
33247          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33248          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33249          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33250          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33251          * @return {Roo.MessageBox} This message box
33252          */
33253         updateProgress : function(value, text){
33254             if(text){
33255                 this.updateText(text);
33256             }
33257             if (pp) { // weird bug on my firefox - for some reason this is not defined
33258                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33259             }
33260             return this;
33261         },        
33262
33263         /**
33264          * Returns true if the message box is currently displayed
33265          * @return {Boolean} True if the message box is visible, else false
33266          */
33267         isVisible : function(){
33268             return dlg && dlg.isVisible();  
33269         },
33270
33271         /**
33272          * Hides the message box if it is displayed
33273          */
33274         hide : function(){
33275             if(this.isVisible()){
33276                 dlg.hide();
33277             }  
33278         },
33279
33280         /**
33281          * Displays a new message box, or reinitializes an existing message box, based on the config options
33282          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33283          * The following config object properties are supported:
33284          * <pre>
33285 Property    Type             Description
33286 ----------  ---------------  ------------------------------------------------------------------------------------
33287 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33288                                    closes (defaults to undefined)
33289 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33290                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33291 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33292                                    progress and wait dialogs will ignore this property and always hide the
33293                                    close button as they can only be closed programmatically.
33294 cls               String           A custom CSS class to apply to the message box element
33295 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33296                                    displayed (defaults to 75)
33297 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33298                                    function will be btn (the name of the button that was clicked, if applicable,
33299                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33300                                    Progress and wait dialogs will ignore this option since they do not respond to
33301                                    user actions and can only be closed programmatically, so any required function
33302                                    should be called by the same code after it closes the dialog.
33303 icon              String           A CSS class that provides a background image to be used as an icon for
33304                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33305 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33306 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33307 modal             Boolean          False to allow user interaction with the page while the message box is
33308                                    displayed (defaults to true)
33309 msg               String           A string that will replace the existing message box body text (defaults
33310                                    to the XHTML-compliant non-breaking space character '&#160;')
33311 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33312 progress          Boolean          True to display a progress bar (defaults to false)
33313 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33314 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33315 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33316 title             String           The title text
33317 value             String           The string value to set into the active textbox element if displayed
33318 wait              Boolean          True to display a progress bar (defaults to false)
33319 width             Number           The width of the dialog in pixels
33320 </pre>
33321          *
33322          * Example usage:
33323          * <pre><code>
33324 Roo.Msg.show({
33325    title: 'Address',
33326    msg: 'Please enter your address:',
33327    width: 300,
33328    buttons: Roo.MessageBox.OKCANCEL,
33329    multiline: true,
33330    fn: saveAddress,
33331    animEl: 'addAddressBtn'
33332 });
33333 </code></pre>
33334          * @param {Object} config Configuration options
33335          * @return {Roo.MessageBox} This message box
33336          */
33337         show : function(options)
33338         {
33339             
33340             // this causes nightmares if you show one dialog after another
33341             // especially on callbacks..
33342              
33343             if(this.isVisible()){
33344                 
33345                 this.hide();
33346                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33347                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33348                 Roo.log("New Dialog Message:" +  options.msg )
33349                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33350                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33351                 
33352             }
33353             var d = this.getDialog();
33354             opt = options;
33355             d.setTitle(opt.title || "&#160;");
33356             d.close.setDisplayed(opt.closable !== false);
33357             activeTextEl = textboxEl;
33358             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33359             if(opt.prompt){
33360                 if(opt.multiline){
33361                     textboxEl.hide();
33362                     textareaEl.show();
33363                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33364                         opt.multiline : this.defaultTextHeight);
33365                     activeTextEl = textareaEl;
33366                 }else{
33367                     textboxEl.show();
33368                     textareaEl.hide();
33369                 }
33370             }else{
33371                 textboxEl.hide();
33372                 textareaEl.hide();
33373             }
33374             progressEl.setDisplayed(opt.progress === true);
33375             this.updateProgress(0);
33376             activeTextEl.dom.value = opt.value || "";
33377             if(opt.prompt){
33378                 dlg.setDefaultButton(activeTextEl);
33379             }else{
33380                 var bs = opt.buttons;
33381                 var db = null;
33382                 if(bs && bs.ok){
33383                     db = buttons["ok"];
33384                 }else if(bs && bs.yes){
33385                     db = buttons["yes"];
33386                 }
33387                 dlg.setDefaultButton(db);
33388             }
33389             bwidth = updateButtons(opt.buttons);
33390             this.updateText(opt.msg);
33391             if(opt.cls){
33392                 d.el.addClass(opt.cls);
33393             }
33394             d.proxyDrag = opt.proxyDrag === true;
33395             d.modal = opt.modal !== false;
33396             d.mask = opt.modal !== false ? mask : false;
33397             if(!d.isVisible()){
33398                 // force it to the end of the z-index stack so it gets a cursor in FF
33399                 document.body.appendChild(dlg.el.dom);
33400                 d.animateTarget = null;
33401                 d.show(options.animEl);
33402             }
33403             return this;
33404         },
33405
33406         /**
33407          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33408          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33409          * and closing the message box when the process is complete.
33410          * @param {String} title The title bar text
33411          * @param {String} msg The message box body text
33412          * @return {Roo.MessageBox} This message box
33413          */
33414         progress : function(title, msg){
33415             this.show({
33416                 title : title,
33417                 msg : msg,
33418                 buttons: false,
33419                 progress:true,
33420                 closable:false,
33421                 minWidth: this.minProgressWidth,
33422                 modal : true
33423             });
33424             return this;
33425         },
33426
33427         /**
33428          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33429          * If a callback function is passed it will be called after the user clicks the button, and the
33430          * id of the button that was clicked will be passed as the only parameter to the callback
33431          * (could also be the top-right close button).
33432          * @param {String} title The title bar text
33433          * @param {String} msg The message box body text
33434          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33435          * @param {Object} scope (optional) The scope of the callback function
33436          * @return {Roo.MessageBox} This message box
33437          */
33438         alert : function(title, msg, fn, scope){
33439             this.show({
33440                 title : title,
33441                 msg : msg,
33442                 buttons: this.OK,
33443                 fn: fn,
33444                 scope : scope,
33445                 modal : true
33446             });
33447             return this;
33448         },
33449
33450         /**
33451          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33452          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33453          * You are responsible for closing the message box when the process is complete.
33454          * @param {String} msg The message box body text
33455          * @param {String} title (optional) The title bar text
33456          * @return {Roo.MessageBox} This message box
33457          */
33458         wait : function(msg, title){
33459             this.show({
33460                 title : title,
33461                 msg : msg,
33462                 buttons: false,
33463                 closable:false,
33464                 progress:true,
33465                 modal:true,
33466                 width:300,
33467                 wait:true
33468             });
33469             waitTimer = Roo.TaskMgr.start({
33470                 run: function(i){
33471                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33472                 },
33473                 interval: 1000
33474             });
33475             return this;
33476         },
33477
33478         /**
33479          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33480          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33481          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33482          * @param {String} title The title bar text
33483          * @param {String} msg The message box body text
33484          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33485          * @param {Object} scope (optional) The scope of the callback function
33486          * @return {Roo.MessageBox} This message box
33487          */
33488         confirm : function(title, msg, fn, scope){
33489             this.show({
33490                 title : title,
33491                 msg : msg,
33492                 buttons: this.YESNO,
33493                 fn: fn,
33494                 scope : scope,
33495                 modal : true
33496             });
33497             return this;
33498         },
33499
33500         /**
33501          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33502          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33503          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33504          * (could also be the top-right close button) and the text that was entered will be passed as the two
33505          * parameters to the callback.
33506          * @param {String} title The title bar text
33507          * @param {String} msg The message box body text
33508          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33509          * @param {Object} scope (optional) The scope of the callback function
33510          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33511          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33512          * @return {Roo.MessageBox} This message box
33513          */
33514         prompt : function(title, msg, fn, scope, multiline){
33515             this.show({
33516                 title : title,
33517                 msg : msg,
33518                 buttons: this.OKCANCEL,
33519                 fn: fn,
33520                 minWidth:250,
33521                 scope : scope,
33522                 prompt:true,
33523                 multiline: multiline,
33524                 modal : true
33525             });
33526             return this;
33527         },
33528
33529         /**
33530          * Button config that displays a single OK button
33531          * @type Object
33532          */
33533         OK : {ok:true},
33534         /**
33535          * Button config that displays Yes and No buttons
33536          * @type Object
33537          */
33538         YESNO : {yes:true, no:true},
33539         /**
33540          * Button config that displays OK and Cancel buttons
33541          * @type Object
33542          */
33543         OKCANCEL : {ok:true, cancel:true},
33544         /**
33545          * Button config that displays Yes, No and Cancel buttons
33546          * @type Object
33547          */
33548         YESNOCANCEL : {yes:true, no:true, cancel:true},
33549
33550         /**
33551          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33552          * @type Number
33553          */
33554         defaultTextHeight : 75,
33555         /**
33556          * The maximum width in pixels of the message box (defaults to 600)
33557          * @type Number
33558          */
33559         maxWidth : 600,
33560         /**
33561          * The minimum width in pixels of the message box (defaults to 100)
33562          * @type Number
33563          */
33564         minWidth : 100,
33565         /**
33566          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33567          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33568          * @type Number
33569          */
33570         minProgressWidth : 250,
33571         /**
33572          * An object containing the default button text strings that can be overriden for localized language support.
33573          * Supported properties are: ok, cancel, yes and no.
33574          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33575          * @type Object
33576          */
33577         buttonText : {
33578             ok : "OK",
33579             cancel : "Cancel",
33580             yes : "Yes",
33581             no : "No"
33582         }
33583     };
33584 }();
33585
33586 /**
33587  * Shorthand for {@link Roo.MessageBox}
33588  */
33589 Roo.Msg = Roo.MessageBox;/*
33590  * Based on:
33591  * Ext JS Library 1.1.1
33592  * Copyright(c) 2006-2007, Ext JS, LLC.
33593  *
33594  * Originally Released Under LGPL - original licence link has changed is not relivant.
33595  *
33596  * Fork - LGPL
33597  * <script type="text/javascript">
33598  */
33599 /**
33600  * @class Roo.QuickTips
33601  * Provides attractive and customizable tooltips for any element.
33602  * @singleton
33603  */
33604 Roo.QuickTips = function(){
33605     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33606     var ce, bd, xy, dd;
33607     var visible = false, disabled = true, inited = false;
33608     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33609     
33610     var onOver = function(e){
33611         if(disabled){
33612             return;
33613         }
33614         var t = e.getTarget();
33615         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33616             return;
33617         }
33618         if(ce && t == ce.el){
33619             clearTimeout(hideProc);
33620             return;
33621         }
33622         if(t && tagEls[t.id]){
33623             tagEls[t.id].el = t;
33624             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33625             return;
33626         }
33627         var ttp, et = Roo.fly(t);
33628         var ns = cfg.namespace;
33629         if(tm.interceptTitles && t.title){
33630             ttp = t.title;
33631             t.qtip = ttp;
33632             t.removeAttribute("title");
33633             e.preventDefault();
33634         }else{
33635             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33636         }
33637         if(ttp){
33638             showProc = show.defer(tm.showDelay, tm, [{
33639                 el: t, 
33640                 text: ttp.replace(/\\n/g,'<br/>'),
33641                 width: et.getAttributeNS(ns, cfg.width),
33642                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33643                 title: et.getAttributeNS(ns, cfg.title),
33644                     cls: et.getAttributeNS(ns, cfg.cls)
33645             }]);
33646         }
33647     };
33648     
33649     var onOut = function(e){
33650         clearTimeout(showProc);
33651         var t = e.getTarget();
33652         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33653             hideProc = setTimeout(hide, tm.hideDelay);
33654         }
33655     };
33656     
33657     var onMove = function(e){
33658         if(disabled){
33659             return;
33660         }
33661         xy = e.getXY();
33662         xy[1] += 18;
33663         if(tm.trackMouse && ce){
33664             el.setXY(xy);
33665         }
33666     };
33667     
33668     var onDown = function(e){
33669         clearTimeout(showProc);
33670         clearTimeout(hideProc);
33671         if(!e.within(el)){
33672             if(tm.hideOnClick){
33673                 hide();
33674                 tm.disable();
33675                 tm.enable.defer(100, tm);
33676             }
33677         }
33678     };
33679     
33680     var getPad = function(){
33681         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33682     };
33683
33684     var show = function(o){
33685         if(disabled){
33686             return;
33687         }
33688         clearTimeout(dismissProc);
33689         ce = o;
33690         if(removeCls){ // in case manually hidden
33691             el.removeClass(removeCls);
33692             removeCls = null;
33693         }
33694         if(ce.cls){
33695             el.addClass(ce.cls);
33696             removeCls = ce.cls;
33697         }
33698         if(ce.title){
33699             tipTitle.update(ce.title);
33700             tipTitle.show();
33701         }else{
33702             tipTitle.update('');
33703             tipTitle.hide();
33704         }
33705         el.dom.style.width  = tm.maxWidth+'px';
33706         //tipBody.dom.style.width = '';
33707         tipBodyText.update(o.text);
33708         var p = getPad(), w = ce.width;
33709         if(!w){
33710             var td = tipBodyText.dom;
33711             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33712             if(aw > tm.maxWidth){
33713                 w = tm.maxWidth;
33714             }else if(aw < tm.minWidth){
33715                 w = tm.minWidth;
33716             }else{
33717                 w = aw;
33718             }
33719         }
33720         //tipBody.setWidth(w);
33721         el.setWidth(parseInt(w, 10) + p);
33722         if(ce.autoHide === false){
33723             close.setDisplayed(true);
33724             if(dd){
33725                 dd.unlock();
33726             }
33727         }else{
33728             close.setDisplayed(false);
33729             if(dd){
33730                 dd.lock();
33731             }
33732         }
33733         if(xy){
33734             el.avoidY = xy[1]-18;
33735             el.setXY(xy);
33736         }
33737         if(tm.animate){
33738             el.setOpacity(.1);
33739             el.setStyle("visibility", "visible");
33740             el.fadeIn({callback: afterShow});
33741         }else{
33742             afterShow();
33743         }
33744     };
33745     
33746     var afterShow = function(){
33747         if(ce){
33748             el.show();
33749             esc.enable();
33750             if(tm.autoDismiss && ce.autoHide !== false){
33751                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33752             }
33753         }
33754     };
33755     
33756     var hide = function(noanim){
33757         clearTimeout(dismissProc);
33758         clearTimeout(hideProc);
33759         ce = null;
33760         if(el.isVisible()){
33761             esc.disable();
33762             if(noanim !== true && tm.animate){
33763                 el.fadeOut({callback: afterHide});
33764             }else{
33765                 afterHide();
33766             } 
33767         }
33768     };
33769     
33770     var afterHide = function(){
33771         el.hide();
33772         if(removeCls){
33773             el.removeClass(removeCls);
33774             removeCls = null;
33775         }
33776     };
33777     
33778     return {
33779         /**
33780         * @cfg {Number} minWidth
33781         * The minimum width of the quick tip (defaults to 40)
33782         */
33783        minWidth : 40,
33784         /**
33785         * @cfg {Number} maxWidth
33786         * The maximum width of the quick tip (defaults to 300)
33787         */
33788        maxWidth : 300,
33789         /**
33790         * @cfg {Boolean} interceptTitles
33791         * True to automatically use the element's DOM title value if available (defaults to false)
33792         */
33793        interceptTitles : false,
33794         /**
33795         * @cfg {Boolean} trackMouse
33796         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33797         */
33798        trackMouse : false,
33799         /**
33800         * @cfg {Boolean} hideOnClick
33801         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33802         */
33803        hideOnClick : true,
33804         /**
33805         * @cfg {Number} showDelay
33806         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33807         */
33808        showDelay : 500,
33809         /**
33810         * @cfg {Number} hideDelay
33811         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33812         */
33813        hideDelay : 200,
33814         /**
33815         * @cfg {Boolean} autoHide
33816         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33817         * Used in conjunction with hideDelay.
33818         */
33819        autoHide : true,
33820         /**
33821         * @cfg {Boolean}
33822         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33823         * (defaults to true).  Used in conjunction with autoDismissDelay.
33824         */
33825        autoDismiss : true,
33826         /**
33827         * @cfg {Number}
33828         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33829         */
33830        autoDismissDelay : 5000,
33831        /**
33832         * @cfg {Boolean} animate
33833         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33834         */
33835        animate : false,
33836
33837        /**
33838         * @cfg {String} title
33839         * Title text to display (defaults to '').  This can be any valid HTML markup.
33840         */
33841         title: '',
33842        /**
33843         * @cfg {String} text
33844         * Body text to display (defaults to '').  This can be any valid HTML markup.
33845         */
33846         text : '',
33847        /**
33848         * @cfg {String} cls
33849         * A CSS class to apply to the base quick tip element (defaults to '').
33850         */
33851         cls : '',
33852        /**
33853         * @cfg {Number} width
33854         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33855         * minWidth or maxWidth.
33856         */
33857         width : null,
33858
33859     /**
33860      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33861      * or display QuickTips in a page.
33862      */
33863        init : function(){
33864           tm = Roo.QuickTips;
33865           cfg = tm.tagConfig;
33866           if(!inited){
33867               if(!Roo.isReady){ // allow calling of init() before onReady
33868                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33869                   return;
33870               }
33871               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33872               el.fxDefaults = {stopFx: true};
33873               // maximum custom styling
33874               //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>');
33875               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>');              
33876               tipTitle = el.child('h3');
33877               tipTitle.enableDisplayMode("block");
33878               tipBody = el.child('div.x-tip-bd');
33879               tipBodyText = el.child('div.x-tip-bd-inner');
33880               //bdLeft = el.child('div.x-tip-bd-left');
33881               //bdRight = el.child('div.x-tip-bd-right');
33882               close = el.child('div.x-tip-close');
33883               close.enableDisplayMode("block");
33884               close.on("click", hide);
33885               var d = Roo.get(document);
33886               d.on("mousedown", onDown);
33887               d.on("mouseover", onOver);
33888               d.on("mouseout", onOut);
33889               d.on("mousemove", onMove);
33890               esc = d.addKeyListener(27, hide);
33891               esc.disable();
33892               if(Roo.dd.DD){
33893                   dd = el.initDD("default", null, {
33894                       onDrag : function(){
33895                           el.sync();  
33896                       }
33897                   });
33898                   dd.setHandleElId(tipTitle.id);
33899                   dd.lock();
33900               }
33901               inited = true;
33902           }
33903           this.enable(); 
33904        },
33905
33906     /**
33907      * Configures a new quick tip instance and assigns it to a target element.  The following config options
33908      * are supported:
33909      * <pre>
33910 Property    Type                   Description
33911 ----------  ---------------------  ------------------------------------------------------------------------
33912 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
33913      * </ul>
33914      * @param {Object} config The config object
33915      */
33916        register : function(config){
33917            var cs = config instanceof Array ? config : arguments;
33918            for(var i = 0, len = cs.length; i < len; i++) {
33919                var c = cs[i];
33920                var target = c.target;
33921                if(target){
33922                    if(target instanceof Array){
33923                        for(var j = 0, jlen = target.length; j < jlen; j++){
33924                            tagEls[target[j]] = c;
33925                        }
33926                    }else{
33927                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
33928                    }
33929                }
33930            }
33931        },
33932
33933     /**
33934      * Removes this quick tip from its element and destroys it.
33935      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
33936      */
33937        unregister : function(el){
33938            delete tagEls[Roo.id(el)];
33939        },
33940
33941     /**
33942      * Enable this quick tip.
33943      */
33944        enable : function(){
33945            if(inited && disabled){
33946                locks.pop();
33947                if(locks.length < 1){
33948                    disabled = false;
33949                }
33950            }
33951        },
33952
33953     /**
33954      * Disable this quick tip.
33955      */
33956        disable : function(){
33957           disabled = true;
33958           clearTimeout(showProc);
33959           clearTimeout(hideProc);
33960           clearTimeout(dismissProc);
33961           if(ce){
33962               hide(true);
33963           }
33964           locks.push(1);
33965        },
33966
33967     /**
33968      * Returns true if the quick tip is enabled, else false.
33969      */
33970        isEnabled : function(){
33971             return !disabled;
33972        },
33973
33974         // private
33975        tagConfig : {
33976            namespace : "roo", // was ext?? this may break..
33977            alt_namespace : "ext",
33978            attribute : "qtip",
33979            width : "width",
33980            target : "target",
33981            title : "qtitle",
33982            hide : "hide",
33983            cls : "qclass"
33984        }
33985    };
33986 }();
33987
33988 // backwards compat
33989 Roo.QuickTips.tips = Roo.QuickTips.register;/*
33990  * Based on:
33991  * Ext JS Library 1.1.1
33992  * Copyright(c) 2006-2007, Ext JS, LLC.
33993  *
33994  * Originally Released Under LGPL - original licence link has changed is not relivant.
33995  *
33996  * Fork - LGPL
33997  * <script type="text/javascript">
33998  */
33999  
34000
34001 /**
34002  * @class Roo.tree.TreePanel
34003  * @extends Roo.data.Tree
34004
34005  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34006  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34007  * @cfg {Boolean} enableDD true to enable drag and drop
34008  * @cfg {Boolean} enableDrag true to enable just drag
34009  * @cfg {Boolean} enableDrop true to enable just drop
34010  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34011  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34012  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34013  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34014  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34015  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34016  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34017  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34018  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34019  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34020  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34021  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34022  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
34023  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34024  * @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>
34025  * @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>
34026  * 
34027  * @constructor
34028  * @param {String/HTMLElement/Element} el The container element
34029  * @param {Object} config
34030  */
34031 Roo.tree.TreePanel = function(el, config){
34032     var root = false;
34033     var loader = false;
34034     if (config.root) {
34035         root = config.root;
34036         delete config.root;
34037     }
34038     if (config.loader) {
34039         loader = config.loader;
34040         delete config.loader;
34041     }
34042     
34043     Roo.apply(this, config);
34044     Roo.tree.TreePanel.superclass.constructor.call(this);
34045     this.el = Roo.get(el);
34046     this.el.addClass('x-tree');
34047     //console.log(root);
34048     if (root) {
34049         this.setRootNode( Roo.factory(root, Roo.tree));
34050     }
34051     if (loader) {
34052         this.loader = Roo.factory(loader, Roo.tree);
34053     }
34054    /**
34055     * Read-only. The id of the container element becomes this TreePanel's id.
34056     */
34057     this.id = this.el.id;
34058     this.addEvents({
34059         /**
34060         * @event beforeload
34061         * Fires before a node is loaded, return false to cancel
34062         * @param {Node} node The node being loaded
34063         */
34064         "beforeload" : true,
34065         /**
34066         * @event load
34067         * Fires when a node is loaded
34068         * @param {Node} node The node that was loaded
34069         */
34070         "load" : true,
34071         /**
34072         * @event textchange
34073         * Fires when the text for a node is changed
34074         * @param {Node} node The node
34075         * @param {String} text The new text
34076         * @param {String} oldText The old text
34077         */
34078         "textchange" : true,
34079         /**
34080         * @event beforeexpand
34081         * Fires before a node is expanded, return false to cancel.
34082         * @param {Node} node The node
34083         * @param {Boolean} deep
34084         * @param {Boolean} anim
34085         */
34086         "beforeexpand" : true,
34087         /**
34088         * @event beforecollapse
34089         * Fires before a node is collapsed, return false to cancel.
34090         * @param {Node} node The node
34091         * @param {Boolean} deep
34092         * @param {Boolean} anim
34093         */
34094         "beforecollapse" : true,
34095         /**
34096         * @event expand
34097         * Fires when a node is expanded
34098         * @param {Node} node The node
34099         */
34100         "expand" : true,
34101         /**
34102         * @event disabledchange
34103         * Fires when the disabled status of a node changes
34104         * @param {Node} node The node
34105         * @param {Boolean} disabled
34106         */
34107         "disabledchange" : true,
34108         /**
34109         * @event collapse
34110         * Fires when a node is collapsed
34111         * @param {Node} node The node
34112         */
34113         "collapse" : true,
34114         /**
34115         * @event beforeclick
34116         * Fires before click processing on a node. Return false to cancel the default action.
34117         * @param {Node} node The node
34118         * @param {Roo.EventObject} e The event object
34119         */
34120         "beforeclick":true,
34121         /**
34122         * @event checkchange
34123         * Fires when a node with a checkbox's checked property changes
34124         * @param {Node} this This node
34125         * @param {Boolean} checked
34126         */
34127         "checkchange":true,
34128         /**
34129         * @event click
34130         * Fires when a node is clicked
34131         * @param {Node} node The node
34132         * @param {Roo.EventObject} e The event object
34133         */
34134         "click":true,
34135         /**
34136         * @event dblclick
34137         * Fires when a node is double clicked
34138         * @param {Node} node The node
34139         * @param {Roo.EventObject} e The event object
34140         */
34141         "dblclick":true,
34142         /**
34143         * @event contextmenu
34144         * Fires when a node is right clicked
34145         * @param {Node} node The node
34146         * @param {Roo.EventObject} e The event object
34147         */
34148         "contextmenu":true,
34149         /**
34150         * @event beforechildrenrendered
34151         * Fires right before the child nodes for a node are rendered
34152         * @param {Node} node The node
34153         */
34154         "beforechildrenrendered":true,
34155         /**
34156         * @event startdrag
34157         * Fires when a node starts being dragged
34158         * @param {Roo.tree.TreePanel} this
34159         * @param {Roo.tree.TreeNode} node
34160         * @param {event} e The raw browser event
34161         */ 
34162        "startdrag" : true,
34163        /**
34164         * @event enddrag
34165         * Fires when a drag operation is complete
34166         * @param {Roo.tree.TreePanel} this
34167         * @param {Roo.tree.TreeNode} node
34168         * @param {event} e The raw browser event
34169         */
34170        "enddrag" : true,
34171        /**
34172         * @event dragdrop
34173         * Fires when a dragged node is dropped on a valid DD target
34174         * @param {Roo.tree.TreePanel} this
34175         * @param {Roo.tree.TreeNode} node
34176         * @param {DD} dd The dd it was dropped on
34177         * @param {event} e The raw browser event
34178         */
34179        "dragdrop" : true,
34180        /**
34181         * @event beforenodedrop
34182         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34183         * passed to handlers has the following properties:<br />
34184         * <ul style="padding:5px;padding-left:16px;">
34185         * <li>tree - The TreePanel</li>
34186         * <li>target - The node being targeted for the drop</li>
34187         * <li>data - The drag data from the drag source</li>
34188         * <li>point - The point of the drop - append, above or below</li>
34189         * <li>source - The drag source</li>
34190         * <li>rawEvent - Raw mouse event</li>
34191         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34192         * to be inserted by setting them on this object.</li>
34193         * <li>cancel - Set this to true to cancel the drop.</li>
34194         * </ul>
34195         * @param {Object} dropEvent
34196         */
34197        "beforenodedrop" : true,
34198        /**
34199         * @event nodedrop
34200         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34201         * passed to handlers has the following properties:<br />
34202         * <ul style="padding:5px;padding-left:16px;">
34203         * <li>tree - The TreePanel</li>
34204         * <li>target - The node being targeted for the drop</li>
34205         * <li>data - The drag data from the drag source</li>
34206         * <li>point - The point of the drop - append, above or below</li>
34207         * <li>source - The drag source</li>
34208         * <li>rawEvent - Raw mouse event</li>
34209         * <li>dropNode - Dropped node(s).</li>
34210         * </ul>
34211         * @param {Object} dropEvent
34212         */
34213        "nodedrop" : true,
34214         /**
34215         * @event nodedragover
34216         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34217         * passed to handlers has the following properties:<br />
34218         * <ul style="padding:5px;padding-left:16px;">
34219         * <li>tree - The TreePanel</li>
34220         * <li>target - The node being targeted for the drop</li>
34221         * <li>data - The drag data from the drag source</li>
34222         * <li>point - The point of the drop - append, above or below</li>
34223         * <li>source - The drag source</li>
34224         * <li>rawEvent - Raw mouse event</li>
34225         * <li>dropNode - Drop node(s) provided by the source.</li>
34226         * <li>cancel - Set this to true to signal drop not allowed.</li>
34227         * </ul>
34228         * @param {Object} dragOverEvent
34229         */
34230        "nodedragover" : true
34231         
34232     });
34233     if(this.singleExpand){
34234        this.on("beforeexpand", this.restrictExpand, this);
34235     }
34236     if (this.editor) {
34237         this.editor.tree = this;
34238         this.editor = Roo.factory(this.editor, Roo.tree);
34239     }
34240     
34241     if (this.selModel) {
34242         this.selModel = Roo.factory(this.selModel, Roo.tree);
34243     }
34244    
34245 };
34246 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34247     rootVisible : true,
34248     animate: Roo.enableFx,
34249     lines : true,
34250     enableDD : false,
34251     hlDrop : Roo.enableFx,
34252   
34253     renderer: false,
34254     
34255     rendererTip: false,
34256     // private
34257     restrictExpand : function(node){
34258         var p = node.parentNode;
34259         if(p){
34260             if(p.expandedChild && p.expandedChild.parentNode == p){
34261                 p.expandedChild.collapse();
34262             }
34263             p.expandedChild = node;
34264         }
34265     },
34266
34267     // private override
34268     setRootNode : function(node){
34269         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34270         if(!this.rootVisible){
34271             node.ui = new Roo.tree.RootTreeNodeUI(node);
34272         }
34273         return node;
34274     },
34275
34276     /**
34277      * Returns the container element for this TreePanel
34278      */
34279     getEl : function(){
34280         return this.el;
34281     },
34282
34283     /**
34284      * Returns the default TreeLoader for this TreePanel
34285      */
34286     getLoader : function(){
34287         return this.loader;
34288     },
34289
34290     /**
34291      * Expand all nodes
34292      */
34293     expandAll : function(){
34294         this.root.expand(true);
34295     },
34296
34297     /**
34298      * Collapse all nodes
34299      */
34300     collapseAll : function(){
34301         this.root.collapse(true);
34302     },
34303
34304     /**
34305      * Returns the selection model used by this TreePanel
34306      */
34307     getSelectionModel : function(){
34308         if(!this.selModel){
34309             this.selModel = new Roo.tree.DefaultSelectionModel();
34310         }
34311         return this.selModel;
34312     },
34313
34314     /**
34315      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34316      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34317      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34318      * @return {Array}
34319      */
34320     getChecked : function(a, startNode){
34321         startNode = startNode || this.root;
34322         var r = [];
34323         var f = function(){
34324             if(this.attributes.checked){
34325                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34326             }
34327         }
34328         startNode.cascade(f);
34329         return r;
34330     },
34331
34332     /**
34333      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34334      * @param {String} path
34335      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34336      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34337      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34338      */
34339     expandPath : function(path, attr, callback){
34340         attr = attr || "id";
34341         var keys = path.split(this.pathSeparator);
34342         var curNode = this.root;
34343         if(curNode.attributes[attr] != keys[1]){ // invalid root
34344             if(callback){
34345                 callback(false, null);
34346             }
34347             return;
34348         }
34349         var index = 1;
34350         var f = function(){
34351             if(++index == keys.length){
34352                 if(callback){
34353                     callback(true, curNode);
34354                 }
34355                 return;
34356             }
34357             var c = curNode.findChild(attr, keys[index]);
34358             if(!c){
34359                 if(callback){
34360                     callback(false, curNode);
34361                 }
34362                 return;
34363             }
34364             curNode = c;
34365             c.expand(false, false, f);
34366         };
34367         curNode.expand(false, false, f);
34368     },
34369
34370     /**
34371      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34372      * @param {String} path
34373      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34374      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34375      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34376      */
34377     selectPath : function(path, attr, callback){
34378         attr = attr || "id";
34379         var keys = path.split(this.pathSeparator);
34380         var v = keys.pop();
34381         if(keys.length > 0){
34382             var f = function(success, node){
34383                 if(success && node){
34384                     var n = node.findChild(attr, v);
34385                     if(n){
34386                         n.select();
34387                         if(callback){
34388                             callback(true, n);
34389                         }
34390                     }else if(callback){
34391                         callback(false, n);
34392                     }
34393                 }else{
34394                     if(callback){
34395                         callback(false, n);
34396                     }
34397                 }
34398             };
34399             this.expandPath(keys.join(this.pathSeparator), attr, f);
34400         }else{
34401             this.root.select();
34402             if(callback){
34403                 callback(true, this.root);
34404             }
34405         }
34406     },
34407
34408     getTreeEl : function(){
34409         return this.el;
34410     },
34411
34412     /**
34413      * Trigger rendering of this TreePanel
34414      */
34415     render : function(){
34416         if (this.innerCt) {
34417             return this; // stop it rendering more than once!!
34418         }
34419         
34420         this.innerCt = this.el.createChild({tag:"ul",
34421                cls:"x-tree-root-ct " +
34422                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34423
34424         if(this.containerScroll){
34425             Roo.dd.ScrollManager.register(this.el);
34426         }
34427         if((this.enableDD || this.enableDrop) && !this.dropZone){
34428            /**
34429             * The dropZone used by this tree if drop is enabled
34430             * @type Roo.tree.TreeDropZone
34431             */
34432              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34433                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34434            });
34435         }
34436         if((this.enableDD || this.enableDrag) && !this.dragZone){
34437            /**
34438             * The dragZone used by this tree if drag is enabled
34439             * @type Roo.tree.TreeDragZone
34440             */
34441             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34442                ddGroup: this.ddGroup || "TreeDD",
34443                scroll: this.ddScroll
34444            });
34445         }
34446         this.getSelectionModel().init(this);
34447         if (!this.root) {
34448             Roo.log("ROOT not set in tree");
34449             return this;
34450         }
34451         this.root.render();
34452         if(!this.rootVisible){
34453             this.root.renderChildren();
34454         }
34455         return this;
34456     }
34457 });/*
34458  * Based on:
34459  * Ext JS Library 1.1.1
34460  * Copyright(c) 2006-2007, Ext JS, LLC.
34461  *
34462  * Originally Released Under LGPL - original licence link has changed is not relivant.
34463  *
34464  * Fork - LGPL
34465  * <script type="text/javascript">
34466  */
34467  
34468
34469 /**
34470  * @class Roo.tree.DefaultSelectionModel
34471  * @extends Roo.util.Observable
34472  * The default single selection for a TreePanel.
34473  * @param {Object} cfg Configuration
34474  */
34475 Roo.tree.DefaultSelectionModel = function(cfg){
34476    this.selNode = null;
34477    
34478    
34479    
34480    this.addEvents({
34481        /**
34482         * @event selectionchange
34483         * Fires when the selected node changes
34484         * @param {DefaultSelectionModel} this
34485         * @param {TreeNode} node the new selection
34486         */
34487        "selectionchange" : true,
34488
34489        /**
34490         * @event beforeselect
34491         * Fires before the selected node changes, return false to cancel the change
34492         * @param {DefaultSelectionModel} this
34493         * @param {TreeNode} node the new selection
34494         * @param {TreeNode} node the old selection
34495         */
34496        "beforeselect" : true
34497    });
34498    
34499     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34500 };
34501
34502 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34503     init : function(tree){
34504         this.tree = tree;
34505         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34506         tree.on("click", this.onNodeClick, this);
34507     },
34508     
34509     onNodeClick : function(node, e){
34510         if (e.ctrlKey && this.selNode == node)  {
34511             this.unselect(node);
34512             return;
34513         }
34514         this.select(node);
34515     },
34516     
34517     /**
34518      * Select a node.
34519      * @param {TreeNode} node The node to select
34520      * @return {TreeNode} The selected node
34521      */
34522     select : function(node){
34523         var last = this.selNode;
34524         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34525             if(last){
34526                 last.ui.onSelectedChange(false);
34527             }
34528             this.selNode = node;
34529             node.ui.onSelectedChange(true);
34530             this.fireEvent("selectionchange", this, node, last);
34531         }
34532         return node;
34533     },
34534     
34535     /**
34536      * Deselect a node.
34537      * @param {TreeNode} node The node to unselect
34538      */
34539     unselect : function(node){
34540         if(this.selNode == node){
34541             this.clearSelections();
34542         }    
34543     },
34544     
34545     /**
34546      * Clear all selections
34547      */
34548     clearSelections : function(){
34549         var n = this.selNode;
34550         if(n){
34551             n.ui.onSelectedChange(false);
34552             this.selNode = null;
34553             this.fireEvent("selectionchange", this, null);
34554         }
34555         return n;
34556     },
34557     
34558     /**
34559      * Get the selected node
34560      * @return {TreeNode} The selected node
34561      */
34562     getSelectedNode : function(){
34563         return this.selNode;    
34564     },
34565     
34566     /**
34567      * Returns true if the node is selected
34568      * @param {TreeNode} node The node to check
34569      * @return {Boolean}
34570      */
34571     isSelected : function(node){
34572         return this.selNode == node;  
34573     },
34574
34575     /**
34576      * Selects the node above the selected node in the tree, intelligently walking the nodes
34577      * @return TreeNode The new selection
34578      */
34579     selectPrevious : function(){
34580         var s = this.selNode || this.lastSelNode;
34581         if(!s){
34582             return null;
34583         }
34584         var ps = s.previousSibling;
34585         if(ps){
34586             if(!ps.isExpanded() || ps.childNodes.length < 1){
34587                 return this.select(ps);
34588             } else{
34589                 var lc = ps.lastChild;
34590                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34591                     lc = lc.lastChild;
34592                 }
34593                 return this.select(lc);
34594             }
34595         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34596             return this.select(s.parentNode);
34597         }
34598         return null;
34599     },
34600
34601     /**
34602      * Selects the node above the selected node in the tree, intelligently walking the nodes
34603      * @return TreeNode The new selection
34604      */
34605     selectNext : function(){
34606         var s = this.selNode || this.lastSelNode;
34607         if(!s){
34608             return null;
34609         }
34610         if(s.firstChild && s.isExpanded()){
34611              return this.select(s.firstChild);
34612          }else if(s.nextSibling){
34613              return this.select(s.nextSibling);
34614          }else if(s.parentNode){
34615             var newS = null;
34616             s.parentNode.bubble(function(){
34617                 if(this.nextSibling){
34618                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34619                     return false;
34620                 }
34621             });
34622             return newS;
34623          }
34624         return null;
34625     },
34626
34627     onKeyDown : function(e){
34628         var s = this.selNode || this.lastSelNode;
34629         // undesirable, but required
34630         var sm = this;
34631         if(!s){
34632             return;
34633         }
34634         var k = e.getKey();
34635         switch(k){
34636              case e.DOWN:
34637                  e.stopEvent();
34638                  this.selectNext();
34639              break;
34640              case e.UP:
34641                  e.stopEvent();
34642                  this.selectPrevious();
34643              break;
34644              case e.RIGHT:
34645                  e.preventDefault();
34646                  if(s.hasChildNodes()){
34647                      if(!s.isExpanded()){
34648                          s.expand();
34649                      }else if(s.firstChild){
34650                          this.select(s.firstChild, e);
34651                      }
34652                  }
34653              break;
34654              case e.LEFT:
34655                  e.preventDefault();
34656                  if(s.hasChildNodes() && s.isExpanded()){
34657                      s.collapse();
34658                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34659                      this.select(s.parentNode, e);
34660                  }
34661              break;
34662         };
34663     }
34664 });
34665
34666 /**
34667  * @class Roo.tree.MultiSelectionModel
34668  * @extends Roo.util.Observable
34669  * Multi selection for a TreePanel.
34670  * @param {Object} cfg Configuration
34671  */
34672 Roo.tree.MultiSelectionModel = function(){
34673    this.selNodes = [];
34674    this.selMap = {};
34675    this.addEvents({
34676        /**
34677         * @event selectionchange
34678         * Fires when the selected nodes change
34679         * @param {MultiSelectionModel} this
34680         * @param {Array} nodes Array of the selected nodes
34681         */
34682        "selectionchange" : true
34683    });
34684    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34685    
34686 };
34687
34688 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34689     init : function(tree){
34690         this.tree = tree;
34691         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34692         tree.on("click", this.onNodeClick, this);
34693     },
34694     
34695     onNodeClick : function(node, e){
34696         this.select(node, e, e.ctrlKey);
34697     },
34698     
34699     /**
34700      * Select a node.
34701      * @param {TreeNode} node The node to select
34702      * @param {EventObject} e (optional) An event associated with the selection
34703      * @param {Boolean} keepExisting True to retain existing selections
34704      * @return {TreeNode} The selected node
34705      */
34706     select : function(node, e, keepExisting){
34707         if(keepExisting !== true){
34708             this.clearSelections(true);
34709         }
34710         if(this.isSelected(node)){
34711             this.lastSelNode = node;
34712             return node;
34713         }
34714         this.selNodes.push(node);
34715         this.selMap[node.id] = node;
34716         this.lastSelNode = node;
34717         node.ui.onSelectedChange(true);
34718         this.fireEvent("selectionchange", this, this.selNodes);
34719         return node;
34720     },
34721     
34722     /**
34723      * Deselect a node.
34724      * @param {TreeNode} node The node to unselect
34725      */
34726     unselect : function(node){
34727         if(this.selMap[node.id]){
34728             node.ui.onSelectedChange(false);
34729             var sn = this.selNodes;
34730             var index = -1;
34731             if(sn.indexOf){
34732                 index = sn.indexOf(node);
34733             }else{
34734                 for(var i = 0, len = sn.length; i < len; i++){
34735                     if(sn[i] == node){
34736                         index = i;
34737                         break;
34738                     }
34739                 }
34740             }
34741             if(index != -1){
34742                 this.selNodes.splice(index, 1);
34743             }
34744             delete this.selMap[node.id];
34745             this.fireEvent("selectionchange", this, this.selNodes);
34746         }
34747     },
34748     
34749     /**
34750      * Clear all selections
34751      */
34752     clearSelections : function(suppressEvent){
34753         var sn = this.selNodes;
34754         if(sn.length > 0){
34755             for(var i = 0, len = sn.length; i < len; i++){
34756                 sn[i].ui.onSelectedChange(false);
34757             }
34758             this.selNodes = [];
34759             this.selMap = {};
34760             if(suppressEvent !== true){
34761                 this.fireEvent("selectionchange", this, this.selNodes);
34762             }
34763         }
34764     },
34765     
34766     /**
34767      * Returns true if the node is selected
34768      * @param {TreeNode} node The node to check
34769      * @return {Boolean}
34770      */
34771     isSelected : function(node){
34772         return this.selMap[node.id] ? true : false;  
34773     },
34774     
34775     /**
34776      * Returns an array of the selected nodes
34777      * @return {Array}
34778      */
34779     getSelectedNodes : function(){
34780         return this.selNodes;    
34781     },
34782
34783     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34784
34785     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34786
34787     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34788 });/*
34789  * Based on:
34790  * Ext JS Library 1.1.1
34791  * Copyright(c) 2006-2007, Ext JS, LLC.
34792  *
34793  * Originally Released Under LGPL - original licence link has changed is not relivant.
34794  *
34795  * Fork - LGPL
34796  * <script type="text/javascript">
34797  */
34798  
34799 /**
34800  * @class Roo.tree.TreeNode
34801  * @extends Roo.data.Node
34802  * @cfg {String} text The text for this node
34803  * @cfg {Boolean} expanded true to start the node expanded
34804  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34805  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34806  * @cfg {Boolean} disabled true to start the node disabled
34807  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34808  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
34809  * @cfg {String} cls A css class to be added to the node
34810  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34811  * @cfg {String} href URL of the link used for the node (defaults to #)
34812  * @cfg {String} hrefTarget target frame for the link
34813  * @cfg {String} qtip An Ext QuickTip for the node
34814  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34815  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34816  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34817  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34818  * (defaults to undefined with no checkbox rendered)
34819  * @constructor
34820  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34821  */
34822 Roo.tree.TreeNode = function(attributes){
34823     attributes = attributes || {};
34824     if(typeof attributes == "string"){
34825         attributes = {text: attributes};
34826     }
34827     this.childrenRendered = false;
34828     this.rendered = false;
34829     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34830     this.expanded = attributes.expanded === true;
34831     this.isTarget = attributes.isTarget !== false;
34832     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34833     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34834
34835     /**
34836      * Read-only. The text for this node. To change it use setText().
34837      * @type String
34838      */
34839     this.text = attributes.text;
34840     /**
34841      * True if this node is disabled.
34842      * @type Boolean
34843      */
34844     this.disabled = attributes.disabled === true;
34845
34846     this.addEvents({
34847         /**
34848         * @event textchange
34849         * Fires when the text for this node is changed
34850         * @param {Node} this This node
34851         * @param {String} text The new text
34852         * @param {String} oldText The old text
34853         */
34854         "textchange" : true,
34855         /**
34856         * @event beforeexpand
34857         * Fires before this node is expanded, return false to cancel.
34858         * @param {Node} this This node
34859         * @param {Boolean} deep
34860         * @param {Boolean} anim
34861         */
34862         "beforeexpand" : true,
34863         /**
34864         * @event beforecollapse
34865         * Fires before this node is collapsed, return false to cancel.
34866         * @param {Node} this This node
34867         * @param {Boolean} deep
34868         * @param {Boolean} anim
34869         */
34870         "beforecollapse" : true,
34871         /**
34872         * @event expand
34873         * Fires when this node is expanded
34874         * @param {Node} this This node
34875         */
34876         "expand" : true,
34877         /**
34878         * @event disabledchange
34879         * Fires when the disabled status of this node changes
34880         * @param {Node} this This node
34881         * @param {Boolean} disabled
34882         */
34883         "disabledchange" : true,
34884         /**
34885         * @event collapse
34886         * Fires when this node is collapsed
34887         * @param {Node} this This node
34888         */
34889         "collapse" : true,
34890         /**
34891         * @event beforeclick
34892         * Fires before click processing. Return false to cancel the default action.
34893         * @param {Node} this This node
34894         * @param {Roo.EventObject} e The event object
34895         */
34896         "beforeclick":true,
34897         /**
34898         * @event checkchange
34899         * Fires when a node with a checkbox's checked property changes
34900         * @param {Node} this This node
34901         * @param {Boolean} checked
34902         */
34903         "checkchange":true,
34904         /**
34905         * @event click
34906         * Fires when this node is clicked
34907         * @param {Node} this This node
34908         * @param {Roo.EventObject} e The event object
34909         */
34910         "click":true,
34911         /**
34912         * @event dblclick
34913         * Fires when this node is double clicked
34914         * @param {Node} this This node
34915         * @param {Roo.EventObject} e The event object
34916         */
34917         "dblclick":true,
34918         /**
34919         * @event contextmenu
34920         * Fires when this node is right clicked
34921         * @param {Node} this This node
34922         * @param {Roo.EventObject} e The event object
34923         */
34924         "contextmenu":true,
34925         /**
34926         * @event beforechildrenrendered
34927         * Fires right before the child nodes for this node are rendered
34928         * @param {Node} this This node
34929         */
34930         "beforechildrenrendered":true
34931     });
34932
34933     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
34934
34935     /**
34936      * Read-only. The UI for this node
34937      * @type TreeNodeUI
34938      */
34939     this.ui = new uiClass(this);
34940     
34941     // finally support items[]
34942     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
34943         return;
34944     }
34945     
34946     
34947     Roo.each(this.attributes.items, function(c) {
34948         this.appendChild(Roo.factory(c,Roo.Tree));
34949     }, this);
34950     delete this.attributes.items;
34951     
34952     
34953     
34954 };
34955 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
34956     preventHScroll: true,
34957     /**
34958      * Returns true if this node is expanded
34959      * @return {Boolean}
34960      */
34961     isExpanded : function(){
34962         return this.expanded;
34963     },
34964
34965     /**
34966      * Returns the UI object for this node
34967      * @return {TreeNodeUI}
34968      */
34969     getUI : function(){
34970         return this.ui;
34971     },
34972
34973     // private override
34974     setFirstChild : function(node){
34975         var of = this.firstChild;
34976         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
34977         if(this.childrenRendered && of && node != of){
34978             of.renderIndent(true, true);
34979         }
34980         if(this.rendered){
34981             this.renderIndent(true, true);
34982         }
34983     },
34984
34985     // private override
34986     setLastChild : function(node){
34987         var ol = this.lastChild;
34988         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
34989         if(this.childrenRendered && ol && node != ol){
34990             ol.renderIndent(true, true);
34991         }
34992         if(this.rendered){
34993             this.renderIndent(true, true);
34994         }
34995     },
34996
34997     // these methods are overridden to provide lazy rendering support
34998     // private override
34999     appendChild : function()
35000     {
35001         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35002         if(node && this.childrenRendered){
35003             node.render();
35004         }
35005         this.ui.updateExpandIcon();
35006         return node;
35007     },
35008
35009     // private override
35010     removeChild : function(node){
35011         this.ownerTree.getSelectionModel().unselect(node);
35012         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35013         // if it's been rendered remove dom node
35014         if(this.childrenRendered){
35015             node.ui.remove();
35016         }
35017         if(this.childNodes.length < 1){
35018             this.collapse(false, false);
35019         }else{
35020             this.ui.updateExpandIcon();
35021         }
35022         if(!this.firstChild) {
35023             this.childrenRendered = false;
35024         }
35025         return node;
35026     },
35027
35028     // private override
35029     insertBefore : function(node, refNode){
35030         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35031         if(newNode && refNode && this.childrenRendered){
35032             node.render();
35033         }
35034         this.ui.updateExpandIcon();
35035         return newNode;
35036     },
35037
35038     /**
35039      * Sets the text for this node
35040      * @param {String} text
35041      */
35042     setText : function(text){
35043         var oldText = this.text;
35044         this.text = text;
35045         this.attributes.text = text;
35046         if(this.rendered){ // event without subscribing
35047             this.ui.onTextChange(this, text, oldText);
35048         }
35049         this.fireEvent("textchange", this, text, oldText);
35050     },
35051
35052     /**
35053      * Triggers selection of this node
35054      */
35055     select : function(){
35056         this.getOwnerTree().getSelectionModel().select(this);
35057     },
35058
35059     /**
35060      * Triggers deselection of this node
35061      */
35062     unselect : function(){
35063         this.getOwnerTree().getSelectionModel().unselect(this);
35064     },
35065
35066     /**
35067      * Returns true if this node is selected
35068      * @return {Boolean}
35069      */
35070     isSelected : function(){
35071         return this.getOwnerTree().getSelectionModel().isSelected(this);
35072     },
35073
35074     /**
35075      * Expand this node.
35076      * @param {Boolean} deep (optional) True to expand all children as well
35077      * @param {Boolean} anim (optional) false to cancel the default animation
35078      * @param {Function} callback (optional) A callback to be called when
35079      * expanding this node completes (does not wait for deep expand to complete).
35080      * Called with 1 parameter, this node.
35081      */
35082     expand : function(deep, anim, callback){
35083         if(!this.expanded){
35084             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35085                 return;
35086             }
35087             if(!this.childrenRendered){
35088                 this.renderChildren();
35089             }
35090             this.expanded = true;
35091             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
35092                 this.ui.animExpand(function(){
35093                     this.fireEvent("expand", this);
35094                     if(typeof callback == "function"){
35095                         callback(this);
35096                     }
35097                     if(deep === true){
35098                         this.expandChildNodes(true);
35099                     }
35100                 }.createDelegate(this));
35101                 return;
35102             }else{
35103                 this.ui.expand();
35104                 this.fireEvent("expand", this);
35105                 if(typeof callback == "function"){
35106                     callback(this);
35107                 }
35108             }
35109         }else{
35110            if(typeof callback == "function"){
35111                callback(this);
35112            }
35113         }
35114         if(deep === true){
35115             this.expandChildNodes(true);
35116         }
35117     },
35118
35119     isHiddenRoot : function(){
35120         return this.isRoot && !this.getOwnerTree().rootVisible;
35121     },
35122
35123     /**
35124      * Collapse this node.
35125      * @param {Boolean} deep (optional) True to collapse all children as well
35126      * @param {Boolean} anim (optional) false to cancel the default animation
35127      */
35128     collapse : function(deep, anim){
35129         if(this.expanded && !this.isHiddenRoot()){
35130             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35131                 return;
35132             }
35133             this.expanded = false;
35134             if((this.getOwnerTree().animate && anim !== false) || anim){
35135                 this.ui.animCollapse(function(){
35136                     this.fireEvent("collapse", this);
35137                     if(deep === true){
35138                         this.collapseChildNodes(true);
35139                     }
35140                 }.createDelegate(this));
35141                 return;
35142             }else{
35143                 this.ui.collapse();
35144                 this.fireEvent("collapse", this);
35145             }
35146         }
35147         if(deep === true){
35148             var cs = this.childNodes;
35149             for(var i = 0, len = cs.length; i < len; i++) {
35150                 cs[i].collapse(true, false);
35151             }
35152         }
35153     },
35154
35155     // private
35156     delayedExpand : function(delay){
35157         if(!this.expandProcId){
35158             this.expandProcId = this.expand.defer(delay, this);
35159         }
35160     },
35161
35162     // private
35163     cancelExpand : function(){
35164         if(this.expandProcId){
35165             clearTimeout(this.expandProcId);
35166         }
35167         this.expandProcId = false;
35168     },
35169
35170     /**
35171      * Toggles expanded/collapsed state of the node
35172      */
35173     toggle : function(){
35174         if(this.expanded){
35175             this.collapse();
35176         }else{
35177             this.expand();
35178         }
35179     },
35180
35181     /**
35182      * Ensures all parent nodes are expanded
35183      */
35184     ensureVisible : function(callback){
35185         var tree = this.getOwnerTree();
35186         tree.expandPath(this.parentNode.getPath(), false, function(){
35187             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35188             Roo.callback(callback);
35189         }.createDelegate(this));
35190     },
35191
35192     /**
35193      * Expand all child nodes
35194      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35195      */
35196     expandChildNodes : function(deep){
35197         var cs = this.childNodes;
35198         for(var i = 0, len = cs.length; i < len; i++) {
35199                 cs[i].expand(deep);
35200         }
35201     },
35202
35203     /**
35204      * Collapse all child nodes
35205      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35206      */
35207     collapseChildNodes : function(deep){
35208         var cs = this.childNodes;
35209         for(var i = 0, len = cs.length; i < len; i++) {
35210                 cs[i].collapse(deep);
35211         }
35212     },
35213
35214     /**
35215      * Disables this node
35216      */
35217     disable : function(){
35218         this.disabled = true;
35219         this.unselect();
35220         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35221             this.ui.onDisableChange(this, true);
35222         }
35223         this.fireEvent("disabledchange", this, true);
35224     },
35225
35226     /**
35227      * Enables this node
35228      */
35229     enable : function(){
35230         this.disabled = false;
35231         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35232             this.ui.onDisableChange(this, false);
35233         }
35234         this.fireEvent("disabledchange", this, false);
35235     },
35236
35237     // private
35238     renderChildren : function(suppressEvent){
35239         if(suppressEvent !== false){
35240             this.fireEvent("beforechildrenrendered", this);
35241         }
35242         var cs = this.childNodes;
35243         for(var i = 0, len = cs.length; i < len; i++){
35244             cs[i].render(true);
35245         }
35246         this.childrenRendered = true;
35247     },
35248
35249     // private
35250     sort : function(fn, scope){
35251         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35252         if(this.childrenRendered){
35253             var cs = this.childNodes;
35254             for(var i = 0, len = cs.length; i < len; i++){
35255                 cs[i].render(true);
35256             }
35257         }
35258     },
35259
35260     // private
35261     render : function(bulkRender){
35262         this.ui.render(bulkRender);
35263         if(!this.rendered){
35264             this.rendered = true;
35265             if(this.expanded){
35266                 this.expanded = false;
35267                 this.expand(false, false);
35268             }
35269         }
35270     },
35271
35272     // private
35273     renderIndent : function(deep, refresh){
35274         if(refresh){
35275             this.ui.childIndent = null;
35276         }
35277         this.ui.renderIndent();
35278         if(deep === true && this.childrenRendered){
35279             var cs = this.childNodes;
35280             for(var i = 0, len = cs.length; i < len; i++){
35281                 cs[i].renderIndent(true, refresh);
35282             }
35283         }
35284     }
35285 });/*
35286  * Based on:
35287  * Ext JS Library 1.1.1
35288  * Copyright(c) 2006-2007, Ext JS, LLC.
35289  *
35290  * Originally Released Under LGPL - original licence link has changed is not relivant.
35291  *
35292  * Fork - LGPL
35293  * <script type="text/javascript">
35294  */
35295  
35296 /**
35297  * @class Roo.tree.AsyncTreeNode
35298  * @extends Roo.tree.TreeNode
35299  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35300  * @constructor
35301  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35302  */
35303  Roo.tree.AsyncTreeNode = function(config){
35304     this.loaded = false;
35305     this.loading = false;
35306     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35307     /**
35308     * @event beforeload
35309     * Fires before this node is loaded, return false to cancel
35310     * @param {Node} this This node
35311     */
35312     this.addEvents({'beforeload':true, 'load': true});
35313     /**
35314     * @event load
35315     * Fires when this node is loaded
35316     * @param {Node} this This node
35317     */
35318     /**
35319      * The loader used by this node (defaults to using the tree's defined loader)
35320      * @type TreeLoader
35321      * @property loader
35322      */
35323 };
35324 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35325     expand : function(deep, anim, callback){
35326         if(this.loading){ // if an async load is already running, waiting til it's done
35327             var timer;
35328             var f = function(){
35329                 if(!this.loading){ // done loading
35330                     clearInterval(timer);
35331                     this.expand(deep, anim, callback);
35332                 }
35333             }.createDelegate(this);
35334             timer = setInterval(f, 200);
35335             return;
35336         }
35337         if(!this.loaded){
35338             if(this.fireEvent("beforeload", this) === false){
35339                 return;
35340             }
35341             this.loading = true;
35342             this.ui.beforeLoad(this);
35343             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35344             if(loader){
35345                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35346                 return;
35347             }
35348         }
35349         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35350     },
35351     
35352     /**
35353      * Returns true if this node is currently loading
35354      * @return {Boolean}
35355      */
35356     isLoading : function(){
35357         return this.loading;  
35358     },
35359     
35360     loadComplete : function(deep, anim, callback){
35361         this.loading = false;
35362         this.loaded = true;
35363         this.ui.afterLoad(this);
35364         this.fireEvent("load", this);
35365         this.expand(deep, anim, callback);
35366     },
35367     
35368     /**
35369      * Returns true if this node has been loaded
35370      * @return {Boolean}
35371      */
35372     isLoaded : function(){
35373         return this.loaded;
35374     },
35375     
35376     hasChildNodes : function(){
35377         if(!this.isLeaf() && !this.loaded){
35378             return true;
35379         }else{
35380             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35381         }
35382     },
35383
35384     /**
35385      * Trigger a reload for this node
35386      * @param {Function} callback
35387      */
35388     reload : function(callback){
35389         this.collapse(false, false);
35390         while(this.firstChild){
35391             this.removeChild(this.firstChild);
35392         }
35393         this.childrenRendered = false;
35394         this.loaded = false;
35395         if(this.isHiddenRoot()){
35396             this.expanded = false;
35397         }
35398         this.expand(false, false, callback);
35399     }
35400 });/*
35401  * Based on:
35402  * Ext JS Library 1.1.1
35403  * Copyright(c) 2006-2007, Ext JS, LLC.
35404  *
35405  * Originally Released Under LGPL - original licence link has changed is not relivant.
35406  *
35407  * Fork - LGPL
35408  * <script type="text/javascript">
35409  */
35410  
35411 /**
35412  * @class Roo.tree.TreeNodeUI
35413  * @constructor
35414  * @param {Object} node The node to render
35415  * The TreeNode UI implementation is separate from the
35416  * tree implementation. Unless you are customizing the tree UI,
35417  * you should never have to use this directly.
35418  */
35419 Roo.tree.TreeNodeUI = function(node){
35420     this.node = node;
35421     this.rendered = false;
35422     this.animating = false;
35423     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35424 };
35425
35426 Roo.tree.TreeNodeUI.prototype = {
35427     removeChild : function(node){
35428         if(this.rendered){
35429             this.ctNode.removeChild(node.ui.getEl());
35430         }
35431     },
35432
35433     beforeLoad : function(){
35434          this.addClass("x-tree-node-loading");
35435     },
35436
35437     afterLoad : function(){
35438          this.removeClass("x-tree-node-loading");
35439     },
35440
35441     onTextChange : function(node, text, oldText){
35442         if(this.rendered){
35443             this.textNode.innerHTML = text;
35444         }
35445     },
35446
35447     onDisableChange : function(node, state){
35448         this.disabled = state;
35449         if(state){
35450             this.addClass("x-tree-node-disabled");
35451         }else{
35452             this.removeClass("x-tree-node-disabled");
35453         }
35454     },
35455
35456     onSelectedChange : function(state){
35457         if(state){
35458             this.focus();
35459             this.addClass("x-tree-selected");
35460         }else{
35461             //this.blur();
35462             this.removeClass("x-tree-selected");
35463         }
35464     },
35465
35466     onMove : function(tree, node, oldParent, newParent, index, refNode){
35467         this.childIndent = null;
35468         if(this.rendered){
35469             var targetNode = newParent.ui.getContainer();
35470             if(!targetNode){//target not rendered
35471                 this.holder = document.createElement("div");
35472                 this.holder.appendChild(this.wrap);
35473                 return;
35474             }
35475             var insertBefore = refNode ? refNode.ui.getEl() : null;
35476             if(insertBefore){
35477                 targetNode.insertBefore(this.wrap, insertBefore);
35478             }else{
35479                 targetNode.appendChild(this.wrap);
35480             }
35481             this.node.renderIndent(true);
35482         }
35483     },
35484
35485     addClass : function(cls){
35486         if(this.elNode){
35487             Roo.fly(this.elNode).addClass(cls);
35488         }
35489     },
35490
35491     removeClass : function(cls){
35492         if(this.elNode){
35493             Roo.fly(this.elNode).removeClass(cls);
35494         }
35495     },
35496
35497     remove : function(){
35498         if(this.rendered){
35499             this.holder = document.createElement("div");
35500             this.holder.appendChild(this.wrap);
35501         }
35502     },
35503
35504     fireEvent : function(){
35505         return this.node.fireEvent.apply(this.node, arguments);
35506     },
35507
35508     initEvents : function(){
35509         this.node.on("move", this.onMove, this);
35510         var E = Roo.EventManager;
35511         var a = this.anchor;
35512
35513         var el = Roo.fly(a, '_treeui');
35514
35515         if(Roo.isOpera){ // opera render bug ignores the CSS
35516             el.setStyle("text-decoration", "none");
35517         }
35518
35519         el.on("click", this.onClick, this);
35520         el.on("dblclick", this.onDblClick, this);
35521
35522         if(this.checkbox){
35523             Roo.EventManager.on(this.checkbox,
35524                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35525         }
35526
35527         el.on("contextmenu", this.onContextMenu, this);
35528
35529         var icon = Roo.fly(this.iconNode);
35530         icon.on("click", this.onClick, this);
35531         icon.on("dblclick", this.onDblClick, this);
35532         icon.on("contextmenu", this.onContextMenu, this);
35533         E.on(this.ecNode, "click", this.ecClick, this, true);
35534
35535         if(this.node.disabled){
35536             this.addClass("x-tree-node-disabled");
35537         }
35538         if(this.node.hidden){
35539             this.addClass("x-tree-node-disabled");
35540         }
35541         var ot = this.node.getOwnerTree();
35542         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
35543         if(dd && (!this.node.isRoot || ot.rootVisible)){
35544             Roo.dd.Registry.register(this.elNode, {
35545                 node: this.node,
35546                 handles: this.getDDHandles(),
35547                 isHandle: false
35548             });
35549         }
35550     },
35551
35552     getDDHandles : function(){
35553         return [this.iconNode, this.textNode];
35554     },
35555
35556     hide : function(){
35557         if(this.rendered){
35558             this.wrap.style.display = "none";
35559         }
35560     },
35561
35562     show : function(){
35563         if(this.rendered){
35564             this.wrap.style.display = "";
35565         }
35566     },
35567
35568     onContextMenu : function(e){
35569         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35570             e.preventDefault();
35571             this.focus();
35572             this.fireEvent("contextmenu", this.node, e);
35573         }
35574     },
35575
35576     onClick : function(e){
35577         if(this.dropping){
35578             e.stopEvent();
35579             return;
35580         }
35581         if(this.fireEvent("beforeclick", this.node, e) !== false){
35582             if(!this.disabled && this.node.attributes.href){
35583                 this.fireEvent("click", this.node, e);
35584                 return;
35585             }
35586             e.preventDefault();
35587             if(this.disabled){
35588                 return;
35589             }
35590
35591             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35592                 this.node.toggle();
35593             }
35594
35595             this.fireEvent("click", this.node, e);
35596         }else{
35597             e.stopEvent();
35598         }
35599     },
35600
35601     onDblClick : function(e){
35602         e.preventDefault();
35603         if(this.disabled){
35604             return;
35605         }
35606         if(this.checkbox){
35607             this.toggleCheck();
35608         }
35609         if(!this.animating && this.node.hasChildNodes()){
35610             this.node.toggle();
35611         }
35612         this.fireEvent("dblclick", this.node, e);
35613     },
35614
35615     onCheckChange : function(){
35616         var checked = this.checkbox.checked;
35617         this.node.attributes.checked = checked;
35618         this.fireEvent('checkchange', this.node, checked);
35619     },
35620
35621     ecClick : function(e){
35622         if(!this.animating && this.node.hasChildNodes()){
35623             this.node.toggle();
35624         }
35625     },
35626
35627     startDrop : function(){
35628         this.dropping = true;
35629     },
35630
35631     // delayed drop so the click event doesn't get fired on a drop
35632     endDrop : function(){
35633        setTimeout(function(){
35634            this.dropping = false;
35635        }.createDelegate(this), 50);
35636     },
35637
35638     expand : function(){
35639         this.updateExpandIcon();
35640         this.ctNode.style.display = "";
35641     },
35642
35643     focus : function(){
35644         if(!this.node.preventHScroll){
35645             try{this.anchor.focus();
35646             }catch(e){}
35647         }else if(!Roo.isIE){
35648             try{
35649                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35650                 var l = noscroll.scrollLeft;
35651                 this.anchor.focus();
35652                 noscroll.scrollLeft = l;
35653             }catch(e){}
35654         }
35655     },
35656
35657     toggleCheck : function(value){
35658         var cb = this.checkbox;
35659         if(cb){
35660             cb.checked = (value === undefined ? !cb.checked : value);
35661         }
35662     },
35663
35664     blur : function(){
35665         try{
35666             this.anchor.blur();
35667         }catch(e){}
35668     },
35669
35670     animExpand : function(callback){
35671         var ct = Roo.get(this.ctNode);
35672         ct.stopFx();
35673         if(!this.node.hasChildNodes()){
35674             this.updateExpandIcon();
35675             this.ctNode.style.display = "";
35676             Roo.callback(callback);
35677             return;
35678         }
35679         this.animating = true;
35680         this.updateExpandIcon();
35681
35682         ct.slideIn('t', {
35683            callback : function(){
35684                this.animating = false;
35685                Roo.callback(callback);
35686             },
35687             scope: this,
35688             duration: this.node.ownerTree.duration || .25
35689         });
35690     },
35691
35692     highlight : function(){
35693         var tree = this.node.getOwnerTree();
35694         Roo.fly(this.wrap).highlight(
35695             tree.hlColor || "C3DAF9",
35696             {endColor: tree.hlBaseColor}
35697         );
35698     },
35699
35700     collapse : function(){
35701         this.updateExpandIcon();
35702         this.ctNode.style.display = "none";
35703     },
35704
35705     animCollapse : function(callback){
35706         var ct = Roo.get(this.ctNode);
35707         ct.enableDisplayMode('block');
35708         ct.stopFx();
35709
35710         this.animating = true;
35711         this.updateExpandIcon();
35712
35713         ct.slideOut('t', {
35714             callback : function(){
35715                this.animating = false;
35716                Roo.callback(callback);
35717             },
35718             scope: this,
35719             duration: this.node.ownerTree.duration || .25
35720         });
35721     },
35722
35723     getContainer : function(){
35724         return this.ctNode;
35725     },
35726
35727     getEl : function(){
35728         return this.wrap;
35729     },
35730
35731     appendDDGhost : function(ghostNode){
35732         ghostNode.appendChild(this.elNode.cloneNode(true));
35733     },
35734
35735     getDDRepairXY : function(){
35736         return Roo.lib.Dom.getXY(this.iconNode);
35737     },
35738
35739     onRender : function(){
35740         this.render();
35741     },
35742
35743     render : function(bulkRender){
35744         var n = this.node, a = n.attributes;
35745         var targetNode = n.parentNode ?
35746               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35747
35748         if(!this.rendered){
35749             this.rendered = true;
35750
35751             this.renderElements(n, a, targetNode, bulkRender);
35752
35753             if(a.qtip){
35754                if(this.textNode.setAttributeNS){
35755                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35756                    if(a.qtipTitle){
35757                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35758                    }
35759                }else{
35760                    this.textNode.setAttribute("ext:qtip", a.qtip);
35761                    if(a.qtipTitle){
35762                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35763                    }
35764                }
35765             }else if(a.qtipCfg){
35766                 a.qtipCfg.target = Roo.id(this.textNode);
35767                 Roo.QuickTips.register(a.qtipCfg);
35768             }
35769             this.initEvents();
35770             if(!this.node.expanded){
35771                 this.updateExpandIcon();
35772             }
35773         }else{
35774             if(bulkRender === true) {
35775                 targetNode.appendChild(this.wrap);
35776             }
35777         }
35778     },
35779
35780     renderElements : function(n, a, targetNode, bulkRender)
35781     {
35782         // add some indent caching, this helps performance when rendering a large tree
35783         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35784         var t = n.getOwnerTree();
35785         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35786         if (typeof(n.attributes.html) != 'undefined') {
35787             txt = n.attributes.html;
35788         }
35789         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
35790         var cb = typeof a.checked == 'boolean';
35791         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35792         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35793             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35794             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35795             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35796             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35797             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35798              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35799                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35800             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35801             "</li>"];
35802
35803         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35804             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35805                                 n.nextSibling.ui.getEl(), buf.join(""));
35806         }else{
35807             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35808         }
35809
35810         this.elNode = this.wrap.childNodes[0];
35811         this.ctNode = this.wrap.childNodes[1];
35812         var cs = this.elNode.childNodes;
35813         this.indentNode = cs[0];
35814         this.ecNode = cs[1];
35815         this.iconNode = cs[2];
35816         var index = 3;
35817         if(cb){
35818             this.checkbox = cs[3];
35819             index++;
35820         }
35821         this.anchor = cs[index];
35822         this.textNode = cs[index].firstChild;
35823     },
35824
35825     getAnchor : function(){
35826         return this.anchor;
35827     },
35828
35829     getTextEl : function(){
35830         return this.textNode;
35831     },
35832
35833     getIconEl : function(){
35834         return this.iconNode;
35835     },
35836
35837     isChecked : function(){
35838         return this.checkbox ? this.checkbox.checked : false;
35839     },
35840
35841     updateExpandIcon : function(){
35842         if(this.rendered){
35843             var n = this.node, c1, c2;
35844             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35845             var hasChild = n.hasChildNodes();
35846             if(hasChild){
35847                 if(n.expanded){
35848                     cls += "-minus";
35849                     c1 = "x-tree-node-collapsed";
35850                     c2 = "x-tree-node-expanded";
35851                 }else{
35852                     cls += "-plus";
35853                     c1 = "x-tree-node-expanded";
35854                     c2 = "x-tree-node-collapsed";
35855                 }
35856                 if(this.wasLeaf){
35857                     this.removeClass("x-tree-node-leaf");
35858                     this.wasLeaf = false;
35859                 }
35860                 if(this.c1 != c1 || this.c2 != c2){
35861                     Roo.fly(this.elNode).replaceClass(c1, c2);
35862                     this.c1 = c1; this.c2 = c2;
35863                 }
35864             }else{
35865                 // this changes non-leafs into leafs if they have no children.
35866                 // it's not very rational behaviour..
35867                 
35868                 if(!this.wasLeaf && this.node.leaf){
35869                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35870                     delete this.c1;
35871                     delete this.c2;
35872                     this.wasLeaf = true;
35873                 }
35874             }
35875             var ecc = "x-tree-ec-icon "+cls;
35876             if(this.ecc != ecc){
35877                 this.ecNode.className = ecc;
35878                 this.ecc = ecc;
35879             }
35880         }
35881     },
35882
35883     getChildIndent : function(){
35884         if(!this.childIndent){
35885             var buf = [];
35886             var p = this.node;
35887             while(p){
35888                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35889                     if(!p.isLast()) {
35890                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35891                     } else {
35892                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35893                     }
35894                 }
35895                 p = p.parentNode;
35896             }
35897             this.childIndent = buf.join("");
35898         }
35899         return this.childIndent;
35900     },
35901
35902     renderIndent : function(){
35903         if(this.rendered){
35904             var indent = "";
35905             var p = this.node.parentNode;
35906             if(p){
35907                 indent = p.ui.getChildIndent();
35908             }
35909             if(this.indentMarkup != indent){ // don't rerender if not required
35910                 this.indentNode.innerHTML = indent;
35911                 this.indentMarkup = indent;
35912             }
35913             this.updateExpandIcon();
35914         }
35915     }
35916 };
35917
35918 Roo.tree.RootTreeNodeUI = function(){
35919     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
35920 };
35921 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
35922     render : function(){
35923         if(!this.rendered){
35924             var targetNode = this.node.ownerTree.innerCt.dom;
35925             this.node.expanded = true;
35926             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
35927             this.wrap = this.ctNode = targetNode.firstChild;
35928         }
35929     },
35930     collapse : function(){
35931     },
35932     expand : function(){
35933     }
35934 });/*
35935  * Based on:
35936  * Ext JS Library 1.1.1
35937  * Copyright(c) 2006-2007, Ext JS, LLC.
35938  *
35939  * Originally Released Under LGPL - original licence link has changed is not relivant.
35940  *
35941  * Fork - LGPL
35942  * <script type="text/javascript">
35943  */
35944 /**
35945  * @class Roo.tree.TreeLoader
35946  * @extends Roo.util.Observable
35947  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
35948  * nodes from a specified URL. The response must be a javascript Array definition
35949  * who's elements are node definition objects. eg:
35950  * <pre><code>
35951 {  success : true,
35952    data :      [
35953    
35954     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
35955     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
35956     ]
35957 }
35958
35959
35960 </code></pre>
35961  * <br><br>
35962  * The old style respose with just an array is still supported, but not recommended.
35963  * <br><br>
35964  *
35965  * A server request is sent, and child nodes are loaded only when a node is expanded.
35966  * The loading node's id is passed to the server under the parameter name "node" to
35967  * enable the server to produce the correct child nodes.
35968  * <br><br>
35969  * To pass extra parameters, an event handler may be attached to the "beforeload"
35970  * event, and the parameters specified in the TreeLoader's baseParams property:
35971  * <pre><code>
35972     myTreeLoader.on("beforeload", function(treeLoader, node) {
35973         this.baseParams.category = node.attributes.category;
35974     }, this);
35975 </code></pre><
35976  * This would pass an HTTP parameter called "category" to the server containing
35977  * the value of the Node's "category" attribute.
35978  * @constructor
35979  * Creates a new Treeloader.
35980  * @param {Object} config A config object containing config properties.
35981  */
35982 Roo.tree.TreeLoader = function(config){
35983     this.baseParams = {};
35984     this.requestMethod = "POST";
35985     Roo.apply(this, config);
35986
35987     this.addEvents({
35988     
35989         /**
35990          * @event beforeload
35991          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
35992          * @param {Object} This TreeLoader object.
35993          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35994          * @param {Object} callback The callback function specified in the {@link #load} call.
35995          */
35996         beforeload : true,
35997         /**
35998          * @event load
35999          * Fires when the node has been successfuly loaded.
36000          * @param {Object} This TreeLoader object.
36001          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36002          * @param {Object} response The response object containing the data from the server.
36003          */
36004         load : true,
36005         /**
36006          * @event loadexception
36007          * Fires if the network request failed.
36008          * @param {Object} This TreeLoader object.
36009          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36010          * @param {Object} response The response object containing the data from the server.
36011          */
36012         loadexception : true,
36013         /**
36014          * @event create
36015          * Fires before a node is created, enabling you to return custom Node types 
36016          * @param {Object} This TreeLoader object.
36017          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36018          */
36019         create : true
36020     });
36021
36022     Roo.tree.TreeLoader.superclass.constructor.call(this);
36023 };
36024
36025 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36026     /**
36027     * @cfg {String} dataUrl The URL from which to request a Json string which
36028     * specifies an array of node definition object representing the child nodes
36029     * to be loaded.
36030     */
36031     /**
36032     * @cfg {String} requestMethod either GET or POST
36033     * defaults to POST (due to BC)
36034     * to be loaded.
36035     */
36036     /**
36037     * @cfg {Object} baseParams (optional) An object containing properties which
36038     * specify HTTP parameters to be passed to each request for child nodes.
36039     */
36040     /**
36041     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36042     * created by this loader. If the attributes sent by the server have an attribute in this object,
36043     * they take priority.
36044     */
36045     /**
36046     * @cfg {Object} uiProviders (optional) An object containing properties which
36047     * 
36048     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36049     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36050     * <i>uiProvider</i> attribute of a returned child node is a string rather
36051     * than a reference to a TreeNodeUI implementation, this that string value
36052     * is used as a property name in the uiProviders object. You can define the provider named
36053     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36054     */
36055     uiProviders : {},
36056
36057     /**
36058     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36059     * child nodes before loading.
36060     */
36061     clearOnLoad : true,
36062
36063     /**
36064     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36065     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36066     * Grid query { data : [ .....] }
36067     */
36068     
36069     root : false,
36070      /**
36071     * @cfg {String} queryParam (optional) 
36072     * Name of the query as it will be passed on the querystring (defaults to 'node')
36073     * eg. the request will be ?node=[id]
36074     */
36075     
36076     
36077     queryParam: false,
36078     
36079     /**
36080      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36081      * This is called automatically when a node is expanded, but may be used to reload
36082      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36083      * @param {Roo.tree.TreeNode} node
36084      * @param {Function} callback
36085      */
36086     load : function(node, callback){
36087         if(this.clearOnLoad){
36088             while(node.firstChild){
36089                 node.removeChild(node.firstChild);
36090             }
36091         }
36092         if(node.attributes.children){ // preloaded json children
36093             var cs = node.attributes.children;
36094             for(var i = 0, len = cs.length; i < len; i++){
36095                 node.appendChild(this.createNode(cs[i]));
36096             }
36097             if(typeof callback == "function"){
36098                 callback();
36099             }
36100         }else if(this.dataUrl){
36101             this.requestData(node, callback);
36102         }
36103     },
36104
36105     getParams: function(node){
36106         var buf = [], bp = this.baseParams;
36107         for(var key in bp){
36108             if(typeof bp[key] != "function"){
36109                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36110             }
36111         }
36112         var n = this.queryParam === false ? 'node' : this.queryParam;
36113         buf.push(n + "=", encodeURIComponent(node.id));
36114         return buf.join("");
36115     },
36116
36117     requestData : function(node, callback){
36118         if(this.fireEvent("beforeload", this, node, callback) !== false){
36119             this.transId = Roo.Ajax.request({
36120                 method:this.requestMethod,
36121                 url: this.dataUrl||this.url,
36122                 success: this.handleResponse,
36123                 failure: this.handleFailure,
36124                 scope: this,
36125                 argument: {callback: callback, node: node},
36126                 params: this.getParams(node)
36127             });
36128         }else{
36129             // if the load is cancelled, make sure we notify
36130             // the node that we are done
36131             if(typeof callback == "function"){
36132                 callback();
36133             }
36134         }
36135     },
36136
36137     isLoading : function(){
36138         return this.transId ? true : false;
36139     },
36140
36141     abort : function(){
36142         if(this.isLoading()){
36143             Roo.Ajax.abort(this.transId);
36144         }
36145     },
36146
36147     // private
36148     createNode : function(attr)
36149     {
36150         // apply baseAttrs, nice idea Corey!
36151         if(this.baseAttrs){
36152             Roo.applyIf(attr, this.baseAttrs);
36153         }
36154         if(this.applyLoader !== false){
36155             attr.loader = this;
36156         }
36157         // uiProvider = depreciated..
36158         
36159         if(typeof(attr.uiProvider) == 'string'){
36160            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36161                 /**  eval:var:attr */ eval(attr.uiProvider);
36162         }
36163         if(typeof(this.uiProviders['default']) != 'undefined') {
36164             attr.uiProvider = this.uiProviders['default'];
36165         }
36166         
36167         this.fireEvent('create', this, attr);
36168         
36169         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36170         return(attr.leaf ?
36171                         new Roo.tree.TreeNode(attr) :
36172                         new Roo.tree.AsyncTreeNode(attr));
36173     },
36174
36175     processResponse : function(response, node, callback)
36176     {
36177         var json = response.responseText;
36178         try {
36179             
36180             var o = Roo.decode(json);
36181             
36182             if (this.root === false && typeof(o.success) != undefined) {
36183                 this.root = 'data'; // the default behaviour for list like data..
36184                 }
36185                 
36186             if (this.root !== false &&  !o.success) {
36187                 // it's a failure condition.
36188                 var a = response.argument;
36189                 this.fireEvent("loadexception", this, a.node, response);
36190                 Roo.log("Load failed - should have a handler really");
36191                 return;
36192             }
36193             
36194             
36195             
36196             if (this.root !== false) {
36197                  o = o[this.root];
36198             }
36199             
36200             for(var i = 0, len = o.length; i < len; i++){
36201                 var n = this.createNode(o[i]);
36202                 if(n){
36203                     node.appendChild(n);
36204                 }
36205             }
36206             if(typeof callback == "function"){
36207                 callback(this, node);
36208             }
36209         }catch(e){
36210             this.handleFailure(response);
36211         }
36212     },
36213
36214     handleResponse : function(response){
36215         this.transId = false;
36216         var a = response.argument;
36217         this.processResponse(response, a.node, a.callback);
36218         this.fireEvent("load", this, a.node, response);
36219     },
36220
36221     handleFailure : function(response)
36222     {
36223         // should handle failure better..
36224         this.transId = false;
36225         var a = response.argument;
36226         this.fireEvent("loadexception", this, a.node, response);
36227         if(typeof a.callback == "function"){
36228             a.callback(this, a.node);
36229         }
36230     }
36231 });/*
36232  * Based on:
36233  * Ext JS Library 1.1.1
36234  * Copyright(c) 2006-2007, Ext JS, LLC.
36235  *
36236  * Originally Released Under LGPL - original licence link has changed is not relivant.
36237  *
36238  * Fork - LGPL
36239  * <script type="text/javascript">
36240  */
36241
36242 /**
36243 * @class Roo.tree.TreeFilter
36244 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36245 * @param {TreePanel} tree
36246 * @param {Object} config (optional)
36247  */
36248 Roo.tree.TreeFilter = function(tree, config){
36249     this.tree = tree;
36250     this.filtered = {};
36251     Roo.apply(this, config);
36252 };
36253
36254 Roo.tree.TreeFilter.prototype = {
36255     clearBlank:false,
36256     reverse:false,
36257     autoClear:false,
36258     remove:false,
36259
36260      /**
36261      * Filter the data by a specific attribute.
36262      * @param {String/RegExp} value Either string that the attribute value
36263      * should start with or a RegExp to test against the attribute
36264      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36265      * @param {TreeNode} startNode (optional) The node to start the filter at.
36266      */
36267     filter : function(value, attr, startNode){
36268         attr = attr || "text";
36269         var f;
36270         if(typeof value == "string"){
36271             var vlen = value.length;
36272             // auto clear empty filter
36273             if(vlen == 0 && this.clearBlank){
36274                 this.clear();
36275                 return;
36276             }
36277             value = value.toLowerCase();
36278             f = function(n){
36279                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36280             };
36281         }else if(value.exec){ // regex?
36282             f = function(n){
36283                 return value.test(n.attributes[attr]);
36284             };
36285         }else{
36286             throw 'Illegal filter type, must be string or regex';
36287         }
36288         this.filterBy(f, null, startNode);
36289         },
36290
36291     /**
36292      * Filter by a function. The passed function will be called with each
36293      * node in the tree (or from the startNode). If the function returns true, the node is kept
36294      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36295      * @param {Function} fn The filter function
36296      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36297      */
36298     filterBy : function(fn, scope, startNode){
36299         startNode = startNode || this.tree.root;
36300         if(this.autoClear){
36301             this.clear();
36302         }
36303         var af = this.filtered, rv = this.reverse;
36304         var f = function(n){
36305             if(n == startNode){
36306                 return true;
36307             }
36308             if(af[n.id]){
36309                 return false;
36310             }
36311             var m = fn.call(scope || n, n);
36312             if(!m || rv){
36313                 af[n.id] = n;
36314                 n.ui.hide();
36315                 return false;
36316             }
36317             return true;
36318         };
36319         startNode.cascade(f);
36320         if(this.remove){
36321            for(var id in af){
36322                if(typeof id != "function"){
36323                    var n = af[id];
36324                    if(n && n.parentNode){
36325                        n.parentNode.removeChild(n);
36326                    }
36327                }
36328            }
36329         }
36330     },
36331
36332     /**
36333      * Clears the current filter. Note: with the "remove" option
36334      * set a filter cannot be cleared.
36335      */
36336     clear : function(){
36337         var t = this.tree;
36338         var af = this.filtered;
36339         for(var id in af){
36340             if(typeof id != "function"){
36341                 var n = af[id];
36342                 if(n){
36343                     n.ui.show();
36344                 }
36345             }
36346         }
36347         this.filtered = {};
36348     }
36349 };
36350 /*
36351  * Based on:
36352  * Ext JS Library 1.1.1
36353  * Copyright(c) 2006-2007, Ext JS, LLC.
36354  *
36355  * Originally Released Under LGPL - original licence link has changed is not relivant.
36356  *
36357  * Fork - LGPL
36358  * <script type="text/javascript">
36359  */
36360  
36361
36362 /**
36363  * @class Roo.tree.TreeSorter
36364  * Provides sorting of nodes in a TreePanel
36365  * 
36366  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36367  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36368  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36369  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36370  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36371  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36372  * @constructor
36373  * @param {TreePanel} tree
36374  * @param {Object} config
36375  */
36376 Roo.tree.TreeSorter = function(tree, config){
36377     Roo.apply(this, config);
36378     tree.on("beforechildrenrendered", this.doSort, this);
36379     tree.on("append", this.updateSort, this);
36380     tree.on("insert", this.updateSort, this);
36381     
36382     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36383     var p = this.property || "text";
36384     var sortType = this.sortType;
36385     var fs = this.folderSort;
36386     var cs = this.caseSensitive === true;
36387     var leafAttr = this.leafAttr || 'leaf';
36388
36389     this.sortFn = function(n1, n2){
36390         if(fs){
36391             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36392                 return 1;
36393             }
36394             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36395                 return -1;
36396             }
36397         }
36398         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36399         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36400         if(v1 < v2){
36401                         return dsc ? +1 : -1;
36402                 }else if(v1 > v2){
36403                         return dsc ? -1 : +1;
36404         }else{
36405                 return 0;
36406         }
36407     };
36408 };
36409
36410 Roo.tree.TreeSorter.prototype = {
36411     doSort : function(node){
36412         node.sort(this.sortFn);
36413     },
36414     
36415     compareNodes : function(n1, n2){
36416         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36417     },
36418     
36419     updateSort : function(tree, node){
36420         if(node.childrenRendered){
36421             this.doSort.defer(1, this, [node]);
36422         }
36423     }
36424 };/*
36425  * Based on:
36426  * Ext JS Library 1.1.1
36427  * Copyright(c) 2006-2007, Ext JS, LLC.
36428  *
36429  * Originally Released Under LGPL - original licence link has changed is not relivant.
36430  *
36431  * Fork - LGPL
36432  * <script type="text/javascript">
36433  */
36434
36435 if(Roo.dd.DropZone){
36436     
36437 Roo.tree.TreeDropZone = function(tree, config){
36438     this.allowParentInsert = false;
36439     this.allowContainerDrop = false;
36440     this.appendOnly = false;
36441     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36442     this.tree = tree;
36443     this.lastInsertClass = "x-tree-no-status";
36444     this.dragOverData = {};
36445 };
36446
36447 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36448     ddGroup : "TreeDD",
36449     scroll:  true,
36450     
36451     expandDelay : 1000,
36452     
36453     expandNode : function(node){
36454         if(node.hasChildNodes() && !node.isExpanded()){
36455             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36456         }
36457     },
36458     
36459     queueExpand : function(node){
36460         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36461     },
36462     
36463     cancelExpand : function(){
36464         if(this.expandProcId){
36465             clearTimeout(this.expandProcId);
36466             this.expandProcId = false;
36467         }
36468     },
36469     
36470     isValidDropPoint : function(n, pt, dd, e, data){
36471         if(!n || !data){ return false; }
36472         var targetNode = n.node;
36473         var dropNode = data.node;
36474         // default drop rules
36475         if(!(targetNode && targetNode.isTarget && pt)){
36476             return false;
36477         }
36478         if(pt == "append" && targetNode.allowChildren === false){
36479             return false;
36480         }
36481         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36482             return false;
36483         }
36484         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36485             return false;
36486         }
36487         // reuse the object
36488         var overEvent = this.dragOverData;
36489         overEvent.tree = this.tree;
36490         overEvent.target = targetNode;
36491         overEvent.data = data;
36492         overEvent.point = pt;
36493         overEvent.source = dd;
36494         overEvent.rawEvent = e;
36495         overEvent.dropNode = dropNode;
36496         overEvent.cancel = false;  
36497         var result = this.tree.fireEvent("nodedragover", overEvent);
36498         return overEvent.cancel === false && result !== false;
36499     },
36500     
36501     getDropPoint : function(e, n, dd)
36502     {
36503         var tn = n.node;
36504         if(tn.isRoot){
36505             return tn.allowChildren !== false ? "append" : false; // always append for root
36506         }
36507         var dragEl = n.ddel;
36508         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36509         var y = Roo.lib.Event.getPageY(e);
36510         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36511         
36512         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36513         var noAppend = tn.allowChildren === false;
36514         if(this.appendOnly || tn.parentNode.allowChildren === false){
36515             return noAppend ? false : "append";
36516         }
36517         var noBelow = false;
36518         if(!this.allowParentInsert){
36519             noBelow = tn.hasChildNodes() && tn.isExpanded();
36520         }
36521         var q = (b - t) / (noAppend ? 2 : 3);
36522         if(y >= t && y < (t + q)){
36523             return "above";
36524         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36525             return "below";
36526         }else{
36527             return "append";
36528         }
36529     },
36530     
36531     onNodeEnter : function(n, dd, e, data)
36532     {
36533         this.cancelExpand();
36534     },
36535     
36536     onNodeOver : function(n, dd, e, data)
36537     {
36538        
36539         var pt = this.getDropPoint(e, n, dd);
36540         var node = n.node;
36541         
36542         // auto node expand check
36543         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36544             this.queueExpand(node);
36545         }else if(pt != "append"){
36546             this.cancelExpand();
36547         }
36548         
36549         // set the insert point style on the target node
36550         var returnCls = this.dropNotAllowed;
36551         if(this.isValidDropPoint(n, pt, dd, e, data)){
36552            if(pt){
36553                var el = n.ddel;
36554                var cls;
36555                if(pt == "above"){
36556                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36557                    cls = "x-tree-drag-insert-above";
36558                }else if(pt == "below"){
36559                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36560                    cls = "x-tree-drag-insert-below";
36561                }else{
36562                    returnCls = "x-tree-drop-ok-append";
36563                    cls = "x-tree-drag-append";
36564                }
36565                if(this.lastInsertClass != cls){
36566                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36567                    this.lastInsertClass = cls;
36568                }
36569            }
36570        }
36571        return returnCls;
36572     },
36573     
36574     onNodeOut : function(n, dd, e, data){
36575         
36576         this.cancelExpand();
36577         this.removeDropIndicators(n);
36578     },
36579     
36580     onNodeDrop : function(n, dd, e, data){
36581         var point = this.getDropPoint(e, n, dd);
36582         var targetNode = n.node;
36583         targetNode.ui.startDrop();
36584         if(!this.isValidDropPoint(n, point, dd, e, data)){
36585             targetNode.ui.endDrop();
36586             return false;
36587         }
36588         // first try to find the drop node
36589         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36590         var dropEvent = {
36591             tree : this.tree,
36592             target: targetNode,
36593             data: data,
36594             point: point,
36595             source: dd,
36596             rawEvent: e,
36597             dropNode: dropNode,
36598             cancel: !dropNode   
36599         };
36600         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36601         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36602             targetNode.ui.endDrop();
36603             return false;
36604         }
36605         // allow target changing
36606         targetNode = dropEvent.target;
36607         if(point == "append" && !targetNode.isExpanded()){
36608             targetNode.expand(false, null, function(){
36609                 this.completeDrop(dropEvent);
36610             }.createDelegate(this));
36611         }else{
36612             this.completeDrop(dropEvent);
36613         }
36614         return true;
36615     },
36616     
36617     completeDrop : function(de){
36618         var ns = de.dropNode, p = de.point, t = de.target;
36619         if(!(ns instanceof Array)){
36620             ns = [ns];
36621         }
36622         var n;
36623         for(var i = 0, len = ns.length; i < len; i++){
36624             n = ns[i];
36625             if(p == "above"){
36626                 t.parentNode.insertBefore(n, t);
36627             }else if(p == "below"){
36628                 t.parentNode.insertBefore(n, t.nextSibling);
36629             }else{
36630                 t.appendChild(n);
36631             }
36632         }
36633         n.ui.focus();
36634         if(this.tree.hlDrop){
36635             n.ui.highlight();
36636         }
36637         t.ui.endDrop();
36638         this.tree.fireEvent("nodedrop", de);
36639     },
36640     
36641     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36642         if(this.tree.hlDrop){
36643             dropNode.ui.focus();
36644             dropNode.ui.highlight();
36645         }
36646         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36647     },
36648     
36649     getTree : function(){
36650         return this.tree;
36651     },
36652     
36653     removeDropIndicators : function(n){
36654         if(n && n.ddel){
36655             var el = n.ddel;
36656             Roo.fly(el).removeClass([
36657                     "x-tree-drag-insert-above",
36658                     "x-tree-drag-insert-below",
36659                     "x-tree-drag-append"]);
36660             this.lastInsertClass = "_noclass";
36661         }
36662     },
36663     
36664     beforeDragDrop : function(target, e, id){
36665         this.cancelExpand();
36666         return true;
36667     },
36668     
36669     afterRepair : function(data){
36670         if(data && Roo.enableFx){
36671             data.node.ui.highlight();
36672         }
36673         this.hideProxy();
36674     } 
36675     
36676 });
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 if(Roo.dd.DragZone){
36692 Roo.tree.TreeDragZone = function(tree, config){
36693     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36694     this.tree = tree;
36695 };
36696
36697 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36698     ddGroup : "TreeDD",
36699    
36700     onBeforeDrag : function(data, e){
36701         var n = data.node;
36702         return n && n.draggable && !n.disabled;
36703     },
36704      
36705     
36706     onInitDrag : function(e){
36707         var data = this.dragData;
36708         this.tree.getSelectionModel().select(data.node);
36709         this.proxy.update("");
36710         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36711         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36712     },
36713     
36714     getRepairXY : function(e, data){
36715         return data.node.ui.getDDRepairXY();
36716     },
36717     
36718     onEndDrag : function(data, e){
36719         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36720         
36721         
36722     },
36723     
36724     onValidDrop : function(dd, e, id){
36725         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36726         this.hideProxy();
36727     },
36728     
36729     beforeInvalidDrop : function(e, id){
36730         // this scrolls the original position back into view
36731         var sm = this.tree.getSelectionModel();
36732         sm.clearSelections();
36733         sm.select(this.dragData.node);
36734     }
36735 });
36736 }/*
36737  * Based on:
36738  * Ext JS Library 1.1.1
36739  * Copyright(c) 2006-2007, Ext JS, LLC.
36740  *
36741  * Originally Released Under LGPL - original licence link has changed is not relivant.
36742  *
36743  * Fork - LGPL
36744  * <script type="text/javascript">
36745  */
36746 /**
36747  * @class Roo.tree.TreeEditor
36748  * @extends Roo.Editor
36749  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36750  * as the editor field.
36751  * @constructor
36752  * @param {Object} config (used to be the tree panel.)
36753  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36754  * 
36755  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36756  * @cfg {Roo.form.TextField|Object} field The field configuration
36757  *
36758  * 
36759  */
36760 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36761     var tree = config;
36762     var field;
36763     if (oldconfig) { // old style..
36764         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36765     } else {
36766         // new style..
36767         tree = config.tree;
36768         config.field = config.field  || {};
36769         config.field.xtype = 'TextField';
36770         field = Roo.factory(config.field, Roo.form);
36771     }
36772     config = config || {};
36773     
36774     
36775     this.addEvents({
36776         /**
36777          * @event beforenodeedit
36778          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36779          * false from the handler of this event.
36780          * @param {Editor} this
36781          * @param {Roo.tree.Node} node 
36782          */
36783         "beforenodeedit" : true
36784     });
36785     
36786     //Roo.log(config);
36787     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36788
36789     this.tree = tree;
36790
36791     tree.on('beforeclick', this.beforeNodeClick, this);
36792     tree.getTreeEl().on('mousedown', this.hide, this);
36793     this.on('complete', this.updateNode, this);
36794     this.on('beforestartedit', this.fitToTree, this);
36795     this.on('startedit', this.bindScroll, this, {delay:10});
36796     this.on('specialkey', this.onSpecialKey, this);
36797 };
36798
36799 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36800     /**
36801      * @cfg {String} alignment
36802      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36803      */
36804     alignment: "l-l",
36805     // inherit
36806     autoSize: false,
36807     /**
36808      * @cfg {Boolean} hideEl
36809      * True to hide the bound element while the editor is displayed (defaults to false)
36810      */
36811     hideEl : false,
36812     /**
36813      * @cfg {String} cls
36814      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36815      */
36816     cls: "x-small-editor x-tree-editor",
36817     /**
36818      * @cfg {Boolean} shim
36819      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36820      */
36821     shim:false,
36822     // inherit
36823     shadow:"frame",
36824     /**
36825      * @cfg {Number} maxWidth
36826      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36827      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36828      * scroll and client offsets into account prior to each edit.
36829      */
36830     maxWidth: 250,
36831
36832     editDelay : 350,
36833
36834     // private
36835     fitToTree : function(ed, el){
36836         var td = this.tree.getTreeEl().dom, nd = el.dom;
36837         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36838             td.scrollLeft = nd.offsetLeft;
36839         }
36840         var w = Math.min(
36841                 this.maxWidth,
36842                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36843         this.setSize(w, '');
36844         
36845         return this.fireEvent('beforenodeedit', this, this.editNode);
36846         
36847     },
36848
36849     // private
36850     triggerEdit : function(node){
36851         this.completeEdit();
36852         this.editNode = node;
36853         this.startEdit(node.ui.textNode, node.text);
36854     },
36855
36856     // private
36857     bindScroll : function(){
36858         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36859     },
36860
36861     // private
36862     beforeNodeClick : function(node, e){
36863         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36864         this.lastClick = new Date();
36865         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36866             e.stopEvent();
36867             this.triggerEdit(node);
36868             return false;
36869         }
36870         return true;
36871     },
36872
36873     // private
36874     updateNode : function(ed, value){
36875         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36876         this.editNode.setText(value);
36877     },
36878
36879     // private
36880     onHide : function(){
36881         Roo.tree.TreeEditor.superclass.onHide.call(this);
36882         if(this.editNode){
36883             this.editNode.ui.focus();
36884         }
36885     },
36886
36887     // private
36888     onSpecialKey : function(field, e){
36889         var k = e.getKey();
36890         if(k == e.ESC){
36891             e.stopEvent();
36892             this.cancelEdit();
36893         }else if(k == e.ENTER && !e.hasModifier()){
36894             e.stopEvent();
36895             this.completeEdit();
36896         }
36897     }
36898 });//<Script type="text/javascript">
36899 /*
36900  * Based on:
36901  * Ext JS Library 1.1.1
36902  * Copyright(c) 2006-2007, Ext JS, LLC.
36903  *
36904  * Originally Released Under LGPL - original licence link has changed is not relivant.
36905  *
36906  * Fork - LGPL
36907  * <script type="text/javascript">
36908  */
36909  
36910 /**
36911  * Not documented??? - probably should be...
36912  */
36913
36914 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
36915     //focus: Roo.emptyFn, // prevent odd scrolling behavior
36916     
36917     renderElements : function(n, a, targetNode, bulkRender){
36918         //consel.log("renderElements?");
36919         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36920
36921         var t = n.getOwnerTree();
36922         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
36923         
36924         var cols = t.columns;
36925         var bw = t.borderWidth;
36926         var c = cols[0];
36927         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36928          var cb = typeof a.checked == "boolean";
36929         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36930         var colcls = 'x-t-' + tid + '-c0';
36931         var buf = [
36932             '<li class="x-tree-node">',
36933             
36934                 
36935                 '<div class="x-tree-node-el ', a.cls,'">',
36936                     // extran...
36937                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
36938                 
36939                 
36940                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
36941                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
36942                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
36943                            (a.icon ? ' x-tree-node-inline-icon' : ''),
36944                            (a.iconCls ? ' '+a.iconCls : ''),
36945                            '" unselectable="on" />',
36946                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
36947                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
36948                              
36949                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36950                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
36951                             '<span unselectable="on" qtip="' + tx + '">',
36952                              tx,
36953                              '</span></a>' ,
36954                     '</div>',
36955                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36956                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
36957                  ];
36958         for(var i = 1, len = cols.length; i < len; i++){
36959             c = cols[i];
36960             colcls = 'x-t-' + tid + '-c' +i;
36961             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36962             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
36963                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
36964                       "</div>");
36965          }
36966          
36967          buf.push(
36968             '</a>',
36969             '<div class="x-clear"></div></div>',
36970             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36971             "</li>");
36972         
36973         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36974             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36975                                 n.nextSibling.ui.getEl(), buf.join(""));
36976         }else{
36977             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36978         }
36979         var el = this.wrap.firstChild;
36980         this.elRow = el;
36981         this.elNode = el.firstChild;
36982         this.ranchor = el.childNodes[1];
36983         this.ctNode = this.wrap.childNodes[1];
36984         var cs = el.firstChild.childNodes;
36985         this.indentNode = cs[0];
36986         this.ecNode = cs[1];
36987         this.iconNode = cs[2];
36988         var index = 3;
36989         if(cb){
36990             this.checkbox = cs[3];
36991             index++;
36992         }
36993         this.anchor = cs[index];
36994         
36995         this.textNode = cs[index].firstChild;
36996         
36997         //el.on("click", this.onClick, this);
36998         //el.on("dblclick", this.onDblClick, this);
36999         
37000         
37001        // console.log(this);
37002     },
37003     initEvents : function(){
37004         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37005         
37006             
37007         var a = this.ranchor;
37008
37009         var el = Roo.get(a);
37010
37011         if(Roo.isOpera){ // opera render bug ignores the CSS
37012             el.setStyle("text-decoration", "none");
37013         }
37014
37015         el.on("click", this.onClick, this);
37016         el.on("dblclick", this.onDblClick, this);
37017         el.on("contextmenu", this.onContextMenu, this);
37018         
37019     },
37020     
37021     /*onSelectedChange : function(state){
37022         if(state){
37023             this.focus();
37024             this.addClass("x-tree-selected");
37025         }else{
37026             //this.blur();
37027             this.removeClass("x-tree-selected");
37028         }
37029     },*/
37030     addClass : function(cls){
37031         if(this.elRow){
37032             Roo.fly(this.elRow).addClass(cls);
37033         }
37034         
37035     },
37036     
37037     
37038     removeClass : function(cls){
37039         if(this.elRow){
37040             Roo.fly(this.elRow).removeClass(cls);
37041         }
37042     }
37043
37044     
37045     
37046 });//<Script type="text/javascript">
37047
37048 /*
37049  * Based on:
37050  * Ext JS Library 1.1.1
37051  * Copyright(c) 2006-2007, Ext JS, LLC.
37052  *
37053  * Originally Released Under LGPL - original licence link has changed is not relivant.
37054  *
37055  * Fork - LGPL
37056  * <script type="text/javascript">
37057  */
37058  
37059
37060 /**
37061  * @class Roo.tree.ColumnTree
37062  * @extends Roo.data.TreePanel
37063  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37064  * @cfg {int} borderWidth  compined right/left border allowance
37065  * @constructor
37066  * @param {String/HTMLElement/Element} el The container element
37067  * @param {Object} config
37068  */
37069 Roo.tree.ColumnTree =  function(el, config)
37070 {
37071    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37072    this.addEvents({
37073         /**
37074         * @event resize
37075         * Fire this event on a container when it resizes
37076         * @param {int} w Width
37077         * @param {int} h Height
37078         */
37079        "resize" : true
37080     });
37081     this.on('resize', this.onResize, this);
37082 };
37083
37084 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37085     //lines:false,
37086     
37087     
37088     borderWidth: Roo.isBorderBox ? 0 : 2, 
37089     headEls : false,
37090     
37091     render : function(){
37092         // add the header.....
37093        
37094         Roo.tree.ColumnTree.superclass.render.apply(this);
37095         
37096         this.el.addClass('x-column-tree');
37097         
37098         this.headers = this.el.createChild(
37099             {cls:'x-tree-headers'},this.innerCt.dom);
37100    
37101         var cols = this.columns, c;
37102         var totalWidth = 0;
37103         this.headEls = [];
37104         var  len = cols.length;
37105         for(var i = 0; i < len; i++){
37106              c = cols[i];
37107              totalWidth += c.width;
37108             this.headEls.push(this.headers.createChild({
37109                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37110                  cn: {
37111                      cls:'x-tree-hd-text',
37112                      html: c.header
37113                  },
37114                  style:'width:'+(c.width-this.borderWidth)+'px;'
37115              }));
37116         }
37117         this.headers.createChild({cls:'x-clear'});
37118         // prevent floats from wrapping when clipped
37119         this.headers.setWidth(totalWidth);
37120         //this.innerCt.setWidth(totalWidth);
37121         this.innerCt.setStyle({ overflow: 'auto' });
37122         this.onResize(this.width, this.height);
37123              
37124         
37125     },
37126     onResize : function(w,h)
37127     {
37128         this.height = h;
37129         this.width = w;
37130         // resize cols..
37131         this.innerCt.setWidth(this.width);
37132         this.innerCt.setHeight(this.height-20);
37133         
37134         // headers...
37135         var cols = this.columns, c;
37136         var totalWidth = 0;
37137         var expEl = false;
37138         var len = cols.length;
37139         for(var i = 0; i < len; i++){
37140             c = cols[i];
37141             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37142                 // it's the expander..
37143                 expEl  = this.headEls[i];
37144                 continue;
37145             }
37146             totalWidth += c.width;
37147             
37148         }
37149         if (expEl) {
37150             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37151         }
37152         this.headers.setWidth(w-20);
37153
37154         
37155         
37156         
37157     }
37158 });
37159 /*
37160  * Based on:
37161  * Ext JS Library 1.1.1
37162  * Copyright(c) 2006-2007, Ext JS, LLC.
37163  *
37164  * Originally Released Under LGPL - original licence link has changed is not relivant.
37165  *
37166  * Fork - LGPL
37167  * <script type="text/javascript">
37168  */
37169  
37170 /**
37171  * @class Roo.menu.Menu
37172  * @extends Roo.util.Observable
37173  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37174  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37175  * @constructor
37176  * Creates a new Menu
37177  * @param {Object} config Configuration options
37178  */
37179 Roo.menu.Menu = function(config){
37180     Roo.apply(this, config);
37181     this.id = this.id || Roo.id();
37182     this.addEvents({
37183         /**
37184          * @event beforeshow
37185          * Fires before this menu is displayed
37186          * @param {Roo.menu.Menu} this
37187          */
37188         beforeshow : true,
37189         /**
37190          * @event beforehide
37191          * Fires before this menu is hidden
37192          * @param {Roo.menu.Menu} this
37193          */
37194         beforehide : true,
37195         /**
37196          * @event show
37197          * Fires after this menu is displayed
37198          * @param {Roo.menu.Menu} this
37199          */
37200         show : true,
37201         /**
37202          * @event hide
37203          * Fires after this menu is hidden
37204          * @param {Roo.menu.Menu} this
37205          */
37206         hide : true,
37207         /**
37208          * @event click
37209          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37210          * @param {Roo.menu.Menu} this
37211          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37212          * @param {Roo.EventObject} e
37213          */
37214         click : true,
37215         /**
37216          * @event mouseover
37217          * Fires when the mouse is hovering over this menu
37218          * @param {Roo.menu.Menu} this
37219          * @param {Roo.EventObject} e
37220          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37221          */
37222         mouseover : true,
37223         /**
37224          * @event mouseout
37225          * Fires when the mouse exits this menu
37226          * @param {Roo.menu.Menu} this
37227          * @param {Roo.EventObject} e
37228          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37229          */
37230         mouseout : true,
37231         /**
37232          * @event itemclick
37233          * Fires when a menu item contained in this menu is clicked
37234          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37235          * @param {Roo.EventObject} e
37236          */
37237         itemclick: true
37238     });
37239     if (this.registerMenu) {
37240         Roo.menu.MenuMgr.register(this);
37241     }
37242     
37243     var mis = this.items;
37244     this.items = new Roo.util.MixedCollection();
37245     if(mis){
37246         this.add.apply(this, mis);
37247     }
37248 };
37249
37250 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37251     /**
37252      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37253      */
37254     minWidth : 120,
37255     /**
37256      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37257      * for bottom-right shadow (defaults to "sides")
37258      */
37259     shadow : "sides",
37260     /**
37261      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37262      * this menu (defaults to "tl-tr?")
37263      */
37264     subMenuAlign : "tl-tr?",
37265     /**
37266      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37267      * relative to its element of origin (defaults to "tl-bl?")
37268      */
37269     defaultAlign : "tl-bl?",
37270     /**
37271      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37272      */
37273     allowOtherMenus : false,
37274     /**
37275      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37276      */
37277     registerMenu : true,
37278
37279     hidden:true,
37280
37281     // private
37282     render : function(){
37283         if(this.el){
37284             return;
37285         }
37286         var el = this.el = new Roo.Layer({
37287             cls: "x-menu",
37288             shadow:this.shadow,
37289             constrain: false,
37290             parentEl: this.parentEl || document.body,
37291             zindex:15000
37292         });
37293
37294         this.keyNav = new Roo.menu.MenuNav(this);
37295
37296         if(this.plain){
37297             el.addClass("x-menu-plain");
37298         }
37299         if(this.cls){
37300             el.addClass(this.cls);
37301         }
37302         // generic focus element
37303         this.focusEl = el.createChild({
37304             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37305         });
37306         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37307         //disabling touch- as it's causing issues ..
37308         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37309         ul.on('click'   , this.onClick, this);
37310         
37311         
37312         ul.on("mouseover", this.onMouseOver, this);
37313         ul.on("mouseout", this.onMouseOut, this);
37314         this.items.each(function(item){
37315             if (item.hidden) {
37316                 return;
37317             }
37318             
37319             var li = document.createElement("li");
37320             li.className = "x-menu-list-item";
37321             ul.dom.appendChild(li);
37322             item.render(li, this);
37323         }, this);
37324         this.ul = ul;
37325         this.autoWidth();
37326     },
37327
37328     // private
37329     autoWidth : function(){
37330         var el = this.el, ul = this.ul;
37331         if(!el){
37332             return;
37333         }
37334         var w = this.width;
37335         if(w){
37336             el.setWidth(w);
37337         }else if(Roo.isIE){
37338             el.setWidth(this.minWidth);
37339             var t = el.dom.offsetWidth; // force recalc
37340             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37341         }
37342     },
37343
37344     // private
37345     delayAutoWidth : function(){
37346         if(this.rendered){
37347             if(!this.awTask){
37348                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37349             }
37350             this.awTask.delay(20);
37351         }
37352     },
37353
37354     // private
37355     findTargetItem : function(e){
37356         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37357         if(t && t.menuItemId){
37358             return this.items.get(t.menuItemId);
37359         }
37360     },
37361
37362     // private
37363     onClick : function(e){
37364         Roo.log("menu.onClick");
37365         var t = this.findTargetItem(e);
37366         if(!t){
37367             return;
37368         }
37369         Roo.log(e);
37370         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37371             if(t == this.activeItem && t.shouldDeactivate(e)){
37372                 this.activeItem.deactivate();
37373                 delete this.activeItem;
37374                 return;
37375             }
37376             if(t.canActivate){
37377                 this.setActiveItem(t, true);
37378             }
37379             return;
37380             
37381             
37382         }
37383         
37384         t.onClick(e);
37385         this.fireEvent("click", this, t, e);
37386     },
37387
37388     // private
37389     setActiveItem : function(item, autoExpand){
37390         if(item != this.activeItem){
37391             if(this.activeItem){
37392                 this.activeItem.deactivate();
37393             }
37394             this.activeItem = item;
37395             item.activate(autoExpand);
37396         }else if(autoExpand){
37397             item.expandMenu();
37398         }
37399     },
37400
37401     // private
37402     tryActivate : function(start, step){
37403         var items = this.items;
37404         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37405             var item = items.get(i);
37406             if(!item.disabled && item.canActivate){
37407                 this.setActiveItem(item, false);
37408                 return item;
37409             }
37410         }
37411         return false;
37412     },
37413
37414     // private
37415     onMouseOver : function(e){
37416         var t;
37417         if(t = this.findTargetItem(e)){
37418             if(t.canActivate && !t.disabled){
37419                 this.setActiveItem(t, true);
37420             }
37421         }
37422         this.fireEvent("mouseover", this, e, t);
37423     },
37424
37425     // private
37426     onMouseOut : function(e){
37427         var t;
37428         if(t = this.findTargetItem(e)){
37429             if(t == this.activeItem && t.shouldDeactivate(e)){
37430                 this.activeItem.deactivate();
37431                 delete this.activeItem;
37432             }
37433         }
37434         this.fireEvent("mouseout", this, e, t);
37435     },
37436
37437     /**
37438      * Read-only.  Returns true if the menu is currently displayed, else false.
37439      * @type Boolean
37440      */
37441     isVisible : function(){
37442         return this.el && !this.hidden;
37443     },
37444
37445     /**
37446      * Displays this menu relative to another element
37447      * @param {String/HTMLElement/Roo.Element} element The element to align to
37448      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37449      * the element (defaults to this.defaultAlign)
37450      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37451      */
37452     show : function(el, pos, parentMenu){
37453         this.parentMenu = parentMenu;
37454         if(!this.el){
37455             this.render();
37456         }
37457         this.fireEvent("beforeshow", this);
37458         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37459     },
37460
37461     /**
37462      * Displays this menu at a specific xy position
37463      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37464      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37465      */
37466     showAt : function(xy, parentMenu, /* private: */_e){
37467         this.parentMenu = parentMenu;
37468         if(!this.el){
37469             this.render();
37470         }
37471         if(_e !== false){
37472             this.fireEvent("beforeshow", this);
37473             xy = this.el.adjustForConstraints(xy);
37474         }
37475         this.el.setXY(xy);
37476         this.el.show();
37477         this.hidden = false;
37478         this.focus();
37479         this.fireEvent("show", this);
37480     },
37481
37482     focus : function(){
37483         if(!this.hidden){
37484             this.doFocus.defer(50, this);
37485         }
37486     },
37487
37488     doFocus : function(){
37489         if(!this.hidden){
37490             this.focusEl.focus();
37491         }
37492     },
37493
37494     /**
37495      * Hides this menu and optionally all parent menus
37496      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37497      */
37498     hide : function(deep){
37499         if(this.el && this.isVisible()){
37500             this.fireEvent("beforehide", this);
37501             if(this.activeItem){
37502                 this.activeItem.deactivate();
37503                 this.activeItem = null;
37504             }
37505             this.el.hide();
37506             this.hidden = true;
37507             this.fireEvent("hide", this);
37508         }
37509         if(deep === true && this.parentMenu){
37510             this.parentMenu.hide(true);
37511         }
37512     },
37513
37514     /**
37515      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37516      * Any of the following are valid:
37517      * <ul>
37518      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37519      * <li>An HTMLElement object which will be converted to a menu item</li>
37520      * <li>A menu item config object that will be created as a new menu item</li>
37521      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37522      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37523      * </ul>
37524      * Usage:
37525      * <pre><code>
37526 // Create the menu
37527 var menu = new Roo.menu.Menu();
37528
37529 // Create a menu item to add by reference
37530 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37531
37532 // Add a bunch of items at once using different methods.
37533 // Only the last item added will be returned.
37534 var item = menu.add(
37535     menuItem,                // add existing item by ref
37536     'Dynamic Item',          // new TextItem
37537     '-',                     // new separator
37538     { text: 'Config Item' }  // new item by config
37539 );
37540 </code></pre>
37541      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37542      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37543      */
37544     add : function(){
37545         var a = arguments, l = a.length, item;
37546         for(var i = 0; i < l; i++){
37547             var el = a[i];
37548             if ((typeof(el) == "object") && el.xtype && el.xns) {
37549                 el = Roo.factory(el, Roo.menu);
37550             }
37551             
37552             if(el.render){ // some kind of Item
37553                 item = this.addItem(el);
37554             }else if(typeof el == "string"){ // string
37555                 if(el == "separator" || el == "-"){
37556                     item = this.addSeparator();
37557                 }else{
37558                     item = this.addText(el);
37559                 }
37560             }else if(el.tagName || el.el){ // element
37561                 item = this.addElement(el);
37562             }else if(typeof el == "object"){ // must be menu item config?
37563                 item = this.addMenuItem(el);
37564             }
37565         }
37566         return item;
37567     },
37568
37569     /**
37570      * Returns this menu's underlying {@link Roo.Element} object
37571      * @return {Roo.Element} The element
37572      */
37573     getEl : function(){
37574         if(!this.el){
37575             this.render();
37576         }
37577         return this.el;
37578     },
37579
37580     /**
37581      * Adds a separator bar to the menu
37582      * @return {Roo.menu.Item} The menu item that was added
37583      */
37584     addSeparator : function(){
37585         return this.addItem(new Roo.menu.Separator());
37586     },
37587
37588     /**
37589      * Adds an {@link Roo.Element} object to the menu
37590      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37591      * @return {Roo.menu.Item} The menu item that was added
37592      */
37593     addElement : function(el){
37594         return this.addItem(new Roo.menu.BaseItem(el));
37595     },
37596
37597     /**
37598      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37599      * @param {Roo.menu.Item} item The menu item to add
37600      * @return {Roo.menu.Item} The menu item that was added
37601      */
37602     addItem : function(item){
37603         this.items.add(item);
37604         if(this.ul){
37605             var li = document.createElement("li");
37606             li.className = "x-menu-list-item";
37607             this.ul.dom.appendChild(li);
37608             item.render(li, this);
37609             this.delayAutoWidth();
37610         }
37611         return item;
37612     },
37613
37614     /**
37615      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37616      * @param {Object} config A MenuItem config object
37617      * @return {Roo.menu.Item} The menu item that was added
37618      */
37619     addMenuItem : function(config){
37620         if(!(config instanceof Roo.menu.Item)){
37621             if(typeof config.checked == "boolean"){ // must be check menu item config?
37622                 config = new Roo.menu.CheckItem(config);
37623             }else{
37624                 config = new Roo.menu.Item(config);
37625             }
37626         }
37627         return this.addItem(config);
37628     },
37629
37630     /**
37631      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37632      * @param {String} text The text to display in the menu item
37633      * @return {Roo.menu.Item} The menu item that was added
37634      */
37635     addText : function(text){
37636         return this.addItem(new Roo.menu.TextItem({ text : text }));
37637     },
37638
37639     /**
37640      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37641      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37642      * @param {Roo.menu.Item} item The menu item to add
37643      * @return {Roo.menu.Item} The menu item that was added
37644      */
37645     insert : function(index, item){
37646         this.items.insert(index, item);
37647         if(this.ul){
37648             var li = document.createElement("li");
37649             li.className = "x-menu-list-item";
37650             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37651             item.render(li, this);
37652             this.delayAutoWidth();
37653         }
37654         return item;
37655     },
37656
37657     /**
37658      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37659      * @param {Roo.menu.Item} item The menu item to remove
37660      */
37661     remove : function(item){
37662         this.items.removeKey(item.id);
37663         item.destroy();
37664     },
37665
37666     /**
37667      * Removes and destroys all items in the menu
37668      */
37669     removeAll : function(){
37670         var f;
37671         while(f = this.items.first()){
37672             this.remove(f);
37673         }
37674     }
37675 });
37676
37677 // MenuNav is a private utility class used internally by the Menu
37678 Roo.menu.MenuNav = function(menu){
37679     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37680     this.scope = this.menu = menu;
37681 };
37682
37683 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37684     doRelay : function(e, h){
37685         var k = e.getKey();
37686         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37687             this.menu.tryActivate(0, 1);
37688             return false;
37689         }
37690         return h.call(this.scope || this, e, this.menu);
37691     },
37692
37693     up : function(e, m){
37694         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37695             m.tryActivate(m.items.length-1, -1);
37696         }
37697     },
37698
37699     down : function(e, m){
37700         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37701             m.tryActivate(0, 1);
37702         }
37703     },
37704
37705     right : function(e, m){
37706         if(m.activeItem){
37707             m.activeItem.expandMenu(true);
37708         }
37709     },
37710
37711     left : function(e, m){
37712         m.hide();
37713         if(m.parentMenu && m.parentMenu.activeItem){
37714             m.parentMenu.activeItem.activate();
37715         }
37716     },
37717
37718     enter : function(e, m){
37719         if(m.activeItem){
37720             e.stopPropagation();
37721             m.activeItem.onClick(e);
37722             m.fireEvent("click", this, m.activeItem);
37723             return true;
37724         }
37725     }
37726 });/*
37727  * Based on:
37728  * Ext JS Library 1.1.1
37729  * Copyright(c) 2006-2007, Ext JS, LLC.
37730  *
37731  * Originally Released Under LGPL - original licence link has changed is not relivant.
37732  *
37733  * Fork - LGPL
37734  * <script type="text/javascript">
37735  */
37736  
37737 /**
37738  * @class Roo.menu.MenuMgr
37739  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37740  * @singleton
37741  */
37742 Roo.menu.MenuMgr = function(){
37743    var menus, active, groups = {}, attached = false, lastShow = new Date();
37744
37745    // private - called when first menu is created
37746    function init(){
37747        menus = {};
37748        active = new Roo.util.MixedCollection();
37749        Roo.get(document).addKeyListener(27, function(){
37750            if(active.length > 0){
37751                hideAll();
37752            }
37753        });
37754    }
37755
37756    // private
37757    function hideAll(){
37758        if(active && active.length > 0){
37759            var c = active.clone();
37760            c.each(function(m){
37761                m.hide();
37762            });
37763        }
37764    }
37765
37766    // private
37767    function onHide(m){
37768        active.remove(m);
37769        if(active.length < 1){
37770            Roo.get(document).un("mousedown", onMouseDown);
37771            attached = false;
37772        }
37773    }
37774
37775    // private
37776    function onShow(m){
37777        var last = active.last();
37778        lastShow = new Date();
37779        active.add(m);
37780        if(!attached){
37781            Roo.get(document).on("mousedown", onMouseDown);
37782            attached = true;
37783        }
37784        if(m.parentMenu){
37785           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37786           m.parentMenu.activeChild = m;
37787        }else if(last && last.isVisible()){
37788           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37789        }
37790    }
37791
37792    // private
37793    function onBeforeHide(m){
37794        if(m.activeChild){
37795            m.activeChild.hide();
37796        }
37797        if(m.autoHideTimer){
37798            clearTimeout(m.autoHideTimer);
37799            delete m.autoHideTimer;
37800        }
37801    }
37802
37803    // private
37804    function onBeforeShow(m){
37805        var pm = m.parentMenu;
37806        if(!pm && !m.allowOtherMenus){
37807            hideAll();
37808        }else if(pm && pm.activeChild && active != m){
37809            pm.activeChild.hide();
37810        }
37811    }
37812
37813    // private
37814    function onMouseDown(e){
37815        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37816            hideAll();
37817        }
37818    }
37819
37820    // private
37821    function onBeforeCheck(mi, state){
37822        if(state){
37823            var g = groups[mi.group];
37824            for(var i = 0, l = g.length; i < l; i++){
37825                if(g[i] != mi){
37826                    g[i].setChecked(false);
37827                }
37828            }
37829        }
37830    }
37831
37832    return {
37833
37834        /**
37835         * Hides all menus that are currently visible
37836         */
37837        hideAll : function(){
37838             hideAll();  
37839        },
37840
37841        // private
37842        register : function(menu){
37843            if(!menus){
37844                init();
37845            }
37846            menus[menu.id] = menu;
37847            menu.on("beforehide", onBeforeHide);
37848            menu.on("hide", onHide);
37849            menu.on("beforeshow", onBeforeShow);
37850            menu.on("show", onShow);
37851            var g = menu.group;
37852            if(g && menu.events["checkchange"]){
37853                if(!groups[g]){
37854                    groups[g] = [];
37855                }
37856                groups[g].push(menu);
37857                menu.on("checkchange", onCheck);
37858            }
37859        },
37860
37861         /**
37862          * Returns a {@link Roo.menu.Menu} object
37863          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37864          * be used to generate and return a new Menu instance.
37865          */
37866        get : function(menu){
37867            if(typeof menu == "string"){ // menu id
37868                return menus[menu];
37869            }else if(menu.events){  // menu instance
37870                return menu;
37871            }else if(typeof menu.length == 'number'){ // array of menu items?
37872                return new Roo.menu.Menu({items:menu});
37873            }else{ // otherwise, must be a config
37874                return new Roo.menu.Menu(menu);
37875            }
37876        },
37877
37878        // private
37879        unregister : function(menu){
37880            delete menus[menu.id];
37881            menu.un("beforehide", onBeforeHide);
37882            menu.un("hide", onHide);
37883            menu.un("beforeshow", onBeforeShow);
37884            menu.un("show", onShow);
37885            var g = menu.group;
37886            if(g && menu.events["checkchange"]){
37887                groups[g].remove(menu);
37888                menu.un("checkchange", onCheck);
37889            }
37890        },
37891
37892        // private
37893        registerCheckable : function(menuItem){
37894            var g = menuItem.group;
37895            if(g){
37896                if(!groups[g]){
37897                    groups[g] = [];
37898                }
37899                groups[g].push(menuItem);
37900                menuItem.on("beforecheckchange", onBeforeCheck);
37901            }
37902        },
37903
37904        // private
37905        unregisterCheckable : function(menuItem){
37906            var g = menuItem.group;
37907            if(g){
37908                groups[g].remove(menuItem);
37909                menuItem.un("beforecheckchange", onBeforeCheck);
37910            }
37911        }
37912    };
37913 }();/*
37914  * Based on:
37915  * Ext JS Library 1.1.1
37916  * Copyright(c) 2006-2007, Ext JS, LLC.
37917  *
37918  * Originally Released Under LGPL - original licence link has changed is not relivant.
37919  *
37920  * Fork - LGPL
37921  * <script type="text/javascript">
37922  */
37923  
37924
37925 /**
37926  * @class Roo.menu.BaseItem
37927  * @extends Roo.Component
37928  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
37929  * management and base configuration options shared by all menu components.
37930  * @constructor
37931  * Creates a new BaseItem
37932  * @param {Object} config Configuration options
37933  */
37934 Roo.menu.BaseItem = function(config){
37935     Roo.menu.BaseItem.superclass.constructor.call(this, config);
37936
37937     this.addEvents({
37938         /**
37939          * @event click
37940          * Fires when this item is clicked
37941          * @param {Roo.menu.BaseItem} this
37942          * @param {Roo.EventObject} e
37943          */
37944         click: true,
37945         /**
37946          * @event activate
37947          * Fires when this item is activated
37948          * @param {Roo.menu.BaseItem} this
37949          */
37950         activate : true,
37951         /**
37952          * @event deactivate
37953          * Fires when this item is deactivated
37954          * @param {Roo.menu.BaseItem} this
37955          */
37956         deactivate : true
37957     });
37958
37959     if(this.handler){
37960         this.on("click", this.handler, this.scope, true);
37961     }
37962 };
37963
37964 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
37965     /**
37966      * @cfg {Function} handler
37967      * A function that will handle the click event of this menu item (defaults to undefined)
37968      */
37969     /**
37970      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
37971      */
37972     canActivate : false,
37973     
37974      /**
37975      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
37976      */
37977     hidden: false,
37978     
37979     /**
37980      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
37981      */
37982     activeClass : "x-menu-item-active",
37983     /**
37984      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
37985      */
37986     hideOnClick : true,
37987     /**
37988      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
37989      */
37990     hideDelay : 100,
37991
37992     // private
37993     ctype: "Roo.menu.BaseItem",
37994
37995     // private
37996     actionMode : "container",
37997
37998     // private
37999     render : function(container, parentMenu){
38000         this.parentMenu = parentMenu;
38001         Roo.menu.BaseItem.superclass.render.call(this, container);
38002         this.container.menuItemId = this.id;
38003     },
38004
38005     // private
38006     onRender : function(container, position){
38007         this.el = Roo.get(this.el);
38008         container.dom.appendChild(this.el.dom);
38009     },
38010
38011     // private
38012     onClick : function(e){
38013         if(!this.disabled && this.fireEvent("click", this, e) !== false
38014                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38015             this.handleClick(e);
38016         }else{
38017             e.stopEvent();
38018         }
38019     },
38020
38021     // private
38022     activate : function(){
38023         if(this.disabled){
38024             return false;
38025         }
38026         var li = this.container;
38027         li.addClass(this.activeClass);
38028         this.region = li.getRegion().adjust(2, 2, -2, -2);
38029         this.fireEvent("activate", this);
38030         return true;
38031     },
38032
38033     // private
38034     deactivate : function(){
38035         this.container.removeClass(this.activeClass);
38036         this.fireEvent("deactivate", this);
38037     },
38038
38039     // private
38040     shouldDeactivate : function(e){
38041         return !this.region || !this.region.contains(e.getPoint());
38042     },
38043
38044     // private
38045     handleClick : function(e){
38046         if(this.hideOnClick){
38047             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38048         }
38049     },
38050
38051     // private
38052     expandMenu : function(autoActivate){
38053         // do nothing
38054     },
38055
38056     // private
38057     hideMenu : function(){
38058         // do nothing
38059     }
38060 });/*
38061  * Based on:
38062  * Ext JS Library 1.1.1
38063  * Copyright(c) 2006-2007, Ext JS, LLC.
38064  *
38065  * Originally Released Under LGPL - original licence link has changed is not relivant.
38066  *
38067  * Fork - LGPL
38068  * <script type="text/javascript">
38069  */
38070  
38071 /**
38072  * @class Roo.menu.Adapter
38073  * @extends Roo.menu.BaseItem
38074  * 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.
38075  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38076  * @constructor
38077  * Creates a new Adapter
38078  * @param {Object} config Configuration options
38079  */
38080 Roo.menu.Adapter = function(component, config){
38081     Roo.menu.Adapter.superclass.constructor.call(this, config);
38082     this.component = component;
38083 };
38084 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38085     // private
38086     canActivate : true,
38087
38088     // private
38089     onRender : function(container, position){
38090         this.component.render(container);
38091         this.el = this.component.getEl();
38092     },
38093
38094     // private
38095     activate : function(){
38096         if(this.disabled){
38097             return false;
38098         }
38099         this.component.focus();
38100         this.fireEvent("activate", this);
38101         return true;
38102     },
38103
38104     // private
38105     deactivate : function(){
38106         this.fireEvent("deactivate", this);
38107     },
38108
38109     // private
38110     disable : function(){
38111         this.component.disable();
38112         Roo.menu.Adapter.superclass.disable.call(this);
38113     },
38114
38115     // private
38116     enable : function(){
38117         this.component.enable();
38118         Roo.menu.Adapter.superclass.enable.call(this);
38119     }
38120 });/*
38121  * Based on:
38122  * Ext JS Library 1.1.1
38123  * Copyright(c) 2006-2007, Ext JS, LLC.
38124  *
38125  * Originally Released Under LGPL - original licence link has changed is not relivant.
38126  *
38127  * Fork - LGPL
38128  * <script type="text/javascript">
38129  */
38130
38131 /**
38132  * @class Roo.menu.TextItem
38133  * @extends Roo.menu.BaseItem
38134  * Adds a static text string to a menu, usually used as either a heading or group separator.
38135  * Note: old style constructor with text is still supported.
38136  * 
38137  * @constructor
38138  * Creates a new TextItem
38139  * @param {Object} cfg Configuration
38140  */
38141 Roo.menu.TextItem = function(cfg){
38142     if (typeof(cfg) == 'string') {
38143         this.text = cfg;
38144     } else {
38145         Roo.apply(this,cfg);
38146     }
38147     
38148     Roo.menu.TextItem.superclass.constructor.call(this);
38149 };
38150
38151 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38152     /**
38153      * @cfg {Boolean} text Text to show on item.
38154      */
38155     text : '',
38156     
38157     /**
38158      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38159      */
38160     hideOnClick : false,
38161     /**
38162      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38163      */
38164     itemCls : "x-menu-text",
38165
38166     // private
38167     onRender : function(){
38168         var s = document.createElement("span");
38169         s.className = this.itemCls;
38170         s.innerHTML = this.text;
38171         this.el = s;
38172         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38173     }
38174 });/*
38175  * Based on:
38176  * Ext JS Library 1.1.1
38177  * Copyright(c) 2006-2007, Ext JS, LLC.
38178  *
38179  * Originally Released Under LGPL - original licence link has changed is not relivant.
38180  *
38181  * Fork - LGPL
38182  * <script type="text/javascript">
38183  */
38184
38185 /**
38186  * @class Roo.menu.Separator
38187  * @extends Roo.menu.BaseItem
38188  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38189  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38190  * @constructor
38191  * @param {Object} config Configuration options
38192  */
38193 Roo.menu.Separator = function(config){
38194     Roo.menu.Separator.superclass.constructor.call(this, config);
38195 };
38196
38197 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38198     /**
38199      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38200      */
38201     itemCls : "x-menu-sep",
38202     /**
38203      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38204      */
38205     hideOnClick : false,
38206
38207     // private
38208     onRender : function(li){
38209         var s = document.createElement("span");
38210         s.className = this.itemCls;
38211         s.innerHTML = "&#160;";
38212         this.el = s;
38213         li.addClass("x-menu-sep-li");
38214         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38215     }
38216 });/*
38217  * Based on:
38218  * Ext JS Library 1.1.1
38219  * Copyright(c) 2006-2007, Ext JS, LLC.
38220  *
38221  * Originally Released Under LGPL - original licence link has changed is not relivant.
38222  *
38223  * Fork - LGPL
38224  * <script type="text/javascript">
38225  */
38226 /**
38227  * @class Roo.menu.Item
38228  * @extends Roo.menu.BaseItem
38229  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38230  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38231  * activation and click handling.
38232  * @constructor
38233  * Creates a new Item
38234  * @param {Object} config Configuration options
38235  */
38236 Roo.menu.Item = function(config){
38237     Roo.menu.Item.superclass.constructor.call(this, config);
38238     if(this.menu){
38239         this.menu = Roo.menu.MenuMgr.get(this.menu);
38240     }
38241 };
38242 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38243     
38244     /**
38245      * @cfg {String} text
38246      * The text to show on the menu item.
38247      */
38248     text: '',
38249      /**
38250      * @cfg {String} HTML to render in menu
38251      * The text to show on the menu item (HTML version).
38252      */
38253     html: '',
38254     /**
38255      * @cfg {String} icon
38256      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38257      */
38258     icon: undefined,
38259     /**
38260      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38261      */
38262     itemCls : "x-menu-item",
38263     /**
38264      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38265      */
38266     canActivate : true,
38267     /**
38268      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38269      */
38270     showDelay: 200,
38271     // doc'd in BaseItem
38272     hideDelay: 200,
38273
38274     // private
38275     ctype: "Roo.menu.Item",
38276     
38277     // private
38278     onRender : function(container, position){
38279         var el = document.createElement("a");
38280         el.hideFocus = true;
38281         el.unselectable = "on";
38282         el.href = this.href || "#";
38283         if(this.hrefTarget){
38284             el.target = this.hrefTarget;
38285         }
38286         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38287         
38288         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38289         
38290         el.innerHTML = String.format(
38291                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38292                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38293         this.el = el;
38294         Roo.menu.Item.superclass.onRender.call(this, container, position);
38295     },
38296
38297     /**
38298      * Sets the text to display in this menu item
38299      * @param {String} text The text to display
38300      * @param {Boolean} isHTML true to indicate text is pure html.
38301      */
38302     setText : function(text, isHTML){
38303         if (isHTML) {
38304             this.html = text;
38305         } else {
38306             this.text = text;
38307             this.html = '';
38308         }
38309         if(this.rendered){
38310             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38311      
38312             this.el.update(String.format(
38313                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38314                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38315             this.parentMenu.autoWidth();
38316         }
38317     },
38318
38319     // private
38320     handleClick : function(e){
38321         if(!this.href){ // if no link defined, stop the event automatically
38322             e.stopEvent();
38323         }
38324         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38325     },
38326
38327     // private
38328     activate : function(autoExpand){
38329         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38330             this.focus();
38331             if(autoExpand){
38332                 this.expandMenu();
38333             }
38334         }
38335         return true;
38336     },
38337
38338     // private
38339     shouldDeactivate : function(e){
38340         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38341             if(this.menu && this.menu.isVisible()){
38342                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38343             }
38344             return true;
38345         }
38346         return false;
38347     },
38348
38349     // private
38350     deactivate : function(){
38351         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38352         this.hideMenu();
38353     },
38354
38355     // private
38356     expandMenu : function(autoActivate){
38357         if(!this.disabled && this.menu){
38358             clearTimeout(this.hideTimer);
38359             delete this.hideTimer;
38360             if(!this.menu.isVisible() && !this.showTimer){
38361                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38362             }else if (this.menu.isVisible() && autoActivate){
38363                 this.menu.tryActivate(0, 1);
38364             }
38365         }
38366     },
38367
38368     // private
38369     deferExpand : function(autoActivate){
38370         delete this.showTimer;
38371         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38372         if(autoActivate){
38373             this.menu.tryActivate(0, 1);
38374         }
38375     },
38376
38377     // private
38378     hideMenu : function(){
38379         clearTimeout(this.showTimer);
38380         delete this.showTimer;
38381         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38382             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38383         }
38384     },
38385
38386     // private
38387     deferHide : function(){
38388         delete this.hideTimer;
38389         this.menu.hide();
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.CheckItem
38404  * @extends Roo.menu.Item
38405  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38406  * @constructor
38407  * Creates a new CheckItem
38408  * @param {Object} config Configuration options
38409  */
38410 Roo.menu.CheckItem = function(config){
38411     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38412     this.addEvents({
38413         /**
38414          * @event beforecheckchange
38415          * Fires before the checked value is set, providing an opportunity to cancel if needed
38416          * @param {Roo.menu.CheckItem} this
38417          * @param {Boolean} checked The new checked value that will be set
38418          */
38419         "beforecheckchange" : true,
38420         /**
38421          * @event checkchange
38422          * Fires after the checked value has been set
38423          * @param {Roo.menu.CheckItem} this
38424          * @param {Boolean} checked The checked value that was set
38425          */
38426         "checkchange" : true
38427     });
38428     if(this.checkHandler){
38429         this.on('checkchange', this.checkHandler, this.scope);
38430     }
38431 };
38432 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38433     /**
38434      * @cfg {String} group
38435      * All check items with the same group name will automatically be grouped into a single-select
38436      * radio button group (defaults to '')
38437      */
38438     /**
38439      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38440      */
38441     itemCls : "x-menu-item x-menu-check-item",
38442     /**
38443      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38444      */
38445     groupClass : "x-menu-group-item",
38446
38447     /**
38448      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38449      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38450      * initialized with checked = true will be rendered as checked.
38451      */
38452     checked: false,
38453
38454     // private
38455     ctype: "Roo.menu.CheckItem",
38456
38457     // private
38458     onRender : function(c){
38459         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38460         if(this.group){
38461             this.el.addClass(this.groupClass);
38462         }
38463         Roo.menu.MenuMgr.registerCheckable(this);
38464         if(this.checked){
38465             this.checked = false;
38466             this.setChecked(true, true);
38467         }
38468     },
38469
38470     // private
38471     destroy : function(){
38472         if(this.rendered){
38473             Roo.menu.MenuMgr.unregisterCheckable(this);
38474         }
38475         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38476     },
38477
38478     /**
38479      * Set the checked state of this item
38480      * @param {Boolean} checked The new checked value
38481      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38482      */
38483     setChecked : function(state, suppressEvent){
38484         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38485             if(this.container){
38486                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38487             }
38488             this.checked = state;
38489             if(suppressEvent !== true){
38490                 this.fireEvent("checkchange", this, state);
38491             }
38492         }
38493     },
38494
38495     // private
38496     handleClick : function(e){
38497        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38498            this.setChecked(!this.checked);
38499        }
38500        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38501     }
38502 });/*
38503  * Based on:
38504  * Ext JS Library 1.1.1
38505  * Copyright(c) 2006-2007, Ext JS, LLC.
38506  *
38507  * Originally Released Under LGPL - original licence link has changed is not relivant.
38508  *
38509  * Fork - LGPL
38510  * <script type="text/javascript">
38511  */
38512  
38513 /**
38514  * @class Roo.menu.DateItem
38515  * @extends Roo.menu.Adapter
38516  * A menu item that wraps the {@link Roo.DatPicker} component.
38517  * @constructor
38518  * Creates a new DateItem
38519  * @param {Object} config Configuration options
38520  */
38521 Roo.menu.DateItem = function(config){
38522     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38523     /** The Roo.DatePicker object @type Roo.DatePicker */
38524     this.picker = this.component;
38525     this.addEvents({select: true});
38526     
38527     this.picker.on("render", function(picker){
38528         picker.getEl().swallowEvent("click");
38529         picker.container.addClass("x-menu-date-item");
38530     });
38531
38532     this.picker.on("select", this.onSelect, this);
38533 };
38534
38535 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38536     // private
38537     onSelect : function(picker, date){
38538         this.fireEvent("select", this, date, picker);
38539         Roo.menu.DateItem.superclass.handleClick.call(this);
38540     }
38541 });/*
38542  * Based on:
38543  * Ext JS Library 1.1.1
38544  * Copyright(c) 2006-2007, Ext JS, LLC.
38545  *
38546  * Originally Released Under LGPL - original licence link has changed is not relivant.
38547  *
38548  * Fork - LGPL
38549  * <script type="text/javascript">
38550  */
38551  
38552 /**
38553  * @class Roo.menu.ColorItem
38554  * @extends Roo.menu.Adapter
38555  * A menu item that wraps the {@link Roo.ColorPalette} component.
38556  * @constructor
38557  * Creates a new ColorItem
38558  * @param {Object} config Configuration options
38559  */
38560 Roo.menu.ColorItem = function(config){
38561     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38562     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38563     this.palette = this.component;
38564     this.relayEvents(this.palette, ["select"]);
38565     if(this.selectHandler){
38566         this.on('select', this.selectHandler, this.scope);
38567     }
38568 };
38569 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38570  * Based on:
38571  * Ext JS Library 1.1.1
38572  * Copyright(c) 2006-2007, Ext JS, LLC.
38573  *
38574  * Originally Released Under LGPL - original licence link has changed is not relivant.
38575  *
38576  * Fork - LGPL
38577  * <script type="text/javascript">
38578  */
38579  
38580
38581 /**
38582  * @class Roo.menu.DateMenu
38583  * @extends Roo.menu.Menu
38584  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38585  * @constructor
38586  * Creates a new DateMenu
38587  * @param {Object} config Configuration options
38588  */
38589 Roo.menu.DateMenu = function(config){
38590     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38591     this.plain = true;
38592     var di = new Roo.menu.DateItem(config);
38593     this.add(di);
38594     /**
38595      * The {@link Roo.DatePicker} instance for this DateMenu
38596      * @type DatePicker
38597      */
38598     this.picker = di.picker;
38599     /**
38600      * @event select
38601      * @param {DatePicker} picker
38602      * @param {Date} date
38603      */
38604     this.relayEvents(di, ["select"]);
38605     this.on('beforeshow', function(){
38606         if(this.picker){
38607             this.picker.hideMonthPicker(false);
38608         }
38609     }, this);
38610 };
38611 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38612     cls:'x-date-menu'
38613 });/*
38614  * Based on:
38615  * Ext JS Library 1.1.1
38616  * Copyright(c) 2006-2007, Ext JS, LLC.
38617  *
38618  * Originally Released Under LGPL - original licence link has changed is not relivant.
38619  *
38620  * Fork - LGPL
38621  * <script type="text/javascript">
38622  */
38623  
38624
38625 /**
38626  * @class Roo.menu.ColorMenu
38627  * @extends Roo.menu.Menu
38628  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38629  * @constructor
38630  * Creates a new ColorMenu
38631  * @param {Object} config Configuration options
38632  */
38633 Roo.menu.ColorMenu = function(config){
38634     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38635     this.plain = true;
38636     var ci = new Roo.menu.ColorItem(config);
38637     this.add(ci);
38638     /**
38639      * The {@link Roo.ColorPalette} instance for this ColorMenu
38640      * @type ColorPalette
38641      */
38642     this.palette = ci.palette;
38643     /**
38644      * @event select
38645      * @param {ColorPalette} palette
38646      * @param {String} color
38647      */
38648     this.relayEvents(ci, ["select"]);
38649 };
38650 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38651  * Based on:
38652  * Ext JS Library 1.1.1
38653  * Copyright(c) 2006-2007, Ext JS, LLC.
38654  *
38655  * Originally Released Under LGPL - original licence link has changed is not relivant.
38656  *
38657  * Fork - LGPL
38658  * <script type="text/javascript">
38659  */
38660  
38661 /**
38662  * @class Roo.form.Field
38663  * @extends Roo.BoxComponent
38664  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38665  * @constructor
38666  * Creates a new Field
38667  * @param {Object} config Configuration options
38668  */
38669 Roo.form.Field = function(config){
38670     Roo.form.Field.superclass.constructor.call(this, config);
38671 };
38672
38673 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38674     /**
38675      * @cfg {String} fieldLabel Label to use when rendering a form.
38676      */
38677        /**
38678      * @cfg {String} qtip Mouse over tip
38679      */
38680      
38681     /**
38682      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38683      */
38684     invalidClass : "x-form-invalid",
38685     /**
38686      * @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")
38687      */
38688     invalidText : "The value in this field is invalid",
38689     /**
38690      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38691      */
38692     focusClass : "x-form-focus",
38693     /**
38694      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38695       automatic validation (defaults to "keyup").
38696      */
38697     validationEvent : "keyup",
38698     /**
38699      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38700      */
38701     validateOnBlur : true,
38702     /**
38703      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38704      */
38705     validationDelay : 250,
38706     /**
38707      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38708      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38709      */
38710     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38711     /**
38712      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38713      */
38714     fieldClass : "x-form-field",
38715     /**
38716      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38717      *<pre>
38718 Value         Description
38719 -----------   ----------------------------------------------------------------------
38720 qtip          Display a quick tip when the user hovers over the field
38721 title         Display a default browser title attribute popup
38722 under         Add a block div beneath the field containing the error text
38723 side          Add an error icon to the right of the field with a popup on hover
38724 [element id]  Add the error text directly to the innerHTML of the specified element
38725 </pre>
38726      */
38727     msgTarget : 'qtip',
38728     /**
38729      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38730      */
38731     msgFx : 'normal',
38732
38733     /**
38734      * @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.
38735      */
38736     readOnly : false,
38737
38738     /**
38739      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38740      */
38741     disabled : false,
38742
38743     /**
38744      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38745      */
38746     inputType : undefined,
38747     
38748     /**
38749      * @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).
38750          */
38751         tabIndex : undefined,
38752         
38753     // private
38754     isFormField : true,
38755
38756     // private
38757     hasFocus : false,
38758     /**
38759      * @property {Roo.Element} fieldEl
38760      * Element Containing the rendered Field (with label etc.)
38761      */
38762     /**
38763      * @cfg {Mixed} value A value to initialize this field with.
38764      */
38765     value : undefined,
38766
38767     /**
38768      * @cfg {String} name The field's HTML name attribute.
38769      */
38770     /**
38771      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38772      */
38773     // private
38774     loadedValue : false,
38775      
38776      
38777         // private ??
38778         initComponent : function(){
38779         Roo.form.Field.superclass.initComponent.call(this);
38780         this.addEvents({
38781             /**
38782              * @event focus
38783              * Fires when this field receives input focus.
38784              * @param {Roo.form.Field} this
38785              */
38786             focus : true,
38787             /**
38788              * @event blur
38789              * Fires when this field loses input focus.
38790              * @param {Roo.form.Field} this
38791              */
38792             blur : true,
38793             /**
38794              * @event specialkey
38795              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38796              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38797              * @param {Roo.form.Field} this
38798              * @param {Roo.EventObject} e The event object
38799              */
38800             specialkey : true,
38801             /**
38802              * @event change
38803              * Fires just before the field blurs if the field value has changed.
38804              * @param {Roo.form.Field} this
38805              * @param {Mixed} newValue The new value
38806              * @param {Mixed} oldValue The original value
38807              */
38808             change : true,
38809             /**
38810              * @event invalid
38811              * Fires after the field has been marked as invalid.
38812              * @param {Roo.form.Field} this
38813              * @param {String} msg The validation message
38814              */
38815             invalid : true,
38816             /**
38817              * @event valid
38818              * Fires after the field has been validated with no errors.
38819              * @param {Roo.form.Field} this
38820              */
38821             valid : true,
38822              /**
38823              * @event keyup
38824              * Fires after the key up
38825              * @param {Roo.form.Field} this
38826              * @param {Roo.EventObject}  e The event Object
38827              */
38828             keyup : true
38829         });
38830     },
38831
38832     /**
38833      * Returns the name attribute of the field if available
38834      * @return {String} name The field name
38835      */
38836     getName: function(){
38837          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
38838     },
38839
38840     // private
38841     onRender : function(ct, position){
38842         Roo.form.Field.superclass.onRender.call(this, ct, position);
38843         if(!this.el){
38844             var cfg = this.getAutoCreate();
38845             if(!cfg.name){
38846                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38847             }
38848             if (!cfg.name.length) {
38849                 delete cfg.name;
38850             }
38851             if(this.inputType){
38852                 cfg.type = this.inputType;
38853             }
38854             this.el = ct.createChild(cfg, position);
38855         }
38856         var type = this.el.dom.type;
38857         if(type){
38858             if(type == 'password'){
38859                 type = 'text';
38860             }
38861             this.el.addClass('x-form-'+type);
38862         }
38863         if(this.readOnly){
38864             this.el.dom.readOnly = true;
38865         }
38866         if(this.tabIndex !== undefined){
38867             this.el.dom.setAttribute('tabIndex', this.tabIndex);
38868         }
38869
38870         this.el.addClass([this.fieldClass, this.cls]);
38871         this.initValue();
38872     },
38873
38874     /**
38875      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
38876      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
38877      * @return {Roo.form.Field} this
38878      */
38879     applyTo : function(target){
38880         this.allowDomMove = false;
38881         this.el = Roo.get(target);
38882         this.render(this.el.dom.parentNode);
38883         return this;
38884     },
38885
38886     // private
38887     initValue : function(){
38888         if(this.value !== undefined){
38889             this.setValue(this.value);
38890         }else if(this.el.dom.value.length > 0){
38891             this.setValue(this.el.dom.value);
38892         }
38893     },
38894
38895     /**
38896      * Returns true if this field has been changed since it was originally loaded and is not disabled.
38897      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
38898      */
38899     isDirty : function() {
38900         if(this.disabled) {
38901             return false;
38902         }
38903         return String(this.getValue()) !== String(this.originalValue);
38904     },
38905
38906     /**
38907      * stores the current value in loadedValue
38908      */
38909     resetHasChanged : function()
38910     {
38911         this.loadedValue = String(this.getValue());
38912     },
38913     /**
38914      * checks the current value against the 'loaded' value.
38915      * Note - will return false if 'resetHasChanged' has not been called first.
38916      */
38917     hasChanged : function()
38918     {
38919         if(this.disabled || this.readOnly) {
38920             return false;
38921         }
38922         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
38923     },
38924     
38925     
38926     
38927     // private
38928     afterRender : function(){
38929         Roo.form.Field.superclass.afterRender.call(this);
38930         this.initEvents();
38931     },
38932
38933     // private
38934     fireKey : function(e){
38935         //Roo.log('field ' + e.getKey());
38936         if(e.isNavKeyPress()){
38937             this.fireEvent("specialkey", this, e);
38938         }
38939     },
38940
38941     /**
38942      * Resets the current field value to the originally loaded value and clears any validation messages
38943      */
38944     reset : function(){
38945         this.setValue(this.resetValue);
38946         this.originalValue = this.getValue();
38947         this.clearInvalid();
38948     },
38949
38950     // private
38951     initEvents : function(){
38952         // safari killled keypress - so keydown is now used..
38953         this.el.on("keydown" , this.fireKey,  this);
38954         this.el.on("focus", this.onFocus,  this);
38955         this.el.on("blur", this.onBlur,  this);
38956         this.el.relayEvent('keyup', this);
38957
38958         // reference to original value for reset
38959         this.originalValue = this.getValue();
38960         this.resetValue =  this.getValue();
38961     },
38962
38963     // private
38964     onFocus : function(){
38965         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38966             this.el.addClass(this.focusClass);
38967         }
38968         if(!this.hasFocus){
38969             this.hasFocus = true;
38970             this.startValue = this.getValue();
38971             this.fireEvent("focus", this);
38972         }
38973     },
38974
38975     beforeBlur : Roo.emptyFn,
38976
38977     // private
38978     onBlur : function(){
38979         this.beforeBlur();
38980         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38981             this.el.removeClass(this.focusClass);
38982         }
38983         this.hasFocus = false;
38984         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
38985             this.validate();
38986         }
38987         var v = this.getValue();
38988         if(String(v) !== String(this.startValue)){
38989             this.fireEvent('change', this, v, this.startValue);
38990         }
38991         this.fireEvent("blur", this);
38992     },
38993
38994     /**
38995      * Returns whether or not the field value is currently valid
38996      * @param {Boolean} preventMark True to disable marking the field invalid
38997      * @return {Boolean} True if the value is valid, else false
38998      */
38999     isValid : function(preventMark){
39000         if(this.disabled){
39001             return true;
39002         }
39003         var restore = this.preventMark;
39004         this.preventMark = preventMark === true;
39005         var v = this.validateValue(this.processValue(this.getRawValue()));
39006         this.preventMark = restore;
39007         return v;
39008     },
39009
39010     /**
39011      * Validates the field value
39012      * @return {Boolean} True if the value is valid, else false
39013      */
39014     validate : function(){
39015         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39016             this.clearInvalid();
39017             return true;
39018         }
39019         return false;
39020     },
39021
39022     processValue : function(value){
39023         return value;
39024     },
39025
39026     // private
39027     // Subclasses should provide the validation implementation by overriding this
39028     validateValue : function(value){
39029         return true;
39030     },
39031
39032     /**
39033      * Mark this field as invalid
39034      * @param {String} msg The validation message
39035      */
39036     markInvalid : function(msg){
39037         if(!this.rendered || this.preventMark){ // not rendered
39038             return;
39039         }
39040         
39041         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39042         
39043         obj.el.addClass(this.invalidClass);
39044         msg = msg || this.invalidText;
39045         switch(this.msgTarget){
39046             case 'qtip':
39047                 obj.el.dom.qtip = msg;
39048                 obj.el.dom.qclass = 'x-form-invalid-tip';
39049                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39050                     Roo.QuickTips.enable();
39051                 }
39052                 break;
39053             case 'title':
39054                 this.el.dom.title = msg;
39055                 break;
39056             case 'under':
39057                 if(!this.errorEl){
39058                     var elp = this.el.findParent('.x-form-element', 5, true);
39059                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39060                     this.errorEl.setWidth(elp.getWidth(true)-20);
39061                 }
39062                 this.errorEl.update(msg);
39063                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39064                 break;
39065             case 'side':
39066                 if(!this.errorIcon){
39067                     var elp = this.el.findParent('.x-form-element', 5, true);
39068                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39069                 }
39070                 this.alignErrorIcon();
39071                 this.errorIcon.dom.qtip = msg;
39072                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39073                 this.errorIcon.show();
39074                 this.on('resize', this.alignErrorIcon, this);
39075                 break;
39076             default:
39077                 var t = Roo.getDom(this.msgTarget);
39078                 t.innerHTML = msg;
39079                 t.style.display = this.msgDisplay;
39080                 break;
39081         }
39082         this.fireEvent('invalid', this, msg);
39083     },
39084
39085     // private
39086     alignErrorIcon : function(){
39087         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39088     },
39089
39090     /**
39091      * Clear any invalid styles/messages for this field
39092      */
39093     clearInvalid : function(){
39094         if(!this.rendered || this.preventMark){ // not rendered
39095             return;
39096         }
39097         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39098         
39099         obj.el.removeClass(this.invalidClass);
39100         switch(this.msgTarget){
39101             case 'qtip':
39102                 obj.el.dom.qtip = '';
39103                 break;
39104             case 'title':
39105                 this.el.dom.title = '';
39106                 break;
39107             case 'under':
39108                 if(this.errorEl){
39109                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39110                 }
39111                 break;
39112             case 'side':
39113                 if(this.errorIcon){
39114                     this.errorIcon.dom.qtip = '';
39115                     this.errorIcon.hide();
39116                     this.un('resize', this.alignErrorIcon, this);
39117                 }
39118                 break;
39119             default:
39120                 var t = Roo.getDom(this.msgTarget);
39121                 t.innerHTML = '';
39122                 t.style.display = 'none';
39123                 break;
39124         }
39125         this.fireEvent('valid', this);
39126     },
39127
39128     /**
39129      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39130      * @return {Mixed} value The field value
39131      */
39132     getRawValue : function(){
39133         var v = this.el.getValue();
39134         
39135         return v;
39136     },
39137
39138     /**
39139      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39140      * @return {Mixed} value The field value
39141      */
39142     getValue : function(){
39143         var v = this.el.getValue();
39144          
39145         return v;
39146     },
39147
39148     /**
39149      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39150      * @param {Mixed} value The value to set
39151      */
39152     setRawValue : function(v){
39153         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39154     },
39155
39156     /**
39157      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39158      * @param {Mixed} value The value to set
39159      */
39160     setValue : function(v){
39161         this.value = v;
39162         if(this.rendered){
39163             this.el.dom.value = (v === null || v === undefined ? '' : v);
39164              this.validate();
39165         }
39166     },
39167
39168     adjustSize : function(w, h){
39169         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39170         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39171         return s;
39172     },
39173
39174     adjustWidth : function(tag, w){
39175         tag = tag.toLowerCase();
39176         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39177             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39178                 if(tag == 'input'){
39179                     return w + 2;
39180                 }
39181                 if(tag == 'textarea'){
39182                     return w-2;
39183                 }
39184             }else if(Roo.isOpera){
39185                 if(tag == 'input'){
39186                     return w + 2;
39187                 }
39188                 if(tag == 'textarea'){
39189                     return w-2;
39190                 }
39191             }
39192         }
39193         return w;
39194     }
39195 });
39196
39197
39198 // anything other than normal should be considered experimental
39199 Roo.form.Field.msgFx = {
39200     normal : {
39201         show: function(msgEl, f){
39202             msgEl.setDisplayed('block');
39203         },
39204
39205         hide : function(msgEl, f){
39206             msgEl.setDisplayed(false).update('');
39207         }
39208     },
39209
39210     slide : {
39211         show: function(msgEl, f){
39212             msgEl.slideIn('t', {stopFx:true});
39213         },
39214
39215         hide : function(msgEl, f){
39216             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39217         }
39218     },
39219
39220     slideRight : {
39221         show: function(msgEl, f){
39222             msgEl.fixDisplay();
39223             msgEl.alignTo(f.el, 'tl-tr');
39224             msgEl.slideIn('l', {stopFx:true});
39225         },
39226
39227         hide : function(msgEl, f){
39228             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39229         }
39230     }
39231 };/*
39232  * Based on:
39233  * Ext JS Library 1.1.1
39234  * Copyright(c) 2006-2007, Ext JS, LLC.
39235  *
39236  * Originally Released Under LGPL - original licence link has changed is not relivant.
39237  *
39238  * Fork - LGPL
39239  * <script type="text/javascript">
39240  */
39241  
39242
39243 /**
39244  * @class Roo.form.TextField
39245  * @extends Roo.form.Field
39246  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39247  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39248  * @constructor
39249  * Creates a new TextField
39250  * @param {Object} config Configuration options
39251  */
39252 Roo.form.TextField = function(config){
39253     Roo.form.TextField.superclass.constructor.call(this, config);
39254     this.addEvents({
39255         /**
39256          * @event autosize
39257          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39258          * according to the default logic, but this event provides a hook for the developer to apply additional
39259          * logic at runtime to resize the field if needed.
39260              * @param {Roo.form.Field} this This text field
39261              * @param {Number} width The new field width
39262              */
39263         autosize : true
39264     });
39265 };
39266
39267 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39268     /**
39269      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39270      */
39271     grow : false,
39272     /**
39273      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39274      */
39275     growMin : 30,
39276     /**
39277      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39278      */
39279     growMax : 800,
39280     /**
39281      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39282      */
39283     vtype : null,
39284     /**
39285      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39286      */
39287     maskRe : null,
39288     /**
39289      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39290      */
39291     disableKeyFilter : false,
39292     /**
39293      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39294      */
39295     allowBlank : true,
39296     /**
39297      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39298      */
39299     minLength : 0,
39300     /**
39301      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39302      */
39303     maxLength : Number.MAX_VALUE,
39304     /**
39305      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39306      */
39307     minLengthText : "The minimum length for this field is {0}",
39308     /**
39309      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39310      */
39311     maxLengthText : "The maximum length for this field is {0}",
39312     /**
39313      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39314      */
39315     selectOnFocus : false,
39316     /**
39317      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39318      */
39319     blankText : "This field is required",
39320     /**
39321      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39322      * If available, this function will be called only after the basic validators all return true, and will be passed the
39323      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39324      */
39325     validator : null,
39326     /**
39327      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39328      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39329      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39330      */
39331     regex : null,
39332     /**
39333      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39334      */
39335     regexText : "",
39336     /**
39337      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39338      */
39339     emptyText : null,
39340    
39341
39342     // private
39343     initEvents : function()
39344     {
39345         if (this.emptyText) {
39346             this.el.attr('placeholder', this.emptyText);
39347         }
39348         
39349         Roo.form.TextField.superclass.initEvents.call(this);
39350         if(this.validationEvent == 'keyup'){
39351             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39352             this.el.on('keyup', this.filterValidation, this);
39353         }
39354         else if(this.validationEvent !== false){
39355             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39356         }
39357         
39358         if(this.selectOnFocus){
39359             this.on("focus", this.preFocus, this);
39360             
39361         }
39362         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39363             this.el.on("keypress", this.filterKeys, this);
39364         }
39365         if(this.grow){
39366             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39367             this.el.on("click", this.autoSize,  this);
39368         }
39369         if(this.el.is('input[type=password]') && Roo.isSafari){
39370             this.el.on('keydown', this.SafariOnKeyDown, this);
39371         }
39372     },
39373
39374     processValue : function(value){
39375         if(this.stripCharsRe){
39376             var newValue = value.replace(this.stripCharsRe, '');
39377             if(newValue !== value){
39378                 this.setRawValue(newValue);
39379                 return newValue;
39380             }
39381         }
39382         return value;
39383     },
39384
39385     filterValidation : function(e){
39386         if(!e.isNavKeyPress()){
39387             this.validationTask.delay(this.validationDelay);
39388         }
39389     },
39390
39391     // private
39392     onKeyUp : function(e){
39393         if(!e.isNavKeyPress()){
39394             this.autoSize();
39395         }
39396     },
39397
39398     /**
39399      * Resets the current field value to the originally-loaded value and clears any validation messages.
39400      *  
39401      */
39402     reset : function(){
39403         Roo.form.TextField.superclass.reset.call(this);
39404        
39405     },
39406
39407     
39408     // private
39409     preFocus : function(){
39410         
39411         if(this.selectOnFocus){
39412             this.el.dom.select();
39413         }
39414     },
39415
39416     
39417     // private
39418     filterKeys : function(e){
39419         var k = e.getKey();
39420         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39421             return;
39422         }
39423         var c = e.getCharCode(), cc = String.fromCharCode(c);
39424         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39425             return;
39426         }
39427         if(!this.maskRe.test(cc)){
39428             e.stopEvent();
39429         }
39430     },
39431
39432     setValue : function(v){
39433         
39434         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39435         
39436         this.autoSize();
39437     },
39438
39439     /**
39440      * Validates a value according to the field's validation rules and marks the field as invalid
39441      * if the validation fails
39442      * @param {Mixed} value The value to validate
39443      * @return {Boolean} True if the value is valid, else false
39444      */
39445     validateValue : function(value){
39446         if(value.length < 1)  { // if it's blank
39447              if(this.allowBlank){
39448                 this.clearInvalid();
39449                 return true;
39450              }else{
39451                 this.markInvalid(this.blankText);
39452                 return false;
39453              }
39454         }
39455         if(value.length < this.minLength){
39456             this.markInvalid(String.format(this.minLengthText, this.minLength));
39457             return false;
39458         }
39459         if(value.length > this.maxLength){
39460             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39461             return false;
39462         }
39463         if(this.vtype){
39464             var vt = Roo.form.VTypes;
39465             if(!vt[this.vtype](value, this)){
39466                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39467                 return false;
39468             }
39469         }
39470         if(typeof this.validator == "function"){
39471             var msg = this.validator(value);
39472             if(msg !== true){
39473                 this.markInvalid(msg);
39474                 return false;
39475             }
39476         }
39477         if(this.regex && !this.regex.test(value)){
39478             this.markInvalid(this.regexText);
39479             return false;
39480         }
39481         return true;
39482     },
39483
39484     /**
39485      * Selects text in this field
39486      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39487      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39488      */
39489     selectText : function(start, end){
39490         var v = this.getRawValue();
39491         if(v.length > 0){
39492             start = start === undefined ? 0 : start;
39493             end = end === undefined ? v.length : end;
39494             var d = this.el.dom;
39495             if(d.setSelectionRange){
39496                 d.setSelectionRange(start, end);
39497             }else if(d.createTextRange){
39498                 var range = d.createTextRange();
39499                 range.moveStart("character", start);
39500                 range.moveEnd("character", v.length-end);
39501                 range.select();
39502             }
39503         }
39504     },
39505
39506     /**
39507      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39508      * This only takes effect if grow = true, and fires the autosize event.
39509      */
39510     autoSize : function(){
39511         if(!this.grow || !this.rendered){
39512             return;
39513         }
39514         if(!this.metrics){
39515             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39516         }
39517         var el = this.el;
39518         var v = el.dom.value;
39519         var d = document.createElement('div');
39520         d.appendChild(document.createTextNode(v));
39521         v = d.innerHTML;
39522         d = null;
39523         v += "&#160;";
39524         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39525         this.el.setWidth(w);
39526         this.fireEvent("autosize", this, w);
39527     },
39528     
39529     // private
39530     SafariOnKeyDown : function(event)
39531     {
39532         // this is a workaround for a password hang bug on chrome/ webkit.
39533         
39534         var isSelectAll = false;
39535         
39536         if(this.el.dom.selectionEnd > 0){
39537             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39538         }
39539         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39540             event.preventDefault();
39541             this.setValue('');
39542             return;
39543         }
39544         
39545         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39546             
39547             event.preventDefault();
39548             // this is very hacky as keydown always get's upper case.
39549             
39550             var cc = String.fromCharCode(event.getCharCode());
39551             
39552             
39553             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39554             
39555         }
39556         
39557         
39558     }
39559 });/*
39560  * Based on:
39561  * Ext JS Library 1.1.1
39562  * Copyright(c) 2006-2007, Ext JS, LLC.
39563  *
39564  * Originally Released Under LGPL - original licence link has changed is not relivant.
39565  *
39566  * Fork - LGPL
39567  * <script type="text/javascript">
39568  */
39569  
39570 /**
39571  * @class Roo.form.Hidden
39572  * @extends Roo.form.TextField
39573  * Simple Hidden element used on forms 
39574  * 
39575  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39576  * 
39577  * @constructor
39578  * Creates a new Hidden form element.
39579  * @param {Object} config Configuration options
39580  */
39581
39582
39583
39584 // easy hidden field...
39585 Roo.form.Hidden = function(config){
39586     Roo.form.Hidden.superclass.constructor.call(this, config);
39587 };
39588   
39589 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39590     fieldLabel:      '',
39591     inputType:      'hidden',
39592     width:          50,
39593     allowBlank:     true,
39594     labelSeparator: '',
39595     hidden:         true,
39596     itemCls :       'x-form-item-display-none'
39597
39598
39599 });
39600
39601
39602 /*
39603  * Based on:
39604  * Ext JS Library 1.1.1
39605  * Copyright(c) 2006-2007, Ext JS, LLC.
39606  *
39607  * Originally Released Under LGPL - original licence link has changed is not relivant.
39608  *
39609  * Fork - LGPL
39610  * <script type="text/javascript">
39611  */
39612  
39613 /**
39614  * @class Roo.form.TriggerField
39615  * @extends Roo.form.TextField
39616  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39617  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39618  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39619  * for which you can provide a custom implementation.  For example:
39620  * <pre><code>
39621 var trigger = new Roo.form.TriggerField();
39622 trigger.onTriggerClick = myTriggerFn;
39623 trigger.applyTo('my-field');
39624 </code></pre>
39625  *
39626  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39627  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39628  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39629  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39630  * @constructor
39631  * Create a new TriggerField.
39632  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39633  * to the base TextField)
39634  */
39635 Roo.form.TriggerField = function(config){
39636     this.mimicing = false;
39637     Roo.form.TriggerField.superclass.constructor.call(this, config);
39638 };
39639
39640 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39641     /**
39642      * @cfg {String} triggerClass A CSS class to apply to the trigger
39643      */
39644     /**
39645      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39646      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39647      */
39648     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39649     /**
39650      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39651      */
39652     hideTrigger:false,
39653
39654     /** @cfg {Boolean} grow @hide */
39655     /** @cfg {Number} growMin @hide */
39656     /** @cfg {Number} growMax @hide */
39657
39658     /**
39659      * @hide 
39660      * @method
39661      */
39662     autoSize: Roo.emptyFn,
39663     // private
39664     monitorTab : true,
39665     // private
39666     deferHeight : true,
39667
39668     
39669     actionMode : 'wrap',
39670     // private
39671     onResize : function(w, h){
39672         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39673         if(typeof w == 'number'){
39674             var x = w - this.trigger.getWidth();
39675             this.el.setWidth(this.adjustWidth('input', x));
39676             this.trigger.setStyle('left', x+'px');
39677         }
39678     },
39679
39680     // private
39681     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39682
39683     // private
39684     getResizeEl : function(){
39685         return this.wrap;
39686     },
39687
39688     // private
39689     getPositionEl : function(){
39690         return this.wrap;
39691     },
39692
39693     // private
39694     alignErrorIcon : function(){
39695         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39696     },
39697
39698     // private
39699     onRender : function(ct, position){
39700         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39701         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39702         this.trigger = this.wrap.createChild(this.triggerConfig ||
39703                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39704         if(this.hideTrigger){
39705             this.trigger.setDisplayed(false);
39706         }
39707         this.initTrigger();
39708         if(!this.width){
39709             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39710         }
39711     },
39712
39713     // private
39714     initTrigger : function(){
39715         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39716         this.trigger.addClassOnOver('x-form-trigger-over');
39717         this.trigger.addClassOnClick('x-form-trigger-click');
39718     },
39719
39720     // private
39721     onDestroy : function(){
39722         if(this.trigger){
39723             this.trigger.removeAllListeners();
39724             this.trigger.remove();
39725         }
39726         if(this.wrap){
39727             this.wrap.remove();
39728         }
39729         Roo.form.TriggerField.superclass.onDestroy.call(this);
39730     },
39731
39732     // private
39733     onFocus : function(){
39734         Roo.form.TriggerField.superclass.onFocus.call(this);
39735         if(!this.mimicing){
39736             this.wrap.addClass('x-trigger-wrap-focus');
39737             this.mimicing = true;
39738             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39739             if(this.monitorTab){
39740                 this.el.on("keydown", this.checkTab, this);
39741             }
39742         }
39743     },
39744
39745     // private
39746     checkTab : function(e){
39747         if(e.getKey() == e.TAB){
39748             this.triggerBlur();
39749         }
39750     },
39751
39752     // private
39753     onBlur : function(){
39754         // do nothing
39755     },
39756
39757     // private
39758     mimicBlur : function(e, t){
39759         if(!this.wrap.contains(t) && this.validateBlur()){
39760             this.triggerBlur();
39761         }
39762     },
39763
39764     // private
39765     triggerBlur : function(){
39766         this.mimicing = false;
39767         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39768         if(this.monitorTab){
39769             this.el.un("keydown", this.checkTab, this);
39770         }
39771         this.wrap.removeClass('x-trigger-wrap-focus');
39772         Roo.form.TriggerField.superclass.onBlur.call(this);
39773     },
39774
39775     // private
39776     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39777     validateBlur : function(e, t){
39778         return true;
39779     },
39780
39781     // private
39782     onDisable : function(){
39783         Roo.form.TriggerField.superclass.onDisable.call(this);
39784         if(this.wrap){
39785             this.wrap.addClass('x-item-disabled');
39786         }
39787     },
39788
39789     // private
39790     onEnable : function(){
39791         Roo.form.TriggerField.superclass.onEnable.call(this);
39792         if(this.wrap){
39793             this.wrap.removeClass('x-item-disabled');
39794         }
39795     },
39796
39797     // private
39798     onShow : function(){
39799         var ae = this.getActionEl();
39800         
39801         if(ae){
39802             ae.dom.style.display = '';
39803             ae.dom.style.visibility = 'visible';
39804         }
39805     },
39806
39807     // private
39808     
39809     onHide : function(){
39810         var ae = this.getActionEl();
39811         ae.dom.style.display = 'none';
39812     },
39813
39814     /**
39815      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39816      * by an implementing function.
39817      * @method
39818      * @param {EventObject} e
39819      */
39820     onTriggerClick : Roo.emptyFn
39821 });
39822
39823 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
39824 // to be extended by an implementing class.  For an example of implementing this class, see the custom
39825 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
39826 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
39827     initComponent : function(){
39828         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
39829
39830         this.triggerConfig = {
39831             tag:'span', cls:'x-form-twin-triggers', cn:[
39832             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
39833             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
39834         ]};
39835     },
39836
39837     getTrigger : function(index){
39838         return this.triggers[index];
39839     },
39840
39841     initTrigger : function(){
39842         var ts = this.trigger.select('.x-form-trigger', true);
39843         this.wrap.setStyle('overflow', 'hidden');
39844         var triggerField = this;
39845         ts.each(function(t, all, index){
39846             t.hide = function(){
39847                 var w = triggerField.wrap.getWidth();
39848                 this.dom.style.display = 'none';
39849                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39850             };
39851             t.show = function(){
39852                 var w = triggerField.wrap.getWidth();
39853                 this.dom.style.display = '';
39854                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39855             };
39856             var triggerIndex = 'Trigger'+(index+1);
39857
39858             if(this['hide'+triggerIndex]){
39859                 t.dom.style.display = 'none';
39860             }
39861             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
39862             t.addClassOnOver('x-form-trigger-over');
39863             t.addClassOnClick('x-form-trigger-click');
39864         }, this);
39865         this.triggers = ts.elements;
39866     },
39867
39868     onTrigger1Click : Roo.emptyFn,
39869     onTrigger2Click : Roo.emptyFn
39870 });/*
39871  * Based on:
39872  * Ext JS Library 1.1.1
39873  * Copyright(c) 2006-2007, Ext JS, LLC.
39874  *
39875  * Originally Released Under LGPL - original licence link has changed is not relivant.
39876  *
39877  * Fork - LGPL
39878  * <script type="text/javascript">
39879  */
39880  
39881 /**
39882  * @class Roo.form.TextArea
39883  * @extends Roo.form.TextField
39884  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
39885  * support for auto-sizing.
39886  * @constructor
39887  * Creates a new TextArea
39888  * @param {Object} config Configuration options
39889  */
39890 Roo.form.TextArea = function(config){
39891     Roo.form.TextArea.superclass.constructor.call(this, config);
39892     // these are provided exchanges for backwards compat
39893     // minHeight/maxHeight were replaced by growMin/growMax to be
39894     // compatible with TextField growing config values
39895     if(this.minHeight !== undefined){
39896         this.growMin = this.minHeight;
39897     }
39898     if(this.maxHeight !== undefined){
39899         this.growMax = this.maxHeight;
39900     }
39901 };
39902
39903 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
39904     /**
39905      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
39906      */
39907     growMin : 60,
39908     /**
39909      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
39910      */
39911     growMax: 1000,
39912     /**
39913      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
39914      * in the field (equivalent to setting overflow: hidden, defaults to false)
39915      */
39916     preventScrollbars: false,
39917     /**
39918      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39919      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
39920      */
39921
39922     // private
39923     onRender : function(ct, position){
39924         if(!this.el){
39925             this.defaultAutoCreate = {
39926                 tag: "textarea",
39927                 style:"width:300px;height:60px;",
39928                 autocomplete: "new-password"
39929             };
39930         }
39931         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
39932         if(this.grow){
39933             this.textSizeEl = Roo.DomHelper.append(document.body, {
39934                 tag: "pre", cls: "x-form-grow-sizer"
39935             });
39936             if(this.preventScrollbars){
39937                 this.el.setStyle("overflow", "hidden");
39938             }
39939             this.el.setHeight(this.growMin);
39940         }
39941     },
39942
39943     onDestroy : function(){
39944         if(this.textSizeEl){
39945             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
39946         }
39947         Roo.form.TextArea.superclass.onDestroy.call(this);
39948     },
39949
39950     // private
39951     onKeyUp : function(e){
39952         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
39953             this.autoSize();
39954         }
39955     },
39956
39957     /**
39958      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
39959      * This only takes effect if grow = true, and fires the autosize event if the height changes.
39960      */
39961     autoSize : function(){
39962         if(!this.grow || !this.textSizeEl){
39963             return;
39964         }
39965         var el = this.el;
39966         var v = el.dom.value;
39967         var ts = this.textSizeEl;
39968
39969         ts.innerHTML = '';
39970         ts.appendChild(document.createTextNode(v));
39971         v = ts.innerHTML;
39972
39973         Roo.fly(ts).setWidth(this.el.getWidth());
39974         if(v.length < 1){
39975             v = "&#160;&#160;";
39976         }else{
39977             if(Roo.isIE){
39978                 v = v.replace(/\n/g, '<p>&#160;</p>');
39979             }
39980             v += "&#160;\n&#160;";
39981         }
39982         ts.innerHTML = v;
39983         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
39984         if(h != this.lastHeight){
39985             this.lastHeight = h;
39986             this.el.setHeight(h);
39987             this.fireEvent("autosize", this, h);
39988         }
39989     }
39990 });/*
39991  * Based on:
39992  * Ext JS Library 1.1.1
39993  * Copyright(c) 2006-2007, Ext JS, LLC.
39994  *
39995  * Originally Released Under LGPL - original licence link has changed is not relivant.
39996  *
39997  * Fork - LGPL
39998  * <script type="text/javascript">
39999  */
40000  
40001
40002 /**
40003  * @class Roo.form.NumberField
40004  * @extends Roo.form.TextField
40005  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40006  * @constructor
40007  * Creates a new NumberField
40008  * @param {Object} config Configuration options
40009  */
40010 Roo.form.NumberField = function(config){
40011     Roo.form.NumberField.superclass.constructor.call(this, config);
40012 };
40013
40014 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40015     /**
40016      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40017      */
40018     fieldClass: "x-form-field x-form-num-field",
40019     /**
40020      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40021      */
40022     allowDecimals : true,
40023     /**
40024      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40025      */
40026     decimalSeparator : ".",
40027     /**
40028      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40029      */
40030     decimalPrecision : 2,
40031     /**
40032      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40033      */
40034     allowNegative : true,
40035     /**
40036      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40037      */
40038     minValue : Number.NEGATIVE_INFINITY,
40039     /**
40040      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40041      */
40042     maxValue : Number.MAX_VALUE,
40043     /**
40044      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40045      */
40046     minText : "The minimum value for this field is {0}",
40047     /**
40048      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40049      */
40050     maxText : "The maximum value for this field is {0}",
40051     /**
40052      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40053      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40054      */
40055     nanText : "{0} is not a valid number",
40056
40057     // private
40058     initEvents : function(){
40059         Roo.form.NumberField.superclass.initEvents.call(this);
40060         var allowed = "0123456789";
40061         if(this.allowDecimals){
40062             allowed += this.decimalSeparator;
40063         }
40064         if(this.allowNegative){
40065             allowed += "-";
40066         }
40067         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40068         var keyPress = function(e){
40069             var k = e.getKey();
40070             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40071                 return;
40072             }
40073             var c = e.getCharCode();
40074             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40075                 e.stopEvent();
40076             }
40077         };
40078         this.el.on("keypress", keyPress, this);
40079     },
40080
40081     // private
40082     validateValue : function(value){
40083         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40084             return false;
40085         }
40086         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40087              return true;
40088         }
40089         var num = this.parseValue(value);
40090         if(isNaN(num)){
40091             this.markInvalid(String.format(this.nanText, value));
40092             return false;
40093         }
40094         if(num < this.minValue){
40095             this.markInvalid(String.format(this.minText, this.minValue));
40096             return false;
40097         }
40098         if(num > this.maxValue){
40099             this.markInvalid(String.format(this.maxText, this.maxValue));
40100             return false;
40101         }
40102         return true;
40103     },
40104
40105     getValue : function(){
40106         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40107     },
40108
40109     // private
40110     parseValue : function(value){
40111         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40112         return isNaN(value) ? '' : value;
40113     },
40114
40115     // private
40116     fixPrecision : function(value){
40117         var nan = isNaN(value);
40118         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40119             return nan ? '' : value;
40120         }
40121         return parseFloat(value).toFixed(this.decimalPrecision);
40122     },
40123
40124     setValue : function(v){
40125         v = this.fixPrecision(v);
40126         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40127     },
40128
40129     // private
40130     decimalPrecisionFcn : function(v){
40131         return Math.floor(v);
40132     },
40133
40134     beforeBlur : function(){
40135         var v = this.parseValue(this.getRawValue());
40136         if(v){
40137             this.setValue(v);
40138         }
40139     }
40140 });/*
40141  * Based on:
40142  * Ext JS Library 1.1.1
40143  * Copyright(c) 2006-2007, Ext JS, LLC.
40144  *
40145  * Originally Released Under LGPL - original licence link has changed is not relivant.
40146  *
40147  * Fork - LGPL
40148  * <script type="text/javascript">
40149  */
40150  
40151 /**
40152  * @class Roo.form.DateField
40153  * @extends Roo.form.TriggerField
40154  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40155 * @constructor
40156 * Create a new DateField
40157 * @param {Object} config
40158  */
40159 Roo.form.DateField = function(config){
40160     Roo.form.DateField.superclass.constructor.call(this, config);
40161     
40162       this.addEvents({
40163          
40164         /**
40165          * @event select
40166          * Fires when a date is selected
40167              * @param {Roo.form.DateField} combo This combo box
40168              * @param {Date} date The date selected
40169              */
40170         'select' : true
40171          
40172     });
40173     
40174     
40175     if(typeof this.minValue == "string") {
40176         this.minValue = this.parseDate(this.minValue);
40177     }
40178     if(typeof this.maxValue == "string") {
40179         this.maxValue = this.parseDate(this.maxValue);
40180     }
40181     this.ddMatch = null;
40182     if(this.disabledDates){
40183         var dd = this.disabledDates;
40184         var re = "(?:";
40185         for(var i = 0; i < dd.length; i++){
40186             re += dd[i];
40187             if(i != dd.length-1) {
40188                 re += "|";
40189             }
40190         }
40191         this.ddMatch = new RegExp(re + ")");
40192     }
40193 };
40194
40195 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40196     /**
40197      * @cfg {String} format
40198      * The default date format string which can be overriden for localization support.  The format must be
40199      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40200      */
40201     format : "m/d/y",
40202     /**
40203      * @cfg {String} altFormats
40204      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40205      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40206      */
40207     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40208     /**
40209      * @cfg {Array} disabledDays
40210      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40211      */
40212     disabledDays : null,
40213     /**
40214      * @cfg {String} disabledDaysText
40215      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40216      */
40217     disabledDaysText : "Disabled",
40218     /**
40219      * @cfg {Array} disabledDates
40220      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40221      * expression so they are very powerful. Some examples:
40222      * <ul>
40223      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40224      * <li>["03/08", "09/16"] would disable those days for every year</li>
40225      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40226      * <li>["03/../2006"] would disable every day in March 2006</li>
40227      * <li>["^03"] would disable every day in every March</li>
40228      * </ul>
40229      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40230      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40231      */
40232     disabledDates : null,
40233     /**
40234      * @cfg {String} disabledDatesText
40235      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40236      */
40237     disabledDatesText : "Disabled",
40238     /**
40239      * @cfg {Date/String} minValue
40240      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40241      * valid format (defaults to null).
40242      */
40243     minValue : null,
40244     /**
40245      * @cfg {Date/String} maxValue
40246      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40247      * valid format (defaults to null).
40248      */
40249     maxValue : null,
40250     /**
40251      * @cfg {String} minText
40252      * The error text to display when the date in the cell is before minValue (defaults to
40253      * 'The date in this field must be after {minValue}').
40254      */
40255     minText : "The date in this field must be equal to or after {0}",
40256     /**
40257      * @cfg {String} maxText
40258      * The error text to display when the date in the cell is after maxValue (defaults to
40259      * 'The date in this field must be before {maxValue}').
40260      */
40261     maxText : "The date in this field must be equal to or before {0}",
40262     /**
40263      * @cfg {String} invalidText
40264      * The error text to display when the date in the field is invalid (defaults to
40265      * '{value} is not a valid date - it must be in the format {format}').
40266      */
40267     invalidText : "{0} is not a valid date - it must be in the format {1}",
40268     /**
40269      * @cfg {String} triggerClass
40270      * An additional CSS class used to style the trigger button.  The trigger will always get the
40271      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40272      * which displays a calendar icon).
40273      */
40274     triggerClass : 'x-form-date-trigger',
40275     
40276
40277     /**
40278      * @cfg {Boolean} useIso
40279      * if enabled, then the date field will use a hidden field to store the 
40280      * real value as iso formated date. default (false)
40281      */ 
40282     useIso : false,
40283     /**
40284      * @cfg {String/Object} autoCreate
40285      * A DomHelper element spec, or true for a default element spec (defaults to
40286      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40287      */ 
40288     // private
40289     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40290     
40291     // private
40292     hiddenField: false,
40293     
40294     onRender : function(ct, position)
40295     {
40296         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40297         if (this.useIso) {
40298             //this.el.dom.removeAttribute('name'); 
40299             Roo.log("Changing name?");
40300             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40301             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40302                     'before', true);
40303             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40304             // prevent input submission
40305             this.hiddenName = this.name;
40306         }
40307             
40308             
40309     },
40310     
40311     // private
40312     validateValue : function(value)
40313     {
40314         value = this.formatDate(value);
40315         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40316             Roo.log('super failed');
40317             return false;
40318         }
40319         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40320              return true;
40321         }
40322         var svalue = value;
40323         value = this.parseDate(value);
40324         if(!value){
40325             Roo.log('parse date failed' + svalue);
40326             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40327             return false;
40328         }
40329         var time = value.getTime();
40330         if(this.minValue && time < this.minValue.getTime()){
40331             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40332             return false;
40333         }
40334         if(this.maxValue && time > this.maxValue.getTime()){
40335             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40336             return false;
40337         }
40338         if(this.disabledDays){
40339             var day = value.getDay();
40340             for(var i = 0; i < this.disabledDays.length; i++) {
40341                 if(day === this.disabledDays[i]){
40342                     this.markInvalid(this.disabledDaysText);
40343                     return false;
40344                 }
40345             }
40346         }
40347         var fvalue = this.formatDate(value);
40348         if(this.ddMatch && this.ddMatch.test(fvalue)){
40349             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40350             return false;
40351         }
40352         return true;
40353     },
40354
40355     // private
40356     // Provides logic to override the default TriggerField.validateBlur which just returns true
40357     validateBlur : function(){
40358         return !this.menu || !this.menu.isVisible();
40359     },
40360     
40361     getName: function()
40362     {
40363         // returns hidden if it's set..
40364         if (!this.rendered) {return ''};
40365         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40366         
40367     },
40368
40369     /**
40370      * Returns the current date value of the date field.
40371      * @return {Date} The date value
40372      */
40373     getValue : function(){
40374         
40375         return  this.hiddenField ?
40376                 this.hiddenField.value :
40377                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40378     },
40379
40380     /**
40381      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40382      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40383      * (the default format used is "m/d/y").
40384      * <br />Usage:
40385      * <pre><code>
40386 //All of these calls set the same date value (May 4, 2006)
40387
40388 //Pass a date object:
40389 var dt = new Date('5/4/06');
40390 dateField.setValue(dt);
40391
40392 //Pass a date string (default format):
40393 dateField.setValue('5/4/06');
40394
40395 //Pass a date string (custom format):
40396 dateField.format = 'Y-m-d';
40397 dateField.setValue('2006-5-4');
40398 </code></pre>
40399      * @param {String/Date} date The date or valid date string
40400      */
40401     setValue : function(date){
40402         if (this.hiddenField) {
40403             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40404         }
40405         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40406         // make sure the value field is always stored as a date..
40407         this.value = this.parseDate(date);
40408         
40409         
40410     },
40411
40412     // private
40413     parseDate : function(value){
40414         if(!value || value instanceof Date){
40415             return value;
40416         }
40417         var v = Date.parseDate(value, this.format);
40418          if (!v && this.useIso) {
40419             v = Date.parseDate(value, 'Y-m-d');
40420         }
40421         if(!v && this.altFormats){
40422             if(!this.altFormatsArray){
40423                 this.altFormatsArray = this.altFormats.split("|");
40424             }
40425             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40426                 v = Date.parseDate(value, this.altFormatsArray[i]);
40427             }
40428         }
40429         return v;
40430     },
40431
40432     // private
40433     formatDate : function(date, fmt){
40434         return (!date || !(date instanceof Date)) ?
40435                date : date.dateFormat(fmt || this.format);
40436     },
40437
40438     // private
40439     menuListeners : {
40440         select: function(m, d){
40441             
40442             this.setValue(d);
40443             this.fireEvent('select', this, d);
40444         },
40445         show : function(){ // retain focus styling
40446             this.onFocus();
40447         },
40448         hide : function(){
40449             this.focus.defer(10, this);
40450             var ml = this.menuListeners;
40451             this.menu.un("select", ml.select,  this);
40452             this.menu.un("show", ml.show,  this);
40453             this.menu.un("hide", ml.hide,  this);
40454         }
40455     },
40456
40457     // private
40458     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40459     onTriggerClick : function(){
40460         if(this.disabled){
40461             return;
40462         }
40463         if(this.menu == null){
40464             this.menu = new Roo.menu.DateMenu();
40465         }
40466         Roo.apply(this.menu.picker,  {
40467             showClear: this.allowBlank,
40468             minDate : this.minValue,
40469             maxDate : this.maxValue,
40470             disabledDatesRE : this.ddMatch,
40471             disabledDatesText : this.disabledDatesText,
40472             disabledDays : this.disabledDays,
40473             disabledDaysText : this.disabledDaysText,
40474             format : this.useIso ? 'Y-m-d' : this.format,
40475             minText : String.format(this.minText, this.formatDate(this.minValue)),
40476             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40477         });
40478         this.menu.on(Roo.apply({}, this.menuListeners, {
40479             scope:this
40480         }));
40481         this.menu.picker.setValue(this.getValue() || new Date());
40482         this.menu.show(this.el, "tl-bl?");
40483     },
40484
40485     beforeBlur : function(){
40486         var v = this.parseDate(this.getRawValue());
40487         if(v){
40488             this.setValue(v);
40489         }
40490     },
40491
40492     /*@
40493      * overide
40494      * 
40495      */
40496     isDirty : function() {
40497         if(this.disabled) {
40498             return false;
40499         }
40500         
40501         if(typeof(this.startValue) === 'undefined'){
40502             return false;
40503         }
40504         
40505         return String(this.getValue()) !== String(this.startValue);
40506         
40507     }
40508 });/*
40509  * Based on:
40510  * Ext JS Library 1.1.1
40511  * Copyright(c) 2006-2007, Ext JS, LLC.
40512  *
40513  * Originally Released Under LGPL - original licence link has changed is not relivant.
40514  *
40515  * Fork - LGPL
40516  * <script type="text/javascript">
40517  */
40518  
40519 /**
40520  * @class Roo.form.MonthField
40521  * @extends Roo.form.TriggerField
40522  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40523 * @constructor
40524 * Create a new MonthField
40525 * @param {Object} config
40526  */
40527 Roo.form.MonthField = function(config){
40528     
40529     Roo.form.MonthField.superclass.constructor.call(this, config);
40530     
40531       this.addEvents({
40532          
40533         /**
40534          * @event select
40535          * Fires when a date is selected
40536              * @param {Roo.form.MonthFieeld} combo This combo box
40537              * @param {Date} date The date selected
40538              */
40539         'select' : true
40540          
40541     });
40542     
40543     
40544     if(typeof this.minValue == "string") {
40545         this.minValue = this.parseDate(this.minValue);
40546     }
40547     if(typeof this.maxValue == "string") {
40548         this.maxValue = this.parseDate(this.maxValue);
40549     }
40550     this.ddMatch = null;
40551     if(this.disabledDates){
40552         var dd = this.disabledDates;
40553         var re = "(?:";
40554         for(var i = 0; i < dd.length; i++){
40555             re += dd[i];
40556             if(i != dd.length-1) {
40557                 re += "|";
40558             }
40559         }
40560         this.ddMatch = new RegExp(re + ")");
40561     }
40562 };
40563
40564 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40565     /**
40566      * @cfg {String} format
40567      * The default date format string which can be overriden for localization support.  The format must be
40568      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40569      */
40570     format : "M Y",
40571     /**
40572      * @cfg {String} altFormats
40573      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40574      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40575      */
40576     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40577     /**
40578      * @cfg {Array} disabledDays
40579      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40580      */
40581     disabledDays : [0,1,2,3,4,5,6],
40582     /**
40583      * @cfg {String} disabledDaysText
40584      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40585      */
40586     disabledDaysText : "Disabled",
40587     /**
40588      * @cfg {Array} disabledDates
40589      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40590      * expression so they are very powerful. Some examples:
40591      * <ul>
40592      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40593      * <li>["03/08", "09/16"] would disable those days for every year</li>
40594      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40595      * <li>["03/../2006"] would disable every day in March 2006</li>
40596      * <li>["^03"] would disable every day in every March</li>
40597      * </ul>
40598      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40599      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40600      */
40601     disabledDates : null,
40602     /**
40603      * @cfg {String} disabledDatesText
40604      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40605      */
40606     disabledDatesText : "Disabled",
40607     /**
40608      * @cfg {Date/String} minValue
40609      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40610      * valid format (defaults to null).
40611      */
40612     minValue : null,
40613     /**
40614      * @cfg {Date/String} maxValue
40615      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40616      * valid format (defaults to null).
40617      */
40618     maxValue : null,
40619     /**
40620      * @cfg {String} minText
40621      * The error text to display when the date in the cell is before minValue (defaults to
40622      * 'The date in this field must be after {minValue}').
40623      */
40624     minText : "The date in this field must be equal to or after {0}",
40625     /**
40626      * @cfg {String} maxTextf
40627      * The error text to display when the date in the cell is after maxValue (defaults to
40628      * 'The date in this field must be before {maxValue}').
40629      */
40630     maxText : "The date in this field must be equal to or before {0}",
40631     /**
40632      * @cfg {String} invalidText
40633      * The error text to display when the date in the field is invalid (defaults to
40634      * '{value} is not a valid date - it must be in the format {format}').
40635      */
40636     invalidText : "{0} is not a valid date - it must be in the format {1}",
40637     /**
40638      * @cfg {String} triggerClass
40639      * An additional CSS class used to style the trigger button.  The trigger will always get the
40640      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40641      * which displays a calendar icon).
40642      */
40643     triggerClass : 'x-form-date-trigger',
40644     
40645
40646     /**
40647      * @cfg {Boolean} useIso
40648      * if enabled, then the date field will use a hidden field to store the 
40649      * real value as iso formated date. default (true)
40650      */ 
40651     useIso : true,
40652     /**
40653      * @cfg {String/Object} autoCreate
40654      * A DomHelper element spec, or true for a default element spec (defaults to
40655      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40656      */ 
40657     // private
40658     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40659     
40660     // private
40661     hiddenField: false,
40662     
40663     hideMonthPicker : false,
40664     
40665     onRender : function(ct, position)
40666     {
40667         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40668         if (this.useIso) {
40669             this.el.dom.removeAttribute('name'); 
40670             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40671                     'before', true);
40672             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40673             // prevent input submission
40674             this.hiddenName = this.name;
40675         }
40676             
40677             
40678     },
40679     
40680     // private
40681     validateValue : function(value)
40682     {
40683         value = this.formatDate(value);
40684         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40685             return false;
40686         }
40687         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40688              return true;
40689         }
40690         var svalue = value;
40691         value = this.parseDate(value);
40692         if(!value){
40693             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40694             return false;
40695         }
40696         var time = value.getTime();
40697         if(this.minValue && time < this.minValue.getTime()){
40698             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40699             return false;
40700         }
40701         if(this.maxValue && time > this.maxValue.getTime()){
40702             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40703             return false;
40704         }
40705         /*if(this.disabledDays){
40706             var day = value.getDay();
40707             for(var i = 0; i < this.disabledDays.length; i++) {
40708                 if(day === this.disabledDays[i]){
40709                     this.markInvalid(this.disabledDaysText);
40710                     return false;
40711                 }
40712             }
40713         }
40714         */
40715         var fvalue = this.formatDate(value);
40716         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40717             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40718             return false;
40719         }
40720         */
40721         return true;
40722     },
40723
40724     // private
40725     // Provides logic to override the default TriggerField.validateBlur which just returns true
40726     validateBlur : function(){
40727         return !this.menu || !this.menu.isVisible();
40728     },
40729
40730     /**
40731      * Returns the current date value of the date field.
40732      * @return {Date} The date value
40733      */
40734     getValue : function(){
40735         
40736         
40737         
40738         return  this.hiddenField ?
40739                 this.hiddenField.value :
40740                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40741     },
40742
40743     /**
40744      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40745      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40746      * (the default format used is "m/d/y").
40747      * <br />Usage:
40748      * <pre><code>
40749 //All of these calls set the same date value (May 4, 2006)
40750
40751 //Pass a date object:
40752 var dt = new Date('5/4/06');
40753 monthField.setValue(dt);
40754
40755 //Pass a date string (default format):
40756 monthField.setValue('5/4/06');
40757
40758 //Pass a date string (custom format):
40759 monthField.format = 'Y-m-d';
40760 monthField.setValue('2006-5-4');
40761 </code></pre>
40762      * @param {String/Date} date The date or valid date string
40763      */
40764     setValue : function(date){
40765         Roo.log('month setValue' + date);
40766         // can only be first of month..
40767         
40768         var val = this.parseDate(date);
40769         
40770         if (this.hiddenField) {
40771             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40772         }
40773         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40774         this.value = this.parseDate(date);
40775     },
40776
40777     // private
40778     parseDate : function(value){
40779         if(!value || value instanceof Date){
40780             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40781             return value;
40782         }
40783         var v = Date.parseDate(value, this.format);
40784         if (!v && this.useIso) {
40785             v = Date.parseDate(value, 'Y-m-d');
40786         }
40787         if (v) {
40788             // 
40789             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40790         }
40791         
40792         
40793         if(!v && this.altFormats){
40794             if(!this.altFormatsArray){
40795                 this.altFormatsArray = this.altFormats.split("|");
40796             }
40797             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40798                 v = Date.parseDate(value, this.altFormatsArray[i]);
40799             }
40800         }
40801         return v;
40802     },
40803
40804     // private
40805     formatDate : function(date, fmt){
40806         return (!date || !(date instanceof Date)) ?
40807                date : date.dateFormat(fmt || this.format);
40808     },
40809
40810     // private
40811     menuListeners : {
40812         select: function(m, d){
40813             this.setValue(d);
40814             this.fireEvent('select', this, d);
40815         },
40816         show : function(){ // retain focus styling
40817             this.onFocus();
40818         },
40819         hide : function(){
40820             this.focus.defer(10, this);
40821             var ml = this.menuListeners;
40822             this.menu.un("select", ml.select,  this);
40823             this.menu.un("show", ml.show,  this);
40824             this.menu.un("hide", ml.hide,  this);
40825         }
40826     },
40827     // private
40828     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40829     onTriggerClick : function(){
40830         if(this.disabled){
40831             return;
40832         }
40833         if(this.menu == null){
40834             this.menu = new Roo.menu.DateMenu();
40835            
40836         }
40837         
40838         Roo.apply(this.menu.picker,  {
40839             
40840             showClear: this.allowBlank,
40841             minDate : this.minValue,
40842             maxDate : this.maxValue,
40843             disabledDatesRE : this.ddMatch,
40844             disabledDatesText : this.disabledDatesText,
40845             
40846             format : this.useIso ? 'Y-m-d' : this.format,
40847             minText : String.format(this.minText, this.formatDate(this.minValue)),
40848             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40849             
40850         });
40851          this.menu.on(Roo.apply({}, this.menuListeners, {
40852             scope:this
40853         }));
40854        
40855         
40856         var m = this.menu;
40857         var p = m.picker;
40858         
40859         // hide month picker get's called when we called by 'before hide';
40860         
40861         var ignorehide = true;
40862         p.hideMonthPicker  = function(disableAnim){
40863             if (ignorehide) {
40864                 return;
40865             }
40866              if(this.monthPicker){
40867                 Roo.log("hideMonthPicker called");
40868                 if(disableAnim === true){
40869                     this.monthPicker.hide();
40870                 }else{
40871                     this.monthPicker.slideOut('t', {duration:.2});
40872                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
40873                     p.fireEvent("select", this, this.value);
40874                     m.hide();
40875                 }
40876             }
40877         }
40878         
40879         Roo.log('picker set value');
40880         Roo.log(this.getValue());
40881         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
40882         m.show(this.el, 'tl-bl?');
40883         ignorehide  = false;
40884         // this will trigger hideMonthPicker..
40885         
40886         
40887         // hidden the day picker
40888         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
40889         
40890         
40891         
40892       
40893         
40894         p.showMonthPicker.defer(100, p);
40895     
40896         
40897        
40898     },
40899
40900     beforeBlur : function(){
40901         var v = this.parseDate(this.getRawValue());
40902         if(v){
40903             this.setValue(v);
40904         }
40905     }
40906
40907     /** @cfg {Boolean} grow @hide */
40908     /** @cfg {Number} growMin @hide */
40909     /** @cfg {Number} growMax @hide */
40910     /**
40911      * @hide
40912      * @method autoSize
40913      */
40914 });/*
40915  * Based on:
40916  * Ext JS Library 1.1.1
40917  * Copyright(c) 2006-2007, Ext JS, LLC.
40918  *
40919  * Originally Released Under LGPL - original licence link has changed is not relivant.
40920  *
40921  * Fork - LGPL
40922  * <script type="text/javascript">
40923  */
40924  
40925
40926 /**
40927  * @class Roo.form.ComboBox
40928  * @extends Roo.form.TriggerField
40929  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
40930  * @constructor
40931  * Create a new ComboBox.
40932  * @param {Object} config Configuration options
40933  */
40934 Roo.form.ComboBox = function(config){
40935     Roo.form.ComboBox.superclass.constructor.call(this, config);
40936     this.addEvents({
40937         /**
40938          * @event expand
40939          * Fires when the dropdown list is expanded
40940              * @param {Roo.form.ComboBox} combo This combo box
40941              */
40942         'expand' : true,
40943         /**
40944          * @event collapse
40945          * Fires when the dropdown list is collapsed
40946              * @param {Roo.form.ComboBox} combo This combo box
40947              */
40948         'collapse' : true,
40949         /**
40950          * @event beforeselect
40951          * Fires before a list item is selected. Return false to cancel the selection.
40952              * @param {Roo.form.ComboBox} combo This combo box
40953              * @param {Roo.data.Record} record The data record returned from the underlying store
40954              * @param {Number} index The index of the selected item in the dropdown list
40955              */
40956         'beforeselect' : true,
40957         /**
40958          * @event select
40959          * Fires when a list item is selected
40960              * @param {Roo.form.ComboBox} combo This combo box
40961              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
40962              * @param {Number} index The index of the selected item in the dropdown list
40963              */
40964         'select' : true,
40965         /**
40966          * @event beforequery
40967          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
40968          * The event object passed has these properties:
40969              * @param {Roo.form.ComboBox} combo This combo box
40970              * @param {String} query The query
40971              * @param {Boolean} forceAll true to force "all" query
40972              * @param {Boolean} cancel true to cancel the query
40973              * @param {Object} e The query event object
40974              */
40975         'beforequery': true,
40976          /**
40977          * @event add
40978          * Fires when the 'add' icon is pressed (add a listener to enable add button)
40979              * @param {Roo.form.ComboBox} combo This combo box
40980              */
40981         'add' : true,
40982         /**
40983          * @event edit
40984          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
40985              * @param {Roo.form.ComboBox} combo This combo box
40986              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
40987              */
40988         'edit' : true
40989         
40990         
40991     });
40992     if(this.transform){
40993         this.allowDomMove = false;
40994         var s = Roo.getDom(this.transform);
40995         if(!this.hiddenName){
40996             this.hiddenName = s.name;
40997         }
40998         if(!this.store){
40999             this.mode = 'local';
41000             var d = [], opts = s.options;
41001             for(var i = 0, len = opts.length;i < len; i++){
41002                 var o = opts[i];
41003                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41004                 if(o.selected) {
41005                     this.value = value;
41006                 }
41007                 d.push([value, o.text]);
41008             }
41009             this.store = new Roo.data.SimpleStore({
41010                 'id': 0,
41011                 fields: ['value', 'text'],
41012                 data : d
41013             });
41014             this.valueField = 'value';
41015             this.displayField = 'text';
41016         }
41017         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41018         if(!this.lazyRender){
41019             this.target = true;
41020             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41021             s.parentNode.removeChild(s); // remove it
41022             this.render(this.el.parentNode);
41023         }else{
41024             s.parentNode.removeChild(s); // remove it
41025         }
41026
41027     }
41028     if (this.store) {
41029         this.store = Roo.factory(this.store, Roo.data);
41030     }
41031     
41032     this.selectedIndex = -1;
41033     if(this.mode == 'local'){
41034         if(config.queryDelay === undefined){
41035             this.queryDelay = 10;
41036         }
41037         if(config.minChars === undefined){
41038             this.minChars = 0;
41039         }
41040     }
41041 };
41042
41043 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41044     /**
41045      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41046      */
41047     /**
41048      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41049      * rendering into an Roo.Editor, defaults to false)
41050      */
41051     /**
41052      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41053      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41054      */
41055     /**
41056      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41057      */
41058     /**
41059      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41060      * the dropdown list (defaults to undefined, with no header element)
41061      */
41062
41063      /**
41064      * @cfg {String/Roo.Template} tpl The template to use to render the output
41065      */
41066      
41067     // private
41068     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41069     /**
41070      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41071      */
41072     listWidth: undefined,
41073     /**
41074      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41075      * mode = 'remote' or 'text' if mode = 'local')
41076      */
41077     displayField: undefined,
41078     /**
41079      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41080      * mode = 'remote' or 'value' if mode = 'local'). 
41081      * Note: use of a valueField requires the user make a selection
41082      * in order for a value to be mapped.
41083      */
41084     valueField: undefined,
41085     
41086     
41087     /**
41088      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41089      * field's data value (defaults to the underlying DOM element's name)
41090      */
41091     hiddenName: undefined,
41092     /**
41093      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41094      */
41095     listClass: '',
41096     /**
41097      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41098      */
41099     selectedClass: 'x-combo-selected',
41100     /**
41101      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41102      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41103      * which displays a downward arrow icon).
41104      */
41105     triggerClass : 'x-form-arrow-trigger',
41106     /**
41107      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41108      */
41109     shadow:'sides',
41110     /**
41111      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41112      * anchor positions (defaults to 'tl-bl')
41113      */
41114     listAlign: 'tl-bl?',
41115     /**
41116      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41117      */
41118     maxHeight: 300,
41119     /**
41120      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41121      * query specified by the allQuery config option (defaults to 'query')
41122      */
41123     triggerAction: 'query',
41124     /**
41125      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41126      * (defaults to 4, does not apply if editable = false)
41127      */
41128     minChars : 4,
41129     /**
41130      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41131      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41132      */
41133     typeAhead: false,
41134     /**
41135      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41136      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41137      */
41138     queryDelay: 500,
41139     /**
41140      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41141      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41142      */
41143     pageSize: 0,
41144     /**
41145      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41146      * when editable = true (defaults to false)
41147      */
41148     selectOnFocus:false,
41149     /**
41150      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41151      */
41152     queryParam: 'query',
41153     /**
41154      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41155      * when mode = 'remote' (defaults to 'Loading...')
41156      */
41157     loadingText: 'Loading...',
41158     /**
41159      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41160      */
41161     resizable: false,
41162     /**
41163      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41164      */
41165     handleHeight : 8,
41166     /**
41167      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41168      * traditional select (defaults to true)
41169      */
41170     editable: true,
41171     /**
41172      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41173      */
41174     allQuery: '',
41175     /**
41176      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41177      */
41178     mode: 'remote',
41179     /**
41180      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41181      * listWidth has a higher value)
41182      */
41183     minListWidth : 70,
41184     /**
41185      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41186      * allow the user to set arbitrary text into the field (defaults to false)
41187      */
41188     forceSelection:false,
41189     /**
41190      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41191      * if typeAhead = true (defaults to 250)
41192      */
41193     typeAheadDelay : 250,
41194     /**
41195      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41196      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41197      */
41198     valueNotFoundText : undefined,
41199     /**
41200      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41201      */
41202     blockFocus : false,
41203     
41204     /**
41205      * @cfg {Boolean} disableClear Disable showing of clear button.
41206      */
41207     disableClear : false,
41208     /**
41209      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41210      */
41211     alwaysQuery : false,
41212     
41213     //private
41214     addicon : false,
41215     editicon: false,
41216     
41217     // element that contains real text value.. (when hidden is used..)
41218      
41219     // private
41220     onRender : function(ct, position){
41221         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41222         if(this.hiddenName){
41223             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41224                     'before', true);
41225             this.hiddenField.value =
41226                 this.hiddenValue !== undefined ? this.hiddenValue :
41227                 this.value !== undefined ? this.value : '';
41228
41229             // prevent input submission
41230             this.el.dom.removeAttribute('name');
41231              
41232              
41233         }
41234         if(Roo.isGecko){
41235             this.el.dom.setAttribute('autocomplete', 'off');
41236         }
41237
41238         var cls = 'x-combo-list';
41239
41240         this.list = new Roo.Layer({
41241             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41242         });
41243
41244         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41245         this.list.setWidth(lw);
41246         this.list.swallowEvent('mousewheel');
41247         this.assetHeight = 0;
41248
41249         if(this.title){
41250             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41251             this.assetHeight += this.header.getHeight();
41252         }
41253
41254         this.innerList = this.list.createChild({cls:cls+'-inner'});
41255         this.innerList.on('mouseover', this.onViewOver, this);
41256         this.innerList.on('mousemove', this.onViewMove, this);
41257         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41258         
41259         if(this.allowBlank && !this.pageSize && !this.disableClear){
41260             this.footer = this.list.createChild({cls:cls+'-ft'});
41261             this.pageTb = new Roo.Toolbar(this.footer);
41262            
41263         }
41264         if(this.pageSize){
41265             this.footer = this.list.createChild({cls:cls+'-ft'});
41266             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41267                     {pageSize: this.pageSize});
41268             
41269         }
41270         
41271         if (this.pageTb && this.allowBlank && !this.disableClear) {
41272             var _this = this;
41273             this.pageTb.add(new Roo.Toolbar.Fill(), {
41274                 cls: 'x-btn-icon x-btn-clear',
41275                 text: '&#160;',
41276                 handler: function()
41277                 {
41278                     _this.collapse();
41279                     _this.clearValue();
41280                     _this.onSelect(false, -1);
41281                 }
41282             });
41283         }
41284         if (this.footer) {
41285             this.assetHeight += this.footer.getHeight();
41286         }
41287         
41288
41289         if(!this.tpl){
41290             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41291         }
41292
41293         this.view = new Roo.View(this.innerList, this.tpl, {
41294             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41295         });
41296
41297         this.view.on('click', this.onViewClick, this);
41298
41299         this.store.on('beforeload', this.onBeforeLoad, this);
41300         this.store.on('load', this.onLoad, this);
41301         this.store.on('loadexception', this.onLoadException, this);
41302
41303         if(this.resizable){
41304             this.resizer = new Roo.Resizable(this.list,  {
41305                pinned:true, handles:'se'
41306             });
41307             this.resizer.on('resize', function(r, w, h){
41308                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41309                 this.listWidth = w;
41310                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41311                 this.restrictHeight();
41312             }, this);
41313             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41314         }
41315         if(!this.editable){
41316             this.editable = true;
41317             this.setEditable(false);
41318         }  
41319         
41320         
41321         if (typeof(this.events.add.listeners) != 'undefined') {
41322             
41323             this.addicon = this.wrap.createChild(
41324                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41325        
41326             this.addicon.on('click', function(e) {
41327                 this.fireEvent('add', this);
41328             }, this);
41329         }
41330         if (typeof(this.events.edit.listeners) != 'undefined') {
41331             
41332             this.editicon = this.wrap.createChild(
41333                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41334             if (this.addicon) {
41335                 this.editicon.setStyle('margin-left', '40px');
41336             }
41337             this.editicon.on('click', function(e) {
41338                 
41339                 // we fire even  if inothing is selected..
41340                 this.fireEvent('edit', this, this.lastData );
41341                 
41342             }, this);
41343         }
41344         
41345         
41346         
41347     },
41348
41349     // private
41350     initEvents : function(){
41351         Roo.form.ComboBox.superclass.initEvents.call(this);
41352
41353         this.keyNav = new Roo.KeyNav(this.el, {
41354             "up" : function(e){
41355                 this.inKeyMode = true;
41356                 this.selectPrev();
41357             },
41358
41359             "down" : function(e){
41360                 if(!this.isExpanded()){
41361                     this.onTriggerClick();
41362                 }else{
41363                     this.inKeyMode = true;
41364                     this.selectNext();
41365                 }
41366             },
41367
41368             "enter" : function(e){
41369                 this.onViewClick();
41370                 //return true;
41371             },
41372
41373             "esc" : function(e){
41374                 this.collapse();
41375             },
41376
41377             "tab" : function(e){
41378                 this.onViewClick(false);
41379                 this.fireEvent("specialkey", this, e);
41380                 return true;
41381             },
41382
41383             scope : this,
41384
41385             doRelay : function(foo, bar, hname){
41386                 if(hname == 'down' || this.scope.isExpanded()){
41387                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41388                 }
41389                 return true;
41390             },
41391
41392             forceKeyDown: true
41393         });
41394         this.queryDelay = Math.max(this.queryDelay || 10,
41395                 this.mode == 'local' ? 10 : 250);
41396         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41397         if(this.typeAhead){
41398             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41399         }
41400         if(this.editable !== false){
41401             this.el.on("keyup", this.onKeyUp, this);
41402         }
41403         if(this.forceSelection){
41404             this.on('blur', this.doForce, this);
41405         }
41406     },
41407
41408     onDestroy : function(){
41409         if(this.view){
41410             this.view.setStore(null);
41411             this.view.el.removeAllListeners();
41412             this.view.el.remove();
41413             this.view.purgeListeners();
41414         }
41415         if(this.list){
41416             this.list.destroy();
41417         }
41418         if(this.store){
41419             this.store.un('beforeload', this.onBeforeLoad, this);
41420             this.store.un('load', this.onLoad, this);
41421             this.store.un('loadexception', this.onLoadException, this);
41422         }
41423         Roo.form.ComboBox.superclass.onDestroy.call(this);
41424     },
41425
41426     // private
41427     fireKey : function(e){
41428         if(e.isNavKeyPress() && !this.list.isVisible()){
41429             this.fireEvent("specialkey", this, e);
41430         }
41431     },
41432
41433     // private
41434     onResize: function(w, h){
41435         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41436         
41437         if(typeof w != 'number'){
41438             // we do not handle it!?!?
41439             return;
41440         }
41441         var tw = this.trigger.getWidth();
41442         tw += this.addicon ? this.addicon.getWidth() : 0;
41443         tw += this.editicon ? this.editicon.getWidth() : 0;
41444         var x = w - tw;
41445         this.el.setWidth( this.adjustWidth('input', x));
41446             
41447         this.trigger.setStyle('left', x+'px');
41448         
41449         if(this.list && this.listWidth === undefined){
41450             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41451             this.list.setWidth(lw);
41452             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41453         }
41454         
41455     
41456         
41457     },
41458
41459     /**
41460      * Allow or prevent the user from directly editing the field text.  If false is passed,
41461      * the user will only be able to select from the items defined in the dropdown list.  This method
41462      * is the runtime equivalent of setting the 'editable' config option at config time.
41463      * @param {Boolean} value True to allow the user to directly edit the field text
41464      */
41465     setEditable : function(value){
41466         if(value == this.editable){
41467             return;
41468         }
41469         this.editable = value;
41470         if(!value){
41471             this.el.dom.setAttribute('readOnly', true);
41472             this.el.on('mousedown', this.onTriggerClick,  this);
41473             this.el.addClass('x-combo-noedit');
41474         }else{
41475             this.el.dom.setAttribute('readOnly', false);
41476             this.el.un('mousedown', this.onTriggerClick,  this);
41477             this.el.removeClass('x-combo-noedit');
41478         }
41479     },
41480
41481     // private
41482     onBeforeLoad : function(){
41483         if(!this.hasFocus){
41484             return;
41485         }
41486         this.innerList.update(this.loadingText ?
41487                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41488         this.restrictHeight();
41489         this.selectedIndex = -1;
41490     },
41491
41492     // private
41493     onLoad : function(){
41494         if(!this.hasFocus){
41495             return;
41496         }
41497         if(this.store.getCount() > 0){
41498             this.expand();
41499             this.restrictHeight();
41500             if(this.lastQuery == this.allQuery){
41501                 if(this.editable){
41502                     this.el.dom.select();
41503                 }
41504                 if(!this.selectByValue(this.value, true)){
41505                     this.select(0, true);
41506                 }
41507             }else{
41508                 this.selectNext();
41509                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41510                     this.taTask.delay(this.typeAheadDelay);
41511                 }
41512             }
41513         }else{
41514             this.onEmptyResults();
41515         }
41516         //this.el.focus();
41517     },
41518     // private
41519     onLoadException : function()
41520     {
41521         this.collapse();
41522         Roo.log(this.store.reader.jsonData);
41523         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41524             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41525         }
41526         
41527         
41528     },
41529     // private
41530     onTypeAhead : function(){
41531         if(this.store.getCount() > 0){
41532             var r = this.store.getAt(0);
41533             var newValue = r.data[this.displayField];
41534             var len = newValue.length;
41535             var selStart = this.getRawValue().length;
41536             if(selStart != len){
41537                 this.setRawValue(newValue);
41538                 this.selectText(selStart, newValue.length);
41539             }
41540         }
41541     },
41542
41543     // private
41544     onSelect : function(record, index){
41545         if(this.fireEvent('beforeselect', this, record, index) !== false){
41546             this.setFromData(index > -1 ? record.data : false);
41547             this.collapse();
41548             this.fireEvent('select', this, record, index);
41549         }
41550     },
41551
41552     /**
41553      * Returns the currently selected field value or empty string if no value is set.
41554      * @return {String} value The selected value
41555      */
41556     getValue : function(){
41557         if(this.valueField){
41558             return typeof this.value != 'undefined' ? this.value : '';
41559         }
41560         return Roo.form.ComboBox.superclass.getValue.call(this);
41561     },
41562
41563     /**
41564      * Clears any text/value currently set in the field
41565      */
41566     clearValue : function(){
41567         if(this.hiddenField){
41568             this.hiddenField.value = '';
41569         }
41570         this.value = '';
41571         this.setRawValue('');
41572         this.lastSelectionText = '';
41573         
41574     },
41575
41576     /**
41577      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41578      * will be displayed in the field.  If the value does not match the data value of an existing item,
41579      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41580      * Otherwise the field will be blank (although the value will still be set).
41581      * @param {String} value The value to match
41582      */
41583     setValue : function(v){
41584         var text = v;
41585         if(this.valueField){
41586             var r = this.findRecord(this.valueField, v);
41587             if(r){
41588                 text = r.data[this.displayField];
41589             }else if(this.valueNotFoundText !== undefined){
41590                 text = this.valueNotFoundText;
41591             }
41592         }
41593         this.lastSelectionText = text;
41594         if(this.hiddenField){
41595             this.hiddenField.value = v;
41596         }
41597         Roo.form.ComboBox.superclass.setValue.call(this, text);
41598         this.value = v;
41599     },
41600     /**
41601      * @property {Object} the last set data for the element
41602      */
41603     
41604     lastData : false,
41605     /**
41606      * Sets the value of the field based on a object which is related to the record format for the store.
41607      * @param {Object} value the value to set as. or false on reset?
41608      */
41609     setFromData : function(o){
41610         var dv = ''; // display value
41611         var vv = ''; // value value..
41612         this.lastData = o;
41613         if (this.displayField) {
41614             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41615         } else {
41616             // this is an error condition!!!
41617             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41618         }
41619         
41620         if(this.valueField){
41621             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41622         }
41623         if(this.hiddenField){
41624             this.hiddenField.value = vv;
41625             
41626             this.lastSelectionText = dv;
41627             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41628             this.value = vv;
41629             return;
41630         }
41631         // no hidden field.. - we store the value in 'value', but still display
41632         // display field!!!!
41633         this.lastSelectionText = dv;
41634         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41635         this.value = vv;
41636         
41637         
41638     },
41639     // private
41640     reset : function(){
41641         // overridden so that last data is reset..
41642         this.setValue(this.resetValue);
41643         this.originalValue = this.getValue();
41644         this.clearInvalid();
41645         this.lastData = false;
41646         if (this.view) {
41647             this.view.clearSelections();
41648         }
41649     },
41650     // private
41651     findRecord : function(prop, value){
41652         var record;
41653         if(this.store.getCount() > 0){
41654             this.store.each(function(r){
41655                 if(r.data[prop] == value){
41656                     record = r;
41657                     return false;
41658                 }
41659                 return true;
41660             });
41661         }
41662         return record;
41663     },
41664     
41665     getName: function()
41666     {
41667         // returns hidden if it's set..
41668         if (!this.rendered) {return ''};
41669         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41670         
41671     },
41672     // private
41673     onViewMove : function(e, t){
41674         this.inKeyMode = false;
41675     },
41676
41677     // private
41678     onViewOver : function(e, t){
41679         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41680             return;
41681         }
41682         var item = this.view.findItemFromChild(t);
41683         if(item){
41684             var index = this.view.indexOf(item);
41685             this.select(index, false);
41686         }
41687     },
41688
41689     // private
41690     onViewClick : function(doFocus)
41691     {
41692         var index = this.view.getSelectedIndexes()[0];
41693         var r = this.store.getAt(index);
41694         if(r){
41695             this.onSelect(r, index);
41696         }
41697         if(doFocus !== false && !this.blockFocus){
41698             this.el.focus();
41699         }
41700     },
41701
41702     // private
41703     restrictHeight : function(){
41704         this.innerList.dom.style.height = '';
41705         var inner = this.innerList.dom;
41706         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41707         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41708         this.list.beginUpdate();
41709         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41710         this.list.alignTo(this.el, this.listAlign);
41711         this.list.endUpdate();
41712     },
41713
41714     // private
41715     onEmptyResults : function(){
41716         this.collapse();
41717     },
41718
41719     /**
41720      * Returns true if the dropdown list is expanded, else false.
41721      */
41722     isExpanded : function(){
41723         return this.list.isVisible();
41724     },
41725
41726     /**
41727      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41728      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41729      * @param {String} value The data value of the item to select
41730      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41731      * selected item if it is not currently in view (defaults to true)
41732      * @return {Boolean} True if the value matched an item in the list, else false
41733      */
41734     selectByValue : function(v, scrollIntoView){
41735         if(v !== undefined && v !== null){
41736             var r = this.findRecord(this.valueField || this.displayField, v);
41737             if(r){
41738                 this.select(this.store.indexOf(r), scrollIntoView);
41739                 return true;
41740             }
41741         }
41742         return false;
41743     },
41744
41745     /**
41746      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41747      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41748      * @param {Number} index The zero-based index of the list item to select
41749      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41750      * selected item if it is not currently in view (defaults to true)
41751      */
41752     select : function(index, scrollIntoView){
41753         this.selectedIndex = index;
41754         this.view.select(index);
41755         if(scrollIntoView !== false){
41756             var el = this.view.getNode(index);
41757             if(el){
41758                 this.innerList.scrollChildIntoView(el, false);
41759             }
41760         }
41761     },
41762
41763     // private
41764     selectNext : function(){
41765         var ct = this.store.getCount();
41766         if(ct > 0){
41767             if(this.selectedIndex == -1){
41768                 this.select(0);
41769             }else if(this.selectedIndex < ct-1){
41770                 this.select(this.selectedIndex+1);
41771             }
41772         }
41773     },
41774
41775     // private
41776     selectPrev : function(){
41777         var ct = this.store.getCount();
41778         if(ct > 0){
41779             if(this.selectedIndex == -1){
41780                 this.select(0);
41781             }else if(this.selectedIndex != 0){
41782                 this.select(this.selectedIndex-1);
41783             }
41784         }
41785     },
41786
41787     // private
41788     onKeyUp : function(e){
41789         if(this.editable !== false && !e.isSpecialKey()){
41790             this.lastKey = e.getKey();
41791             this.dqTask.delay(this.queryDelay);
41792         }
41793     },
41794
41795     // private
41796     validateBlur : function(){
41797         return !this.list || !this.list.isVisible();   
41798     },
41799
41800     // private
41801     initQuery : function(){
41802         this.doQuery(this.getRawValue());
41803     },
41804
41805     // private
41806     doForce : function(){
41807         if(this.el.dom.value.length > 0){
41808             this.el.dom.value =
41809                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
41810              
41811         }
41812     },
41813
41814     /**
41815      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
41816      * query allowing the query action to be canceled if needed.
41817      * @param {String} query The SQL query to execute
41818      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
41819      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
41820      * saved in the current store (defaults to false)
41821      */
41822     doQuery : function(q, forceAll){
41823         if(q === undefined || q === null){
41824             q = '';
41825         }
41826         var qe = {
41827             query: q,
41828             forceAll: forceAll,
41829             combo: this,
41830             cancel:false
41831         };
41832         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
41833             return false;
41834         }
41835         q = qe.query;
41836         forceAll = qe.forceAll;
41837         if(forceAll === true || (q.length >= this.minChars)){
41838             if(this.lastQuery != q || this.alwaysQuery){
41839                 this.lastQuery = q;
41840                 if(this.mode == 'local'){
41841                     this.selectedIndex = -1;
41842                     if(forceAll){
41843                         this.store.clearFilter();
41844                     }else{
41845                         this.store.filter(this.displayField, q);
41846                     }
41847                     this.onLoad();
41848                 }else{
41849                     this.store.baseParams[this.queryParam] = q;
41850                     this.store.load({
41851                         params: this.getParams(q)
41852                     });
41853                     this.expand();
41854                 }
41855             }else{
41856                 this.selectedIndex = -1;
41857                 this.onLoad();   
41858             }
41859         }
41860     },
41861
41862     // private
41863     getParams : function(q){
41864         var p = {};
41865         //p[this.queryParam] = q;
41866         if(this.pageSize){
41867             p.start = 0;
41868             p.limit = this.pageSize;
41869         }
41870         return p;
41871     },
41872
41873     /**
41874      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
41875      */
41876     collapse : function(){
41877         if(!this.isExpanded()){
41878             return;
41879         }
41880         this.list.hide();
41881         Roo.get(document).un('mousedown', this.collapseIf, this);
41882         Roo.get(document).un('mousewheel', this.collapseIf, this);
41883         if (!this.editable) {
41884             Roo.get(document).un('keydown', this.listKeyPress, this);
41885         }
41886         this.fireEvent('collapse', this);
41887     },
41888
41889     // private
41890     collapseIf : function(e){
41891         if(!e.within(this.wrap) && !e.within(this.list)){
41892             this.collapse();
41893         }
41894     },
41895
41896     /**
41897      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
41898      */
41899     expand : function(){
41900         if(this.isExpanded() || !this.hasFocus){
41901             return;
41902         }
41903         this.list.alignTo(this.el, this.listAlign);
41904         this.list.show();
41905         Roo.get(document).on('mousedown', this.collapseIf, this);
41906         Roo.get(document).on('mousewheel', this.collapseIf, this);
41907         if (!this.editable) {
41908             Roo.get(document).on('keydown', this.listKeyPress, this);
41909         }
41910         
41911         this.fireEvent('expand', this);
41912     },
41913
41914     // private
41915     // Implements the default empty TriggerField.onTriggerClick function
41916     onTriggerClick : function(){
41917         if(this.disabled){
41918             return;
41919         }
41920         if(this.isExpanded()){
41921             this.collapse();
41922             if (!this.blockFocus) {
41923                 this.el.focus();
41924             }
41925             
41926         }else {
41927             this.hasFocus = true;
41928             if(this.triggerAction == 'all') {
41929                 this.doQuery(this.allQuery, true);
41930             } else {
41931                 this.doQuery(this.getRawValue());
41932             }
41933             if (!this.blockFocus) {
41934                 this.el.focus();
41935             }
41936         }
41937     },
41938     listKeyPress : function(e)
41939     {
41940         //Roo.log('listkeypress');
41941         // scroll to first matching element based on key pres..
41942         if (e.isSpecialKey()) {
41943             return false;
41944         }
41945         var k = String.fromCharCode(e.getKey()).toUpperCase();
41946         //Roo.log(k);
41947         var match  = false;
41948         var csel = this.view.getSelectedNodes();
41949         var cselitem = false;
41950         if (csel.length) {
41951             var ix = this.view.indexOf(csel[0]);
41952             cselitem  = this.store.getAt(ix);
41953             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
41954                 cselitem = false;
41955             }
41956             
41957         }
41958         
41959         this.store.each(function(v) { 
41960             if (cselitem) {
41961                 // start at existing selection.
41962                 if (cselitem.id == v.id) {
41963                     cselitem = false;
41964                 }
41965                 return;
41966             }
41967                 
41968             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
41969                 match = this.store.indexOf(v);
41970                 return false;
41971             }
41972         }, this);
41973         
41974         if (match === false) {
41975             return true; // no more action?
41976         }
41977         // scroll to?
41978         this.view.select(match);
41979         var sn = Roo.get(this.view.getSelectedNodes()[0]);
41980         sn.scrollIntoView(sn.dom.parentNode, false);
41981     }
41982
41983     /** 
41984     * @cfg {Boolean} grow 
41985     * @hide 
41986     */
41987     /** 
41988     * @cfg {Number} growMin 
41989     * @hide 
41990     */
41991     /** 
41992     * @cfg {Number} growMax 
41993     * @hide 
41994     */
41995     /**
41996      * @hide
41997      * @method autoSize
41998      */
41999 });/*
42000  * Copyright(c) 2010-2012, Roo J Solutions Limited
42001  *
42002  * Licence LGPL
42003  *
42004  */
42005
42006 /**
42007  * @class Roo.form.ComboBoxArray
42008  * @extends Roo.form.TextField
42009  * A facebook style adder... for lists of email / people / countries  etc...
42010  * pick multiple items from a combo box, and shows each one.
42011  *
42012  *  Fred [x]  Brian [x]  [Pick another |v]
42013  *
42014  *
42015  *  For this to work: it needs various extra information
42016  *    - normal combo problay has
42017  *      name, hiddenName
42018  *    + displayField, valueField
42019  *
42020  *    For our purpose...
42021  *
42022  *
42023  *   If we change from 'extends' to wrapping...
42024  *   
42025  *  
42026  *
42027  
42028  
42029  * @constructor
42030  * Create a new ComboBoxArray.
42031  * @param {Object} config Configuration options
42032  */
42033  
42034
42035 Roo.form.ComboBoxArray = function(config)
42036 {
42037     this.addEvents({
42038         /**
42039          * @event beforeremove
42040          * Fires before remove the value from the list
42041              * @param {Roo.form.ComboBoxArray} _self This combo box array
42042              * @param {Roo.form.ComboBoxArray.Item} item removed item
42043              */
42044         'beforeremove' : true,
42045         /**
42046          * @event remove
42047          * Fires when remove the value from the list
42048              * @param {Roo.form.ComboBoxArray} _self This combo box array
42049              * @param {Roo.form.ComboBoxArray.Item} item removed item
42050              */
42051         'remove' : true
42052         
42053         
42054     });
42055     
42056     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42057     
42058     this.items = new Roo.util.MixedCollection(false);
42059     
42060     // construct the child combo...
42061     
42062     
42063     
42064     
42065    
42066     
42067 }
42068
42069  
42070 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42071
42072     /**
42073      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
42074      */
42075     
42076     lastData : false,
42077     
42078     // behavies liek a hiddne field
42079     inputType:      'hidden',
42080     /**
42081      * @cfg {Number} width The width of the box that displays the selected element
42082      */ 
42083     width:          300,
42084
42085     
42086     
42087     /**
42088      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42089      */
42090     name : false,
42091     /**
42092      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42093      */
42094     hiddenName : false,
42095     
42096     
42097     // private the array of items that are displayed..
42098     items  : false,
42099     // private - the hidden field el.
42100     hiddenEl : false,
42101     // private - the filed el..
42102     el : false,
42103     
42104     //validateValue : function() { return true; }, // all values are ok!
42105     //onAddClick: function() { },
42106     
42107     onRender : function(ct, position) 
42108     {
42109         
42110         // create the standard hidden element
42111         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42112         
42113         
42114         // give fake names to child combo;
42115         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42116         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
42117         
42118         this.combo = Roo.factory(this.combo, Roo.form);
42119         this.combo.onRender(ct, position);
42120         if (typeof(this.combo.width) != 'undefined') {
42121             this.combo.onResize(this.combo.width,0);
42122         }
42123         
42124         this.combo.initEvents();
42125         
42126         // assigned so form know we need to do this..
42127         this.store          = this.combo.store;
42128         this.valueField     = this.combo.valueField;
42129         this.displayField   = this.combo.displayField ;
42130         
42131         
42132         this.combo.wrap.addClass('x-cbarray-grp');
42133         
42134         var cbwrap = this.combo.wrap.createChild(
42135             {tag: 'div', cls: 'x-cbarray-cb'},
42136             this.combo.el.dom
42137         );
42138         
42139              
42140         this.hiddenEl = this.combo.wrap.createChild({
42141             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42142         });
42143         this.el = this.combo.wrap.createChild({
42144             tag: 'input',  type:'hidden' , name: this.name, value : ''
42145         });
42146          //   this.el.dom.removeAttribute("name");
42147         
42148         
42149         this.outerWrap = this.combo.wrap;
42150         this.wrap = cbwrap;
42151         
42152         this.outerWrap.setWidth(this.width);
42153         this.outerWrap.dom.removeChild(this.el.dom);
42154         
42155         this.wrap.dom.appendChild(this.el.dom);
42156         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42157         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42158         
42159         this.combo.trigger.setStyle('position','relative');
42160         this.combo.trigger.setStyle('left', '0px');
42161         this.combo.trigger.setStyle('top', '2px');
42162         
42163         this.combo.el.setStyle('vertical-align', 'text-bottom');
42164         
42165         //this.trigger.setStyle('vertical-align', 'top');
42166         
42167         // this should use the code from combo really... on('add' ....)
42168         if (this.adder) {
42169             
42170         
42171             this.adder = this.outerWrap.createChild(
42172                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42173             var _t = this;
42174             this.adder.on('click', function(e) {
42175                 _t.fireEvent('adderclick', this, e);
42176             }, _t);
42177         }
42178         //var _t = this;
42179         //this.adder.on('click', this.onAddClick, _t);
42180         
42181         
42182         this.combo.on('select', function(cb, rec, ix) {
42183             this.addItem(rec.data);
42184             
42185             cb.setValue('');
42186             cb.el.dom.value = '';
42187             //cb.lastData = rec.data;
42188             // add to list
42189             
42190         }, this);
42191         
42192         
42193     },
42194     
42195     
42196     getName: function()
42197     {
42198         // returns hidden if it's set..
42199         if (!this.rendered) {return ''};
42200         return  this.hiddenName ? this.hiddenName : this.name;
42201         
42202     },
42203     
42204     
42205     onResize: function(w, h){
42206         
42207         return;
42208         // not sure if this is needed..
42209         //this.combo.onResize(w,h);
42210         
42211         if(typeof w != 'number'){
42212             // we do not handle it!?!?
42213             return;
42214         }
42215         var tw = this.combo.trigger.getWidth();
42216         tw += this.addicon ? this.addicon.getWidth() : 0;
42217         tw += this.editicon ? this.editicon.getWidth() : 0;
42218         var x = w - tw;
42219         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42220             
42221         this.combo.trigger.setStyle('left', '0px');
42222         
42223         if(this.list && this.listWidth === undefined){
42224             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42225             this.list.setWidth(lw);
42226             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42227         }
42228         
42229     
42230         
42231     },
42232     
42233     addItem: function(rec)
42234     {
42235         var valueField = this.combo.valueField;
42236         var displayField = this.combo.displayField;
42237         if (this.items.indexOfKey(rec[valueField]) > -1) {
42238             //console.log("GOT " + rec.data.id);
42239             return;
42240         }
42241         
42242         var x = new Roo.form.ComboBoxArray.Item({
42243             //id : rec[this.idField],
42244             data : rec,
42245             displayField : displayField ,
42246             tipField : displayField ,
42247             cb : this
42248         });
42249         // use the 
42250         this.items.add(rec[valueField],x);
42251         // add it before the element..
42252         this.updateHiddenEl();
42253         x.render(this.outerWrap, this.wrap.dom);
42254         // add the image handler..
42255     },
42256     
42257     updateHiddenEl : function()
42258     {
42259         this.validate();
42260         if (!this.hiddenEl) {
42261             return;
42262         }
42263         var ar = [];
42264         var idField = this.combo.valueField;
42265         
42266         this.items.each(function(f) {
42267             ar.push(f.data[idField]);
42268            
42269         });
42270         this.hiddenEl.dom.value = ar.join(',');
42271         this.validate();
42272     },
42273     
42274     reset : function()
42275     {
42276         this.items.clear();
42277         
42278         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42279            el.remove();
42280         });
42281         
42282         this.el.dom.value = '';
42283         if (this.hiddenEl) {
42284             this.hiddenEl.dom.value = '';
42285         }
42286         
42287     },
42288     getValue: function()
42289     {
42290         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42291     },
42292     setValue: function(v) // not a valid action - must use addItems..
42293     {
42294          
42295         this.reset();
42296         
42297         
42298         
42299         if (this.store.isLocal && (typeof(v) == 'string')) {
42300             // then we can use the store to find the values..
42301             // comma seperated at present.. this needs to allow JSON based encoding..
42302             this.hiddenEl.value  = v;
42303             var v_ar = [];
42304             Roo.each(v.split(','), function(k) {
42305                 Roo.log("CHECK " + this.valueField + ',' + k);
42306                 var li = this.store.query(this.valueField, k);
42307                 if (!li.length) {
42308                     return;
42309                 }
42310                 var add = {};
42311                 add[this.valueField] = k;
42312                 add[this.displayField] = li.item(0).data[this.displayField];
42313                 
42314                 this.addItem(add);
42315             }, this) 
42316              
42317         }
42318         if (typeof(v) == 'object' ) {
42319             // then let's assume it's an array of objects..
42320             Roo.each(v, function(l) {
42321                 this.addItem(l);
42322             }, this);
42323              
42324         }
42325         
42326         
42327     },
42328     setFromData: function(v)
42329     {
42330         // this recieves an object, if setValues is called.
42331         this.reset();
42332         this.el.dom.value = v[this.displayField];
42333         this.hiddenEl.dom.value = v[this.valueField];
42334         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42335             return;
42336         }
42337         var kv = v[this.valueField];
42338         var dv = v[this.displayField];
42339         kv = typeof(kv) != 'string' ? '' : kv;
42340         dv = typeof(dv) != 'string' ? '' : dv;
42341         
42342         
42343         var keys = kv.split(',');
42344         var display = dv.split(',');
42345         for (var i = 0 ; i < keys.length; i++) {
42346             
42347             add = {};
42348             add[this.valueField] = keys[i];
42349             add[this.displayField] = display[i];
42350             this.addItem(add);
42351         }
42352       
42353         
42354     },
42355     
42356     /**
42357      * Validates the combox array value
42358      * @return {Boolean} True if the value is valid, else false
42359      */
42360     validate : function(){
42361         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42362             this.clearInvalid();
42363             return true;
42364         }
42365         return false;
42366     },
42367     
42368     validateValue : function(value){
42369         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42370         
42371     },
42372     
42373     /*@
42374      * overide
42375      * 
42376      */
42377     isDirty : function() {
42378         if(this.disabled) {
42379             return false;
42380         }
42381         
42382         try {
42383             var d = Roo.decode(String(this.originalValue));
42384         } catch (e) {
42385             return String(this.getValue()) !== String(this.originalValue);
42386         }
42387         
42388         var originalValue = [];
42389         
42390         for (var i = 0; i < d.length; i++){
42391             originalValue.push(d[i][this.valueField]);
42392         }
42393         
42394         return String(this.getValue()) !== String(originalValue.join(','));
42395         
42396     }
42397     
42398 });
42399
42400
42401
42402 /**
42403  * @class Roo.form.ComboBoxArray.Item
42404  * @extends Roo.BoxComponent
42405  * A selected item in the list
42406  *  Fred [x]  Brian [x]  [Pick another |v]
42407  * 
42408  * @constructor
42409  * Create a new item.
42410  * @param {Object} config Configuration options
42411  */
42412  
42413 Roo.form.ComboBoxArray.Item = function(config) {
42414     config.id = Roo.id();
42415     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42416 }
42417
42418 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42419     data : {},
42420     cb: false,
42421     displayField : false,
42422     tipField : false,
42423     
42424     
42425     defaultAutoCreate : {
42426         tag: 'div',
42427         cls: 'x-cbarray-item',
42428         cn : [ 
42429             { tag: 'div' },
42430             {
42431                 tag: 'img',
42432                 width:16,
42433                 height : 16,
42434                 src : Roo.BLANK_IMAGE_URL ,
42435                 align: 'center'
42436             }
42437         ]
42438         
42439     },
42440     
42441  
42442     onRender : function(ct, position)
42443     {
42444         Roo.form.Field.superclass.onRender.call(this, ct, position);
42445         
42446         if(!this.el){
42447             var cfg = this.getAutoCreate();
42448             this.el = ct.createChild(cfg, position);
42449         }
42450         
42451         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42452         
42453         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42454             this.cb.renderer(this.data) :
42455             String.format('{0}',this.data[this.displayField]);
42456         
42457             
42458         this.el.child('div').dom.setAttribute('qtip',
42459                         String.format('{0}',this.data[this.tipField])
42460         );
42461         
42462         this.el.child('img').on('click', this.remove, this);
42463         
42464     },
42465    
42466     remove : function()
42467     {
42468         if(this.cb.disabled){
42469             return;
42470         }
42471         
42472         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42473             this.cb.items.remove(this);
42474             this.el.child('img').un('click', this.remove, this);
42475             this.el.remove();
42476             this.cb.updateHiddenEl();
42477
42478             this.cb.fireEvent('remove', this.cb, this);
42479         }
42480         
42481     }
42482 });/*
42483  * Based on:
42484  * Ext JS Library 1.1.1
42485  * Copyright(c) 2006-2007, Ext JS, LLC.
42486  *
42487  * Originally Released Under LGPL - original licence link has changed is not relivant.
42488  *
42489  * Fork - LGPL
42490  * <script type="text/javascript">
42491  */
42492 /**
42493  * @class Roo.form.Checkbox
42494  * @extends Roo.form.Field
42495  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
42496  * @constructor
42497  * Creates a new Checkbox
42498  * @param {Object} config Configuration options
42499  */
42500 Roo.form.Checkbox = function(config){
42501     Roo.form.Checkbox.superclass.constructor.call(this, config);
42502     this.addEvents({
42503         /**
42504          * @event check
42505          * Fires when the checkbox is checked or unchecked.
42506              * @param {Roo.form.Checkbox} this This checkbox
42507              * @param {Boolean} checked The new checked value
42508              */
42509         check : true
42510     });
42511 };
42512
42513 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
42514     /**
42515      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42516      */
42517     focusClass : undefined,
42518     /**
42519      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42520      */
42521     fieldClass: "x-form-field",
42522     /**
42523      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
42524      */
42525     checked: false,
42526     /**
42527      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42528      * {tag: "input", type: "checkbox", autocomplete: "off"})
42529      */
42530     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42531     /**
42532      * @cfg {String} boxLabel The text that appears beside the checkbox
42533      */
42534     boxLabel : "",
42535     /**
42536      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
42537      */  
42538     inputValue : '1',
42539     /**
42540      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
42541      */
42542      valueOff: '0', // value when not checked..
42543
42544     actionMode : 'viewEl', 
42545     //
42546     // private
42547     itemCls : 'x-menu-check-item x-form-item',
42548     groupClass : 'x-menu-group-item',
42549     inputType : 'hidden',
42550     
42551     
42552     inSetChecked: false, // check that we are not calling self...
42553     
42554     inputElement: false, // real input element?
42555     basedOn: false, // ????
42556     
42557     isFormField: true, // not sure where this is needed!!!!
42558
42559     onResize : function(){
42560         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42561         if(!this.boxLabel){
42562             this.el.alignTo(this.wrap, 'c-c');
42563         }
42564     },
42565
42566     initEvents : function(){
42567         Roo.form.Checkbox.superclass.initEvents.call(this);
42568         this.el.on("click", this.onClick,  this);
42569         this.el.on("change", this.onClick,  this);
42570     },
42571
42572
42573     getResizeEl : function(){
42574         return this.wrap;
42575     },
42576
42577     getPositionEl : function(){
42578         return this.wrap;
42579     },
42580
42581     // private
42582     onRender : function(ct, position){
42583         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42584         /*
42585         if(this.inputValue !== undefined){
42586             this.el.dom.value = this.inputValue;
42587         }
42588         */
42589         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42590         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42591         var viewEl = this.wrap.createChild({ 
42592             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42593         this.viewEl = viewEl;   
42594         this.wrap.on('click', this.onClick,  this); 
42595         
42596         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42597         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42598         
42599         
42600         
42601         if(this.boxLabel){
42602             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42603         //    viewEl.on('click', this.onClick,  this); 
42604         }
42605         //if(this.checked){
42606             this.setChecked(this.checked);
42607         //}else{
42608             //this.checked = this.el.dom;
42609         //}
42610
42611     },
42612
42613     // private
42614     initValue : Roo.emptyFn,
42615
42616     /**
42617      * Returns the checked state of the checkbox.
42618      * @return {Boolean} True if checked, else false
42619      */
42620     getValue : function(){
42621         if(this.el){
42622             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
42623         }
42624         return this.valueOff;
42625         
42626     },
42627
42628         // private
42629     onClick : function(){ 
42630         if (this.disabled) {
42631             return;
42632         }
42633         this.setChecked(!this.checked);
42634
42635         //if(this.el.dom.checked != this.checked){
42636         //    this.setValue(this.el.dom.checked);
42637        // }
42638     },
42639
42640     /**
42641      * Sets the checked state of the checkbox.
42642      * On is always based on a string comparison between inputValue and the param.
42643      * @param {Boolean/String} value - the value to set 
42644      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42645      */
42646     setValue : function(v,suppressEvent){
42647         
42648         
42649         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
42650         //if(this.el && this.el.dom){
42651         //    this.el.dom.checked = this.checked;
42652         //    this.el.dom.defaultChecked = this.checked;
42653         //}
42654         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
42655         //this.fireEvent("check", this, this.checked);
42656     },
42657     // private..
42658     setChecked : function(state,suppressEvent)
42659     {
42660         if (this.inSetChecked) {
42661             this.checked = state;
42662             return;
42663         }
42664         
42665     
42666         if(this.wrap){
42667             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
42668         }
42669         this.checked = state;
42670         if(suppressEvent !== true){
42671             this.fireEvent('check', this, state);
42672         }
42673         this.inSetChecked = true;
42674         this.el.dom.value = state ? this.inputValue : this.valueOff;
42675         this.inSetChecked = false;
42676         
42677     },
42678     // handle setting of hidden value by some other method!!?!?
42679     setFromHidden: function()
42680     {
42681         if(!this.el){
42682             return;
42683         }
42684         //console.log("SET FROM HIDDEN");
42685         //alert('setFrom hidden');
42686         this.setValue(this.el.dom.value);
42687     },
42688     
42689     onDestroy : function()
42690     {
42691         if(this.viewEl){
42692             Roo.get(this.viewEl).remove();
42693         }
42694          
42695         Roo.form.Checkbox.superclass.onDestroy.call(this);
42696     },
42697     
42698     setBoxLabel : function(str)
42699     {
42700         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
42701     }
42702
42703 });/*
42704  * Based on:
42705  * Ext JS Library 1.1.1
42706  * Copyright(c) 2006-2007, Ext JS, LLC.
42707  *
42708  * Originally Released Under LGPL - original licence link has changed is not relivant.
42709  *
42710  * Fork - LGPL
42711  * <script type="text/javascript">
42712  */
42713  
42714 /**
42715  * @class Roo.form.Radio
42716  * @extends Roo.form.Checkbox
42717  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
42718  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
42719  * @constructor
42720  * Creates a new Radio
42721  * @param {Object} config Configuration options
42722  */
42723 Roo.form.Radio = function(){
42724     Roo.form.Radio.superclass.constructor.apply(this, arguments);
42725 };
42726 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
42727     inputType: 'radio',
42728
42729     /**
42730      * If this radio is part of a group, it will return the selected value
42731      * @return {String}
42732      */
42733     getGroupValue : function(){
42734         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
42735     },
42736     
42737     
42738     onRender : function(ct, position){
42739         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42740         
42741         if(this.inputValue !== undefined){
42742             this.el.dom.value = this.inputValue;
42743         }
42744          
42745         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42746         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42747         //var viewEl = this.wrap.createChild({ 
42748         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42749         //this.viewEl = viewEl;   
42750         //this.wrap.on('click', this.onClick,  this); 
42751         
42752         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42753         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
42754         
42755         
42756         
42757         if(this.boxLabel){
42758             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42759         //    viewEl.on('click', this.onClick,  this); 
42760         }
42761          if(this.checked){
42762             this.el.dom.checked =   'checked' ;
42763         }
42764          
42765     } 
42766     
42767     
42768 });//<script type="text/javascript">
42769
42770 /*
42771  * Based  Ext JS Library 1.1.1
42772  * Copyright(c) 2006-2007, Ext JS, LLC.
42773  * LGPL
42774  *
42775  */
42776  
42777 /**
42778  * @class Roo.HtmlEditorCore
42779  * @extends Roo.Component
42780  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
42781  *
42782  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42783  */
42784
42785 Roo.HtmlEditorCore = function(config){
42786     
42787     
42788     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
42789     
42790     
42791     this.addEvents({
42792         /**
42793          * @event initialize
42794          * Fires when the editor is fully initialized (including the iframe)
42795          * @param {Roo.HtmlEditorCore} this
42796          */
42797         initialize: true,
42798         /**
42799          * @event activate
42800          * Fires when the editor is first receives the focus. Any insertion must wait
42801          * until after this event.
42802          * @param {Roo.HtmlEditorCore} this
42803          */
42804         activate: true,
42805          /**
42806          * @event beforesync
42807          * Fires before the textarea is updated with content from the editor iframe. Return false
42808          * to cancel the sync.
42809          * @param {Roo.HtmlEditorCore} this
42810          * @param {String} html
42811          */
42812         beforesync: true,
42813          /**
42814          * @event beforepush
42815          * Fires before the iframe editor is updated with content from the textarea. Return false
42816          * to cancel the push.
42817          * @param {Roo.HtmlEditorCore} this
42818          * @param {String} html
42819          */
42820         beforepush: true,
42821          /**
42822          * @event sync
42823          * Fires when the textarea is updated with content from the editor iframe.
42824          * @param {Roo.HtmlEditorCore} this
42825          * @param {String} html
42826          */
42827         sync: true,
42828          /**
42829          * @event push
42830          * Fires when the iframe editor is updated with content from the textarea.
42831          * @param {Roo.HtmlEditorCore} this
42832          * @param {String} html
42833          */
42834         push: true,
42835         
42836         /**
42837          * @event editorevent
42838          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42839          * @param {Roo.HtmlEditorCore} this
42840          */
42841         editorevent: true
42842         
42843     });
42844     
42845     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
42846     
42847     // defaults : white / black...
42848     this.applyBlacklists();
42849     
42850     
42851     
42852 };
42853
42854
42855 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
42856
42857
42858      /**
42859      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
42860      */
42861     
42862     owner : false,
42863     
42864      /**
42865      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42866      *                        Roo.resizable.
42867      */
42868     resizable : false,
42869      /**
42870      * @cfg {Number} height (in pixels)
42871      */   
42872     height: 300,
42873    /**
42874      * @cfg {Number} width (in pixels)
42875      */   
42876     width: 500,
42877     
42878     /**
42879      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42880      * 
42881      */
42882     stylesheets: false,
42883     
42884     // id of frame..
42885     frameId: false,
42886     
42887     // private properties
42888     validationEvent : false,
42889     deferHeight: true,
42890     initialized : false,
42891     activated : false,
42892     sourceEditMode : false,
42893     onFocus : Roo.emptyFn,
42894     iframePad:3,
42895     hideMode:'offsets',
42896     
42897     clearUp: true,
42898     
42899     // blacklist + whitelisted elements..
42900     black: false,
42901     white: false,
42902      
42903     bodyCls : '',
42904
42905     /**
42906      * Protected method that will not generally be called directly. It
42907      * is called when the editor initializes the iframe with HTML contents. Override this method if you
42908      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
42909      */
42910     getDocMarkup : function(){
42911         // body styles..
42912         var st = '';
42913         
42914         // inherit styels from page...?? 
42915         if (this.stylesheets === false) {
42916             
42917             Roo.get(document.head).select('style').each(function(node) {
42918                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42919             });
42920             
42921             Roo.get(document.head).select('link').each(function(node) { 
42922                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42923             });
42924             
42925         } else if (!this.stylesheets.length) {
42926                 // simple..
42927                 st = '<style type="text/css">' +
42928                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42929                    '</style>';
42930         } else { 
42931             st = '<style type="text/css">' +
42932                     this.stylesheets +
42933                 '</style>';
42934         }
42935         
42936         st +=  '<style type="text/css">' +
42937             'IMG { cursor: pointer } ' +
42938         '</style>';
42939
42940         var cls = 'roo-htmleditor-body';
42941         
42942         if(this.bodyCls.length){
42943             cls += ' ' + this.bodyCls;
42944         }
42945         
42946         return '<html><head>' + st  +
42947             //<style type="text/css">' +
42948             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42949             //'</style>' +
42950             ' </head><body class="' +  cls + '"></body></html>';
42951     },
42952
42953     // private
42954     onRender : function(ct, position)
42955     {
42956         var _t = this;
42957         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
42958         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
42959         
42960         
42961         this.el.dom.style.border = '0 none';
42962         this.el.dom.setAttribute('tabIndex', -1);
42963         this.el.addClass('x-hidden hide');
42964         
42965         
42966         
42967         if(Roo.isIE){ // fix IE 1px bogus margin
42968             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
42969         }
42970        
42971         
42972         this.frameId = Roo.id();
42973         
42974          
42975         
42976         var iframe = this.owner.wrap.createChild({
42977             tag: 'iframe',
42978             cls: 'form-control', // bootstrap..
42979             id: this.frameId,
42980             name: this.frameId,
42981             frameBorder : 'no',
42982             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
42983         }, this.el
42984         );
42985         
42986         
42987         this.iframe = iframe.dom;
42988
42989          this.assignDocWin();
42990         
42991         this.doc.designMode = 'on';
42992        
42993         this.doc.open();
42994         this.doc.write(this.getDocMarkup());
42995         this.doc.close();
42996
42997         
42998         var task = { // must defer to wait for browser to be ready
42999             run : function(){
43000                 //console.log("run task?" + this.doc.readyState);
43001                 this.assignDocWin();
43002                 if(this.doc.body || this.doc.readyState == 'complete'){
43003                     try {
43004                         this.doc.designMode="on";
43005                     } catch (e) {
43006                         return;
43007                     }
43008                     Roo.TaskMgr.stop(task);
43009                     this.initEditor.defer(10, this);
43010                 }
43011             },
43012             interval : 10,
43013             duration: 10000,
43014             scope: this
43015         };
43016         Roo.TaskMgr.start(task);
43017
43018     },
43019
43020     // private
43021     onResize : function(w, h)
43022     {
43023          Roo.log('resize: ' +w + ',' + h );
43024         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43025         if(!this.iframe){
43026             return;
43027         }
43028         if(typeof w == 'number'){
43029             
43030             this.iframe.style.width = w + 'px';
43031         }
43032         if(typeof h == 'number'){
43033             
43034             this.iframe.style.height = h + 'px';
43035             if(this.doc){
43036                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43037             }
43038         }
43039         
43040     },
43041
43042     /**
43043      * Toggles the editor between standard and source edit mode.
43044      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43045      */
43046     toggleSourceEdit : function(sourceEditMode){
43047         
43048         this.sourceEditMode = sourceEditMode === true;
43049         
43050         if(this.sourceEditMode){
43051  
43052             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43053             
43054         }else{
43055             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43056             //this.iframe.className = '';
43057             this.deferFocus();
43058         }
43059         //this.setSize(this.owner.wrap.getSize());
43060         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43061     },
43062
43063     
43064   
43065
43066     /**
43067      * Protected method that will not generally be called directly. If you need/want
43068      * custom HTML cleanup, this is the method you should override.
43069      * @param {String} html The HTML to be cleaned
43070      * return {String} The cleaned HTML
43071      */
43072     cleanHtml : function(html){
43073         html = String(html);
43074         if(html.length > 5){
43075             if(Roo.isSafari){ // strip safari nonsense
43076                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
43077             }
43078         }
43079         if(html == '&nbsp;'){
43080             html = '';
43081         }
43082         return html;
43083     },
43084
43085     /**
43086      * HTML Editor -> Textarea
43087      * Protected method that will not generally be called directly. Syncs the contents
43088      * of the editor iframe with the textarea.
43089      */
43090     syncValue : function(){
43091         if(this.initialized){
43092             var bd = (this.doc.body || this.doc.documentElement);
43093             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43094             var html = bd.innerHTML;
43095             if(Roo.isSafari){
43096                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43097                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43098                 if(m && m[1]){
43099                     html = '<div style="'+m[0]+'">' + html + '</div>';
43100                 }
43101             }
43102             html = this.cleanHtml(html);
43103             // fix up the special chars.. normaly like back quotes in word...
43104             // however we do not want to do this with chinese..
43105             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
43106                 var cc = b.charCodeAt();
43107                 if (
43108                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43109                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43110                     (cc >= 0xf900 && cc < 0xfb00 )
43111                 ) {
43112                         return b;
43113                 }
43114                 return "&#"+cc+";" 
43115             });
43116             if(this.owner.fireEvent('beforesync', this, html) !== false){
43117                 this.el.dom.value = html;
43118                 this.owner.fireEvent('sync', this, html);
43119             }
43120         }
43121     },
43122
43123     /**
43124      * Protected method that will not generally be called directly. Pushes the value of the textarea
43125      * into the iframe editor.
43126      */
43127     pushValue : function(){
43128         if(this.initialized){
43129             var v = this.el.dom.value.trim();
43130             
43131 //            if(v.length < 1){
43132 //                v = '&#160;';
43133 //            }
43134             
43135             if(this.owner.fireEvent('beforepush', this, v) !== false){
43136                 var d = (this.doc.body || this.doc.documentElement);
43137                 d.innerHTML = v;
43138                 this.cleanUpPaste();
43139                 this.el.dom.value = d.innerHTML;
43140                 this.owner.fireEvent('push', this, v);
43141             }
43142         }
43143     },
43144
43145     // private
43146     deferFocus : function(){
43147         this.focus.defer(10, this);
43148     },
43149
43150     // doc'ed in Field
43151     focus : function(){
43152         if(this.win && !this.sourceEditMode){
43153             this.win.focus();
43154         }else{
43155             this.el.focus();
43156         }
43157     },
43158     
43159     assignDocWin: function()
43160     {
43161         var iframe = this.iframe;
43162         
43163          if(Roo.isIE){
43164             this.doc = iframe.contentWindow.document;
43165             this.win = iframe.contentWindow;
43166         } else {
43167 //            if (!Roo.get(this.frameId)) {
43168 //                return;
43169 //            }
43170 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43171 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43172             
43173             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43174                 return;
43175             }
43176             
43177             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43178             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43179         }
43180     },
43181     
43182     // private
43183     initEditor : function(){
43184         //console.log("INIT EDITOR");
43185         this.assignDocWin();
43186         
43187         
43188         
43189         this.doc.designMode="on";
43190         this.doc.open();
43191         this.doc.write(this.getDocMarkup());
43192         this.doc.close();
43193         
43194         var dbody = (this.doc.body || this.doc.documentElement);
43195         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43196         // this copies styles from the containing element into thsi one..
43197         // not sure why we need all of this..
43198         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43199         
43200         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43201         //ss['background-attachment'] = 'fixed'; // w3c
43202         dbody.bgProperties = 'fixed'; // ie
43203         //Roo.DomHelper.applyStyles(dbody, ss);
43204         Roo.EventManager.on(this.doc, {
43205             //'mousedown': this.onEditorEvent,
43206             'mouseup': this.onEditorEvent,
43207             'dblclick': this.onEditorEvent,
43208             'click': this.onEditorEvent,
43209             'keyup': this.onEditorEvent,
43210             buffer:100,
43211             scope: this
43212         });
43213         if(Roo.isGecko){
43214             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43215         }
43216         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43217             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43218         }
43219         this.initialized = true;
43220
43221         this.owner.fireEvent('initialize', this);
43222         this.pushValue();
43223     },
43224
43225     // private
43226     onDestroy : function(){
43227         
43228         
43229         
43230         if(this.rendered){
43231             
43232             //for (var i =0; i < this.toolbars.length;i++) {
43233             //    // fixme - ask toolbars for heights?
43234             //    this.toolbars[i].onDestroy();
43235            // }
43236             
43237             //this.wrap.dom.innerHTML = '';
43238             //this.wrap.remove();
43239         }
43240     },
43241
43242     // private
43243     onFirstFocus : function(){
43244         
43245         this.assignDocWin();
43246         
43247         
43248         this.activated = true;
43249          
43250     
43251         if(Roo.isGecko){ // prevent silly gecko errors
43252             this.win.focus();
43253             var s = this.win.getSelection();
43254             if(!s.focusNode || s.focusNode.nodeType != 3){
43255                 var r = s.getRangeAt(0);
43256                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43257                 r.collapse(true);
43258                 this.deferFocus();
43259             }
43260             try{
43261                 this.execCmd('useCSS', true);
43262                 this.execCmd('styleWithCSS', false);
43263             }catch(e){}
43264         }
43265         this.owner.fireEvent('activate', this);
43266     },
43267
43268     // private
43269     adjustFont: function(btn){
43270         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43271         //if(Roo.isSafari){ // safari
43272         //    adjust *= 2;
43273        // }
43274         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43275         if(Roo.isSafari){ // safari
43276             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43277             v =  (v < 10) ? 10 : v;
43278             v =  (v > 48) ? 48 : v;
43279             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43280             
43281         }
43282         
43283         
43284         v = Math.max(1, v+adjust);
43285         
43286         this.execCmd('FontSize', v  );
43287     },
43288
43289     onEditorEvent : function(e)
43290     {
43291         this.owner.fireEvent('editorevent', this, e);
43292       //  this.updateToolbar();
43293         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43294     },
43295
43296     insertTag : function(tg)
43297     {
43298         // could be a bit smarter... -> wrap the current selected tRoo..
43299         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
43300             
43301             range = this.createRange(this.getSelection());
43302             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43303             wrappingNode.appendChild(range.extractContents());
43304             range.insertNode(wrappingNode);
43305
43306             return;
43307             
43308             
43309             
43310         }
43311         this.execCmd("formatblock",   tg);
43312         
43313     },
43314     
43315     insertText : function(txt)
43316     {
43317         
43318         
43319         var range = this.createRange();
43320         range.deleteContents();
43321                //alert(Sender.getAttribute('label'));
43322                
43323         range.insertNode(this.doc.createTextNode(txt));
43324     } ,
43325     
43326      
43327
43328     /**
43329      * Executes a Midas editor command on the editor document and performs necessary focus and
43330      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43331      * @param {String} cmd The Midas command
43332      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43333      */
43334     relayCmd : function(cmd, value){
43335         this.win.focus();
43336         this.execCmd(cmd, value);
43337         this.owner.fireEvent('editorevent', this);
43338         //this.updateToolbar();
43339         this.owner.deferFocus();
43340     },
43341
43342     /**
43343      * Executes a Midas editor command directly on the editor document.
43344      * For visual commands, you should use {@link #relayCmd} instead.
43345      * <b>This should only be called after the editor is initialized.</b>
43346      * @param {String} cmd The Midas command
43347      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43348      */
43349     execCmd : function(cmd, value){
43350         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43351         this.syncValue();
43352     },
43353  
43354  
43355    
43356     /**
43357      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43358      * to insert tRoo.
43359      * @param {String} text | dom node.. 
43360      */
43361     insertAtCursor : function(text)
43362     {
43363         
43364         if(!this.activated){
43365             return;
43366         }
43367         /*
43368         if(Roo.isIE){
43369             this.win.focus();
43370             var r = this.doc.selection.createRange();
43371             if(r){
43372                 r.collapse(true);
43373                 r.pasteHTML(text);
43374                 this.syncValue();
43375                 this.deferFocus();
43376             
43377             }
43378             return;
43379         }
43380         */
43381         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43382             this.win.focus();
43383             
43384             
43385             // from jquery ui (MIT licenced)
43386             var range, node;
43387             var win = this.win;
43388             
43389             if (win.getSelection && win.getSelection().getRangeAt) {
43390                 range = win.getSelection().getRangeAt(0);
43391                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43392                 range.insertNode(node);
43393             } else if (win.document.selection && win.document.selection.createRange) {
43394                 // no firefox support
43395                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43396                 win.document.selection.createRange().pasteHTML(txt);
43397             } else {
43398                 // no firefox support
43399                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43400                 this.execCmd('InsertHTML', txt);
43401             } 
43402             
43403             this.syncValue();
43404             
43405             this.deferFocus();
43406         }
43407     },
43408  // private
43409     mozKeyPress : function(e){
43410         if(e.ctrlKey){
43411             var c = e.getCharCode(), cmd;
43412           
43413             if(c > 0){
43414                 c = String.fromCharCode(c).toLowerCase();
43415                 switch(c){
43416                     case 'b':
43417                         cmd = 'bold';
43418                         break;
43419                     case 'i':
43420                         cmd = 'italic';
43421                         break;
43422                     
43423                     case 'u':
43424                         cmd = 'underline';
43425                         break;
43426                     
43427                     case 'v':
43428                         this.cleanUpPaste.defer(100, this);
43429                         return;
43430                         
43431                 }
43432                 if(cmd){
43433                     this.win.focus();
43434                     this.execCmd(cmd);
43435                     this.deferFocus();
43436                     e.preventDefault();
43437                 }
43438                 
43439             }
43440         }
43441     },
43442
43443     // private
43444     fixKeys : function(){ // load time branching for fastest keydown performance
43445         if(Roo.isIE){
43446             return function(e){
43447                 var k = e.getKey(), r;
43448                 if(k == e.TAB){
43449                     e.stopEvent();
43450                     r = this.doc.selection.createRange();
43451                     if(r){
43452                         r.collapse(true);
43453                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43454                         this.deferFocus();
43455                     }
43456                     return;
43457                 }
43458                 
43459                 if(k == e.ENTER){
43460                     r = this.doc.selection.createRange();
43461                     if(r){
43462                         var target = r.parentElement();
43463                         if(!target || target.tagName.toLowerCase() != 'li'){
43464                             e.stopEvent();
43465                             r.pasteHTML('<br />');
43466                             r.collapse(false);
43467                             r.select();
43468                         }
43469                     }
43470                 }
43471                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43472                     this.cleanUpPaste.defer(100, this);
43473                     return;
43474                 }
43475                 
43476                 
43477             };
43478         }else if(Roo.isOpera){
43479             return function(e){
43480                 var k = e.getKey();
43481                 if(k == e.TAB){
43482                     e.stopEvent();
43483                     this.win.focus();
43484                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
43485                     this.deferFocus();
43486                 }
43487                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43488                     this.cleanUpPaste.defer(100, this);
43489                     return;
43490                 }
43491                 
43492             };
43493         }else if(Roo.isSafari){
43494             return function(e){
43495                 var k = e.getKey();
43496                 
43497                 if(k == e.TAB){
43498                     e.stopEvent();
43499                     this.execCmd('InsertText','\t');
43500                     this.deferFocus();
43501                     return;
43502                 }
43503                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43504                     this.cleanUpPaste.defer(100, this);
43505                     return;
43506                 }
43507                 
43508              };
43509         }
43510     }(),
43511     
43512     getAllAncestors: function()
43513     {
43514         var p = this.getSelectedNode();
43515         var a = [];
43516         if (!p) {
43517             a.push(p); // push blank onto stack..
43518             p = this.getParentElement();
43519         }
43520         
43521         
43522         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
43523             a.push(p);
43524             p = p.parentNode;
43525         }
43526         a.push(this.doc.body);
43527         return a;
43528     },
43529     lastSel : false,
43530     lastSelNode : false,
43531     
43532     
43533     getSelection : function() 
43534     {
43535         this.assignDocWin();
43536         return Roo.isIE ? this.doc.selection : this.win.getSelection();
43537     },
43538     
43539     getSelectedNode: function() 
43540     {
43541         // this may only work on Gecko!!!
43542         
43543         // should we cache this!!!!
43544         
43545         
43546         
43547          
43548         var range = this.createRange(this.getSelection()).cloneRange();
43549         
43550         if (Roo.isIE) {
43551             var parent = range.parentElement();
43552             while (true) {
43553                 var testRange = range.duplicate();
43554                 testRange.moveToElementText(parent);
43555                 if (testRange.inRange(range)) {
43556                     break;
43557                 }
43558                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
43559                     break;
43560                 }
43561                 parent = parent.parentElement;
43562             }
43563             return parent;
43564         }
43565         
43566         // is ancestor a text element.
43567         var ac =  range.commonAncestorContainer;
43568         if (ac.nodeType == 3) {
43569             ac = ac.parentNode;
43570         }
43571         
43572         var ar = ac.childNodes;
43573          
43574         var nodes = [];
43575         var other_nodes = [];
43576         var has_other_nodes = false;
43577         for (var i=0;i<ar.length;i++) {
43578             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
43579                 continue;
43580             }
43581             // fullly contained node.
43582             
43583             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
43584                 nodes.push(ar[i]);
43585                 continue;
43586             }
43587             
43588             // probably selected..
43589             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
43590                 other_nodes.push(ar[i]);
43591                 continue;
43592             }
43593             // outer..
43594             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
43595                 continue;
43596             }
43597             
43598             
43599             has_other_nodes = true;
43600         }
43601         if (!nodes.length && other_nodes.length) {
43602             nodes= other_nodes;
43603         }
43604         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
43605             return false;
43606         }
43607         
43608         return nodes[0];
43609     },
43610     createRange: function(sel)
43611     {
43612         // this has strange effects when using with 
43613         // top toolbar - not sure if it's a great idea.
43614         //this.editor.contentWindow.focus();
43615         if (typeof sel != "undefined") {
43616             try {
43617                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
43618             } catch(e) {
43619                 return this.doc.createRange();
43620             }
43621         } else {
43622             return this.doc.createRange();
43623         }
43624     },
43625     getParentElement: function()
43626     {
43627         
43628         this.assignDocWin();
43629         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
43630         
43631         var range = this.createRange(sel);
43632          
43633         try {
43634             var p = range.commonAncestorContainer;
43635             while (p.nodeType == 3) { // text node
43636                 p = p.parentNode;
43637             }
43638             return p;
43639         } catch (e) {
43640             return null;
43641         }
43642     
43643     },
43644     /***
43645      *
43646      * Range intersection.. the hard stuff...
43647      *  '-1' = before
43648      *  '0' = hits..
43649      *  '1' = after.
43650      *         [ -- selected range --- ]
43651      *   [fail]                        [fail]
43652      *
43653      *    basically..
43654      *      if end is before start or  hits it. fail.
43655      *      if start is after end or hits it fail.
43656      *
43657      *   if either hits (but other is outside. - then it's not 
43658      *   
43659      *    
43660      **/
43661     
43662     
43663     // @see http://www.thismuchiknow.co.uk/?p=64.
43664     rangeIntersectsNode : function(range, node)
43665     {
43666         var nodeRange = node.ownerDocument.createRange();
43667         try {
43668             nodeRange.selectNode(node);
43669         } catch (e) {
43670             nodeRange.selectNodeContents(node);
43671         }
43672     
43673         var rangeStartRange = range.cloneRange();
43674         rangeStartRange.collapse(true);
43675     
43676         var rangeEndRange = range.cloneRange();
43677         rangeEndRange.collapse(false);
43678     
43679         var nodeStartRange = nodeRange.cloneRange();
43680         nodeStartRange.collapse(true);
43681     
43682         var nodeEndRange = nodeRange.cloneRange();
43683         nodeEndRange.collapse(false);
43684     
43685         return rangeStartRange.compareBoundaryPoints(
43686                  Range.START_TO_START, nodeEndRange) == -1 &&
43687                rangeEndRange.compareBoundaryPoints(
43688                  Range.START_TO_START, nodeStartRange) == 1;
43689         
43690          
43691     },
43692     rangeCompareNode : function(range, node)
43693     {
43694         var nodeRange = node.ownerDocument.createRange();
43695         try {
43696             nodeRange.selectNode(node);
43697         } catch (e) {
43698             nodeRange.selectNodeContents(node);
43699         }
43700         
43701         
43702         range.collapse(true);
43703     
43704         nodeRange.collapse(true);
43705      
43706         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
43707         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
43708          
43709         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
43710         
43711         var nodeIsBefore   =  ss == 1;
43712         var nodeIsAfter    = ee == -1;
43713         
43714         if (nodeIsBefore && nodeIsAfter) {
43715             return 0; // outer
43716         }
43717         if (!nodeIsBefore && nodeIsAfter) {
43718             return 1; //right trailed.
43719         }
43720         
43721         if (nodeIsBefore && !nodeIsAfter) {
43722             return 2;  // left trailed.
43723         }
43724         // fully contined.
43725         return 3;
43726     },
43727
43728     // private? - in a new class?
43729     cleanUpPaste :  function()
43730     {
43731         // cleans up the whole document..
43732         Roo.log('cleanuppaste');
43733         
43734         this.cleanUpChildren(this.doc.body);
43735         var clean = this.cleanWordChars(this.doc.body.innerHTML);
43736         if (clean != this.doc.body.innerHTML) {
43737             this.doc.body.innerHTML = clean;
43738         }
43739         
43740     },
43741     
43742     cleanWordChars : function(input) {// change the chars to hex code
43743         var he = Roo.HtmlEditorCore;
43744         
43745         var output = input;
43746         Roo.each(he.swapCodes, function(sw) { 
43747             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
43748             
43749             output = output.replace(swapper, sw[1]);
43750         });
43751         
43752         return output;
43753     },
43754     
43755     
43756     cleanUpChildren : function (n)
43757     {
43758         if (!n.childNodes.length) {
43759             return;
43760         }
43761         for (var i = n.childNodes.length-1; i > -1 ; i--) {
43762            this.cleanUpChild(n.childNodes[i]);
43763         }
43764     },
43765     
43766     
43767         
43768     
43769     cleanUpChild : function (node)
43770     {
43771         var ed = this;
43772         //console.log(node);
43773         if (node.nodeName == "#text") {
43774             // clean up silly Windows -- stuff?
43775             return; 
43776         }
43777         if (node.nodeName == "#comment") {
43778             node.parentNode.removeChild(node);
43779             // clean up silly Windows -- stuff?
43780             return; 
43781         }
43782         var lcname = node.tagName.toLowerCase();
43783         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
43784         // whitelist of tags..
43785         
43786         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
43787             // remove node.
43788             node.parentNode.removeChild(node);
43789             return;
43790             
43791         }
43792         
43793         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
43794         
43795         // remove <a name=....> as rendering on yahoo mailer is borked with this.
43796         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
43797         
43798         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
43799         //    remove_keep_children = true;
43800         //}
43801         
43802         if (remove_keep_children) {
43803             this.cleanUpChildren(node);
43804             // inserts everything just before this node...
43805             while (node.childNodes.length) {
43806                 var cn = node.childNodes[0];
43807                 node.removeChild(cn);
43808                 node.parentNode.insertBefore(cn, node);
43809             }
43810             node.parentNode.removeChild(node);
43811             return;
43812         }
43813         
43814         if (!node.attributes || !node.attributes.length) {
43815             this.cleanUpChildren(node);
43816             return;
43817         }
43818         
43819         function cleanAttr(n,v)
43820         {
43821             
43822             if (v.match(/^\./) || v.match(/^\//)) {
43823                 return;
43824             }
43825             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
43826                 return;
43827             }
43828             if (v.match(/^#/)) {
43829                 return;
43830             }
43831 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
43832             node.removeAttribute(n);
43833             
43834         }
43835         
43836         var cwhite = this.cwhite;
43837         var cblack = this.cblack;
43838             
43839         function cleanStyle(n,v)
43840         {
43841             if (v.match(/expression/)) { //XSS?? should we even bother..
43842                 node.removeAttribute(n);
43843                 return;
43844             }
43845             
43846             var parts = v.split(/;/);
43847             var clean = [];
43848             
43849             Roo.each(parts, function(p) {
43850                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
43851                 if (!p.length) {
43852                     return true;
43853                 }
43854                 var l = p.split(':').shift().replace(/\s+/g,'');
43855                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
43856                 
43857                 if ( cwhite.length && cblack.indexOf(l) > -1) {
43858 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43859                     //node.removeAttribute(n);
43860                     return true;
43861                 }
43862                 //Roo.log()
43863                 // only allow 'c whitelisted system attributes'
43864                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
43865 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43866                     //node.removeAttribute(n);
43867                     return true;
43868                 }
43869                 
43870                 
43871                  
43872                 
43873                 clean.push(p);
43874                 return true;
43875             });
43876             if (clean.length) { 
43877                 node.setAttribute(n, clean.join(';'));
43878             } else {
43879                 node.removeAttribute(n);
43880             }
43881             
43882         }
43883         
43884         
43885         for (var i = node.attributes.length-1; i > -1 ; i--) {
43886             var a = node.attributes[i];
43887             //console.log(a);
43888             
43889             if (a.name.toLowerCase().substr(0,2)=='on')  {
43890                 node.removeAttribute(a.name);
43891                 continue;
43892             }
43893             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
43894                 node.removeAttribute(a.name);
43895                 continue;
43896             }
43897             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
43898                 cleanAttr(a.name,a.value); // fixme..
43899                 continue;
43900             }
43901             if (a.name == 'style') {
43902                 cleanStyle(a.name,a.value);
43903                 continue;
43904             }
43905             /// clean up MS crap..
43906             // tecnically this should be a list of valid class'es..
43907             
43908             
43909             if (a.name == 'class') {
43910                 if (a.value.match(/^Mso/)) {
43911                     node.className = '';
43912                 }
43913                 
43914                 if (a.value.match(/^body$/)) {
43915                     node.className = '';
43916                 }
43917                 continue;
43918             }
43919             
43920             // style cleanup!?
43921             // class cleanup?
43922             
43923         }
43924         
43925         
43926         this.cleanUpChildren(node);
43927         
43928         
43929     },
43930     
43931     /**
43932      * Clean up MS wordisms...
43933      */
43934     cleanWord : function(node)
43935     {
43936         
43937         
43938         if (!node) {
43939             this.cleanWord(this.doc.body);
43940             return;
43941         }
43942         if (node.nodeName == "#text") {
43943             // clean up silly Windows -- stuff?
43944             return; 
43945         }
43946         if (node.nodeName == "#comment") {
43947             node.parentNode.removeChild(node);
43948             // clean up silly Windows -- stuff?
43949             return; 
43950         }
43951         
43952         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
43953             node.parentNode.removeChild(node);
43954             return;
43955         }
43956         
43957         // remove - but keep children..
43958         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
43959             while (node.childNodes.length) {
43960                 var cn = node.childNodes[0];
43961                 node.removeChild(cn);
43962                 node.parentNode.insertBefore(cn, node);
43963             }
43964             node.parentNode.removeChild(node);
43965             this.iterateChildren(node, this.cleanWord);
43966             return;
43967         }
43968         // clean styles
43969         if (node.className.length) {
43970             
43971             var cn = node.className.split(/\W+/);
43972             var cna = [];
43973             Roo.each(cn, function(cls) {
43974                 if (cls.match(/Mso[a-zA-Z]+/)) {
43975                     return;
43976                 }
43977                 cna.push(cls);
43978             });
43979             node.className = cna.length ? cna.join(' ') : '';
43980             if (!cna.length) {
43981                 node.removeAttribute("class");
43982             }
43983         }
43984         
43985         if (node.hasAttribute("lang")) {
43986             node.removeAttribute("lang");
43987         }
43988         
43989         if (node.hasAttribute("style")) {
43990             
43991             var styles = node.getAttribute("style").split(";");
43992             var nstyle = [];
43993             Roo.each(styles, function(s) {
43994                 if (!s.match(/:/)) {
43995                     return;
43996                 }
43997                 var kv = s.split(":");
43998                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
43999                     return;
44000                 }
44001                 // what ever is left... we allow.
44002                 nstyle.push(s);
44003             });
44004             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44005             if (!nstyle.length) {
44006                 node.removeAttribute('style');
44007             }
44008         }
44009         this.iterateChildren(node, this.cleanWord);
44010         
44011         
44012         
44013     },
44014     /**
44015      * iterateChildren of a Node, calling fn each time, using this as the scole..
44016      * @param {DomNode} node node to iterate children of.
44017      * @param {Function} fn method of this class to call on each item.
44018      */
44019     iterateChildren : function(node, fn)
44020     {
44021         if (!node.childNodes.length) {
44022                 return;
44023         }
44024         for (var i = node.childNodes.length-1; i > -1 ; i--) {
44025            fn.call(this, node.childNodes[i])
44026         }
44027     },
44028     
44029     
44030     /**
44031      * cleanTableWidths.
44032      *
44033      * Quite often pasting from word etc.. results in tables with column and widths.
44034      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44035      *
44036      */
44037     cleanTableWidths : function(node)
44038     {
44039          
44040          
44041         if (!node) {
44042             this.cleanTableWidths(this.doc.body);
44043             return;
44044         }
44045         
44046         // ignore list...
44047         if (node.nodeName == "#text" || node.nodeName == "#comment") {
44048             return; 
44049         }
44050         Roo.log(node.tagName);
44051         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
44052             this.iterateChildren(node, this.cleanTableWidths);
44053             return;
44054         }
44055         if (node.hasAttribute('width')) {
44056             node.removeAttribute('width');
44057         }
44058         
44059          
44060         if (node.hasAttribute("style")) {
44061             // pretty basic...
44062             
44063             var styles = node.getAttribute("style").split(";");
44064             var nstyle = [];
44065             Roo.each(styles, function(s) {
44066                 if (!s.match(/:/)) {
44067                     return;
44068                 }
44069                 var kv = s.split(":");
44070                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44071                     return;
44072                 }
44073                 // what ever is left... we allow.
44074                 nstyle.push(s);
44075             });
44076             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44077             if (!nstyle.length) {
44078                 node.removeAttribute('style');
44079             }
44080         }
44081         
44082         this.iterateChildren(node, this.cleanTableWidths);
44083         
44084         
44085     },
44086     
44087     
44088     
44089     
44090     domToHTML : function(currentElement, depth, nopadtext) {
44091         
44092         depth = depth || 0;
44093         nopadtext = nopadtext || false;
44094     
44095         if (!currentElement) {
44096             return this.domToHTML(this.doc.body);
44097         }
44098         
44099         //Roo.log(currentElement);
44100         var j;
44101         var allText = false;
44102         var nodeName = currentElement.nodeName;
44103         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44104         
44105         if  (nodeName == '#text') {
44106             
44107             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44108         }
44109         
44110         
44111         var ret = '';
44112         if (nodeName != 'BODY') {
44113              
44114             var i = 0;
44115             // Prints the node tagName, such as <A>, <IMG>, etc
44116             if (tagName) {
44117                 var attr = [];
44118                 for(i = 0; i < currentElement.attributes.length;i++) {
44119                     // quoting?
44120                     var aname = currentElement.attributes.item(i).name;
44121                     if (!currentElement.attributes.item(i).value.length) {
44122                         continue;
44123                     }
44124                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44125                 }
44126                 
44127                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44128             } 
44129             else {
44130                 
44131                 // eack
44132             }
44133         } else {
44134             tagName = false;
44135         }
44136         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44137             return ret;
44138         }
44139         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44140             nopadtext = true;
44141         }
44142         
44143         
44144         // Traverse the tree
44145         i = 0;
44146         var currentElementChild = currentElement.childNodes.item(i);
44147         var allText = true;
44148         var innerHTML  = '';
44149         lastnode = '';
44150         while (currentElementChild) {
44151             // Formatting code (indent the tree so it looks nice on the screen)
44152             var nopad = nopadtext;
44153             if (lastnode == 'SPAN') {
44154                 nopad  = true;
44155             }
44156             // text
44157             if  (currentElementChild.nodeName == '#text') {
44158                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44159                 toadd = nopadtext ? toadd : toadd.trim();
44160                 if (!nopad && toadd.length > 80) {
44161                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44162                 }
44163                 innerHTML  += toadd;
44164                 
44165                 i++;
44166                 currentElementChild = currentElement.childNodes.item(i);
44167                 lastNode = '';
44168                 continue;
44169             }
44170             allText = false;
44171             
44172             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44173                 
44174             // Recursively traverse the tree structure of the child node
44175             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44176             lastnode = currentElementChild.nodeName;
44177             i++;
44178             currentElementChild=currentElement.childNodes.item(i);
44179         }
44180         
44181         ret += innerHTML;
44182         
44183         if (!allText) {
44184                 // The remaining code is mostly for formatting the tree
44185             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44186         }
44187         
44188         
44189         if (tagName) {
44190             ret+= "</"+tagName+">";
44191         }
44192         return ret;
44193         
44194     },
44195         
44196     applyBlacklists : function()
44197     {
44198         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44199         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44200         
44201         this.white = [];
44202         this.black = [];
44203         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44204             if (b.indexOf(tag) > -1) {
44205                 return;
44206             }
44207             this.white.push(tag);
44208             
44209         }, this);
44210         
44211         Roo.each(w, function(tag) {
44212             if (b.indexOf(tag) > -1) {
44213                 return;
44214             }
44215             if (this.white.indexOf(tag) > -1) {
44216                 return;
44217             }
44218             this.white.push(tag);
44219             
44220         }, this);
44221         
44222         
44223         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44224             if (w.indexOf(tag) > -1) {
44225                 return;
44226             }
44227             this.black.push(tag);
44228             
44229         }, this);
44230         
44231         Roo.each(b, function(tag) {
44232             if (w.indexOf(tag) > -1) {
44233                 return;
44234             }
44235             if (this.black.indexOf(tag) > -1) {
44236                 return;
44237             }
44238             this.black.push(tag);
44239             
44240         }, this);
44241         
44242         
44243         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44244         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44245         
44246         this.cwhite = [];
44247         this.cblack = [];
44248         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44249             if (b.indexOf(tag) > -1) {
44250                 return;
44251             }
44252             this.cwhite.push(tag);
44253             
44254         }, this);
44255         
44256         Roo.each(w, function(tag) {
44257             if (b.indexOf(tag) > -1) {
44258                 return;
44259             }
44260             if (this.cwhite.indexOf(tag) > -1) {
44261                 return;
44262             }
44263             this.cwhite.push(tag);
44264             
44265         }, this);
44266         
44267         
44268         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44269             if (w.indexOf(tag) > -1) {
44270                 return;
44271             }
44272             this.cblack.push(tag);
44273             
44274         }, this);
44275         
44276         Roo.each(b, function(tag) {
44277             if (w.indexOf(tag) > -1) {
44278                 return;
44279             }
44280             if (this.cblack.indexOf(tag) > -1) {
44281                 return;
44282             }
44283             this.cblack.push(tag);
44284             
44285         }, this);
44286     },
44287     
44288     setStylesheets : function(stylesheets)
44289     {
44290         if(typeof(stylesheets) == 'string'){
44291             Roo.get(this.iframe.contentDocument.head).createChild({
44292                 tag : 'link',
44293                 rel : 'stylesheet',
44294                 type : 'text/css',
44295                 href : stylesheets
44296             });
44297             
44298             return;
44299         }
44300         var _this = this;
44301      
44302         Roo.each(stylesheets, function(s) {
44303             if(!s.length){
44304                 return;
44305             }
44306             
44307             Roo.get(_this.iframe.contentDocument.head).createChild({
44308                 tag : 'link',
44309                 rel : 'stylesheet',
44310                 type : 'text/css',
44311                 href : s
44312             });
44313         });
44314
44315         
44316     },
44317     
44318     removeStylesheets : function()
44319     {
44320         var _this = this;
44321         
44322         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44323             s.remove();
44324         });
44325     },
44326     
44327     setStyle : function(style)
44328     {
44329         Roo.get(this.iframe.contentDocument.head).createChild({
44330             tag : 'style',
44331             type : 'text/css',
44332             html : style
44333         });
44334
44335         return;
44336     }
44337     
44338     // hide stuff that is not compatible
44339     /**
44340      * @event blur
44341      * @hide
44342      */
44343     /**
44344      * @event change
44345      * @hide
44346      */
44347     /**
44348      * @event focus
44349      * @hide
44350      */
44351     /**
44352      * @event specialkey
44353      * @hide
44354      */
44355     /**
44356      * @cfg {String} fieldClass @hide
44357      */
44358     /**
44359      * @cfg {String} focusClass @hide
44360      */
44361     /**
44362      * @cfg {String} autoCreate @hide
44363      */
44364     /**
44365      * @cfg {String} inputType @hide
44366      */
44367     /**
44368      * @cfg {String} invalidClass @hide
44369      */
44370     /**
44371      * @cfg {String} invalidText @hide
44372      */
44373     /**
44374      * @cfg {String} msgFx @hide
44375      */
44376     /**
44377      * @cfg {String} validateOnBlur @hide
44378      */
44379 });
44380
44381 Roo.HtmlEditorCore.white = [
44382         'area', 'br', 'img', 'input', 'hr', 'wbr',
44383         
44384        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44385        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44386        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44387        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44388        'table',   'ul',         'xmp', 
44389        
44390        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44391       'thead',   'tr', 
44392      
44393       'dir', 'menu', 'ol', 'ul', 'dl',
44394        
44395       'embed',  'object'
44396 ];
44397
44398
44399 Roo.HtmlEditorCore.black = [
44400     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44401         'applet', // 
44402         'base',   'basefont', 'bgsound', 'blink',  'body', 
44403         'frame',  'frameset', 'head',    'html',   'ilayer', 
44404         'iframe', 'layer',  'link',     'meta',    'object',   
44405         'script', 'style' ,'title',  'xml' // clean later..
44406 ];
44407 Roo.HtmlEditorCore.clean = [
44408     'script', 'style', 'title', 'xml'
44409 ];
44410 Roo.HtmlEditorCore.remove = [
44411     'font'
44412 ];
44413 // attributes..
44414
44415 Roo.HtmlEditorCore.ablack = [
44416     'on'
44417 ];
44418     
44419 Roo.HtmlEditorCore.aclean = [ 
44420     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44421 ];
44422
44423 // protocols..
44424 Roo.HtmlEditorCore.pwhite= [
44425         'http',  'https',  'mailto'
44426 ];
44427
44428 // white listed style attributes.
44429 Roo.HtmlEditorCore.cwhite= [
44430       //  'text-align', /// default is to allow most things..
44431       
44432          
44433 //        'font-size'//??
44434 ];
44435
44436 // black listed style attributes.
44437 Roo.HtmlEditorCore.cblack= [
44438       //  'font-size' -- this can be set by the project 
44439 ];
44440
44441
44442 Roo.HtmlEditorCore.swapCodes   =[ 
44443     [    8211, "--" ], 
44444     [    8212, "--" ], 
44445     [    8216,  "'" ],  
44446     [    8217, "'" ],  
44447     [    8220, '"' ],  
44448     [    8221, '"' ],  
44449     [    8226, "*" ],  
44450     [    8230, "..." ]
44451 ]; 
44452
44453     //<script type="text/javascript">
44454
44455 /*
44456  * Ext JS Library 1.1.1
44457  * Copyright(c) 2006-2007, Ext JS, LLC.
44458  * Licence LGPL
44459  * 
44460  */
44461  
44462  
44463 Roo.form.HtmlEditor = function(config){
44464     
44465     
44466     
44467     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
44468     
44469     if (!this.toolbars) {
44470         this.toolbars = [];
44471     }
44472     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
44473     
44474     
44475 };
44476
44477 /**
44478  * @class Roo.form.HtmlEditor
44479  * @extends Roo.form.Field
44480  * Provides a lightweight HTML Editor component.
44481  *
44482  * This has been tested on Fireforx / Chrome.. IE may not be so great..
44483  * 
44484  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
44485  * supported by this editor.</b><br/><br/>
44486  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
44487  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44488  */
44489 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
44490     /**
44491      * @cfg {Boolean} clearUp
44492      */
44493     clearUp : true,
44494       /**
44495      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
44496      */
44497     toolbars : false,
44498    
44499      /**
44500      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44501      *                        Roo.resizable.
44502      */
44503     resizable : false,
44504      /**
44505      * @cfg {Number} height (in pixels)
44506      */   
44507     height: 300,
44508    /**
44509      * @cfg {Number} width (in pixels)
44510      */   
44511     width: 500,
44512     
44513     /**
44514      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44515      * 
44516      */
44517     stylesheets: false,
44518     
44519     
44520      /**
44521      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
44522      * 
44523      */
44524     cblack: false,
44525     /**
44526      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
44527      * 
44528      */
44529     cwhite: false,
44530     
44531      /**
44532      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
44533      * 
44534      */
44535     black: false,
44536     /**
44537      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
44538      * 
44539      */
44540     white: false,
44541     
44542     // id of frame..
44543     frameId: false,
44544     
44545     // private properties
44546     validationEvent : false,
44547     deferHeight: true,
44548     initialized : false,
44549     activated : false,
44550     
44551     onFocus : Roo.emptyFn,
44552     iframePad:3,
44553     hideMode:'offsets',
44554     
44555     actionMode : 'container', // defaults to hiding it...
44556     
44557     defaultAutoCreate : { // modified by initCompnoent..
44558         tag: "textarea",
44559         style:"width:500px;height:300px;",
44560         autocomplete: "new-password"
44561     },
44562
44563     // private
44564     initComponent : function(){
44565         this.addEvents({
44566             /**
44567              * @event initialize
44568              * Fires when the editor is fully initialized (including the iframe)
44569              * @param {HtmlEditor} this
44570              */
44571             initialize: true,
44572             /**
44573              * @event activate
44574              * Fires when the editor is first receives the focus. Any insertion must wait
44575              * until after this event.
44576              * @param {HtmlEditor} this
44577              */
44578             activate: true,
44579              /**
44580              * @event beforesync
44581              * Fires before the textarea is updated with content from the editor iframe. Return false
44582              * to cancel the sync.
44583              * @param {HtmlEditor} this
44584              * @param {String} html
44585              */
44586             beforesync: true,
44587              /**
44588              * @event beforepush
44589              * Fires before the iframe editor is updated with content from the textarea. Return false
44590              * to cancel the push.
44591              * @param {HtmlEditor} this
44592              * @param {String} html
44593              */
44594             beforepush: true,
44595              /**
44596              * @event sync
44597              * Fires when the textarea is updated with content from the editor iframe.
44598              * @param {HtmlEditor} this
44599              * @param {String} html
44600              */
44601             sync: true,
44602              /**
44603              * @event push
44604              * Fires when the iframe editor is updated with content from the textarea.
44605              * @param {HtmlEditor} this
44606              * @param {String} html
44607              */
44608             push: true,
44609              /**
44610              * @event editmodechange
44611              * Fires when the editor switches edit modes
44612              * @param {HtmlEditor} this
44613              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
44614              */
44615             editmodechange: true,
44616             /**
44617              * @event editorevent
44618              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44619              * @param {HtmlEditor} this
44620              */
44621             editorevent: true,
44622             /**
44623              * @event firstfocus
44624              * Fires when on first focus - needed by toolbars..
44625              * @param {HtmlEditor} this
44626              */
44627             firstfocus: true,
44628             /**
44629              * @event autosave
44630              * Auto save the htmlEditor value as a file into Events
44631              * @param {HtmlEditor} this
44632              */
44633             autosave: true,
44634             /**
44635              * @event savedpreview
44636              * preview the saved version of htmlEditor
44637              * @param {HtmlEditor} this
44638              */
44639             savedpreview: true,
44640             
44641             /**
44642             * @event stylesheetsclick
44643             * Fires when press the Sytlesheets button
44644             * @param {Roo.HtmlEditorCore} this
44645             */
44646             stylesheetsclick: true
44647         });
44648         this.defaultAutoCreate =  {
44649             tag: "textarea",
44650             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
44651             autocomplete: "new-password"
44652         };
44653     },
44654
44655     /**
44656      * Protected method that will not generally be called directly. It
44657      * is called when the editor creates its toolbar. Override this method if you need to
44658      * add custom toolbar buttons.
44659      * @param {HtmlEditor} editor
44660      */
44661     createToolbar : function(editor){
44662         Roo.log("create toolbars");
44663         if (!editor.toolbars || !editor.toolbars.length) {
44664             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
44665         }
44666         
44667         for (var i =0 ; i < editor.toolbars.length;i++) {
44668             editor.toolbars[i] = Roo.factory(
44669                     typeof(editor.toolbars[i]) == 'string' ?
44670                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
44671                 Roo.form.HtmlEditor);
44672             editor.toolbars[i].init(editor);
44673         }
44674          
44675         
44676     },
44677
44678      
44679     // private
44680     onRender : function(ct, position)
44681     {
44682         var _t = this;
44683         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
44684         
44685         this.wrap = this.el.wrap({
44686             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
44687         });
44688         
44689         this.editorcore.onRender(ct, position);
44690          
44691         if (this.resizable) {
44692             this.resizeEl = new Roo.Resizable(this.wrap, {
44693                 pinned : true,
44694                 wrap: true,
44695                 dynamic : true,
44696                 minHeight : this.height,
44697                 height: this.height,
44698                 handles : this.resizable,
44699                 width: this.width,
44700                 listeners : {
44701                     resize : function(r, w, h) {
44702                         _t.onResize(w,h); // -something
44703                     }
44704                 }
44705             });
44706             
44707         }
44708         this.createToolbar(this);
44709        
44710         
44711         if(!this.width){
44712             this.setSize(this.wrap.getSize());
44713         }
44714         if (this.resizeEl) {
44715             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
44716             // should trigger onReize..
44717         }
44718         
44719         this.keyNav = new Roo.KeyNav(this.el, {
44720             
44721             "tab" : function(e){
44722                 e.preventDefault();
44723                 
44724                 var value = this.getValue();
44725                 
44726                 var start = this.el.dom.selectionStart;
44727                 var end = this.el.dom.selectionEnd;
44728                 
44729                 if(!e.shiftKey){
44730                     
44731                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
44732                     this.el.dom.setSelectionRange(end + 1, end + 1);
44733                     return;
44734                 }
44735                 
44736                 var f = value.substring(0, start).split("\t");
44737                 
44738                 if(f.pop().length != 0){
44739                     return;
44740                 }
44741                 
44742                 this.setValue(f.join("\t") + value.substring(end));
44743                 this.el.dom.setSelectionRange(start - 1, start - 1);
44744                 
44745             },
44746             
44747             "home" : function(e){
44748                 e.preventDefault();
44749                 
44750                 var curr = this.el.dom.selectionStart;
44751                 var lines = this.getValue().split("\n");
44752                 
44753                 if(!lines.length){
44754                     return;
44755                 }
44756                 
44757                 if(e.ctrlKey){
44758                     this.el.dom.setSelectionRange(0, 0);
44759                     return;
44760                 }
44761                 
44762                 var pos = 0;
44763                 
44764                 for (var i = 0; i < lines.length;i++) {
44765                     pos += lines[i].length;
44766                     
44767                     if(i != 0){
44768                         pos += 1;
44769                     }
44770                     
44771                     if(pos < curr){
44772                         continue;
44773                     }
44774                     
44775                     pos -= lines[i].length;
44776                     
44777                     break;
44778                 }
44779                 
44780                 if(!e.shiftKey){
44781                     this.el.dom.setSelectionRange(pos, pos);
44782                     return;
44783                 }
44784                 
44785                 this.el.dom.selectionStart = pos;
44786                 this.el.dom.selectionEnd = curr;
44787             },
44788             
44789             "end" : function(e){
44790                 e.preventDefault();
44791                 
44792                 var curr = this.el.dom.selectionStart;
44793                 var lines = this.getValue().split("\n");
44794                 
44795                 if(!lines.length){
44796                     return;
44797                 }
44798                 
44799                 if(e.ctrlKey){
44800                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
44801                     return;
44802                 }
44803                 
44804                 var pos = 0;
44805                 
44806                 for (var i = 0; i < lines.length;i++) {
44807                     
44808                     pos += lines[i].length;
44809                     
44810                     if(i != 0){
44811                         pos += 1;
44812                     }
44813                     
44814                     if(pos < curr){
44815                         continue;
44816                     }
44817                     
44818                     break;
44819                 }
44820                 
44821                 if(!e.shiftKey){
44822                     this.el.dom.setSelectionRange(pos, pos);
44823                     return;
44824                 }
44825                 
44826                 this.el.dom.selectionStart = curr;
44827                 this.el.dom.selectionEnd = pos;
44828             },
44829
44830             scope : this,
44831
44832             doRelay : function(foo, bar, hname){
44833                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44834             },
44835
44836             forceKeyDown: true
44837         });
44838         
44839 //        if(this.autosave && this.w){
44840 //            this.autoSaveFn = setInterval(this.autosave, 1000);
44841 //        }
44842     },
44843
44844     // private
44845     onResize : function(w, h)
44846     {
44847         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
44848         var ew = false;
44849         var eh = false;
44850         
44851         if(this.el ){
44852             if(typeof w == 'number'){
44853                 var aw = w - this.wrap.getFrameWidth('lr');
44854                 this.el.setWidth(this.adjustWidth('textarea', aw));
44855                 ew = aw;
44856             }
44857             if(typeof h == 'number'){
44858                 var tbh = 0;
44859                 for (var i =0; i < this.toolbars.length;i++) {
44860                     // fixme - ask toolbars for heights?
44861                     tbh += this.toolbars[i].tb.el.getHeight();
44862                     if (this.toolbars[i].footer) {
44863                         tbh += this.toolbars[i].footer.el.getHeight();
44864                     }
44865                 }
44866                 
44867                 
44868                 
44869                 
44870                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
44871                 ah -= 5; // knock a few pixes off for look..
44872 //                Roo.log(ah);
44873                 this.el.setHeight(this.adjustWidth('textarea', ah));
44874                 var eh = ah;
44875             }
44876         }
44877         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
44878         this.editorcore.onResize(ew,eh);
44879         
44880     },
44881
44882     /**
44883      * Toggles the editor between standard and source edit mode.
44884      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
44885      */
44886     toggleSourceEdit : function(sourceEditMode)
44887     {
44888         this.editorcore.toggleSourceEdit(sourceEditMode);
44889         
44890         if(this.editorcore.sourceEditMode){
44891             Roo.log('editor - showing textarea');
44892             
44893 //            Roo.log('in');
44894 //            Roo.log(this.syncValue());
44895             this.editorcore.syncValue();
44896             this.el.removeClass('x-hidden');
44897             this.el.dom.removeAttribute('tabIndex');
44898             this.el.focus();
44899             
44900             for (var i = 0; i < this.toolbars.length; i++) {
44901                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44902                     this.toolbars[i].tb.hide();
44903                     this.toolbars[i].footer.hide();
44904                 }
44905             }
44906             
44907         }else{
44908             Roo.log('editor - hiding textarea');
44909 //            Roo.log('out')
44910 //            Roo.log(this.pushValue()); 
44911             this.editorcore.pushValue();
44912             
44913             this.el.addClass('x-hidden');
44914             this.el.dom.setAttribute('tabIndex', -1);
44915             
44916             for (var i = 0; i < this.toolbars.length; i++) {
44917                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44918                     this.toolbars[i].tb.show();
44919                     this.toolbars[i].footer.show();
44920                 }
44921             }
44922             
44923             //this.deferFocus();
44924         }
44925         
44926         this.setSize(this.wrap.getSize());
44927         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
44928         
44929         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
44930     },
44931  
44932     // private (for BoxComponent)
44933     adjustSize : Roo.BoxComponent.prototype.adjustSize,
44934
44935     // private (for BoxComponent)
44936     getResizeEl : function(){
44937         return this.wrap;
44938     },
44939
44940     // private (for BoxComponent)
44941     getPositionEl : function(){
44942         return this.wrap;
44943     },
44944
44945     // private
44946     initEvents : function(){
44947         this.originalValue = this.getValue();
44948     },
44949
44950     /**
44951      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44952      * @method
44953      */
44954     markInvalid : Roo.emptyFn,
44955     /**
44956      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44957      * @method
44958      */
44959     clearInvalid : Roo.emptyFn,
44960
44961     setValue : function(v){
44962         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
44963         this.editorcore.pushValue();
44964     },
44965
44966      
44967     // private
44968     deferFocus : function(){
44969         this.focus.defer(10, this);
44970     },
44971
44972     // doc'ed in Field
44973     focus : function(){
44974         this.editorcore.focus();
44975         
44976     },
44977       
44978
44979     // private
44980     onDestroy : function(){
44981         
44982         
44983         
44984         if(this.rendered){
44985             
44986             for (var i =0; i < this.toolbars.length;i++) {
44987                 // fixme - ask toolbars for heights?
44988                 this.toolbars[i].onDestroy();
44989             }
44990             
44991             this.wrap.dom.innerHTML = '';
44992             this.wrap.remove();
44993         }
44994     },
44995
44996     // private
44997     onFirstFocus : function(){
44998         //Roo.log("onFirstFocus");
44999         this.editorcore.onFirstFocus();
45000          for (var i =0; i < this.toolbars.length;i++) {
45001             this.toolbars[i].onFirstFocus();
45002         }
45003         
45004     },
45005     
45006     // private
45007     syncValue : function()
45008     {
45009         this.editorcore.syncValue();
45010     },
45011     
45012     pushValue : function()
45013     {
45014         this.editorcore.pushValue();
45015     },
45016     
45017     setStylesheets : function(stylesheets)
45018     {
45019         this.editorcore.setStylesheets(stylesheets);
45020     },
45021     
45022     removeStylesheets : function()
45023     {
45024         this.editorcore.removeStylesheets();
45025     }
45026      
45027     
45028     // hide stuff that is not compatible
45029     /**
45030      * @event blur
45031      * @hide
45032      */
45033     /**
45034      * @event change
45035      * @hide
45036      */
45037     /**
45038      * @event focus
45039      * @hide
45040      */
45041     /**
45042      * @event specialkey
45043      * @hide
45044      */
45045     /**
45046      * @cfg {String} fieldClass @hide
45047      */
45048     /**
45049      * @cfg {String} focusClass @hide
45050      */
45051     /**
45052      * @cfg {String} autoCreate @hide
45053      */
45054     /**
45055      * @cfg {String} inputType @hide
45056      */
45057     /**
45058      * @cfg {String} invalidClass @hide
45059      */
45060     /**
45061      * @cfg {String} invalidText @hide
45062      */
45063     /**
45064      * @cfg {String} msgFx @hide
45065      */
45066     /**
45067      * @cfg {String} validateOnBlur @hide
45068      */
45069 });
45070  
45071     // <script type="text/javascript">
45072 /*
45073  * Based on
45074  * Ext JS Library 1.1.1
45075  * Copyright(c) 2006-2007, Ext JS, LLC.
45076  *  
45077  
45078  */
45079
45080 /**
45081  * @class Roo.form.HtmlEditorToolbar1
45082  * Basic Toolbar
45083  * 
45084  * Usage:
45085  *
45086  new Roo.form.HtmlEditor({
45087     ....
45088     toolbars : [
45089         new Roo.form.HtmlEditorToolbar1({
45090             disable : { fonts: 1 , format: 1, ..., ... , ...],
45091             btns : [ .... ]
45092         })
45093     }
45094      
45095  * 
45096  * @cfg {Object} disable List of elements to disable..
45097  * @cfg {Array} btns List of additional buttons.
45098  * 
45099  * 
45100  * NEEDS Extra CSS? 
45101  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45102  */
45103  
45104 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45105 {
45106     
45107     Roo.apply(this, config);
45108     
45109     // default disabled, based on 'good practice'..
45110     this.disable = this.disable || {};
45111     Roo.applyIf(this.disable, {
45112         fontSize : true,
45113         colors : true,
45114         specialElements : true
45115     });
45116     
45117     
45118     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45119     // dont call parent... till later.
45120 }
45121
45122 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45123     
45124     tb: false,
45125     
45126     rendered: false,
45127     
45128     editor : false,
45129     editorcore : false,
45130     /**
45131      * @cfg {Object} disable  List of toolbar elements to disable
45132          
45133      */
45134     disable : false,
45135     
45136     
45137      /**
45138      * @cfg {String} createLinkText The default text for the create link prompt
45139      */
45140     createLinkText : 'Please enter the URL for the link:',
45141     /**
45142      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45143      */
45144     defaultLinkValue : 'http:/'+'/',
45145    
45146     
45147       /**
45148      * @cfg {Array} fontFamilies An array of available font families
45149      */
45150     fontFamilies : [
45151         'Arial',
45152         'Courier New',
45153         'Tahoma',
45154         'Times New Roman',
45155         'Verdana'
45156     ],
45157     
45158     specialChars : [
45159            "&#169;",
45160           "&#174;",     
45161           "&#8482;",    
45162           "&#163;" ,    
45163          // "&#8212;",    
45164           "&#8230;",    
45165           "&#247;" ,    
45166         //  "&#225;" ,     ?? a acute?
45167            "&#8364;"    , //Euro
45168        //   "&#8220;"    ,
45169         //  "&#8221;"    ,
45170         //  "&#8226;"    ,
45171           "&#176;"  //   , // degrees
45172
45173          // "&#233;"     , // e ecute
45174          // "&#250;"     , // u ecute?
45175     ],
45176     
45177     specialElements : [
45178         {
45179             text: "Insert Table",
45180             xtype: 'MenuItem',
45181             xns : Roo.Menu,
45182             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45183                 
45184         },
45185         {    
45186             text: "Insert Image",
45187             xtype: 'MenuItem',
45188             xns : Roo.Menu,
45189             ihtml : '<img src="about:blank"/>'
45190             
45191         }
45192         
45193          
45194     ],
45195     
45196     
45197     inputElements : [ 
45198             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45199             "input:submit", "input:button", "select", "textarea", "label" ],
45200     formats : [
45201         ["p"] ,  
45202         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45203         ["pre"],[ "code"], 
45204         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45205         ['div'],['span']
45206     ],
45207     
45208     cleanStyles : [
45209         "font-size"
45210     ],
45211      /**
45212      * @cfg {String} defaultFont default font to use.
45213      */
45214     defaultFont: 'tahoma',
45215    
45216     fontSelect : false,
45217     
45218     
45219     formatCombo : false,
45220     
45221     init : function(editor)
45222     {
45223         this.editor = editor;
45224         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45225         var editorcore = this.editorcore;
45226         
45227         var _t = this;
45228         
45229         var fid = editorcore.frameId;
45230         var etb = this;
45231         function btn(id, toggle, handler){
45232             var xid = fid + '-'+ id ;
45233             return {
45234                 id : xid,
45235                 cmd : id,
45236                 cls : 'x-btn-icon x-edit-'+id,
45237                 enableToggle:toggle !== false,
45238                 scope: _t, // was editor...
45239                 handler:handler||_t.relayBtnCmd,
45240                 clickEvent:'mousedown',
45241                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45242                 tabIndex:-1
45243             };
45244         }
45245         
45246         
45247         
45248         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45249         this.tb = tb;
45250          // stop form submits
45251         tb.el.on('click', function(e){
45252             e.preventDefault(); // what does this do?
45253         });
45254
45255         if(!this.disable.font) { // && !Roo.isSafari){
45256             /* why no safari for fonts 
45257             editor.fontSelect = tb.el.createChild({
45258                 tag:'select',
45259                 tabIndex: -1,
45260                 cls:'x-font-select',
45261                 html: this.createFontOptions()
45262             });
45263             
45264             editor.fontSelect.on('change', function(){
45265                 var font = editor.fontSelect.dom.value;
45266                 editor.relayCmd('fontname', font);
45267                 editor.deferFocus();
45268             }, editor);
45269             
45270             tb.add(
45271                 editor.fontSelect.dom,
45272                 '-'
45273             );
45274             */
45275             
45276         };
45277         if(!this.disable.formats){
45278             this.formatCombo = new Roo.form.ComboBox({
45279                 store: new Roo.data.SimpleStore({
45280                     id : 'tag',
45281                     fields: ['tag'],
45282                     data : this.formats // from states.js
45283                 }),
45284                 blockFocus : true,
45285                 name : '',
45286                 //autoCreate : {tag: "div",  size: "20"},
45287                 displayField:'tag',
45288                 typeAhead: false,
45289                 mode: 'local',
45290                 editable : false,
45291                 triggerAction: 'all',
45292                 emptyText:'Add tag',
45293                 selectOnFocus:true,
45294                 width:135,
45295                 listeners : {
45296                     'select': function(c, r, i) {
45297                         editorcore.insertTag(r.get('tag'));
45298                         editor.focus();
45299                     }
45300                 }
45301
45302             });
45303             tb.addField(this.formatCombo);
45304             
45305         }
45306         
45307         if(!this.disable.format){
45308             tb.add(
45309                 btn('bold'),
45310                 btn('italic'),
45311                 btn('underline'),
45312                 btn('strikethrough')
45313             );
45314         };
45315         if(!this.disable.fontSize){
45316             tb.add(
45317                 '-',
45318                 
45319                 
45320                 btn('increasefontsize', false, editorcore.adjustFont),
45321                 btn('decreasefontsize', false, editorcore.adjustFont)
45322             );
45323         };
45324         
45325         
45326         if(!this.disable.colors){
45327             tb.add(
45328                 '-', {
45329                     id:editorcore.frameId +'-forecolor',
45330                     cls:'x-btn-icon x-edit-forecolor',
45331                     clickEvent:'mousedown',
45332                     tooltip: this.buttonTips['forecolor'] || undefined,
45333                     tabIndex:-1,
45334                     menu : new Roo.menu.ColorMenu({
45335                         allowReselect: true,
45336                         focus: Roo.emptyFn,
45337                         value:'000000',
45338                         plain:true,
45339                         selectHandler: function(cp, color){
45340                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45341                             editor.deferFocus();
45342                         },
45343                         scope: editorcore,
45344                         clickEvent:'mousedown'
45345                     })
45346                 }, {
45347                     id:editorcore.frameId +'backcolor',
45348                     cls:'x-btn-icon x-edit-backcolor',
45349                     clickEvent:'mousedown',
45350                     tooltip: this.buttonTips['backcolor'] || undefined,
45351                     tabIndex:-1,
45352                     menu : new Roo.menu.ColorMenu({
45353                         focus: Roo.emptyFn,
45354                         value:'FFFFFF',
45355                         plain:true,
45356                         allowReselect: true,
45357                         selectHandler: function(cp, color){
45358                             if(Roo.isGecko){
45359                                 editorcore.execCmd('useCSS', false);
45360                                 editorcore.execCmd('hilitecolor', color);
45361                                 editorcore.execCmd('useCSS', true);
45362                                 editor.deferFocus();
45363                             }else{
45364                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45365                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45366                                 editor.deferFocus();
45367                             }
45368                         },
45369                         scope:editorcore,
45370                         clickEvent:'mousedown'
45371                     })
45372                 }
45373             );
45374         };
45375         // now add all the items...
45376         
45377
45378         if(!this.disable.alignments){
45379             tb.add(
45380                 '-',
45381                 btn('justifyleft'),
45382                 btn('justifycenter'),
45383                 btn('justifyright')
45384             );
45385         };
45386
45387         //if(!Roo.isSafari){
45388             if(!this.disable.links){
45389                 tb.add(
45390                     '-',
45391                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45392                 );
45393             };
45394
45395             if(!this.disable.lists){
45396                 tb.add(
45397                     '-',
45398                     btn('insertorderedlist'),
45399                     btn('insertunorderedlist')
45400                 );
45401             }
45402             if(!this.disable.sourceEdit){
45403                 tb.add(
45404                     '-',
45405                     btn('sourceedit', true, function(btn){
45406                         this.toggleSourceEdit(btn.pressed);
45407                     })
45408                 );
45409             }
45410         //}
45411         
45412         var smenu = { };
45413         // special menu.. - needs to be tidied up..
45414         if (!this.disable.special) {
45415             smenu = {
45416                 text: "&#169;",
45417                 cls: 'x-edit-none',
45418                 
45419                 menu : {
45420                     items : []
45421                 }
45422             };
45423             for (var i =0; i < this.specialChars.length; i++) {
45424                 smenu.menu.items.push({
45425                     
45426                     html: this.specialChars[i],
45427                     handler: function(a,b) {
45428                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
45429                         //editor.insertAtCursor(a.html);
45430                         
45431                     },
45432                     tabIndex:-1
45433                 });
45434             }
45435             
45436             
45437             tb.add(smenu);
45438             
45439             
45440         }
45441         
45442         var cmenu = { };
45443         if (!this.disable.cleanStyles) {
45444             cmenu = {
45445                 cls: 'x-btn-icon x-btn-clear',
45446                 
45447                 menu : {
45448                     items : []
45449                 }
45450             };
45451             for (var i =0; i < this.cleanStyles.length; i++) {
45452                 cmenu.menu.items.push({
45453                     actiontype : this.cleanStyles[i],
45454                     html: 'Remove ' + this.cleanStyles[i],
45455                     handler: function(a,b) {
45456 //                        Roo.log(a);
45457 //                        Roo.log(b);
45458                         var c = Roo.get(editorcore.doc.body);
45459                         c.select('[style]').each(function(s) {
45460                             s.dom.style.removeProperty(a.actiontype);
45461                         });
45462                         editorcore.syncValue();
45463                     },
45464                     tabIndex:-1
45465                 });
45466             }
45467              cmenu.menu.items.push({
45468                 actiontype : 'tablewidths',
45469                 html: 'Remove Table Widths',
45470                 handler: function(a,b) {
45471                     editorcore.cleanTableWidths();
45472                     editorcore.syncValue();
45473                 },
45474                 tabIndex:-1
45475             });
45476             cmenu.menu.items.push({
45477                 actiontype : 'word',
45478                 html: 'Remove MS Word Formating',
45479                 handler: function(a,b) {
45480                     editorcore.cleanWord();
45481                     editorcore.syncValue();
45482                 },
45483                 tabIndex:-1
45484             });
45485             
45486             cmenu.menu.items.push({
45487                 actiontype : 'all',
45488                 html: 'Remove All Styles',
45489                 handler: function(a,b) {
45490                     
45491                     var c = Roo.get(editorcore.doc.body);
45492                     c.select('[style]').each(function(s) {
45493                         s.dom.removeAttribute('style');
45494                     });
45495                     editorcore.syncValue();
45496                 },
45497                 tabIndex:-1
45498             });
45499             
45500             cmenu.menu.items.push({
45501                 actiontype : 'all',
45502                 html: 'Remove All CSS Classes',
45503                 handler: function(a,b) {
45504                     
45505                     var c = Roo.get(editorcore.doc.body);
45506                     c.select('[class]').each(function(s) {
45507                         s.dom.className = '';
45508                     });
45509                     editorcore.syncValue();
45510                 },
45511                 tabIndex:-1
45512             });
45513             
45514              cmenu.menu.items.push({
45515                 actiontype : 'tidy',
45516                 html: 'Tidy HTML Source',
45517                 handler: function(a,b) {
45518                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
45519                     editorcore.syncValue();
45520                 },
45521                 tabIndex:-1
45522             });
45523             
45524             
45525             tb.add(cmenu);
45526         }
45527          
45528         if (!this.disable.specialElements) {
45529             var semenu = {
45530                 text: "Other;",
45531                 cls: 'x-edit-none',
45532                 menu : {
45533                     items : []
45534                 }
45535             };
45536             for (var i =0; i < this.specialElements.length; i++) {
45537                 semenu.menu.items.push(
45538                     Roo.apply({ 
45539                         handler: function(a,b) {
45540                             editor.insertAtCursor(this.ihtml);
45541                         }
45542                     }, this.specialElements[i])
45543                 );
45544                     
45545             }
45546             
45547             tb.add(semenu);
45548             
45549             
45550         }
45551          
45552         
45553         if (this.btns) {
45554             for(var i =0; i< this.btns.length;i++) {
45555                 var b = Roo.factory(this.btns[i],Roo.form);
45556                 b.cls =  'x-edit-none';
45557                 
45558                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
45559                     b.cls += ' x-init-enable';
45560                 }
45561                 
45562                 b.scope = editorcore;
45563                 tb.add(b);
45564             }
45565         
45566         }
45567         
45568         
45569         
45570         // disable everything...
45571         
45572         this.tb.items.each(function(item){
45573             
45574            if(
45575                 item.id != editorcore.frameId+ '-sourceedit' && 
45576                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
45577             ){
45578                 
45579                 item.disable();
45580             }
45581         });
45582         this.rendered = true;
45583         
45584         // the all the btns;
45585         editor.on('editorevent', this.updateToolbar, this);
45586         // other toolbars need to implement this..
45587         //editor.on('editmodechange', this.updateToolbar, this);
45588     },
45589     
45590     
45591     relayBtnCmd : function(btn) {
45592         this.editorcore.relayCmd(btn.cmd);
45593     },
45594     // private used internally
45595     createLink : function(){
45596         Roo.log("create link?");
45597         var url = prompt(this.createLinkText, this.defaultLinkValue);
45598         if(url && url != 'http:/'+'/'){
45599             this.editorcore.relayCmd('createlink', url);
45600         }
45601     },
45602
45603     
45604     /**
45605      * Protected method that will not generally be called directly. It triggers
45606      * a toolbar update by reading the markup state of the current selection in the editor.
45607      */
45608     updateToolbar: function(){
45609
45610         if(!this.editorcore.activated){
45611             this.editor.onFirstFocus();
45612             return;
45613         }
45614
45615         var btns = this.tb.items.map, 
45616             doc = this.editorcore.doc,
45617             frameId = this.editorcore.frameId;
45618
45619         if(!this.disable.font && !Roo.isSafari){
45620             /*
45621             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
45622             if(name != this.fontSelect.dom.value){
45623                 this.fontSelect.dom.value = name;
45624             }
45625             */
45626         }
45627         if(!this.disable.format){
45628             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
45629             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
45630             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
45631             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
45632         }
45633         if(!this.disable.alignments){
45634             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
45635             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
45636             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
45637         }
45638         if(!Roo.isSafari && !this.disable.lists){
45639             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
45640             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
45641         }
45642         
45643         var ans = this.editorcore.getAllAncestors();
45644         if (this.formatCombo) {
45645             
45646             
45647             var store = this.formatCombo.store;
45648             this.formatCombo.setValue("");
45649             for (var i =0; i < ans.length;i++) {
45650                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
45651                     // select it..
45652                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
45653                     break;
45654                 }
45655             }
45656         }
45657         
45658         
45659         
45660         // hides menus... - so this cant be on a menu...
45661         Roo.menu.MenuMgr.hideAll();
45662
45663         //this.editorsyncValue();
45664     },
45665    
45666     
45667     createFontOptions : function(){
45668         var buf = [], fs = this.fontFamilies, ff, lc;
45669         
45670         
45671         
45672         for(var i = 0, len = fs.length; i< len; i++){
45673             ff = fs[i];
45674             lc = ff.toLowerCase();
45675             buf.push(
45676                 '<option value="',lc,'" style="font-family:',ff,';"',
45677                     (this.defaultFont == lc ? ' selected="true">' : '>'),
45678                     ff,
45679                 '</option>'
45680             );
45681         }
45682         return buf.join('');
45683     },
45684     
45685     toggleSourceEdit : function(sourceEditMode){
45686         
45687         Roo.log("toolbar toogle");
45688         if(sourceEditMode === undefined){
45689             sourceEditMode = !this.sourceEditMode;
45690         }
45691         this.sourceEditMode = sourceEditMode === true;
45692         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
45693         // just toggle the button?
45694         if(btn.pressed !== this.sourceEditMode){
45695             btn.toggle(this.sourceEditMode);
45696             return;
45697         }
45698         
45699         if(sourceEditMode){
45700             Roo.log("disabling buttons");
45701             this.tb.items.each(function(item){
45702                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
45703                     item.disable();
45704                 }
45705             });
45706           
45707         }else{
45708             Roo.log("enabling buttons");
45709             if(this.editorcore.initialized){
45710                 this.tb.items.each(function(item){
45711                     item.enable();
45712                 });
45713             }
45714             
45715         }
45716         Roo.log("calling toggole on editor");
45717         // tell the editor that it's been pressed..
45718         this.editor.toggleSourceEdit(sourceEditMode);
45719        
45720     },
45721      /**
45722      * Object collection of toolbar tooltips for the buttons in the editor. The key
45723      * is the command id associated with that button and the value is a valid QuickTips object.
45724      * For example:
45725 <pre><code>
45726 {
45727     bold : {
45728         title: 'Bold (Ctrl+B)',
45729         text: 'Make the selected text bold.',
45730         cls: 'x-html-editor-tip'
45731     },
45732     italic : {
45733         title: 'Italic (Ctrl+I)',
45734         text: 'Make the selected text italic.',
45735         cls: 'x-html-editor-tip'
45736     },
45737     ...
45738 </code></pre>
45739     * @type Object
45740      */
45741     buttonTips : {
45742         bold : {
45743             title: 'Bold (Ctrl+B)',
45744             text: 'Make the selected text bold.',
45745             cls: 'x-html-editor-tip'
45746         },
45747         italic : {
45748             title: 'Italic (Ctrl+I)',
45749             text: 'Make the selected text italic.',
45750             cls: 'x-html-editor-tip'
45751         },
45752         underline : {
45753             title: 'Underline (Ctrl+U)',
45754             text: 'Underline the selected text.',
45755             cls: 'x-html-editor-tip'
45756         },
45757         strikethrough : {
45758             title: 'Strikethrough',
45759             text: 'Strikethrough the selected text.',
45760             cls: 'x-html-editor-tip'
45761         },
45762         increasefontsize : {
45763             title: 'Grow Text',
45764             text: 'Increase the font size.',
45765             cls: 'x-html-editor-tip'
45766         },
45767         decreasefontsize : {
45768             title: 'Shrink Text',
45769             text: 'Decrease the font size.',
45770             cls: 'x-html-editor-tip'
45771         },
45772         backcolor : {
45773             title: 'Text Highlight Color',
45774             text: 'Change the background color of the selected text.',
45775             cls: 'x-html-editor-tip'
45776         },
45777         forecolor : {
45778             title: 'Font Color',
45779             text: 'Change the color of the selected text.',
45780             cls: 'x-html-editor-tip'
45781         },
45782         justifyleft : {
45783             title: 'Align Text Left',
45784             text: 'Align text to the left.',
45785             cls: 'x-html-editor-tip'
45786         },
45787         justifycenter : {
45788             title: 'Center Text',
45789             text: 'Center text in the editor.',
45790             cls: 'x-html-editor-tip'
45791         },
45792         justifyright : {
45793             title: 'Align Text Right',
45794             text: 'Align text to the right.',
45795             cls: 'x-html-editor-tip'
45796         },
45797         insertunorderedlist : {
45798             title: 'Bullet List',
45799             text: 'Start a bulleted list.',
45800             cls: 'x-html-editor-tip'
45801         },
45802         insertorderedlist : {
45803             title: 'Numbered List',
45804             text: 'Start a numbered list.',
45805             cls: 'x-html-editor-tip'
45806         },
45807         createlink : {
45808             title: 'Hyperlink',
45809             text: 'Make the selected text a hyperlink.',
45810             cls: 'x-html-editor-tip'
45811         },
45812         sourceedit : {
45813             title: 'Source Edit',
45814             text: 'Switch to source editing mode.',
45815             cls: 'x-html-editor-tip'
45816         }
45817     },
45818     // private
45819     onDestroy : function(){
45820         if(this.rendered){
45821             
45822             this.tb.items.each(function(item){
45823                 if(item.menu){
45824                     item.menu.removeAll();
45825                     if(item.menu.el){
45826                         item.menu.el.destroy();
45827                     }
45828                 }
45829                 item.destroy();
45830             });
45831              
45832         }
45833     },
45834     onFirstFocus: function() {
45835         this.tb.items.each(function(item){
45836            item.enable();
45837         });
45838     }
45839 });
45840
45841
45842
45843
45844 // <script type="text/javascript">
45845 /*
45846  * Based on
45847  * Ext JS Library 1.1.1
45848  * Copyright(c) 2006-2007, Ext JS, LLC.
45849  *  
45850  
45851  */
45852
45853  
45854 /**
45855  * @class Roo.form.HtmlEditor.ToolbarContext
45856  * Context Toolbar
45857  * 
45858  * Usage:
45859  *
45860  new Roo.form.HtmlEditor({
45861     ....
45862     toolbars : [
45863         { xtype: 'ToolbarStandard', styles : {} }
45864         { xtype: 'ToolbarContext', disable : {} }
45865     ]
45866 })
45867
45868      
45869  * 
45870  * @config : {Object} disable List of elements to disable.. (not done yet.)
45871  * @config : {Object} styles  Map of styles available.
45872  * 
45873  */
45874
45875 Roo.form.HtmlEditor.ToolbarContext = function(config)
45876 {
45877     
45878     Roo.apply(this, config);
45879     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45880     // dont call parent... till later.
45881     this.styles = this.styles || {};
45882 }
45883
45884  
45885
45886 Roo.form.HtmlEditor.ToolbarContext.types = {
45887     'IMG' : {
45888         width : {
45889             title: "Width",
45890             width: 40
45891         },
45892         height:  {
45893             title: "Height",
45894             width: 40
45895         },
45896         align: {
45897             title: "Align",
45898             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
45899             width : 80
45900             
45901         },
45902         border: {
45903             title: "Border",
45904             width: 40
45905         },
45906         alt: {
45907             title: "Alt",
45908             width: 120
45909         },
45910         src : {
45911             title: "Src",
45912             width: 220
45913         }
45914         
45915     },
45916     'A' : {
45917         name : {
45918             title: "Name",
45919             width: 50
45920         },
45921         target:  {
45922             title: "Target",
45923             width: 120
45924         },
45925         href:  {
45926             title: "Href",
45927             width: 220
45928         } // border?
45929         
45930     },
45931     'TABLE' : {
45932         rows : {
45933             title: "Rows",
45934             width: 20
45935         },
45936         cols : {
45937             title: "Cols",
45938             width: 20
45939         },
45940         width : {
45941             title: "Width",
45942             width: 40
45943         },
45944         height : {
45945             title: "Height",
45946             width: 40
45947         },
45948         border : {
45949             title: "Border",
45950             width: 20
45951         }
45952     },
45953     'TD' : {
45954         width : {
45955             title: "Width",
45956             width: 40
45957         },
45958         height : {
45959             title: "Height",
45960             width: 40
45961         },   
45962         align: {
45963             title: "Align",
45964             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
45965             width: 80
45966         },
45967         valign: {
45968             title: "Valign",
45969             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
45970             width: 80
45971         },
45972         colspan: {
45973             title: "Colspan",
45974             width: 20
45975             
45976         },
45977          'font-family'  : {
45978             title : "Font",
45979             style : 'fontFamily',
45980             displayField: 'display',
45981             optname : 'font-family',
45982             width: 140
45983         }
45984     },
45985     'INPUT' : {
45986         name : {
45987             title: "name",
45988             width: 120
45989         },
45990         value : {
45991             title: "Value",
45992             width: 120
45993         },
45994         width : {
45995             title: "Width",
45996             width: 40
45997         }
45998     },
45999     'LABEL' : {
46000         'for' : {
46001             title: "For",
46002             width: 120
46003         }
46004     },
46005     'TEXTAREA' : {
46006           name : {
46007             title: "name",
46008             width: 120
46009         },
46010         rows : {
46011             title: "Rows",
46012             width: 20
46013         },
46014         cols : {
46015             title: "Cols",
46016             width: 20
46017         }
46018     },
46019     'SELECT' : {
46020         name : {
46021             title: "name",
46022             width: 120
46023         },
46024         selectoptions : {
46025             title: "Options",
46026             width: 200
46027         }
46028     },
46029     
46030     // should we really allow this??
46031     // should this just be 
46032     'BODY' : {
46033         title : {
46034             title: "Title",
46035             width: 200,
46036             disabled : true
46037         }
46038     },
46039     'SPAN' : {
46040         'font-family'  : {
46041             title : "Font",
46042             style : 'fontFamily',
46043             displayField: 'display',
46044             optname : 'font-family',
46045             width: 140
46046         }
46047     },
46048     'DIV' : {
46049         'font-family'  : {
46050             title : "Font",
46051             style : 'fontFamily',
46052             displayField: 'display',
46053             optname : 'font-family',
46054             width: 140
46055         }
46056     },
46057      'P' : {
46058         'font-family'  : {
46059             title : "Font",
46060             style : 'fontFamily',
46061             displayField: 'display',
46062             optname : 'font-family',
46063             width: 140
46064         }
46065     },
46066     
46067     '*' : {
46068         // empty..
46069     }
46070
46071 };
46072
46073 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
46074 Roo.form.HtmlEditor.ToolbarContext.stores = false;
46075
46076 Roo.form.HtmlEditor.ToolbarContext.options = {
46077         'font-family'  : [ 
46078                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
46079                 [ 'Courier New', 'Courier New'],
46080                 [ 'Tahoma', 'Tahoma'],
46081                 [ 'Times New Roman,serif', 'Times'],
46082                 [ 'Verdana','Verdana' ]
46083         ]
46084 };
46085
46086 // fixme - these need to be configurable..
46087  
46088
46089 //Roo.form.HtmlEditor.ToolbarContext.types
46090
46091
46092 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
46093     
46094     tb: false,
46095     
46096     rendered: false,
46097     
46098     editor : false,
46099     editorcore : false,
46100     /**
46101      * @cfg {Object} disable  List of toolbar elements to disable
46102          
46103      */
46104     disable : false,
46105     /**
46106      * @cfg {Object} styles List of styles 
46107      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46108      *
46109      * These must be defined in the page, so they get rendered correctly..
46110      * .headline { }
46111      * TD.underline { }
46112      * 
46113      */
46114     styles : false,
46115     
46116     options: false,
46117     
46118     toolbars : false,
46119     
46120     init : function(editor)
46121     {
46122         this.editor = editor;
46123         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46124         var editorcore = this.editorcore;
46125         
46126         var fid = editorcore.frameId;
46127         var etb = this;
46128         function btn(id, toggle, handler){
46129             var xid = fid + '-'+ id ;
46130             return {
46131                 id : xid,
46132                 cmd : id,
46133                 cls : 'x-btn-icon x-edit-'+id,
46134                 enableToggle:toggle !== false,
46135                 scope: editorcore, // was editor...
46136                 handler:handler||editorcore.relayBtnCmd,
46137                 clickEvent:'mousedown',
46138                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46139                 tabIndex:-1
46140             };
46141         }
46142         // create a new element.
46143         var wdiv = editor.wrap.createChild({
46144                 tag: 'div'
46145             }, editor.wrap.dom.firstChild.nextSibling, true);
46146         
46147         // can we do this more than once??
46148         
46149          // stop form submits
46150       
46151  
46152         // disable everything...
46153         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46154         this.toolbars = {};
46155            
46156         for (var i in  ty) {
46157           
46158             this.toolbars[i] = this.buildToolbar(ty[i],i);
46159         }
46160         this.tb = this.toolbars.BODY;
46161         this.tb.el.show();
46162         this.buildFooter();
46163         this.footer.show();
46164         editor.on('hide', function( ) { this.footer.hide() }, this);
46165         editor.on('show', function( ) { this.footer.show() }, this);
46166         
46167          
46168         this.rendered = true;
46169         
46170         // the all the btns;
46171         editor.on('editorevent', this.updateToolbar, this);
46172         // other toolbars need to implement this..
46173         //editor.on('editmodechange', this.updateToolbar, this);
46174     },
46175     
46176     
46177     
46178     /**
46179      * Protected method that will not generally be called directly. It triggers
46180      * a toolbar update by reading the markup state of the current selection in the editor.
46181      *
46182      * Note you can force an update by calling on('editorevent', scope, false)
46183      */
46184     updateToolbar: function(editor,ev,sel){
46185
46186         //Roo.log(ev);
46187         // capture mouse up - this is handy for selecting images..
46188         // perhaps should go somewhere else...
46189         if(!this.editorcore.activated){
46190              this.editor.onFirstFocus();
46191             return;
46192         }
46193         
46194         
46195         
46196         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46197         // selectNode - might want to handle IE?
46198         if (ev &&
46199             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46200             ev.target && ev.target.tagName == 'IMG') {
46201             // they have click on an image...
46202             // let's see if we can change the selection...
46203             sel = ev.target;
46204          
46205               var nodeRange = sel.ownerDocument.createRange();
46206             try {
46207                 nodeRange.selectNode(sel);
46208             } catch (e) {
46209                 nodeRange.selectNodeContents(sel);
46210             }
46211             //nodeRange.collapse(true);
46212             var s = this.editorcore.win.getSelection();
46213             s.removeAllRanges();
46214             s.addRange(nodeRange);
46215         }  
46216         
46217       
46218         var updateFooter = sel ? false : true;
46219         
46220         
46221         var ans = this.editorcore.getAllAncestors();
46222         
46223         // pick
46224         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46225         
46226         if (!sel) { 
46227             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46228             sel = sel ? sel : this.editorcore.doc.body;
46229             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46230             
46231         }
46232         // pick a menu that exists..
46233         var tn = sel.tagName.toUpperCase();
46234         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46235         
46236         tn = sel.tagName.toUpperCase();
46237         
46238         var lastSel = this.tb.selectedNode;
46239         
46240         this.tb.selectedNode = sel;
46241         
46242         // if current menu does not match..
46243         
46244         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46245                 
46246             this.tb.el.hide();
46247             ///console.log("show: " + tn);
46248             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46249             this.tb.el.show();
46250             // update name
46251             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46252             
46253             
46254             // update attributes
46255             if (this.tb.fields) {
46256                 this.tb.fields.each(function(e) {
46257                     if (e.stylename) {
46258                         e.setValue(sel.style[e.stylename]);
46259                         return;
46260                     } 
46261                    e.setValue(sel.getAttribute(e.attrname));
46262                 });
46263             }
46264             
46265             var hasStyles = false;
46266             for(var i in this.styles) {
46267                 hasStyles = true;
46268                 break;
46269             }
46270             
46271             // update styles
46272             if (hasStyles) { 
46273                 var st = this.tb.fields.item(0);
46274                 
46275                 st.store.removeAll();
46276                
46277                 
46278                 var cn = sel.className.split(/\s+/);
46279                 
46280                 var avs = [];
46281                 if (this.styles['*']) {
46282                     
46283                     Roo.each(this.styles['*'], function(v) {
46284                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46285                     });
46286                 }
46287                 if (this.styles[tn]) { 
46288                     Roo.each(this.styles[tn], function(v) {
46289                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46290                     });
46291                 }
46292                 
46293                 st.store.loadData(avs);
46294                 st.collapse();
46295                 st.setValue(cn);
46296             }
46297             // flag our selected Node.
46298             this.tb.selectedNode = sel;
46299            
46300            
46301             Roo.menu.MenuMgr.hideAll();
46302
46303         }
46304         
46305         if (!updateFooter) {
46306             //this.footDisp.dom.innerHTML = ''; 
46307             return;
46308         }
46309         // update the footer
46310         //
46311         var html = '';
46312         
46313         this.footerEls = ans.reverse();
46314         Roo.each(this.footerEls, function(a,i) {
46315             if (!a) { return; }
46316             html += html.length ? ' &gt; '  :  '';
46317             
46318             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46319             
46320         });
46321        
46322         // 
46323         var sz = this.footDisp.up('td').getSize();
46324         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46325         this.footDisp.dom.style.marginLeft = '5px';
46326         
46327         this.footDisp.dom.style.overflow = 'hidden';
46328         
46329         this.footDisp.dom.innerHTML = html;
46330             
46331         //this.editorsyncValue();
46332     },
46333      
46334     
46335    
46336        
46337     // private
46338     onDestroy : function(){
46339         if(this.rendered){
46340             
46341             this.tb.items.each(function(item){
46342                 if(item.menu){
46343                     item.menu.removeAll();
46344                     if(item.menu.el){
46345                         item.menu.el.destroy();
46346                     }
46347                 }
46348                 item.destroy();
46349             });
46350              
46351         }
46352     },
46353     onFirstFocus: function() {
46354         // need to do this for all the toolbars..
46355         this.tb.items.each(function(item){
46356            item.enable();
46357         });
46358     },
46359     buildToolbar: function(tlist, nm)
46360     {
46361         var editor = this.editor;
46362         var editorcore = this.editorcore;
46363          // create a new element.
46364         var wdiv = editor.wrap.createChild({
46365                 tag: 'div'
46366             }, editor.wrap.dom.firstChild.nextSibling, true);
46367         
46368        
46369         var tb = new Roo.Toolbar(wdiv);
46370         // add the name..
46371         
46372         tb.add(nm+ ":&nbsp;");
46373         
46374         var styles = [];
46375         for(var i in this.styles) {
46376             styles.push(i);
46377         }
46378         
46379         // styles...
46380         if (styles && styles.length) {
46381             
46382             // this needs a multi-select checkbox...
46383             tb.addField( new Roo.form.ComboBox({
46384                 store: new Roo.data.SimpleStore({
46385                     id : 'val',
46386                     fields: ['val', 'selected'],
46387                     data : [] 
46388                 }),
46389                 name : '-roo-edit-className',
46390                 attrname : 'className',
46391                 displayField: 'val',
46392                 typeAhead: false,
46393                 mode: 'local',
46394                 editable : false,
46395                 triggerAction: 'all',
46396                 emptyText:'Select Style',
46397                 selectOnFocus:true,
46398                 width: 130,
46399                 listeners : {
46400                     'select': function(c, r, i) {
46401                         // initial support only for on class per el..
46402                         tb.selectedNode.className =  r ? r.get('val') : '';
46403                         editorcore.syncValue();
46404                     }
46405                 }
46406     
46407             }));
46408         }
46409         
46410         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46411         var tbops = tbc.options;
46412         
46413         for (var i in tlist) {
46414             
46415             var item = tlist[i];
46416             tb.add(item.title + ":&nbsp;");
46417             
46418             
46419             //optname == used so you can configure the options available..
46420             var opts = item.opts ? item.opts : false;
46421             if (item.optname) {
46422                 opts = tbops[item.optname];
46423            
46424             }
46425             
46426             if (opts) {
46427                 // opts == pulldown..
46428                 tb.addField( new Roo.form.ComboBox({
46429                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
46430                         id : 'val',
46431                         fields: ['val', 'display'],
46432                         data : opts  
46433                     }),
46434                     name : '-roo-edit-' + i,
46435                     attrname : i,
46436                     stylename : item.style ? item.style : false,
46437                     displayField: item.displayField ? item.displayField : 'val',
46438                     valueField :  'val',
46439                     typeAhead: false,
46440                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
46441                     editable : false,
46442                     triggerAction: 'all',
46443                     emptyText:'Select',
46444                     selectOnFocus:true,
46445                     width: item.width ? item.width  : 130,
46446                     listeners : {
46447                         'select': function(c, r, i) {
46448                             if (c.stylename) {
46449                                 tb.selectedNode.style[c.stylename] =  r.get('val');
46450                                 return;
46451                             }
46452                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
46453                         }
46454                     }
46455
46456                 }));
46457                 continue;
46458                     
46459                  
46460                 
46461                 tb.addField( new Roo.form.TextField({
46462                     name: i,
46463                     width: 100,
46464                     //allowBlank:false,
46465                     value: ''
46466                 }));
46467                 continue;
46468             }
46469             tb.addField( new Roo.form.TextField({
46470                 name: '-roo-edit-' + i,
46471                 attrname : i,
46472                 
46473                 width: item.width,
46474                 //allowBlank:true,
46475                 value: '',
46476                 listeners: {
46477                     'change' : function(f, nv, ov) {
46478                         tb.selectedNode.setAttribute(f.attrname, nv);
46479                         editorcore.syncValue();
46480                     }
46481                 }
46482             }));
46483              
46484         }
46485         
46486         var _this = this;
46487         
46488         if(nm == 'BODY'){
46489             tb.addSeparator();
46490         
46491             tb.addButton( {
46492                 text: 'Stylesheets',
46493
46494                 listeners : {
46495                     click : function ()
46496                     {
46497                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
46498                     }
46499                 }
46500             });
46501         }
46502         
46503         tb.addFill();
46504         tb.addButton( {
46505             text: 'Remove Tag',
46506     
46507             listeners : {
46508                 click : function ()
46509                 {
46510                     // remove
46511                     // undo does not work.
46512                      
46513                     var sn = tb.selectedNode;
46514                     
46515                     var pn = sn.parentNode;
46516                     
46517                     var stn =  sn.childNodes[0];
46518                     var en = sn.childNodes[sn.childNodes.length - 1 ];
46519                     while (sn.childNodes.length) {
46520                         var node = sn.childNodes[0];
46521                         sn.removeChild(node);
46522                         //Roo.log(node);
46523                         pn.insertBefore(node, sn);
46524                         
46525                     }
46526                     pn.removeChild(sn);
46527                     var range = editorcore.createRange();
46528         
46529                     range.setStart(stn,0);
46530                     range.setEnd(en,0); //????
46531                     //range.selectNode(sel);
46532                     
46533                     
46534                     var selection = editorcore.getSelection();
46535                     selection.removeAllRanges();
46536                     selection.addRange(range);
46537                     
46538                     
46539                     
46540                     //_this.updateToolbar(null, null, pn);
46541                     _this.updateToolbar(null, null, null);
46542                     _this.footDisp.dom.innerHTML = ''; 
46543                 }
46544             }
46545             
46546                     
46547                 
46548             
46549         });
46550         
46551         
46552         tb.el.on('click', function(e){
46553             e.preventDefault(); // what does this do?
46554         });
46555         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
46556         tb.el.hide();
46557         tb.name = nm;
46558         // dont need to disable them... as they will get hidden
46559         return tb;
46560          
46561         
46562     },
46563     buildFooter : function()
46564     {
46565         
46566         var fel = this.editor.wrap.createChild();
46567         this.footer = new Roo.Toolbar(fel);
46568         // toolbar has scrolly on left / right?
46569         var footDisp= new Roo.Toolbar.Fill();
46570         var _t = this;
46571         this.footer.add(
46572             {
46573                 text : '&lt;',
46574                 xtype: 'Button',
46575                 handler : function() {
46576                     _t.footDisp.scrollTo('left',0,true)
46577                 }
46578             }
46579         );
46580         this.footer.add( footDisp );
46581         this.footer.add( 
46582             {
46583                 text : '&gt;',
46584                 xtype: 'Button',
46585                 handler : function() {
46586                     // no animation..
46587                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
46588                 }
46589             }
46590         );
46591         var fel = Roo.get(footDisp.el);
46592         fel.addClass('x-editor-context');
46593         this.footDispWrap = fel; 
46594         this.footDispWrap.overflow  = 'hidden';
46595         
46596         this.footDisp = fel.createChild();
46597         this.footDispWrap.on('click', this.onContextClick, this)
46598         
46599         
46600     },
46601     onContextClick : function (ev,dom)
46602     {
46603         ev.preventDefault();
46604         var  cn = dom.className;
46605         //Roo.log(cn);
46606         if (!cn.match(/x-ed-loc-/)) {
46607             return;
46608         }
46609         var n = cn.split('-').pop();
46610         var ans = this.footerEls;
46611         var sel = ans[n];
46612         
46613          // pick
46614         var range = this.editorcore.createRange();
46615         
46616         range.selectNodeContents(sel);
46617         //range.selectNode(sel);
46618         
46619         
46620         var selection = this.editorcore.getSelection();
46621         selection.removeAllRanges();
46622         selection.addRange(range);
46623         
46624         
46625         
46626         this.updateToolbar(null, null, sel);
46627         
46628         
46629     }
46630     
46631     
46632     
46633     
46634     
46635 });
46636
46637
46638
46639
46640
46641 /*
46642  * Based on:
46643  * Ext JS Library 1.1.1
46644  * Copyright(c) 2006-2007, Ext JS, LLC.
46645  *
46646  * Originally Released Under LGPL - original licence link has changed is not relivant.
46647  *
46648  * Fork - LGPL
46649  * <script type="text/javascript">
46650  */
46651  
46652 /**
46653  * @class Roo.form.BasicForm
46654  * @extends Roo.util.Observable
46655  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
46656  * @constructor
46657  * @param {String/HTMLElement/Roo.Element} el The form element or its id
46658  * @param {Object} config Configuration options
46659  */
46660 Roo.form.BasicForm = function(el, config){
46661     this.allItems = [];
46662     this.childForms = [];
46663     Roo.apply(this, config);
46664     /*
46665      * The Roo.form.Field items in this form.
46666      * @type MixedCollection
46667      */
46668      
46669      
46670     this.items = new Roo.util.MixedCollection(false, function(o){
46671         return o.id || (o.id = Roo.id());
46672     });
46673     this.addEvents({
46674         /**
46675          * @event beforeaction
46676          * Fires before any action is performed. Return false to cancel the action.
46677          * @param {Form} this
46678          * @param {Action} action The action to be performed
46679          */
46680         beforeaction: true,
46681         /**
46682          * @event actionfailed
46683          * Fires when an action fails.
46684          * @param {Form} this
46685          * @param {Action} action The action that failed
46686          */
46687         actionfailed : true,
46688         /**
46689          * @event actioncomplete
46690          * Fires when an action is completed.
46691          * @param {Form} this
46692          * @param {Action} action The action that completed
46693          */
46694         actioncomplete : true
46695     });
46696     if(el){
46697         this.initEl(el);
46698     }
46699     Roo.form.BasicForm.superclass.constructor.call(this);
46700 };
46701
46702 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
46703     /**
46704      * @cfg {String} method
46705      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
46706      */
46707     /**
46708      * @cfg {DataReader} reader
46709      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
46710      * This is optional as there is built-in support for processing JSON.
46711      */
46712     /**
46713      * @cfg {DataReader} errorReader
46714      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
46715      * This is completely optional as there is built-in support for processing JSON.
46716      */
46717     /**
46718      * @cfg {String} url
46719      * The URL to use for form actions if one isn't supplied in the action options.
46720      */
46721     /**
46722      * @cfg {Boolean} fileUpload
46723      * Set to true if this form is a file upload.
46724      */
46725      
46726     /**
46727      * @cfg {Object} baseParams
46728      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
46729      */
46730      /**
46731      
46732     /**
46733      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
46734      */
46735     timeout: 30,
46736
46737     // private
46738     activeAction : null,
46739
46740     /**
46741      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
46742      * or setValues() data instead of when the form was first created.
46743      */
46744     trackResetOnLoad : false,
46745     
46746     
46747     /**
46748      * childForms - used for multi-tab forms
46749      * @type {Array}
46750      */
46751     childForms : false,
46752     
46753     /**
46754      * allItems - full list of fields.
46755      * @type {Array}
46756      */
46757     allItems : false,
46758     
46759     /**
46760      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
46761      * element by passing it or its id or mask the form itself by passing in true.
46762      * @type Mixed
46763      */
46764     waitMsgTarget : false,
46765
46766     // private
46767     initEl : function(el){
46768         this.el = Roo.get(el);
46769         this.id = this.el.id || Roo.id();
46770         this.el.on('submit', this.onSubmit, this);
46771         this.el.addClass('x-form');
46772     },
46773
46774     // private
46775     onSubmit : function(e){
46776         e.stopEvent();
46777     },
46778
46779     /**
46780      * Returns true if client-side validation on the form is successful.
46781      * @return Boolean
46782      */
46783     isValid : function(){
46784         var valid = true;
46785         this.items.each(function(f){
46786            if(!f.validate()){
46787                valid = false;
46788            }
46789         });
46790         return valid;
46791     },
46792
46793     /**
46794      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
46795      * @return Boolean
46796      */
46797     isDirty : function(){
46798         var dirty = false;
46799         this.items.each(function(f){
46800            if(f.isDirty()){
46801                dirty = true;
46802                return false;
46803            }
46804         });
46805         return dirty;
46806     },
46807     
46808     /**
46809      * Returns true if any fields in this form have changed since their original load. (New version)
46810      * @return Boolean
46811      */
46812     
46813     hasChanged : function()
46814     {
46815         var dirty = false;
46816         this.items.each(function(f){
46817            if(f.hasChanged()){
46818                dirty = true;
46819                return false;
46820            }
46821         });
46822         return dirty;
46823         
46824     },
46825     /**
46826      * Resets all hasChanged to 'false' -
46827      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
46828      * So hasChanged storage is only to be used for this purpose
46829      * @return Boolean
46830      */
46831     resetHasChanged : function()
46832     {
46833         this.items.each(function(f){
46834            f.resetHasChanged();
46835         });
46836         
46837     },
46838     
46839     
46840     /**
46841      * Performs a predefined action (submit or load) or custom actions you define on this form.
46842      * @param {String} actionName The name of the action type
46843      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
46844      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
46845      * accept other config options):
46846      * <pre>
46847 Property          Type             Description
46848 ----------------  ---------------  ----------------------------------------------------------------------------------
46849 url               String           The url for the action (defaults to the form's url)
46850 method            String           The form method to use (defaults to the form's method, or POST if not defined)
46851 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
46852 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
46853                                    validate the form on the client (defaults to false)
46854      * </pre>
46855      * @return {BasicForm} this
46856      */
46857     doAction : function(action, options){
46858         if(typeof action == 'string'){
46859             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
46860         }
46861         if(this.fireEvent('beforeaction', this, action) !== false){
46862             this.beforeAction(action);
46863             action.run.defer(100, action);
46864         }
46865         return this;
46866     },
46867
46868     /**
46869      * Shortcut to do a submit action.
46870      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46871      * @return {BasicForm} this
46872      */
46873     submit : function(options){
46874         this.doAction('submit', options);
46875         return this;
46876     },
46877
46878     /**
46879      * Shortcut to do a load action.
46880      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46881      * @return {BasicForm} this
46882      */
46883     load : function(options){
46884         this.doAction('load', options);
46885         return this;
46886     },
46887
46888     /**
46889      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
46890      * @param {Record} record The record to edit
46891      * @return {BasicForm} this
46892      */
46893     updateRecord : function(record){
46894         record.beginEdit();
46895         var fs = record.fields;
46896         fs.each(function(f){
46897             var field = this.findField(f.name);
46898             if(field){
46899                 record.set(f.name, field.getValue());
46900             }
46901         }, this);
46902         record.endEdit();
46903         return this;
46904     },
46905
46906     /**
46907      * Loads an Roo.data.Record into this form.
46908      * @param {Record} record The record to load
46909      * @return {BasicForm} this
46910      */
46911     loadRecord : function(record){
46912         this.setValues(record.data);
46913         return this;
46914     },
46915
46916     // private
46917     beforeAction : function(action){
46918         var o = action.options;
46919         
46920        
46921         if(this.waitMsgTarget === true){
46922             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
46923         }else if(this.waitMsgTarget){
46924             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
46925             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
46926         }else {
46927             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
46928         }
46929          
46930     },
46931
46932     // private
46933     afterAction : function(action, success){
46934         this.activeAction = null;
46935         var o = action.options;
46936         
46937         if(this.waitMsgTarget === true){
46938             this.el.unmask();
46939         }else if(this.waitMsgTarget){
46940             this.waitMsgTarget.unmask();
46941         }else{
46942             Roo.MessageBox.updateProgress(1);
46943             Roo.MessageBox.hide();
46944         }
46945          
46946         if(success){
46947             if(o.reset){
46948                 this.reset();
46949             }
46950             Roo.callback(o.success, o.scope, [this, action]);
46951             this.fireEvent('actioncomplete', this, action);
46952             
46953         }else{
46954             
46955             // failure condition..
46956             // we have a scenario where updates need confirming.
46957             // eg. if a locking scenario exists..
46958             // we look for { errors : { needs_confirm : true }} in the response.
46959             if (
46960                 (typeof(action.result) != 'undefined')  &&
46961                 (typeof(action.result.errors) != 'undefined')  &&
46962                 (typeof(action.result.errors.needs_confirm) != 'undefined')
46963            ){
46964                 var _t = this;
46965                 Roo.MessageBox.confirm(
46966                     "Change requires confirmation",
46967                     action.result.errorMsg,
46968                     function(r) {
46969                         if (r != 'yes') {
46970                             return;
46971                         }
46972                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
46973                     }
46974                     
46975                 );
46976                 
46977                 
46978                 
46979                 return;
46980             }
46981             
46982             Roo.callback(o.failure, o.scope, [this, action]);
46983             // show an error message if no failed handler is set..
46984             if (!this.hasListener('actionfailed')) {
46985                 Roo.MessageBox.alert("Error",
46986                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
46987                         action.result.errorMsg :
46988                         "Saving Failed, please check your entries or try again"
46989                 );
46990             }
46991             
46992             this.fireEvent('actionfailed', this, action);
46993         }
46994         
46995     },
46996
46997     /**
46998      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
46999      * @param {String} id The value to search for
47000      * @return Field
47001      */
47002     findField : function(id){
47003         var field = this.items.get(id);
47004         if(!field){
47005             this.items.each(function(f){
47006                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
47007                     field = f;
47008                     return false;
47009                 }
47010             });
47011         }
47012         return field || null;
47013     },
47014
47015     /**
47016      * Add a secondary form to this one, 
47017      * Used to provide tabbed forms. One form is primary, with hidden values 
47018      * which mirror the elements from the other forms.
47019      * 
47020      * @param {Roo.form.Form} form to add.
47021      * 
47022      */
47023     addForm : function(form)
47024     {
47025        
47026         if (this.childForms.indexOf(form) > -1) {
47027             // already added..
47028             return;
47029         }
47030         this.childForms.push(form);
47031         var n = '';
47032         Roo.each(form.allItems, function (fe) {
47033             
47034             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
47035             if (this.findField(n)) { // already added..
47036                 return;
47037             }
47038             var add = new Roo.form.Hidden({
47039                 name : n
47040             });
47041             add.render(this.el);
47042             
47043             this.add( add );
47044         }, this);
47045         
47046     },
47047     /**
47048      * Mark fields in this form invalid in bulk.
47049      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
47050      * @return {BasicForm} this
47051      */
47052     markInvalid : function(errors){
47053         if(errors instanceof Array){
47054             for(var i = 0, len = errors.length; i < len; i++){
47055                 var fieldError = errors[i];
47056                 var f = this.findField(fieldError.id);
47057                 if(f){
47058                     f.markInvalid(fieldError.msg);
47059                 }
47060             }
47061         }else{
47062             var field, id;
47063             for(id in errors){
47064                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
47065                     field.markInvalid(errors[id]);
47066                 }
47067             }
47068         }
47069         Roo.each(this.childForms || [], function (f) {
47070             f.markInvalid(errors);
47071         });
47072         
47073         return this;
47074     },
47075
47076     /**
47077      * Set values for fields in this form in bulk.
47078      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
47079      * @return {BasicForm} this
47080      */
47081     setValues : function(values){
47082         if(values instanceof Array){ // array of objects
47083             for(var i = 0, len = values.length; i < len; i++){
47084                 var v = values[i];
47085                 var f = this.findField(v.id);
47086                 if(f){
47087                     f.setValue(v.value);
47088                     if(this.trackResetOnLoad){
47089                         f.originalValue = f.getValue();
47090                     }
47091                 }
47092             }
47093         }else{ // object hash
47094             var field, id;
47095             for(id in values){
47096                 if(typeof values[id] != 'function' && (field = this.findField(id))){
47097                     
47098                     if (field.setFromData && 
47099                         field.valueField && 
47100                         field.displayField &&
47101                         // combos' with local stores can 
47102                         // be queried via setValue()
47103                         // to set their value..
47104                         (field.store && !field.store.isLocal)
47105                         ) {
47106                         // it's a combo
47107                         var sd = { };
47108                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
47109                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47110                         field.setFromData(sd);
47111                         
47112                     } else {
47113                         field.setValue(values[id]);
47114                     }
47115                     
47116                     
47117                     if(this.trackResetOnLoad){
47118                         field.originalValue = field.getValue();
47119                     }
47120                 }
47121             }
47122         }
47123         this.resetHasChanged();
47124         
47125         
47126         Roo.each(this.childForms || [], function (f) {
47127             f.setValues(values);
47128             f.resetHasChanged();
47129         });
47130                 
47131         return this;
47132     },
47133
47134     /**
47135      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47136      * they are returned as an array.
47137      * @param {Boolean} asString
47138      * @return {Object}
47139      */
47140     getValues : function(asString){
47141         if (this.childForms) {
47142             // copy values from the child forms
47143             Roo.each(this.childForms, function (f) {
47144                 this.setValues(f.getValues());
47145             }, this);
47146         }
47147         
47148         
47149         
47150         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47151         if(asString === true){
47152             return fs;
47153         }
47154         return Roo.urlDecode(fs);
47155     },
47156     
47157     /**
47158      * Returns the fields in this form as an object with key/value pairs. 
47159      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47160      * @return {Object}
47161      */
47162     getFieldValues : function(with_hidden)
47163     {
47164         if (this.childForms) {
47165             // copy values from the child forms
47166             // should this call getFieldValues - probably not as we do not currently copy
47167             // hidden fields when we generate..
47168             Roo.each(this.childForms, function (f) {
47169                 this.setValues(f.getValues());
47170             }, this);
47171         }
47172         
47173         var ret = {};
47174         this.items.each(function(f){
47175             if (!f.getName()) {
47176                 return;
47177             }
47178             var v = f.getValue();
47179             if (f.inputType =='radio') {
47180                 if (typeof(ret[f.getName()]) == 'undefined') {
47181                     ret[f.getName()] = ''; // empty..
47182                 }
47183                 
47184                 if (!f.el.dom.checked) {
47185                     return;
47186                     
47187                 }
47188                 v = f.el.dom.value;
47189                 
47190             }
47191             
47192             // not sure if this supported any more..
47193             if ((typeof(v) == 'object') && f.getRawValue) {
47194                 v = f.getRawValue() ; // dates..
47195             }
47196             // combo boxes where name != hiddenName...
47197             if (f.name != f.getName()) {
47198                 ret[f.name] = f.getRawValue();
47199             }
47200             ret[f.getName()] = v;
47201         });
47202         
47203         return ret;
47204     },
47205
47206     /**
47207      * Clears all invalid messages in this form.
47208      * @return {BasicForm} this
47209      */
47210     clearInvalid : function(){
47211         this.items.each(function(f){
47212            f.clearInvalid();
47213         });
47214         
47215         Roo.each(this.childForms || [], function (f) {
47216             f.clearInvalid();
47217         });
47218         
47219         
47220         return this;
47221     },
47222
47223     /**
47224      * Resets this form.
47225      * @return {BasicForm} this
47226      */
47227     reset : function(){
47228         this.items.each(function(f){
47229             f.reset();
47230         });
47231         
47232         Roo.each(this.childForms || [], function (f) {
47233             f.reset();
47234         });
47235         this.resetHasChanged();
47236         
47237         return this;
47238     },
47239
47240     /**
47241      * Add Roo.form components to this form.
47242      * @param {Field} field1
47243      * @param {Field} field2 (optional)
47244      * @param {Field} etc (optional)
47245      * @return {BasicForm} this
47246      */
47247     add : function(){
47248         this.items.addAll(Array.prototype.slice.call(arguments, 0));
47249         return this;
47250     },
47251
47252
47253     /**
47254      * Removes a field from the items collection (does NOT remove its markup).
47255      * @param {Field} field
47256      * @return {BasicForm} this
47257      */
47258     remove : function(field){
47259         this.items.remove(field);
47260         return this;
47261     },
47262
47263     /**
47264      * Looks at the fields in this form, checks them for an id attribute,
47265      * and calls applyTo on the existing dom element with that id.
47266      * @return {BasicForm} this
47267      */
47268     render : function(){
47269         this.items.each(function(f){
47270             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47271                 f.applyTo(f.id);
47272             }
47273         });
47274         return this;
47275     },
47276
47277     /**
47278      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47279      * @param {Object} values
47280      * @return {BasicForm} this
47281      */
47282     applyToFields : function(o){
47283         this.items.each(function(f){
47284            Roo.apply(f, o);
47285         });
47286         return this;
47287     },
47288
47289     /**
47290      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47291      * @param {Object} values
47292      * @return {BasicForm} this
47293      */
47294     applyIfToFields : function(o){
47295         this.items.each(function(f){
47296            Roo.applyIf(f, o);
47297         });
47298         return this;
47299     }
47300 });
47301
47302 // back compat
47303 Roo.BasicForm = Roo.form.BasicForm;/*
47304  * Based on:
47305  * Ext JS Library 1.1.1
47306  * Copyright(c) 2006-2007, Ext JS, LLC.
47307  *
47308  * Originally Released Under LGPL - original licence link has changed is not relivant.
47309  *
47310  * Fork - LGPL
47311  * <script type="text/javascript">
47312  */
47313
47314 /**
47315  * @class Roo.form.Form
47316  * @extends Roo.form.BasicForm
47317  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
47318  * @constructor
47319  * @param {Object} config Configuration options
47320  */
47321 Roo.form.Form = function(config){
47322     var xitems =  [];
47323     if (config.items) {
47324         xitems = config.items;
47325         delete config.items;
47326     }
47327    
47328     
47329     Roo.form.Form.superclass.constructor.call(this, null, config);
47330     this.url = this.url || this.action;
47331     if(!this.root){
47332         this.root = new Roo.form.Layout(Roo.applyIf({
47333             id: Roo.id()
47334         }, config));
47335     }
47336     this.active = this.root;
47337     /**
47338      * Array of all the buttons that have been added to this form via {@link addButton}
47339      * @type Array
47340      */
47341     this.buttons = [];
47342     this.allItems = [];
47343     this.addEvents({
47344         /**
47345          * @event clientvalidation
47346          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
47347          * @param {Form} this
47348          * @param {Boolean} valid true if the form has passed client-side validation
47349          */
47350         clientvalidation: true,
47351         /**
47352          * @event rendered
47353          * Fires when the form is rendered
47354          * @param {Roo.form.Form} form
47355          */
47356         rendered : true
47357     });
47358     
47359     if (this.progressUrl) {
47360             // push a hidden field onto the list of fields..
47361             this.addxtype( {
47362                     xns: Roo.form, 
47363                     xtype : 'Hidden', 
47364                     name : 'UPLOAD_IDENTIFIER' 
47365             });
47366         }
47367         
47368     
47369     Roo.each(xitems, this.addxtype, this);
47370     
47371     
47372     
47373 };
47374
47375 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
47376     /**
47377      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
47378      */
47379     /**
47380      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
47381      */
47382     /**
47383      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
47384      */
47385     buttonAlign:'center',
47386
47387     /**
47388      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
47389      */
47390     minButtonWidth:75,
47391
47392     /**
47393      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
47394      * This property cascades to child containers if not set.
47395      */
47396     labelAlign:'left',
47397
47398     /**
47399      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
47400      * fires a looping event with that state. This is required to bind buttons to the valid
47401      * state using the config value formBind:true on the button.
47402      */
47403     monitorValid : false,
47404
47405     /**
47406      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
47407      */
47408     monitorPoll : 200,
47409     
47410     /**
47411      * @cfg {String} progressUrl - Url to return progress data 
47412      */
47413     
47414     progressUrl : false,
47415   
47416     /**
47417      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
47418      * fields are added and the column is closed. If no fields are passed the column remains open
47419      * until end() is called.
47420      * @param {Object} config The config to pass to the column
47421      * @param {Field} field1 (optional)
47422      * @param {Field} field2 (optional)
47423      * @param {Field} etc (optional)
47424      * @return Column The column container object
47425      */
47426     column : function(c){
47427         var col = new Roo.form.Column(c);
47428         this.start(col);
47429         if(arguments.length > 1){ // duplicate code required because of Opera
47430             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47431             this.end();
47432         }
47433         return col;
47434     },
47435
47436     /**
47437      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
47438      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
47439      * until end() is called.
47440      * @param {Object} config The config to pass to the fieldset
47441      * @param {Field} field1 (optional)
47442      * @param {Field} field2 (optional)
47443      * @param {Field} etc (optional)
47444      * @return FieldSet The fieldset container object
47445      */
47446     fieldset : function(c){
47447         var fs = new Roo.form.FieldSet(c);
47448         this.start(fs);
47449         if(arguments.length > 1){ // duplicate code required because of Opera
47450             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47451             this.end();
47452         }
47453         return fs;
47454     },
47455
47456     /**
47457      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
47458      * fields are added and the container is closed. If no fields are passed the container remains open
47459      * until end() is called.
47460      * @param {Object} config The config to pass to the Layout
47461      * @param {Field} field1 (optional)
47462      * @param {Field} field2 (optional)
47463      * @param {Field} etc (optional)
47464      * @return Layout The container object
47465      */
47466     container : function(c){
47467         var l = new Roo.form.Layout(c);
47468         this.start(l);
47469         if(arguments.length > 1){ // duplicate code required because of Opera
47470             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47471             this.end();
47472         }
47473         return l;
47474     },
47475
47476     /**
47477      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
47478      * @param {Object} container A Roo.form.Layout or subclass of Layout
47479      * @return {Form} this
47480      */
47481     start : function(c){
47482         // cascade label info
47483         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
47484         this.active.stack.push(c);
47485         c.ownerCt = this.active;
47486         this.active = c;
47487         return this;
47488     },
47489
47490     /**
47491      * Closes the current open container
47492      * @return {Form} this
47493      */
47494     end : function(){
47495         if(this.active == this.root){
47496             return this;
47497         }
47498         this.active = this.active.ownerCt;
47499         return this;
47500     },
47501
47502     /**
47503      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
47504      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
47505      * as the label of the field.
47506      * @param {Field} field1
47507      * @param {Field} field2 (optional)
47508      * @param {Field} etc. (optional)
47509      * @return {Form} this
47510      */
47511     add : function(){
47512         this.active.stack.push.apply(this.active.stack, arguments);
47513         this.allItems.push.apply(this.allItems,arguments);
47514         var r = [];
47515         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
47516             if(a[i].isFormField){
47517                 r.push(a[i]);
47518             }
47519         }
47520         if(r.length > 0){
47521             Roo.form.Form.superclass.add.apply(this, r);
47522         }
47523         return this;
47524     },
47525     
47526
47527     
47528     
47529     
47530      /**
47531      * Find any element that has been added to a form, using it's ID or name
47532      * This can include framesets, columns etc. along with regular fields..
47533      * @param {String} id - id or name to find.
47534      
47535      * @return {Element} e - or false if nothing found.
47536      */
47537     findbyId : function(id)
47538     {
47539         var ret = false;
47540         if (!id) {
47541             return ret;
47542         }
47543         Roo.each(this.allItems, function(f){
47544             if (f.id == id || f.name == id ){
47545                 ret = f;
47546                 return false;
47547             }
47548         });
47549         return ret;
47550     },
47551
47552     
47553     
47554     /**
47555      * Render this form into the passed container. This should only be called once!
47556      * @param {String/HTMLElement/Element} container The element this component should be rendered into
47557      * @return {Form} this
47558      */
47559     render : function(ct)
47560     {
47561         
47562         
47563         
47564         ct = Roo.get(ct);
47565         var o = this.autoCreate || {
47566             tag: 'form',
47567             method : this.method || 'POST',
47568             id : this.id || Roo.id()
47569         };
47570         this.initEl(ct.createChild(o));
47571
47572         this.root.render(this.el);
47573         
47574        
47575              
47576         this.items.each(function(f){
47577             f.render('x-form-el-'+f.id);
47578         });
47579
47580         if(this.buttons.length > 0){
47581             // tables are required to maintain order and for correct IE layout
47582             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
47583                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
47584                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
47585             }}, null, true);
47586             var tr = tb.getElementsByTagName('tr')[0];
47587             for(var i = 0, len = this.buttons.length; i < len; i++) {
47588                 var b = this.buttons[i];
47589                 var td = document.createElement('td');
47590                 td.className = 'x-form-btn-td';
47591                 b.render(tr.appendChild(td));
47592             }
47593         }
47594         if(this.monitorValid){ // initialize after render
47595             this.startMonitoring();
47596         }
47597         this.fireEvent('rendered', this);
47598         return this;
47599     },
47600
47601     /**
47602      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
47603      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
47604      * object or a valid Roo.DomHelper element config
47605      * @param {Function} handler The function called when the button is clicked
47606      * @param {Object} scope (optional) The scope of the handler function
47607      * @return {Roo.Button}
47608      */
47609     addButton : function(config, handler, scope){
47610         var bc = {
47611             handler: handler,
47612             scope: scope,
47613             minWidth: this.minButtonWidth,
47614             hideParent:true
47615         };
47616         if(typeof config == "string"){
47617             bc.text = config;
47618         }else{
47619             Roo.apply(bc, config);
47620         }
47621         var btn = new Roo.Button(null, bc);
47622         this.buttons.push(btn);
47623         return btn;
47624     },
47625
47626      /**
47627      * Adds a series of form elements (using the xtype property as the factory method.
47628      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
47629      * @param {Object} config 
47630      */
47631     
47632     addxtype : function()
47633     {
47634         var ar = Array.prototype.slice.call(arguments, 0);
47635         var ret = false;
47636         for(var i = 0; i < ar.length; i++) {
47637             if (!ar[i]) {
47638                 continue; // skip -- if this happends something invalid got sent, we 
47639                 // should ignore it, as basically that interface element will not show up
47640                 // and that should be pretty obvious!!
47641             }
47642             
47643             if (Roo.form[ar[i].xtype]) {
47644                 ar[i].form = this;
47645                 var fe = Roo.factory(ar[i], Roo.form);
47646                 if (!ret) {
47647                     ret = fe;
47648                 }
47649                 fe.form = this;
47650                 if (fe.store) {
47651                     fe.store.form = this;
47652                 }
47653                 if (fe.isLayout) {  
47654                          
47655                     this.start(fe);
47656                     this.allItems.push(fe);
47657                     if (fe.items && fe.addxtype) {
47658                         fe.addxtype.apply(fe, fe.items);
47659                         delete fe.items;
47660                     }
47661                      this.end();
47662                     continue;
47663                 }
47664                 
47665                 
47666                  
47667                 this.add(fe);
47668               //  console.log('adding ' + ar[i].xtype);
47669             }
47670             if (ar[i].xtype == 'Button') {  
47671                 //console.log('adding button');
47672                 //console.log(ar[i]);
47673                 this.addButton(ar[i]);
47674                 this.allItems.push(fe);
47675                 continue;
47676             }
47677             
47678             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
47679                 alert('end is not supported on xtype any more, use items');
47680             //    this.end();
47681             //    //console.log('adding end');
47682             }
47683             
47684         }
47685         return ret;
47686     },
47687     
47688     /**
47689      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
47690      * option "monitorValid"
47691      */
47692     startMonitoring : function(){
47693         if(!this.bound){
47694             this.bound = true;
47695             Roo.TaskMgr.start({
47696                 run : this.bindHandler,
47697                 interval : this.monitorPoll || 200,
47698                 scope: this
47699             });
47700         }
47701     },
47702
47703     /**
47704      * Stops monitoring of the valid state of this form
47705      */
47706     stopMonitoring : function(){
47707         this.bound = false;
47708     },
47709
47710     // private
47711     bindHandler : function(){
47712         if(!this.bound){
47713             return false; // stops binding
47714         }
47715         var valid = true;
47716         this.items.each(function(f){
47717             if(!f.isValid(true)){
47718                 valid = false;
47719                 return false;
47720             }
47721         });
47722         for(var i = 0, len = this.buttons.length; i < len; i++){
47723             var btn = this.buttons[i];
47724             if(btn.formBind === true && btn.disabled === valid){
47725                 btn.setDisabled(!valid);
47726             }
47727         }
47728         this.fireEvent('clientvalidation', this, valid);
47729     }
47730     
47731     
47732     
47733     
47734     
47735     
47736     
47737     
47738 });
47739
47740
47741 // back compat
47742 Roo.Form = Roo.form.Form;
47743 /*
47744  * Based on:
47745  * Ext JS Library 1.1.1
47746  * Copyright(c) 2006-2007, Ext JS, LLC.
47747  *
47748  * Originally Released Under LGPL - original licence link has changed is not relivant.
47749  *
47750  * Fork - LGPL
47751  * <script type="text/javascript">
47752  */
47753
47754 // as we use this in bootstrap.
47755 Roo.namespace('Roo.form');
47756  /**
47757  * @class Roo.form.Action
47758  * Internal Class used to handle form actions
47759  * @constructor
47760  * @param {Roo.form.BasicForm} el The form element or its id
47761  * @param {Object} config Configuration options
47762  */
47763
47764  
47765  
47766 // define the action interface
47767 Roo.form.Action = function(form, options){
47768     this.form = form;
47769     this.options = options || {};
47770 };
47771 /**
47772  * Client Validation Failed
47773  * @const 
47774  */
47775 Roo.form.Action.CLIENT_INVALID = 'client';
47776 /**
47777  * Server Validation Failed
47778  * @const 
47779  */
47780 Roo.form.Action.SERVER_INVALID = 'server';
47781  /**
47782  * Connect to Server Failed
47783  * @const 
47784  */
47785 Roo.form.Action.CONNECT_FAILURE = 'connect';
47786 /**
47787  * Reading Data from Server Failed
47788  * @const 
47789  */
47790 Roo.form.Action.LOAD_FAILURE = 'load';
47791
47792 Roo.form.Action.prototype = {
47793     type : 'default',
47794     failureType : undefined,
47795     response : undefined,
47796     result : undefined,
47797
47798     // interface method
47799     run : function(options){
47800
47801     },
47802
47803     // interface method
47804     success : function(response){
47805
47806     },
47807
47808     // interface method
47809     handleResponse : function(response){
47810
47811     },
47812
47813     // default connection failure
47814     failure : function(response){
47815         
47816         this.response = response;
47817         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47818         this.form.afterAction(this, false);
47819     },
47820
47821     processResponse : function(response){
47822         this.response = response;
47823         if(!response.responseText){
47824             return true;
47825         }
47826         this.result = this.handleResponse(response);
47827         return this.result;
47828     },
47829
47830     // utility functions used internally
47831     getUrl : function(appendParams){
47832         var url = this.options.url || this.form.url || this.form.el.dom.action;
47833         if(appendParams){
47834             var p = this.getParams();
47835             if(p){
47836                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
47837             }
47838         }
47839         return url;
47840     },
47841
47842     getMethod : function(){
47843         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
47844     },
47845
47846     getParams : function(){
47847         var bp = this.form.baseParams;
47848         var p = this.options.params;
47849         if(p){
47850             if(typeof p == "object"){
47851                 p = Roo.urlEncode(Roo.applyIf(p, bp));
47852             }else if(typeof p == 'string' && bp){
47853                 p += '&' + Roo.urlEncode(bp);
47854             }
47855         }else if(bp){
47856             p = Roo.urlEncode(bp);
47857         }
47858         return p;
47859     },
47860
47861     createCallback : function(){
47862         return {
47863             success: this.success,
47864             failure: this.failure,
47865             scope: this,
47866             timeout: (this.form.timeout*1000),
47867             upload: this.form.fileUpload ? this.success : undefined
47868         };
47869     }
47870 };
47871
47872 Roo.form.Action.Submit = function(form, options){
47873     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
47874 };
47875
47876 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
47877     type : 'submit',
47878
47879     haveProgress : false,
47880     uploadComplete : false,
47881     
47882     // uploadProgress indicator.
47883     uploadProgress : function()
47884     {
47885         if (!this.form.progressUrl) {
47886             return;
47887         }
47888         
47889         if (!this.haveProgress) {
47890             Roo.MessageBox.progress("Uploading", "Uploading");
47891         }
47892         if (this.uploadComplete) {
47893            Roo.MessageBox.hide();
47894            return;
47895         }
47896         
47897         this.haveProgress = true;
47898    
47899         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
47900         
47901         var c = new Roo.data.Connection();
47902         c.request({
47903             url : this.form.progressUrl,
47904             params: {
47905                 id : uid
47906             },
47907             method: 'GET',
47908             success : function(req){
47909                //console.log(data);
47910                 var rdata = false;
47911                 var edata;
47912                 try  {
47913                    rdata = Roo.decode(req.responseText)
47914                 } catch (e) {
47915                     Roo.log("Invalid data from server..");
47916                     Roo.log(edata);
47917                     return;
47918                 }
47919                 if (!rdata || !rdata.success) {
47920                     Roo.log(rdata);
47921                     Roo.MessageBox.alert(Roo.encode(rdata));
47922                     return;
47923                 }
47924                 var data = rdata.data;
47925                 
47926                 if (this.uploadComplete) {
47927                    Roo.MessageBox.hide();
47928                    return;
47929                 }
47930                    
47931                 if (data){
47932                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
47933                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
47934                     );
47935                 }
47936                 this.uploadProgress.defer(2000,this);
47937             },
47938        
47939             failure: function(data) {
47940                 Roo.log('progress url failed ');
47941                 Roo.log(data);
47942             },
47943             scope : this
47944         });
47945            
47946     },
47947     
47948     
47949     run : function()
47950     {
47951         // run get Values on the form, so it syncs any secondary forms.
47952         this.form.getValues();
47953         
47954         var o = this.options;
47955         var method = this.getMethod();
47956         var isPost = method == 'POST';
47957         if(o.clientValidation === false || this.form.isValid()){
47958             
47959             if (this.form.progressUrl) {
47960                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
47961                     (new Date() * 1) + '' + Math.random());
47962                     
47963             } 
47964             
47965             
47966             Roo.Ajax.request(Roo.apply(this.createCallback(), {
47967                 form:this.form.el.dom,
47968                 url:this.getUrl(!isPost),
47969                 method: method,
47970                 params:isPost ? this.getParams() : null,
47971                 isUpload: this.form.fileUpload
47972             }));
47973             
47974             this.uploadProgress();
47975
47976         }else if (o.clientValidation !== false){ // client validation failed
47977             this.failureType = Roo.form.Action.CLIENT_INVALID;
47978             this.form.afterAction(this, false);
47979         }
47980     },
47981
47982     success : function(response)
47983     {
47984         this.uploadComplete= true;
47985         if (this.haveProgress) {
47986             Roo.MessageBox.hide();
47987         }
47988         
47989         
47990         var result = this.processResponse(response);
47991         if(result === true || result.success){
47992             this.form.afterAction(this, true);
47993             return;
47994         }
47995         if(result.errors){
47996             this.form.markInvalid(result.errors);
47997             this.failureType = Roo.form.Action.SERVER_INVALID;
47998         }
47999         this.form.afterAction(this, false);
48000     },
48001     failure : function(response)
48002     {
48003         this.uploadComplete= true;
48004         if (this.haveProgress) {
48005             Roo.MessageBox.hide();
48006         }
48007         
48008         this.response = response;
48009         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48010         this.form.afterAction(this, false);
48011     },
48012     
48013     handleResponse : function(response){
48014         if(this.form.errorReader){
48015             var rs = this.form.errorReader.read(response);
48016             var errors = [];
48017             if(rs.records){
48018                 for(var i = 0, len = rs.records.length; i < len; i++) {
48019                     var r = rs.records[i];
48020                     errors[i] = r.data;
48021                 }
48022             }
48023             if(errors.length < 1){
48024                 errors = null;
48025             }
48026             return {
48027                 success : rs.success,
48028                 errors : errors
48029             };
48030         }
48031         var ret = false;
48032         try {
48033             ret = Roo.decode(response.responseText);
48034         } catch (e) {
48035             ret = {
48036                 success: false,
48037                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
48038                 errors : []
48039             };
48040         }
48041         return ret;
48042         
48043     }
48044 });
48045
48046
48047 Roo.form.Action.Load = function(form, options){
48048     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
48049     this.reader = this.form.reader;
48050 };
48051
48052 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
48053     type : 'load',
48054
48055     run : function(){
48056         
48057         Roo.Ajax.request(Roo.apply(
48058                 this.createCallback(), {
48059                     method:this.getMethod(),
48060                     url:this.getUrl(false),
48061                     params:this.getParams()
48062         }));
48063     },
48064
48065     success : function(response){
48066         
48067         var result = this.processResponse(response);
48068         if(result === true || !result.success || !result.data){
48069             this.failureType = Roo.form.Action.LOAD_FAILURE;
48070             this.form.afterAction(this, false);
48071             return;
48072         }
48073         this.form.clearInvalid();
48074         this.form.setValues(result.data);
48075         this.form.afterAction(this, true);
48076     },
48077
48078     handleResponse : function(response){
48079         if(this.form.reader){
48080             var rs = this.form.reader.read(response);
48081             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
48082             return {
48083                 success : rs.success,
48084                 data : data
48085             };
48086         }
48087         return Roo.decode(response.responseText);
48088     }
48089 });
48090
48091 Roo.form.Action.ACTION_TYPES = {
48092     'load' : Roo.form.Action.Load,
48093     'submit' : Roo.form.Action.Submit
48094 };/*
48095  * Based on:
48096  * Ext JS Library 1.1.1
48097  * Copyright(c) 2006-2007, Ext JS, LLC.
48098  *
48099  * Originally Released Under LGPL - original licence link has changed is not relivant.
48100  *
48101  * Fork - LGPL
48102  * <script type="text/javascript">
48103  */
48104  
48105 /**
48106  * @class Roo.form.Layout
48107  * @extends Roo.Component
48108  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
48109  * @constructor
48110  * @param {Object} config Configuration options
48111  */
48112 Roo.form.Layout = function(config){
48113     var xitems = [];
48114     if (config.items) {
48115         xitems = config.items;
48116         delete config.items;
48117     }
48118     Roo.form.Layout.superclass.constructor.call(this, config);
48119     this.stack = [];
48120     Roo.each(xitems, this.addxtype, this);
48121      
48122 };
48123
48124 Roo.extend(Roo.form.Layout, Roo.Component, {
48125     /**
48126      * @cfg {String/Object} autoCreate
48127      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
48128      */
48129     /**
48130      * @cfg {String/Object/Function} style
48131      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
48132      * a function which returns such a specification.
48133      */
48134     /**
48135      * @cfg {String} labelAlign
48136      * Valid values are "left," "top" and "right" (defaults to "left")
48137      */
48138     /**
48139      * @cfg {Number} labelWidth
48140      * Fixed width in pixels of all field labels (defaults to undefined)
48141      */
48142     /**
48143      * @cfg {Boolean} clear
48144      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
48145      */
48146     clear : true,
48147     /**
48148      * @cfg {String} labelSeparator
48149      * The separator to use after field labels (defaults to ':')
48150      */
48151     labelSeparator : ':',
48152     /**
48153      * @cfg {Boolean} hideLabels
48154      * True to suppress the display of field labels in this layout (defaults to false)
48155      */
48156     hideLabels : false,
48157
48158     // private
48159     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
48160     
48161     isLayout : true,
48162     
48163     // private
48164     onRender : function(ct, position){
48165         if(this.el){ // from markup
48166             this.el = Roo.get(this.el);
48167         }else {  // generate
48168             var cfg = this.getAutoCreate();
48169             this.el = ct.createChild(cfg, position);
48170         }
48171         if(this.style){
48172             this.el.applyStyles(this.style);
48173         }
48174         if(this.labelAlign){
48175             this.el.addClass('x-form-label-'+this.labelAlign);
48176         }
48177         if(this.hideLabels){
48178             this.labelStyle = "display:none";
48179             this.elementStyle = "padding-left:0;";
48180         }else{
48181             if(typeof this.labelWidth == 'number'){
48182                 this.labelStyle = "width:"+this.labelWidth+"px;";
48183                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
48184             }
48185             if(this.labelAlign == 'top'){
48186                 this.labelStyle = "width:auto;";
48187                 this.elementStyle = "padding-left:0;";
48188             }
48189         }
48190         var stack = this.stack;
48191         var slen = stack.length;
48192         if(slen > 0){
48193             if(!this.fieldTpl){
48194                 var t = new Roo.Template(
48195                     '<div class="x-form-item {5}">',
48196                         '<label for="{0}" style="{2}">{1}{4}</label>',
48197                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48198                         '</div>',
48199                     '</div><div class="x-form-clear-left"></div>'
48200                 );
48201                 t.disableFormats = true;
48202                 t.compile();
48203                 Roo.form.Layout.prototype.fieldTpl = t;
48204             }
48205             for(var i = 0; i < slen; i++) {
48206                 if(stack[i].isFormField){
48207                     this.renderField(stack[i]);
48208                 }else{
48209                     this.renderComponent(stack[i]);
48210                 }
48211             }
48212         }
48213         if(this.clear){
48214             this.el.createChild({cls:'x-form-clear'});
48215         }
48216     },
48217
48218     // private
48219     renderField : function(f){
48220         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
48221                f.id, //0
48222                f.fieldLabel, //1
48223                f.labelStyle||this.labelStyle||'', //2
48224                this.elementStyle||'', //3
48225                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
48226                f.itemCls||this.itemCls||''  //5
48227        ], true).getPrevSibling());
48228     },
48229
48230     // private
48231     renderComponent : function(c){
48232         c.render(c.isLayout ? this.el : this.el.createChild());    
48233     },
48234     /**
48235      * Adds a object form elements (using the xtype property as the factory method.)
48236      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
48237      * @param {Object} config 
48238      */
48239     addxtype : function(o)
48240     {
48241         // create the lement.
48242         o.form = this.form;
48243         var fe = Roo.factory(o, Roo.form);
48244         this.form.allItems.push(fe);
48245         this.stack.push(fe);
48246         
48247         if (fe.isFormField) {
48248             this.form.items.add(fe);
48249         }
48250          
48251         return fe;
48252     }
48253 });
48254
48255 /**
48256  * @class Roo.form.Column
48257  * @extends Roo.form.Layout
48258  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
48259  * @constructor
48260  * @param {Object} config Configuration options
48261  */
48262 Roo.form.Column = function(config){
48263     Roo.form.Column.superclass.constructor.call(this, config);
48264 };
48265
48266 Roo.extend(Roo.form.Column, Roo.form.Layout, {
48267     /**
48268      * @cfg {Number/String} width
48269      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48270      */
48271     /**
48272      * @cfg {String/Object} autoCreate
48273      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
48274      */
48275
48276     // private
48277     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
48278
48279     // private
48280     onRender : function(ct, position){
48281         Roo.form.Column.superclass.onRender.call(this, ct, position);
48282         if(this.width){
48283             this.el.setWidth(this.width);
48284         }
48285     }
48286 });
48287
48288
48289 /**
48290  * @class Roo.form.Row
48291  * @extends Roo.form.Layout
48292  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
48293  * @constructor
48294  * @param {Object} config Configuration options
48295  */
48296
48297  
48298 Roo.form.Row = function(config){
48299     Roo.form.Row.superclass.constructor.call(this, config);
48300 };
48301  
48302 Roo.extend(Roo.form.Row, Roo.form.Layout, {
48303       /**
48304      * @cfg {Number/String} width
48305      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48306      */
48307     /**
48308      * @cfg {Number/String} height
48309      * The fixed height of the column in pixels or CSS value (defaults to "auto")
48310      */
48311     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
48312     
48313     padWidth : 20,
48314     // private
48315     onRender : function(ct, position){
48316         //console.log('row render');
48317         if(!this.rowTpl){
48318             var t = new Roo.Template(
48319                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
48320                     '<label for="{0}" style="{2}">{1}{4}</label>',
48321                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48322                     '</div>',
48323                 '</div>'
48324             );
48325             t.disableFormats = true;
48326             t.compile();
48327             Roo.form.Layout.prototype.rowTpl = t;
48328         }
48329         this.fieldTpl = this.rowTpl;
48330         
48331         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
48332         var labelWidth = 100;
48333         
48334         if ((this.labelAlign != 'top')) {
48335             if (typeof this.labelWidth == 'number') {
48336                 labelWidth = this.labelWidth
48337             }
48338             this.padWidth =  20 + labelWidth;
48339             
48340         }
48341         
48342         Roo.form.Column.superclass.onRender.call(this, ct, position);
48343         if(this.width){
48344             this.el.setWidth(this.width);
48345         }
48346         if(this.height){
48347             this.el.setHeight(this.height);
48348         }
48349     },
48350     
48351     // private
48352     renderField : function(f){
48353         f.fieldEl = this.fieldTpl.append(this.el, [
48354                f.id, f.fieldLabel,
48355                f.labelStyle||this.labelStyle||'',
48356                this.elementStyle||'',
48357                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
48358                f.itemCls||this.itemCls||'',
48359                f.width ? f.width + this.padWidth : 160 + this.padWidth
48360        ],true);
48361     }
48362 });
48363  
48364
48365 /**
48366  * @class Roo.form.FieldSet
48367  * @extends Roo.form.Layout
48368  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
48369  * @constructor
48370  * @param {Object} config Configuration options
48371  */
48372 Roo.form.FieldSet = function(config){
48373     Roo.form.FieldSet.superclass.constructor.call(this, config);
48374 };
48375
48376 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
48377     /**
48378      * @cfg {String} legend
48379      * The text to display as the legend for the FieldSet (defaults to '')
48380      */
48381     /**
48382      * @cfg {String/Object} autoCreate
48383      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
48384      */
48385
48386     // private
48387     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
48388
48389     // private
48390     onRender : function(ct, position){
48391         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
48392         if(this.legend){
48393             this.setLegend(this.legend);
48394         }
48395     },
48396
48397     // private
48398     setLegend : function(text){
48399         if(this.rendered){
48400             this.el.child('legend').update(text);
48401         }
48402     }
48403 });/*
48404  * Based on:
48405  * Ext JS Library 1.1.1
48406  * Copyright(c) 2006-2007, Ext JS, LLC.
48407  *
48408  * Originally Released Under LGPL - original licence link has changed is not relivant.
48409  *
48410  * Fork - LGPL
48411  * <script type="text/javascript">
48412  */
48413 /**
48414  * @class Roo.form.VTypes
48415  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
48416  * @singleton
48417  */
48418 Roo.form.VTypes = function(){
48419     // closure these in so they are only created once.
48420     var alpha = /^[a-zA-Z_]+$/;
48421     var alphanum = /^[a-zA-Z0-9_]+$/;
48422     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
48423     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
48424
48425     // All these messages and functions are configurable
48426     return {
48427         /**
48428          * The function used to validate email addresses
48429          * @param {String} value The email address
48430          */
48431         'email' : function(v){
48432             return email.test(v);
48433         },
48434         /**
48435          * The error text to display when the email validation function returns false
48436          * @type String
48437          */
48438         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
48439         /**
48440          * The keystroke filter mask to be applied on email input
48441          * @type RegExp
48442          */
48443         'emailMask' : /[a-z0-9_\.\-@]/i,
48444
48445         /**
48446          * The function used to validate URLs
48447          * @param {String} value The URL
48448          */
48449         'url' : function(v){
48450             return url.test(v);
48451         },
48452         /**
48453          * The error text to display when the url validation function returns false
48454          * @type String
48455          */
48456         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
48457         
48458         /**
48459          * The function used to validate alpha values
48460          * @param {String} value The value
48461          */
48462         'alpha' : function(v){
48463             return alpha.test(v);
48464         },
48465         /**
48466          * The error text to display when the alpha validation function returns false
48467          * @type String
48468          */
48469         'alphaText' : 'This field should only contain letters and _',
48470         /**
48471          * The keystroke filter mask to be applied on alpha input
48472          * @type RegExp
48473          */
48474         'alphaMask' : /[a-z_]/i,
48475
48476         /**
48477          * The function used to validate alphanumeric values
48478          * @param {String} value The value
48479          */
48480         'alphanum' : function(v){
48481             return alphanum.test(v);
48482         },
48483         /**
48484          * The error text to display when the alphanumeric validation function returns false
48485          * @type String
48486          */
48487         'alphanumText' : 'This field should only contain letters, numbers and _',
48488         /**
48489          * The keystroke filter mask to be applied on alphanumeric input
48490          * @type RegExp
48491          */
48492         'alphanumMask' : /[a-z0-9_]/i
48493     };
48494 }();//<script type="text/javascript">
48495
48496 /**
48497  * @class Roo.form.FCKeditor
48498  * @extends Roo.form.TextArea
48499  * Wrapper around the FCKEditor http://www.fckeditor.net
48500  * @constructor
48501  * Creates a new FCKeditor
48502  * @param {Object} config Configuration options
48503  */
48504 Roo.form.FCKeditor = function(config){
48505     Roo.form.FCKeditor.superclass.constructor.call(this, config);
48506     this.addEvents({
48507          /**
48508          * @event editorinit
48509          * Fired when the editor is initialized - you can add extra handlers here..
48510          * @param {FCKeditor} this
48511          * @param {Object} the FCK object.
48512          */
48513         editorinit : true
48514     });
48515     
48516     
48517 };
48518 Roo.form.FCKeditor.editors = { };
48519 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
48520 {
48521     //defaultAutoCreate : {
48522     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
48523     //},
48524     // private
48525     /**
48526      * @cfg {Object} fck options - see fck manual for details.
48527      */
48528     fckconfig : false,
48529     
48530     /**
48531      * @cfg {Object} fck toolbar set (Basic or Default)
48532      */
48533     toolbarSet : 'Basic',
48534     /**
48535      * @cfg {Object} fck BasePath
48536      */ 
48537     basePath : '/fckeditor/',
48538     
48539     
48540     frame : false,
48541     
48542     value : '',
48543     
48544    
48545     onRender : function(ct, position)
48546     {
48547         if(!this.el){
48548             this.defaultAutoCreate = {
48549                 tag: "textarea",
48550                 style:"width:300px;height:60px;",
48551                 autocomplete: "new-password"
48552             };
48553         }
48554         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
48555         /*
48556         if(this.grow){
48557             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
48558             if(this.preventScrollbars){
48559                 this.el.setStyle("overflow", "hidden");
48560             }
48561             this.el.setHeight(this.growMin);
48562         }
48563         */
48564         //console.log('onrender' + this.getId() );
48565         Roo.form.FCKeditor.editors[this.getId()] = this;
48566          
48567
48568         this.replaceTextarea() ;
48569         
48570     },
48571     
48572     getEditor : function() {
48573         return this.fckEditor;
48574     },
48575     /**
48576      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
48577      * @param {Mixed} value The value to set
48578      */
48579     
48580     
48581     setValue : function(value)
48582     {
48583         //console.log('setValue: ' + value);
48584         
48585         if(typeof(value) == 'undefined') { // not sure why this is happending...
48586             return;
48587         }
48588         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48589         
48590         //if(!this.el || !this.getEditor()) {
48591         //    this.value = value;
48592             //this.setValue.defer(100,this,[value]);    
48593         //    return;
48594         //} 
48595         
48596         if(!this.getEditor()) {
48597             return;
48598         }
48599         
48600         this.getEditor().SetData(value);
48601         
48602         //
48603
48604     },
48605
48606     /**
48607      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
48608      * @return {Mixed} value The field value
48609      */
48610     getValue : function()
48611     {
48612         
48613         if (this.frame && this.frame.dom.style.display == 'none') {
48614             return Roo.form.FCKeditor.superclass.getValue.call(this);
48615         }
48616         
48617         if(!this.el || !this.getEditor()) {
48618            
48619            // this.getValue.defer(100,this); 
48620             return this.value;
48621         }
48622        
48623         
48624         var value=this.getEditor().GetData();
48625         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48626         return Roo.form.FCKeditor.superclass.getValue.call(this);
48627         
48628
48629     },
48630
48631     /**
48632      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
48633      * @return {Mixed} value The field value
48634      */
48635     getRawValue : function()
48636     {
48637         if (this.frame && this.frame.dom.style.display == 'none') {
48638             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48639         }
48640         
48641         if(!this.el || !this.getEditor()) {
48642             //this.getRawValue.defer(100,this); 
48643             return this.value;
48644             return;
48645         }
48646         
48647         
48648         
48649         var value=this.getEditor().GetData();
48650         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
48651         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48652          
48653     },
48654     
48655     setSize : function(w,h) {
48656         
48657         
48658         
48659         //if (this.frame && this.frame.dom.style.display == 'none') {
48660         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48661         //    return;
48662         //}
48663         //if(!this.el || !this.getEditor()) {
48664         //    this.setSize.defer(100,this, [w,h]); 
48665         //    return;
48666         //}
48667         
48668         
48669         
48670         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48671         
48672         this.frame.dom.setAttribute('width', w);
48673         this.frame.dom.setAttribute('height', h);
48674         this.frame.setSize(w,h);
48675         
48676     },
48677     
48678     toggleSourceEdit : function(value) {
48679         
48680       
48681          
48682         this.el.dom.style.display = value ? '' : 'none';
48683         this.frame.dom.style.display = value ?  'none' : '';
48684         
48685     },
48686     
48687     
48688     focus: function(tag)
48689     {
48690         if (this.frame.dom.style.display == 'none') {
48691             return Roo.form.FCKeditor.superclass.focus.call(this);
48692         }
48693         if(!this.el || !this.getEditor()) {
48694             this.focus.defer(100,this, [tag]); 
48695             return;
48696         }
48697         
48698         
48699         
48700         
48701         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
48702         this.getEditor().Focus();
48703         if (tgs.length) {
48704             if (!this.getEditor().Selection.GetSelection()) {
48705                 this.focus.defer(100,this, [tag]); 
48706                 return;
48707             }
48708             
48709             
48710             var r = this.getEditor().EditorDocument.createRange();
48711             r.setStart(tgs[0],0);
48712             r.setEnd(tgs[0],0);
48713             this.getEditor().Selection.GetSelection().removeAllRanges();
48714             this.getEditor().Selection.GetSelection().addRange(r);
48715             this.getEditor().Focus();
48716         }
48717         
48718     },
48719     
48720     
48721     
48722     replaceTextarea : function()
48723     {
48724         if ( document.getElementById( this.getId() + '___Frame' ) ) {
48725             return ;
48726         }
48727         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
48728         //{
48729             // We must check the elements firstly using the Id and then the name.
48730         var oTextarea = document.getElementById( this.getId() );
48731         
48732         var colElementsByName = document.getElementsByName( this.getId() ) ;
48733          
48734         oTextarea.style.display = 'none' ;
48735
48736         if ( oTextarea.tabIndex ) {            
48737             this.TabIndex = oTextarea.tabIndex ;
48738         }
48739         
48740         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
48741         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
48742         this.frame = Roo.get(this.getId() + '___Frame')
48743     },
48744     
48745     _getConfigHtml : function()
48746     {
48747         var sConfig = '' ;
48748
48749         for ( var o in this.fckconfig ) {
48750             sConfig += sConfig.length > 0  ? '&amp;' : '';
48751             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
48752         }
48753
48754         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
48755     },
48756     
48757     
48758     _getIFrameHtml : function()
48759     {
48760         var sFile = 'fckeditor.html' ;
48761         /* no idea what this is about..
48762         try
48763         {
48764             if ( (/fcksource=true/i).test( window.top.location.search ) )
48765                 sFile = 'fckeditor.original.html' ;
48766         }
48767         catch (e) { 
48768         */
48769
48770         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
48771         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
48772         
48773         
48774         var html = '<iframe id="' + this.getId() +
48775             '___Frame" src="' + sLink +
48776             '" width="' + this.width +
48777             '" height="' + this.height + '"' +
48778             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
48779             ' frameborder="0" scrolling="no"></iframe>' ;
48780
48781         return html ;
48782     },
48783     
48784     _insertHtmlBefore : function( html, element )
48785     {
48786         if ( element.insertAdjacentHTML )       {
48787             // IE
48788             element.insertAdjacentHTML( 'beforeBegin', html ) ;
48789         } else { // Gecko
48790             var oRange = document.createRange() ;
48791             oRange.setStartBefore( element ) ;
48792             var oFragment = oRange.createContextualFragment( html );
48793             element.parentNode.insertBefore( oFragment, element ) ;
48794         }
48795     }
48796     
48797     
48798   
48799     
48800     
48801     
48802     
48803
48804 });
48805
48806 //Roo.reg('fckeditor', Roo.form.FCKeditor);
48807
48808 function FCKeditor_OnComplete(editorInstance){
48809     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
48810     f.fckEditor = editorInstance;
48811     //console.log("loaded");
48812     f.fireEvent('editorinit', f, editorInstance);
48813
48814   
48815
48816  
48817
48818
48819
48820
48821
48822
48823
48824
48825
48826
48827
48828
48829
48830
48831
48832 //<script type="text/javascript">
48833 /**
48834  * @class Roo.form.GridField
48835  * @extends Roo.form.Field
48836  * Embed a grid (or editable grid into a form)
48837  * STATUS ALPHA
48838  * 
48839  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
48840  * it needs 
48841  * xgrid.store = Roo.data.Store
48842  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
48843  * xgrid.store.reader = Roo.data.JsonReader 
48844  * 
48845  * 
48846  * @constructor
48847  * Creates a new GridField
48848  * @param {Object} config Configuration options
48849  */
48850 Roo.form.GridField = function(config){
48851     Roo.form.GridField.superclass.constructor.call(this, config);
48852      
48853 };
48854
48855 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
48856     /**
48857      * @cfg {Number} width  - used to restrict width of grid..
48858      */
48859     width : 100,
48860     /**
48861      * @cfg {Number} height - used to restrict height of grid..
48862      */
48863     height : 50,
48864      /**
48865      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
48866          * 
48867          *}
48868      */
48869     xgrid : false, 
48870     /**
48871      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48872      * {tag: "input", type: "checkbox", autocomplete: "off"})
48873      */
48874    // defaultAutoCreate : { tag: 'div' },
48875     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
48876     /**
48877      * @cfg {String} addTitle Text to include for adding a title.
48878      */
48879     addTitle : false,
48880     //
48881     onResize : function(){
48882         Roo.form.Field.superclass.onResize.apply(this, arguments);
48883     },
48884
48885     initEvents : function(){
48886         // Roo.form.Checkbox.superclass.initEvents.call(this);
48887         // has no events...
48888        
48889     },
48890
48891
48892     getResizeEl : function(){
48893         return this.wrap;
48894     },
48895
48896     getPositionEl : function(){
48897         return this.wrap;
48898     },
48899
48900     // private
48901     onRender : function(ct, position){
48902         
48903         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
48904         var style = this.style;
48905         delete this.style;
48906         
48907         Roo.form.GridField.superclass.onRender.call(this, ct, position);
48908         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
48909         this.viewEl = this.wrap.createChild({ tag: 'div' });
48910         if (style) {
48911             this.viewEl.applyStyles(style);
48912         }
48913         if (this.width) {
48914             this.viewEl.setWidth(this.width);
48915         }
48916         if (this.height) {
48917             this.viewEl.setHeight(this.height);
48918         }
48919         //if(this.inputValue !== undefined){
48920         //this.setValue(this.value);
48921         
48922         
48923         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
48924         
48925         
48926         this.grid.render();
48927         this.grid.getDataSource().on('remove', this.refreshValue, this);
48928         this.grid.getDataSource().on('update', this.refreshValue, this);
48929         this.grid.on('afteredit', this.refreshValue, this);
48930  
48931     },
48932      
48933     
48934     /**
48935      * Sets the value of the item. 
48936      * @param {String} either an object  or a string..
48937      */
48938     setValue : function(v){
48939         //this.value = v;
48940         v = v || []; // empty set..
48941         // this does not seem smart - it really only affects memoryproxy grids..
48942         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
48943             var ds = this.grid.getDataSource();
48944             // assumes a json reader..
48945             var data = {}
48946             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
48947             ds.loadData( data);
48948         }
48949         // clear selection so it does not get stale.
48950         if (this.grid.sm) { 
48951             this.grid.sm.clearSelections();
48952         }
48953         
48954         Roo.form.GridField.superclass.setValue.call(this, v);
48955         this.refreshValue();
48956         // should load data in the grid really....
48957     },
48958     
48959     // private
48960     refreshValue: function() {
48961          var val = [];
48962         this.grid.getDataSource().each(function(r) {
48963             val.push(r.data);
48964         });
48965         this.el.dom.value = Roo.encode(val);
48966     }
48967     
48968      
48969     
48970     
48971 });/*
48972  * Based on:
48973  * Ext JS Library 1.1.1
48974  * Copyright(c) 2006-2007, Ext JS, LLC.
48975  *
48976  * Originally Released Under LGPL - original licence link has changed is not relivant.
48977  *
48978  * Fork - LGPL
48979  * <script type="text/javascript">
48980  */
48981 /**
48982  * @class Roo.form.DisplayField
48983  * @extends Roo.form.Field
48984  * A generic Field to display non-editable data.
48985  * @cfg {Boolean} closable (true|false) default false
48986  * @constructor
48987  * Creates a new Display Field item.
48988  * @param {Object} config Configuration options
48989  */
48990 Roo.form.DisplayField = function(config){
48991     Roo.form.DisplayField.superclass.constructor.call(this, config);
48992     
48993     this.addEvents({
48994         /**
48995          * @event close
48996          * Fires after the click the close btn
48997              * @param {Roo.form.DisplayField} this
48998              */
48999         close : true
49000     });
49001 };
49002
49003 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
49004     inputType:      'hidden',
49005     allowBlank:     true,
49006     readOnly:         true,
49007     
49008  
49009     /**
49010      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49011      */
49012     focusClass : undefined,
49013     /**
49014      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49015      */
49016     fieldClass: 'x-form-field',
49017     
49018      /**
49019      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
49020      */
49021     valueRenderer: undefined,
49022     
49023     width: 100,
49024     /**
49025      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49026      * {tag: "input", type: "checkbox", autocomplete: "off"})
49027      */
49028      
49029  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
49030  
49031     closable : false,
49032     
49033     onResize : function(){
49034         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
49035         
49036     },
49037
49038     initEvents : function(){
49039         // Roo.form.Checkbox.superclass.initEvents.call(this);
49040         // has no events...
49041         
49042         if(this.closable){
49043             this.closeEl.on('click', this.onClose, this);
49044         }
49045        
49046     },
49047
49048
49049     getResizeEl : function(){
49050         return this.wrap;
49051     },
49052
49053     getPositionEl : function(){
49054         return this.wrap;
49055     },
49056
49057     // private
49058     onRender : function(ct, position){
49059         
49060         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
49061         //if(this.inputValue !== undefined){
49062         this.wrap = this.el.wrap();
49063         
49064         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
49065         
49066         if(this.closable){
49067             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
49068         }
49069         
49070         if (this.bodyStyle) {
49071             this.viewEl.applyStyles(this.bodyStyle);
49072         }
49073         //this.viewEl.setStyle('padding', '2px');
49074         
49075         this.setValue(this.value);
49076         
49077     },
49078 /*
49079     // private
49080     initValue : Roo.emptyFn,
49081
49082   */
49083
49084         // private
49085     onClick : function(){
49086         
49087     },
49088
49089     /**
49090      * Sets the checked state of the checkbox.
49091      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
49092      */
49093     setValue : function(v){
49094         this.value = v;
49095         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
49096         // this might be called before we have a dom element..
49097         if (!this.viewEl) {
49098             return;
49099         }
49100         this.viewEl.dom.innerHTML = html;
49101         Roo.form.DisplayField.superclass.setValue.call(this, v);
49102
49103     },
49104     
49105     onClose : function(e)
49106     {
49107         e.preventDefault();
49108         
49109         this.fireEvent('close', this);
49110     }
49111 });/*
49112  * 
49113  * Licence- LGPL
49114  * 
49115  */
49116
49117 /**
49118  * @class Roo.form.DayPicker
49119  * @extends Roo.form.Field
49120  * A Day picker show [M] [T] [W] ....
49121  * @constructor
49122  * Creates a new Day Picker
49123  * @param {Object} config Configuration options
49124  */
49125 Roo.form.DayPicker= function(config){
49126     Roo.form.DayPicker.superclass.constructor.call(this, config);
49127      
49128 };
49129
49130 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
49131     /**
49132      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49133      */
49134     focusClass : undefined,
49135     /**
49136      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49137      */
49138     fieldClass: "x-form-field",
49139    
49140     /**
49141      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49142      * {tag: "input", type: "checkbox", autocomplete: "off"})
49143      */
49144     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
49145     
49146    
49147     actionMode : 'viewEl', 
49148     //
49149     // private
49150  
49151     inputType : 'hidden',
49152     
49153      
49154     inputElement: false, // real input element?
49155     basedOn: false, // ????
49156     
49157     isFormField: true, // not sure where this is needed!!!!
49158
49159     onResize : function(){
49160         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
49161         if(!this.boxLabel){
49162             this.el.alignTo(this.wrap, 'c-c');
49163         }
49164     },
49165
49166     initEvents : function(){
49167         Roo.form.Checkbox.superclass.initEvents.call(this);
49168         this.el.on("click", this.onClick,  this);
49169         this.el.on("change", this.onClick,  this);
49170     },
49171
49172
49173     getResizeEl : function(){
49174         return this.wrap;
49175     },
49176
49177     getPositionEl : function(){
49178         return this.wrap;
49179     },
49180
49181     
49182     // private
49183     onRender : function(ct, position){
49184         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
49185        
49186         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
49187         
49188         var r1 = '<table><tr>';
49189         var r2 = '<tr class="x-form-daypick-icons">';
49190         for (var i=0; i < 7; i++) {
49191             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
49192             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
49193         }
49194         
49195         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
49196         viewEl.select('img').on('click', this.onClick, this);
49197         this.viewEl = viewEl;   
49198         
49199         
49200         // this will not work on Chrome!!!
49201         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
49202         this.el.on('propertychange', this.setFromHidden,  this);  //ie
49203         
49204         
49205           
49206
49207     },
49208
49209     // private
49210     initValue : Roo.emptyFn,
49211
49212     /**
49213      * Returns the checked state of the checkbox.
49214      * @return {Boolean} True if checked, else false
49215      */
49216     getValue : function(){
49217         return this.el.dom.value;
49218         
49219     },
49220
49221         // private
49222     onClick : function(e){ 
49223         //this.setChecked(!this.checked);
49224         Roo.get(e.target).toggleClass('x-menu-item-checked');
49225         this.refreshValue();
49226         //if(this.el.dom.checked != this.checked){
49227         //    this.setValue(this.el.dom.checked);
49228        // }
49229     },
49230     
49231     // private
49232     refreshValue : function()
49233     {
49234         var val = '';
49235         this.viewEl.select('img',true).each(function(e,i,n)  {
49236             val += e.is(".x-menu-item-checked") ? String(n) : '';
49237         });
49238         this.setValue(val, true);
49239     },
49240
49241     /**
49242      * Sets the checked state of the checkbox.
49243      * On is always based on a string comparison between inputValue and the param.
49244      * @param {Boolean/String} value - the value to set 
49245      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
49246      */
49247     setValue : function(v,suppressEvent){
49248         if (!this.el.dom) {
49249             return;
49250         }
49251         var old = this.el.dom.value ;
49252         this.el.dom.value = v;
49253         if (suppressEvent) {
49254             return ;
49255         }
49256          
49257         // update display..
49258         this.viewEl.select('img',true).each(function(e,i,n)  {
49259             
49260             var on = e.is(".x-menu-item-checked");
49261             var newv = v.indexOf(String(n)) > -1;
49262             if (on != newv) {
49263                 e.toggleClass('x-menu-item-checked');
49264             }
49265             
49266         });
49267         
49268         
49269         this.fireEvent('change', this, v, old);
49270         
49271         
49272     },
49273    
49274     // handle setting of hidden value by some other method!!?!?
49275     setFromHidden: function()
49276     {
49277         if(!this.el){
49278             return;
49279         }
49280         //console.log("SET FROM HIDDEN");
49281         //alert('setFrom hidden');
49282         this.setValue(this.el.dom.value);
49283     },
49284     
49285     onDestroy : function()
49286     {
49287         if(this.viewEl){
49288             Roo.get(this.viewEl).remove();
49289         }
49290          
49291         Roo.form.DayPicker.superclass.onDestroy.call(this);
49292     }
49293
49294 });/*
49295  * RooJS Library 1.1.1
49296  * Copyright(c) 2008-2011  Alan Knowles
49297  *
49298  * License - LGPL
49299  */
49300  
49301
49302 /**
49303  * @class Roo.form.ComboCheck
49304  * @extends Roo.form.ComboBox
49305  * A combobox for multiple select items.
49306  *
49307  * FIXME - could do with a reset button..
49308  * 
49309  * @constructor
49310  * Create a new ComboCheck
49311  * @param {Object} config Configuration options
49312  */
49313 Roo.form.ComboCheck = function(config){
49314     Roo.form.ComboCheck.superclass.constructor.call(this, config);
49315     // should verify some data...
49316     // like
49317     // hiddenName = required..
49318     // displayField = required
49319     // valudField == required
49320     var req= [ 'hiddenName', 'displayField', 'valueField' ];
49321     var _t = this;
49322     Roo.each(req, function(e) {
49323         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
49324             throw "Roo.form.ComboCheck : missing value for: " + e;
49325         }
49326     });
49327     
49328     
49329 };
49330
49331 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
49332      
49333      
49334     editable : false,
49335      
49336     selectedClass: 'x-menu-item-checked', 
49337     
49338     // private
49339     onRender : function(ct, position){
49340         var _t = this;
49341         
49342         
49343         
49344         if(!this.tpl){
49345             var cls = 'x-combo-list';
49346
49347             
49348             this.tpl =  new Roo.Template({
49349                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
49350                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
49351                    '<span>{' + this.displayField + '}</span>' +
49352                     '</div>' 
49353                 
49354             });
49355         }
49356  
49357         
49358         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
49359         this.view.singleSelect = false;
49360         this.view.multiSelect = true;
49361         this.view.toggleSelect = true;
49362         this.pageTb.add(new Roo.Toolbar.Fill(), {
49363             
49364             text: 'Done',
49365             handler: function()
49366             {
49367                 _t.collapse();
49368             }
49369         });
49370     },
49371     
49372     onViewOver : function(e, t){
49373         // do nothing...
49374         return;
49375         
49376     },
49377     
49378     onViewClick : function(doFocus,index){
49379         return;
49380         
49381     },
49382     select: function () {
49383         //Roo.log("SELECT CALLED");
49384     },
49385      
49386     selectByValue : function(xv, scrollIntoView){
49387         var ar = this.getValueArray();
49388         var sels = [];
49389         
49390         Roo.each(ar, function(v) {
49391             if(v === undefined || v === null){
49392                 return;
49393             }
49394             var r = this.findRecord(this.valueField, v);
49395             if(r){
49396                 sels.push(this.store.indexOf(r))
49397                 
49398             }
49399         },this);
49400         this.view.select(sels);
49401         return false;
49402     },
49403     
49404     
49405     
49406     onSelect : function(record, index){
49407        // Roo.log("onselect Called");
49408        // this is only called by the clear button now..
49409         this.view.clearSelections();
49410         this.setValue('[]');
49411         if (this.value != this.valueBefore) {
49412             this.fireEvent('change', this, this.value, this.valueBefore);
49413             this.valueBefore = this.value;
49414         }
49415     },
49416     getValueArray : function()
49417     {
49418         var ar = [] ;
49419         
49420         try {
49421             //Roo.log(this.value);
49422             if (typeof(this.value) == 'undefined') {
49423                 return [];
49424             }
49425             var ar = Roo.decode(this.value);
49426             return  ar instanceof Array ? ar : []; //?? valid?
49427             
49428         } catch(e) {
49429             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
49430             return [];
49431         }
49432          
49433     },
49434     expand : function ()
49435     {
49436         
49437         Roo.form.ComboCheck.superclass.expand.call(this);
49438         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
49439         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
49440         
49441
49442     },
49443     
49444     collapse : function(){
49445         Roo.form.ComboCheck.superclass.collapse.call(this);
49446         var sl = this.view.getSelectedIndexes();
49447         var st = this.store;
49448         var nv = [];
49449         var tv = [];
49450         var r;
49451         Roo.each(sl, function(i) {
49452             r = st.getAt(i);
49453             nv.push(r.get(this.valueField));
49454         },this);
49455         this.setValue(Roo.encode(nv));
49456         if (this.value != this.valueBefore) {
49457
49458             this.fireEvent('change', this, this.value, this.valueBefore);
49459             this.valueBefore = this.value;
49460         }
49461         
49462     },
49463     
49464     setValue : function(v){
49465         // Roo.log(v);
49466         this.value = v;
49467         
49468         var vals = this.getValueArray();
49469         var tv = [];
49470         Roo.each(vals, function(k) {
49471             var r = this.findRecord(this.valueField, k);
49472             if(r){
49473                 tv.push(r.data[this.displayField]);
49474             }else if(this.valueNotFoundText !== undefined){
49475                 tv.push( this.valueNotFoundText );
49476             }
49477         },this);
49478        // Roo.log(tv);
49479         
49480         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
49481         this.hiddenField.value = v;
49482         this.value = v;
49483     }
49484     
49485 });/*
49486  * Based on:
49487  * Ext JS Library 1.1.1
49488  * Copyright(c) 2006-2007, Ext JS, LLC.
49489  *
49490  * Originally Released Under LGPL - original licence link has changed is not relivant.
49491  *
49492  * Fork - LGPL
49493  * <script type="text/javascript">
49494  */
49495  
49496 /**
49497  * @class Roo.form.Signature
49498  * @extends Roo.form.Field
49499  * Signature field.  
49500  * @constructor
49501  * 
49502  * @param {Object} config Configuration options
49503  */
49504
49505 Roo.form.Signature = function(config){
49506     Roo.form.Signature.superclass.constructor.call(this, config);
49507     
49508     this.addEvents({// not in used??
49509          /**
49510          * @event confirm
49511          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
49512              * @param {Roo.form.Signature} combo This combo box
49513              */
49514         'confirm' : true,
49515         /**
49516          * @event reset
49517          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
49518              * @param {Roo.form.ComboBox} combo This combo box
49519              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
49520              */
49521         'reset' : true
49522     });
49523 };
49524
49525 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
49526     /**
49527      * @cfg {Object} labels Label to use when rendering a form.
49528      * defaults to 
49529      * labels : { 
49530      *      clear : "Clear",
49531      *      confirm : "Confirm"
49532      *  }
49533      */
49534     labels : { 
49535         clear : "Clear",
49536         confirm : "Confirm"
49537     },
49538     /**
49539      * @cfg {Number} width The signature panel width (defaults to 300)
49540      */
49541     width: 300,
49542     /**
49543      * @cfg {Number} height The signature panel height (defaults to 100)
49544      */
49545     height : 100,
49546     /**
49547      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
49548      */
49549     allowBlank : false,
49550     
49551     //private
49552     // {Object} signPanel The signature SVG panel element (defaults to {})
49553     signPanel : {},
49554     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
49555     isMouseDown : false,
49556     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
49557     isConfirmed : false,
49558     // {String} signatureTmp SVG mapping string (defaults to empty string)
49559     signatureTmp : '',
49560     
49561     
49562     defaultAutoCreate : { // modified by initCompnoent..
49563         tag: "input",
49564         type:"hidden"
49565     },
49566
49567     // private
49568     onRender : function(ct, position){
49569         
49570         Roo.form.Signature.superclass.onRender.call(this, ct, position);
49571         
49572         this.wrap = this.el.wrap({
49573             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
49574         });
49575         
49576         this.createToolbar(this);
49577         this.signPanel = this.wrap.createChild({
49578                 tag: 'div',
49579                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
49580             }, this.el
49581         );
49582             
49583         this.svgID = Roo.id();
49584         this.svgEl = this.signPanel.createChild({
49585               xmlns : 'http://www.w3.org/2000/svg',
49586               tag : 'svg',
49587               id : this.svgID + "-svg",
49588               width: this.width,
49589               height: this.height,
49590               viewBox: '0 0 '+this.width+' '+this.height,
49591               cn : [
49592                 {
49593                     tag: "rect",
49594                     id: this.svgID + "-svg-r",
49595                     width: this.width,
49596                     height: this.height,
49597                     fill: "#ffa"
49598                 },
49599                 {
49600                     tag: "line",
49601                     id: this.svgID + "-svg-l",
49602                     x1: "0", // start
49603                     y1: (this.height*0.8), // start set the line in 80% of height
49604                     x2: this.width, // end
49605                     y2: (this.height*0.8), // end set the line in 80% of height
49606                     'stroke': "#666",
49607                     'stroke-width': "1",
49608                     'stroke-dasharray': "3",
49609                     'shape-rendering': "crispEdges",
49610                     'pointer-events': "none"
49611                 },
49612                 {
49613                     tag: "path",
49614                     id: this.svgID + "-svg-p",
49615                     'stroke': "navy",
49616                     'stroke-width': "3",
49617                     'fill': "none",
49618                     'pointer-events': 'none'
49619                 }
49620               ]
49621         });
49622         this.createSVG();
49623         this.svgBox = this.svgEl.dom.getScreenCTM();
49624     },
49625     createSVG : function(){ 
49626         var svg = this.signPanel;
49627         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
49628         var t = this;
49629
49630         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
49631         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
49632         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
49633         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
49634         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
49635         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
49636         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
49637         
49638     },
49639     isTouchEvent : function(e){
49640         return e.type.match(/^touch/);
49641     },
49642     getCoords : function (e) {
49643         var pt    = this.svgEl.dom.createSVGPoint();
49644         pt.x = e.clientX; 
49645         pt.y = e.clientY;
49646         if (this.isTouchEvent(e)) {
49647             pt.x =  e.targetTouches[0].clientX;
49648             pt.y = e.targetTouches[0].clientY;
49649         }
49650         var a = this.svgEl.dom.getScreenCTM();
49651         var b = a.inverse();
49652         var mx = pt.matrixTransform(b);
49653         return mx.x + ',' + mx.y;
49654     },
49655     //mouse event headler 
49656     down : function (e) {
49657         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
49658         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
49659         
49660         this.isMouseDown = true;
49661         
49662         e.preventDefault();
49663     },
49664     move : function (e) {
49665         if (this.isMouseDown) {
49666             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
49667             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
49668         }
49669         
49670         e.preventDefault();
49671     },
49672     up : function (e) {
49673         this.isMouseDown = false;
49674         var sp = this.signatureTmp.split(' ');
49675         
49676         if(sp.length > 1){
49677             if(!sp[sp.length-2].match(/^L/)){
49678                 sp.pop();
49679                 sp.pop();
49680                 sp.push("");
49681                 this.signatureTmp = sp.join(" ");
49682             }
49683         }
49684         if(this.getValue() != this.signatureTmp){
49685             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49686             this.isConfirmed = false;
49687         }
49688         e.preventDefault();
49689     },
49690     
49691     /**
49692      * Protected method that will not generally be called directly. It
49693      * is called when the editor creates its toolbar. Override this method if you need to
49694      * add custom toolbar buttons.
49695      * @param {HtmlEditor} editor
49696      */
49697     createToolbar : function(editor){
49698          function btn(id, toggle, handler){
49699             var xid = fid + '-'+ id ;
49700             return {
49701                 id : xid,
49702                 cmd : id,
49703                 cls : 'x-btn-icon x-edit-'+id,
49704                 enableToggle:toggle !== false,
49705                 scope: editor, // was editor...
49706                 handler:handler||editor.relayBtnCmd,
49707                 clickEvent:'mousedown',
49708                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49709                 tabIndex:-1
49710             };
49711         }
49712         
49713         
49714         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
49715         this.tb = tb;
49716         this.tb.add(
49717            {
49718                 cls : ' x-signature-btn x-signature-'+id,
49719                 scope: editor, // was editor...
49720                 handler: this.reset,
49721                 clickEvent:'mousedown',
49722                 text: this.labels.clear
49723             },
49724             {
49725                  xtype : 'Fill',
49726                  xns: Roo.Toolbar
49727             }, 
49728             {
49729                 cls : '  x-signature-btn x-signature-'+id,
49730                 scope: editor, // was editor...
49731                 handler: this.confirmHandler,
49732                 clickEvent:'mousedown',
49733                 text: this.labels.confirm
49734             }
49735         );
49736     
49737     },
49738     //public
49739     /**
49740      * when user is clicked confirm then show this image.....
49741      * 
49742      * @return {String} Image Data URI
49743      */
49744     getImageDataURI : function(){
49745         var svg = this.svgEl.dom.parentNode.innerHTML;
49746         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
49747         return src; 
49748     },
49749     /**
49750      * 
49751      * @return {Boolean} this.isConfirmed
49752      */
49753     getConfirmed : function(){
49754         return this.isConfirmed;
49755     },
49756     /**
49757      * 
49758      * @return {Number} this.width
49759      */
49760     getWidth : function(){
49761         return this.width;
49762     },
49763     /**
49764      * 
49765      * @return {Number} this.height
49766      */
49767     getHeight : function(){
49768         return this.height;
49769     },
49770     // private
49771     getSignature : function(){
49772         return this.signatureTmp;
49773     },
49774     // private
49775     reset : function(){
49776         this.signatureTmp = '';
49777         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49778         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
49779         this.isConfirmed = false;
49780         Roo.form.Signature.superclass.reset.call(this);
49781     },
49782     setSignature : function(s){
49783         this.signatureTmp = s;
49784         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49785         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
49786         this.setValue(s);
49787         this.isConfirmed = false;
49788         Roo.form.Signature.superclass.reset.call(this);
49789     }, 
49790     test : function(){
49791 //        Roo.log(this.signPanel.dom.contentWindow.up())
49792     },
49793     //private
49794     setConfirmed : function(){
49795         
49796         
49797         
49798 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
49799     },
49800     // private
49801     confirmHandler : function(){
49802         if(!this.getSignature()){
49803             return;
49804         }
49805         
49806         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
49807         this.setValue(this.getSignature());
49808         this.isConfirmed = true;
49809         
49810         this.fireEvent('confirm', this);
49811     },
49812     // private
49813     // Subclasses should provide the validation implementation by overriding this
49814     validateValue : function(value){
49815         if(this.allowBlank){
49816             return true;
49817         }
49818         
49819         if(this.isConfirmed){
49820             return true;
49821         }
49822         return false;
49823     }
49824 });/*
49825  * Based on:
49826  * Ext JS Library 1.1.1
49827  * Copyright(c) 2006-2007, Ext JS, LLC.
49828  *
49829  * Originally Released Under LGPL - original licence link has changed is not relivant.
49830  *
49831  * Fork - LGPL
49832  * <script type="text/javascript">
49833  */
49834  
49835
49836 /**
49837  * @class Roo.form.ComboBox
49838  * @extends Roo.form.TriggerField
49839  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
49840  * @constructor
49841  * Create a new ComboBox.
49842  * @param {Object} config Configuration options
49843  */
49844 Roo.form.Select = function(config){
49845     Roo.form.Select.superclass.constructor.call(this, config);
49846      
49847 };
49848
49849 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
49850     /**
49851      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
49852      */
49853     /**
49854      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
49855      * rendering into an Roo.Editor, defaults to false)
49856      */
49857     /**
49858      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
49859      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
49860      */
49861     /**
49862      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
49863      */
49864     /**
49865      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
49866      * the dropdown list (defaults to undefined, with no header element)
49867      */
49868
49869      /**
49870      * @cfg {String/Roo.Template} tpl The template to use to render the output
49871      */
49872      
49873     // private
49874     defaultAutoCreate : {tag: "select"  },
49875     /**
49876      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
49877      */
49878     listWidth: undefined,
49879     /**
49880      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
49881      * mode = 'remote' or 'text' if mode = 'local')
49882      */
49883     displayField: undefined,
49884     /**
49885      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
49886      * mode = 'remote' or 'value' if mode = 'local'). 
49887      * Note: use of a valueField requires the user make a selection
49888      * in order for a value to be mapped.
49889      */
49890     valueField: undefined,
49891     
49892     
49893     /**
49894      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
49895      * field's data value (defaults to the underlying DOM element's name)
49896      */
49897     hiddenName: undefined,
49898     /**
49899      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
49900      */
49901     listClass: '',
49902     /**
49903      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
49904      */
49905     selectedClass: 'x-combo-selected',
49906     /**
49907      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
49908      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
49909      * which displays a downward arrow icon).
49910      */
49911     triggerClass : 'x-form-arrow-trigger',
49912     /**
49913      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
49914      */
49915     shadow:'sides',
49916     /**
49917      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
49918      * anchor positions (defaults to 'tl-bl')
49919      */
49920     listAlign: 'tl-bl?',
49921     /**
49922      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
49923      */
49924     maxHeight: 300,
49925     /**
49926      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
49927      * query specified by the allQuery config option (defaults to 'query')
49928      */
49929     triggerAction: 'query',
49930     /**
49931      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
49932      * (defaults to 4, does not apply if editable = false)
49933      */
49934     minChars : 4,
49935     /**
49936      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
49937      * delay (typeAheadDelay) if it matches a known value (defaults to false)
49938      */
49939     typeAhead: false,
49940     /**
49941      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
49942      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
49943      */
49944     queryDelay: 500,
49945     /**
49946      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
49947      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
49948      */
49949     pageSize: 0,
49950     /**
49951      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
49952      * when editable = true (defaults to false)
49953      */
49954     selectOnFocus:false,
49955     /**
49956      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
49957      */
49958     queryParam: 'query',
49959     /**
49960      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
49961      * when mode = 'remote' (defaults to 'Loading...')
49962      */
49963     loadingText: 'Loading...',
49964     /**
49965      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
49966      */
49967     resizable: false,
49968     /**
49969      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
49970      */
49971     handleHeight : 8,
49972     /**
49973      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
49974      * traditional select (defaults to true)
49975      */
49976     editable: true,
49977     /**
49978      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
49979      */
49980     allQuery: '',
49981     /**
49982      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
49983      */
49984     mode: 'remote',
49985     /**
49986      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
49987      * listWidth has a higher value)
49988      */
49989     minListWidth : 70,
49990     /**
49991      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
49992      * allow the user to set arbitrary text into the field (defaults to false)
49993      */
49994     forceSelection:false,
49995     /**
49996      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
49997      * if typeAhead = true (defaults to 250)
49998      */
49999     typeAheadDelay : 250,
50000     /**
50001      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
50002      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
50003      */
50004     valueNotFoundText : undefined,
50005     
50006     /**
50007      * @cfg {String} defaultValue The value displayed after loading the store.
50008      */
50009     defaultValue: '',
50010     
50011     /**
50012      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
50013      */
50014     blockFocus : false,
50015     
50016     /**
50017      * @cfg {Boolean} disableClear Disable showing of clear button.
50018      */
50019     disableClear : false,
50020     /**
50021      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
50022      */
50023     alwaysQuery : false,
50024     
50025     //private
50026     addicon : false,
50027     editicon: false,
50028     
50029     // element that contains real text value.. (when hidden is used..)
50030      
50031     // private
50032     onRender : function(ct, position){
50033         Roo.form.Field.prototype.onRender.call(this, ct, position);
50034         
50035         if(this.store){
50036             this.store.on('beforeload', this.onBeforeLoad, this);
50037             this.store.on('load', this.onLoad, this);
50038             this.store.on('loadexception', this.onLoadException, this);
50039             this.store.load({});
50040         }
50041         
50042         
50043         
50044     },
50045
50046     // private
50047     initEvents : function(){
50048         //Roo.form.ComboBox.superclass.initEvents.call(this);
50049  
50050     },
50051
50052     onDestroy : function(){
50053        
50054         if(this.store){
50055             this.store.un('beforeload', this.onBeforeLoad, this);
50056             this.store.un('load', this.onLoad, this);
50057             this.store.un('loadexception', this.onLoadException, this);
50058         }
50059         //Roo.form.ComboBox.superclass.onDestroy.call(this);
50060     },
50061
50062     // private
50063     fireKey : function(e){
50064         if(e.isNavKeyPress() && !this.list.isVisible()){
50065             this.fireEvent("specialkey", this, e);
50066         }
50067     },
50068
50069     // private
50070     onResize: function(w, h){
50071         
50072         return; 
50073     
50074         
50075     },
50076
50077     /**
50078      * Allow or prevent the user from directly editing the field text.  If false is passed,
50079      * the user will only be able to select from the items defined in the dropdown list.  This method
50080      * is the runtime equivalent of setting the 'editable' config option at config time.
50081      * @param {Boolean} value True to allow the user to directly edit the field text
50082      */
50083     setEditable : function(value){
50084          
50085     },
50086
50087     // private
50088     onBeforeLoad : function(){
50089         
50090         Roo.log("Select before load");
50091         return;
50092     
50093         this.innerList.update(this.loadingText ?
50094                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
50095         //this.restrictHeight();
50096         this.selectedIndex = -1;
50097     },
50098
50099     // private
50100     onLoad : function(){
50101
50102     
50103         var dom = this.el.dom;
50104         dom.innerHTML = '';
50105          var od = dom.ownerDocument;
50106          
50107         if (this.emptyText) {
50108             var op = od.createElement('option');
50109             op.setAttribute('value', '');
50110             op.innerHTML = String.format('{0}', this.emptyText);
50111             dom.appendChild(op);
50112         }
50113         if(this.store.getCount() > 0){
50114            
50115             var vf = this.valueField;
50116             var df = this.displayField;
50117             this.store.data.each(function(r) {
50118                 // which colmsn to use... testing - cdoe / title..
50119                 var op = od.createElement('option');
50120                 op.setAttribute('value', r.data[vf]);
50121                 op.innerHTML = String.format('{0}', r.data[df]);
50122                 dom.appendChild(op);
50123             });
50124             if (typeof(this.defaultValue != 'undefined')) {
50125                 this.setValue(this.defaultValue);
50126             }
50127             
50128              
50129         }else{
50130             //this.onEmptyResults();
50131         }
50132         //this.el.focus();
50133     },
50134     // private
50135     onLoadException : function()
50136     {
50137         dom.innerHTML = '';
50138             
50139         Roo.log("Select on load exception");
50140         return;
50141     
50142         this.collapse();
50143         Roo.log(this.store.reader.jsonData);
50144         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
50145             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
50146         }
50147         
50148         
50149     },
50150     // private
50151     onTypeAhead : function(){
50152          
50153     },
50154
50155     // private
50156     onSelect : function(record, index){
50157         Roo.log('on select?');
50158         return;
50159         if(this.fireEvent('beforeselect', this, record, index) !== false){
50160             this.setFromData(index > -1 ? record.data : false);
50161             this.collapse();
50162             this.fireEvent('select', this, record, index);
50163         }
50164     },
50165
50166     /**
50167      * Returns the currently selected field value or empty string if no value is set.
50168      * @return {String} value The selected value
50169      */
50170     getValue : function(){
50171         var dom = this.el.dom;
50172         this.value = dom.options[dom.selectedIndex].value;
50173         return this.value;
50174         
50175     },
50176
50177     /**
50178      * Clears any text/value currently set in the field
50179      */
50180     clearValue : function(){
50181         this.value = '';
50182         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
50183         
50184     },
50185
50186     /**
50187      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
50188      * will be displayed in the field.  If the value does not match the data value of an existing item,
50189      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
50190      * Otherwise the field will be blank (although the value will still be set).
50191      * @param {String} value The value to match
50192      */
50193     setValue : function(v){
50194         var d = this.el.dom;
50195         for (var i =0; i < d.options.length;i++) {
50196             if (v == d.options[i].value) {
50197                 d.selectedIndex = i;
50198                 this.value = v;
50199                 return;
50200             }
50201         }
50202         this.clearValue();
50203     },
50204     /**
50205      * @property {Object} the last set data for the element
50206      */
50207     
50208     lastData : false,
50209     /**
50210      * Sets the value of the field based on a object which is related to the record format for the store.
50211      * @param {Object} value the value to set as. or false on reset?
50212      */
50213     setFromData : function(o){
50214         Roo.log('setfrom data?');
50215          
50216         
50217         
50218     },
50219     // private
50220     reset : function(){
50221         this.clearValue();
50222     },
50223     // private
50224     findRecord : function(prop, value){
50225         
50226         return false;
50227     
50228         var record;
50229         if(this.store.getCount() > 0){
50230             this.store.each(function(r){
50231                 if(r.data[prop] == value){
50232                     record = r;
50233                     return false;
50234                 }
50235                 return true;
50236             });
50237         }
50238         return record;
50239     },
50240     
50241     getName: function()
50242     {
50243         // returns hidden if it's set..
50244         if (!this.rendered) {return ''};
50245         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
50246         
50247     },
50248      
50249
50250     
50251
50252     // private
50253     onEmptyResults : function(){
50254         Roo.log('empty results');
50255         //this.collapse();
50256     },
50257
50258     /**
50259      * Returns true if the dropdown list is expanded, else false.
50260      */
50261     isExpanded : function(){
50262         return false;
50263     },
50264
50265     /**
50266      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
50267      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50268      * @param {String} value The data value of the item to select
50269      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50270      * selected item if it is not currently in view (defaults to true)
50271      * @return {Boolean} True if the value matched an item in the list, else false
50272      */
50273     selectByValue : function(v, scrollIntoView){
50274         Roo.log('select By Value');
50275         return false;
50276     
50277         if(v !== undefined && v !== null){
50278             var r = this.findRecord(this.valueField || this.displayField, v);
50279             if(r){
50280                 this.select(this.store.indexOf(r), scrollIntoView);
50281                 return true;
50282             }
50283         }
50284         return false;
50285     },
50286
50287     /**
50288      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
50289      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50290      * @param {Number} index The zero-based index of the list item to select
50291      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50292      * selected item if it is not currently in view (defaults to true)
50293      */
50294     select : function(index, scrollIntoView){
50295         Roo.log('select ');
50296         return  ;
50297         
50298         this.selectedIndex = index;
50299         this.view.select(index);
50300         if(scrollIntoView !== false){
50301             var el = this.view.getNode(index);
50302             if(el){
50303                 this.innerList.scrollChildIntoView(el, false);
50304             }
50305         }
50306     },
50307
50308       
50309
50310     // private
50311     validateBlur : function(){
50312         
50313         return;
50314         
50315     },
50316
50317     // private
50318     initQuery : function(){
50319         this.doQuery(this.getRawValue());
50320     },
50321
50322     // private
50323     doForce : function(){
50324         if(this.el.dom.value.length > 0){
50325             this.el.dom.value =
50326                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
50327              
50328         }
50329     },
50330
50331     /**
50332      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
50333      * query allowing the query action to be canceled if needed.
50334      * @param {String} query The SQL query to execute
50335      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
50336      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
50337      * saved in the current store (defaults to false)
50338      */
50339     doQuery : function(q, forceAll){
50340         
50341         Roo.log('doQuery?');
50342         if(q === undefined || q === null){
50343             q = '';
50344         }
50345         var qe = {
50346             query: q,
50347             forceAll: forceAll,
50348             combo: this,
50349             cancel:false
50350         };
50351         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
50352             return false;
50353         }
50354         q = qe.query;
50355         forceAll = qe.forceAll;
50356         if(forceAll === true || (q.length >= this.minChars)){
50357             if(this.lastQuery != q || this.alwaysQuery){
50358                 this.lastQuery = q;
50359                 if(this.mode == 'local'){
50360                     this.selectedIndex = -1;
50361                     if(forceAll){
50362                         this.store.clearFilter();
50363                     }else{
50364                         this.store.filter(this.displayField, q);
50365                     }
50366                     this.onLoad();
50367                 }else{
50368                     this.store.baseParams[this.queryParam] = q;
50369                     this.store.load({
50370                         params: this.getParams(q)
50371                     });
50372                     this.expand();
50373                 }
50374             }else{
50375                 this.selectedIndex = -1;
50376                 this.onLoad();   
50377             }
50378         }
50379     },
50380
50381     // private
50382     getParams : function(q){
50383         var p = {};
50384         //p[this.queryParam] = q;
50385         if(this.pageSize){
50386             p.start = 0;
50387             p.limit = this.pageSize;
50388         }
50389         return p;
50390     },
50391
50392     /**
50393      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
50394      */
50395     collapse : function(){
50396         
50397     },
50398
50399     // private
50400     collapseIf : function(e){
50401         
50402     },
50403
50404     /**
50405      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
50406      */
50407     expand : function(){
50408         
50409     } ,
50410
50411     // private
50412      
50413
50414     /** 
50415     * @cfg {Boolean} grow 
50416     * @hide 
50417     */
50418     /** 
50419     * @cfg {Number} growMin 
50420     * @hide 
50421     */
50422     /** 
50423     * @cfg {Number} growMax 
50424     * @hide 
50425     */
50426     /**
50427      * @hide
50428      * @method autoSize
50429      */
50430     
50431     setWidth : function()
50432     {
50433         
50434     },
50435     getResizeEl : function(){
50436         return this.el;
50437     }
50438 });//<script type="text/javasscript">
50439  
50440
50441 /**
50442  * @class Roo.DDView
50443  * A DnD enabled version of Roo.View.
50444  * @param {Element/String} container The Element in which to create the View.
50445  * @param {String} tpl The template string used to create the markup for each element of the View
50446  * @param {Object} config The configuration properties. These include all the config options of
50447  * {@link Roo.View} plus some specific to this class.<br>
50448  * <p>
50449  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
50450  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
50451  * <p>
50452  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
50453 .x-view-drag-insert-above {
50454         border-top:1px dotted #3366cc;
50455 }
50456 .x-view-drag-insert-below {
50457         border-bottom:1px dotted #3366cc;
50458 }
50459 </code></pre>
50460  * 
50461  */
50462  
50463 Roo.DDView = function(container, tpl, config) {
50464     Roo.DDView.superclass.constructor.apply(this, arguments);
50465     this.getEl().setStyle("outline", "0px none");
50466     this.getEl().unselectable();
50467     if (this.dragGroup) {
50468                 this.setDraggable(this.dragGroup.split(","));
50469     }
50470     if (this.dropGroup) {
50471                 this.setDroppable(this.dropGroup.split(","));
50472     }
50473     if (this.deletable) {
50474         this.setDeletable();
50475     }
50476     this.isDirtyFlag = false;
50477         this.addEvents({
50478                 "drop" : true
50479         });
50480 };
50481
50482 Roo.extend(Roo.DDView, Roo.View, {
50483 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
50484 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
50485 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
50486 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
50487
50488         isFormField: true,
50489
50490         reset: Roo.emptyFn,
50491         
50492         clearInvalid: Roo.form.Field.prototype.clearInvalid,
50493
50494         validate: function() {
50495                 return true;
50496         },
50497         
50498         destroy: function() {
50499                 this.purgeListeners();
50500                 this.getEl.removeAllListeners();
50501                 this.getEl().remove();
50502                 if (this.dragZone) {
50503                         if (this.dragZone.destroy) {
50504                                 this.dragZone.destroy();
50505                         }
50506                 }
50507                 if (this.dropZone) {
50508                         if (this.dropZone.destroy) {
50509                                 this.dropZone.destroy();
50510                         }
50511                 }
50512         },
50513
50514 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
50515         getName: function() {
50516                 return this.name;
50517         },
50518
50519 /**     Loads the View from a JSON string representing the Records to put into the Store. */
50520         setValue: function(v) {
50521                 if (!this.store) {
50522                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
50523                 }
50524                 var data = {};
50525                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
50526                 this.store.proxy = new Roo.data.MemoryProxy(data);
50527                 this.store.load();
50528         },
50529
50530 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
50531         getValue: function() {
50532                 var result = '(';
50533                 this.store.each(function(rec) {
50534                         result += rec.id + ',';
50535                 });
50536                 return result.substr(0, result.length - 1) + ')';
50537         },
50538         
50539         getIds: function() {
50540                 var i = 0, result = new Array(this.store.getCount());
50541                 this.store.each(function(rec) {
50542                         result[i++] = rec.id;
50543                 });
50544                 return result;
50545         },
50546         
50547         isDirty: function() {
50548                 return this.isDirtyFlag;
50549         },
50550
50551 /**
50552  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
50553  *      whole Element becomes the target, and this causes the drop gesture to append.
50554  */
50555     getTargetFromEvent : function(e) {
50556                 var target = e.getTarget();
50557                 while ((target !== null) && (target.parentNode != this.el.dom)) {
50558                 target = target.parentNode;
50559                 }
50560                 if (!target) {
50561                         target = this.el.dom.lastChild || this.el.dom;
50562                 }
50563                 return target;
50564     },
50565
50566 /**
50567  *      Create the drag data which consists of an object which has the property "ddel" as
50568  *      the drag proxy element. 
50569  */
50570     getDragData : function(e) {
50571         var target = this.findItemFromChild(e.getTarget());
50572                 if(target) {
50573                         this.handleSelection(e);
50574                         var selNodes = this.getSelectedNodes();
50575             var dragData = {
50576                 source: this,
50577                 copy: this.copy || (this.allowCopy && e.ctrlKey),
50578                 nodes: selNodes,
50579                 records: []
50580                         };
50581                         var selectedIndices = this.getSelectedIndexes();
50582                         for (var i = 0; i < selectedIndices.length; i++) {
50583                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
50584                         }
50585                         if (selNodes.length == 1) {
50586                                 dragData.ddel = target.cloneNode(true); // the div element
50587                         } else {
50588                                 var div = document.createElement('div'); // create the multi element drag "ghost"
50589                                 div.className = 'multi-proxy';
50590                                 for (var i = 0, len = selNodes.length; i < len; i++) {
50591                                         div.appendChild(selNodes[i].cloneNode(true));
50592                                 }
50593                                 dragData.ddel = div;
50594                         }
50595             //console.log(dragData)
50596             //console.log(dragData.ddel.innerHTML)
50597                         return dragData;
50598                 }
50599         //console.log('nodragData')
50600                 return false;
50601     },
50602     
50603 /**     Specify to which ddGroup items in this DDView may be dragged. */
50604     setDraggable: function(ddGroup) {
50605         if (ddGroup instanceof Array) {
50606                 Roo.each(ddGroup, this.setDraggable, this);
50607                 return;
50608         }
50609         if (this.dragZone) {
50610                 this.dragZone.addToGroup(ddGroup);
50611         } else {
50612                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
50613                                 containerScroll: true,
50614                                 ddGroup: ddGroup 
50615
50616                         });
50617 //                      Draggability implies selection. DragZone's mousedown selects the element.
50618                         if (!this.multiSelect) { this.singleSelect = true; }
50619
50620 //                      Wire the DragZone's handlers up to methods in *this*
50621                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
50622                 }
50623     },
50624
50625 /**     Specify from which ddGroup this DDView accepts drops. */
50626     setDroppable: function(ddGroup) {
50627         if (ddGroup instanceof Array) {
50628                 Roo.each(ddGroup, this.setDroppable, this);
50629                 return;
50630         }
50631         if (this.dropZone) {
50632                 this.dropZone.addToGroup(ddGroup);
50633         } else {
50634                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
50635                                 containerScroll: true,
50636                                 ddGroup: ddGroup
50637                         });
50638
50639 //                      Wire the DropZone's handlers up to methods in *this*
50640                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
50641                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
50642                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
50643                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
50644                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
50645                 }
50646     },
50647
50648 /**     Decide whether to drop above or below a View node. */
50649     getDropPoint : function(e, n, dd){
50650         if (n == this.el.dom) { return "above"; }
50651                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
50652                 var c = t + (b - t) / 2;
50653                 var y = Roo.lib.Event.getPageY(e);
50654                 if(y <= c) {
50655                         return "above";
50656                 }else{
50657                         return "below";
50658                 }
50659     },
50660
50661     onNodeEnter : function(n, dd, e, data){
50662                 return false;
50663     },
50664     
50665     onNodeOver : function(n, dd, e, data){
50666                 var pt = this.getDropPoint(e, n, dd);
50667                 // set the insert point style on the target node
50668                 var dragElClass = this.dropNotAllowed;
50669                 if (pt) {
50670                         var targetElClass;
50671                         if (pt == "above"){
50672                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
50673                                 targetElClass = "x-view-drag-insert-above";
50674                         } else {
50675                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
50676                                 targetElClass = "x-view-drag-insert-below";
50677                         }
50678                         if (this.lastInsertClass != targetElClass){
50679                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
50680                                 this.lastInsertClass = targetElClass;
50681                         }
50682                 }
50683                 return dragElClass;
50684         },
50685
50686     onNodeOut : function(n, dd, e, data){
50687                 this.removeDropIndicators(n);
50688     },
50689
50690     onNodeDrop : function(n, dd, e, data){
50691         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
50692                 return false;
50693         }
50694         var pt = this.getDropPoint(e, n, dd);
50695                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
50696                 if (pt == "below") { insertAt++; }
50697                 for (var i = 0; i < data.records.length; i++) {
50698                         var r = data.records[i];
50699                         var dup = this.store.getById(r.id);
50700                         if (dup && (dd != this.dragZone)) {
50701                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
50702                         } else {
50703                                 if (data.copy) {
50704                                         this.store.insert(insertAt++, r.copy());
50705                                 } else {
50706                                         data.source.isDirtyFlag = true;
50707                                         r.store.remove(r);
50708                                         this.store.insert(insertAt++, r);
50709                                 }
50710                                 this.isDirtyFlag = true;
50711                         }
50712                 }
50713                 this.dragZone.cachedTarget = null;
50714                 return true;
50715     },
50716
50717     removeDropIndicators : function(n){
50718                 if(n){
50719                         Roo.fly(n).removeClass([
50720                                 "x-view-drag-insert-above",
50721                                 "x-view-drag-insert-below"]);
50722                         this.lastInsertClass = "_noclass";
50723                 }
50724     },
50725
50726 /**
50727  *      Utility method. Add a delete option to the DDView's context menu.
50728  *      @param {String} imageUrl The URL of the "delete" icon image.
50729  */
50730         setDeletable: function(imageUrl) {
50731                 if (!this.singleSelect && !this.multiSelect) {
50732                         this.singleSelect = true;
50733                 }
50734                 var c = this.getContextMenu();
50735                 this.contextMenu.on("itemclick", function(item) {
50736                         switch (item.id) {
50737                                 case "delete":
50738                                         this.remove(this.getSelectedIndexes());
50739                                         break;
50740                         }
50741                 }, this);
50742                 this.contextMenu.add({
50743                         icon: imageUrl,
50744                         id: "delete",
50745                         text: 'Delete'
50746                 });
50747         },
50748         
50749 /**     Return the context menu for this DDView. */
50750         getContextMenu: function() {
50751                 if (!this.contextMenu) {
50752 //                      Create the View's context menu
50753                         this.contextMenu = new Roo.menu.Menu({
50754                                 id: this.id + "-contextmenu"
50755                         });
50756                         this.el.on("contextmenu", this.showContextMenu, this);
50757                 }
50758                 return this.contextMenu;
50759         },
50760         
50761         disableContextMenu: function() {
50762                 if (this.contextMenu) {
50763                         this.el.un("contextmenu", this.showContextMenu, this);
50764                 }
50765         },
50766
50767         showContextMenu: function(e, item) {
50768         item = this.findItemFromChild(e.getTarget());
50769                 if (item) {
50770                         e.stopEvent();
50771                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
50772                         this.contextMenu.showAt(e.getXY());
50773             }
50774     },
50775
50776 /**
50777  *      Remove {@link Roo.data.Record}s at the specified indices.
50778  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
50779  */
50780     remove: function(selectedIndices) {
50781                 selectedIndices = [].concat(selectedIndices);
50782                 for (var i = 0; i < selectedIndices.length; i++) {
50783                         var rec = this.store.getAt(selectedIndices[i]);
50784                         this.store.remove(rec);
50785                 }
50786     },
50787
50788 /**
50789  *      Double click fires the event, but also, if this is draggable, and there is only one other
50790  *      related DropZone, it transfers the selected node.
50791  */
50792     onDblClick : function(e){
50793         var item = this.findItemFromChild(e.getTarget());
50794         if(item){
50795             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
50796                 return false;
50797             }
50798             if (this.dragGroup) {
50799                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
50800                     while (targets.indexOf(this.dropZone) > -1) {
50801                             targets.remove(this.dropZone);
50802                                 }
50803                     if (targets.length == 1) {
50804                                         this.dragZone.cachedTarget = null;
50805                         var el = Roo.get(targets[0].getEl());
50806                         var box = el.getBox(true);
50807                         targets[0].onNodeDrop(el.dom, {
50808                                 target: el.dom,
50809                                 xy: [box.x, box.y + box.height - 1]
50810                         }, null, this.getDragData(e));
50811                     }
50812                 }
50813         }
50814     },
50815     
50816     handleSelection: function(e) {
50817                 this.dragZone.cachedTarget = null;
50818         var item = this.findItemFromChild(e.getTarget());
50819         if (!item) {
50820                 this.clearSelections(true);
50821                 return;
50822         }
50823                 if (item && (this.multiSelect || this.singleSelect)){
50824                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
50825                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
50826                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
50827                                 this.unselect(item);
50828                         } else {
50829                                 this.select(item, this.multiSelect && e.ctrlKey);
50830                                 this.lastSelection = item;
50831                         }
50832                 }
50833     },
50834
50835     onItemClick : function(item, index, e){
50836                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
50837                         return false;
50838                 }
50839                 return true;
50840     },
50841
50842     unselect : function(nodeInfo, suppressEvent){
50843                 var node = this.getNode(nodeInfo);
50844                 if(node && this.isSelected(node)){
50845                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
50846                                 Roo.fly(node).removeClass(this.selectedClass);
50847                                 this.selections.remove(node);
50848                                 if(!suppressEvent){
50849                                         this.fireEvent("selectionchange", this, this.selections);
50850                                 }
50851                         }
50852                 }
50853     }
50854 });
50855 /*
50856  * Based on:
50857  * Ext JS Library 1.1.1
50858  * Copyright(c) 2006-2007, Ext JS, LLC.
50859  *
50860  * Originally Released Under LGPL - original licence link has changed is not relivant.
50861  *
50862  * Fork - LGPL
50863  * <script type="text/javascript">
50864  */
50865  
50866 /**
50867  * @class Roo.LayoutManager
50868  * @extends Roo.util.Observable
50869  * Base class for layout managers.
50870  */
50871 Roo.LayoutManager = function(container, config){
50872     Roo.LayoutManager.superclass.constructor.call(this);
50873     this.el = Roo.get(container);
50874     // ie scrollbar fix
50875     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
50876         document.body.scroll = "no";
50877     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
50878         this.el.position('relative');
50879     }
50880     this.id = this.el.id;
50881     this.el.addClass("x-layout-container");
50882     /** false to disable window resize monitoring @type Boolean */
50883     this.monitorWindowResize = true;
50884     this.regions = {};
50885     this.addEvents({
50886         /**
50887          * @event layout
50888          * Fires when a layout is performed. 
50889          * @param {Roo.LayoutManager} this
50890          */
50891         "layout" : true,
50892         /**
50893          * @event regionresized
50894          * Fires when the user resizes a region. 
50895          * @param {Roo.LayoutRegion} region The resized region
50896          * @param {Number} newSize The new size (width for east/west, height for north/south)
50897          */
50898         "regionresized" : true,
50899         /**
50900          * @event regioncollapsed
50901          * Fires when a region is collapsed. 
50902          * @param {Roo.LayoutRegion} region The collapsed region
50903          */
50904         "regioncollapsed" : true,
50905         /**
50906          * @event regionexpanded
50907          * Fires when a region is expanded.  
50908          * @param {Roo.LayoutRegion} region The expanded region
50909          */
50910         "regionexpanded" : true
50911     });
50912     this.updating = false;
50913     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
50914 };
50915
50916 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
50917     /**
50918      * Returns true if this layout is currently being updated
50919      * @return {Boolean}
50920      */
50921     isUpdating : function(){
50922         return this.updating; 
50923     },
50924     
50925     /**
50926      * Suspend the LayoutManager from doing auto-layouts while
50927      * making multiple add or remove calls
50928      */
50929     beginUpdate : function(){
50930         this.updating = true;    
50931     },
50932     
50933     /**
50934      * Restore auto-layouts and optionally disable the manager from performing a layout
50935      * @param {Boolean} noLayout true to disable a layout update 
50936      */
50937     endUpdate : function(noLayout){
50938         this.updating = false;
50939         if(!noLayout){
50940             this.layout();
50941         }    
50942     },
50943     
50944     layout: function(){
50945         
50946     },
50947     
50948     onRegionResized : function(region, newSize){
50949         this.fireEvent("regionresized", region, newSize);
50950         this.layout();
50951     },
50952     
50953     onRegionCollapsed : function(region){
50954         this.fireEvent("regioncollapsed", region);
50955     },
50956     
50957     onRegionExpanded : function(region){
50958         this.fireEvent("regionexpanded", region);
50959     },
50960         
50961     /**
50962      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
50963      * performs box-model adjustments.
50964      * @return {Object} The size as an object {width: (the width), height: (the height)}
50965      */
50966     getViewSize : function(){
50967         var size;
50968         if(this.el.dom != document.body){
50969             size = this.el.getSize();
50970         }else{
50971             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
50972         }
50973         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
50974         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
50975         return size;
50976     },
50977     
50978     /**
50979      * Returns the Element this layout is bound to.
50980      * @return {Roo.Element}
50981      */
50982     getEl : function(){
50983         return this.el;
50984     },
50985     
50986     /**
50987      * Returns the specified region.
50988      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
50989      * @return {Roo.LayoutRegion}
50990      */
50991     getRegion : function(target){
50992         return this.regions[target.toLowerCase()];
50993     },
50994     
50995     onWindowResize : function(){
50996         if(this.monitorWindowResize){
50997             this.layout();
50998         }
50999     }
51000 });/*
51001  * Based on:
51002  * Ext JS Library 1.1.1
51003  * Copyright(c) 2006-2007, Ext JS, LLC.
51004  *
51005  * Originally Released Under LGPL - original licence link has changed is not relivant.
51006  *
51007  * Fork - LGPL
51008  * <script type="text/javascript">
51009  */
51010 /**
51011  * @class Roo.BorderLayout
51012  * @extends Roo.LayoutManager
51013  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
51014  * please see: <br><br>
51015  * <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>
51016  * <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>
51017  * Example:
51018  <pre><code>
51019  var layout = new Roo.BorderLayout(document.body, {
51020     north: {
51021         initialSize: 25,
51022         titlebar: false
51023     },
51024     west: {
51025         split:true,
51026         initialSize: 200,
51027         minSize: 175,
51028         maxSize: 400,
51029         titlebar: true,
51030         collapsible: true
51031     },
51032     east: {
51033         split:true,
51034         initialSize: 202,
51035         minSize: 175,
51036         maxSize: 400,
51037         titlebar: true,
51038         collapsible: true
51039     },
51040     south: {
51041         split:true,
51042         initialSize: 100,
51043         minSize: 100,
51044         maxSize: 200,
51045         titlebar: true,
51046         collapsible: true
51047     },
51048     center: {
51049         titlebar: true,
51050         autoScroll:true,
51051         resizeTabs: true,
51052         minTabWidth: 50,
51053         preferredTabWidth: 150
51054     }
51055 });
51056
51057 // shorthand
51058 var CP = Roo.ContentPanel;
51059
51060 layout.beginUpdate();
51061 layout.add("north", new CP("north", "North"));
51062 layout.add("south", new CP("south", {title: "South", closable: true}));
51063 layout.add("west", new CP("west", {title: "West"}));
51064 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
51065 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
51066 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
51067 layout.getRegion("center").showPanel("center1");
51068 layout.endUpdate();
51069 </code></pre>
51070
51071 <b>The container the layout is rendered into can be either the body element or any other element.
51072 If it is not the body element, the container needs to either be an absolute positioned element,
51073 or you will need to add "position:relative" to the css of the container.  You will also need to specify
51074 the container size if it is not the body element.</b>
51075
51076 * @constructor
51077 * Create a new BorderLayout
51078 * @param {String/HTMLElement/Element} container The container this layout is bound to
51079 * @param {Object} config Configuration options
51080  */
51081 Roo.BorderLayout = function(container, config){
51082     config = config || {};
51083     Roo.BorderLayout.superclass.constructor.call(this, container, config);
51084     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
51085     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
51086         var target = this.factory.validRegions[i];
51087         if(config[target]){
51088             this.addRegion(target, config[target]);
51089         }
51090     }
51091 };
51092
51093 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
51094     /**
51095      * Creates and adds a new region if it doesn't already exist.
51096      * @param {String} target The target region key (north, south, east, west or center).
51097      * @param {Object} config The regions config object
51098      * @return {BorderLayoutRegion} The new region
51099      */
51100     addRegion : function(target, config){
51101         if(!this.regions[target]){
51102             var r = this.factory.create(target, this, config);
51103             this.bindRegion(target, r);
51104         }
51105         return this.regions[target];
51106     },
51107
51108     // private (kinda)
51109     bindRegion : function(name, r){
51110         this.regions[name] = r;
51111         r.on("visibilitychange", this.layout, this);
51112         r.on("paneladded", this.layout, this);
51113         r.on("panelremoved", this.layout, this);
51114         r.on("invalidated", this.layout, this);
51115         r.on("resized", this.onRegionResized, this);
51116         r.on("collapsed", this.onRegionCollapsed, this);
51117         r.on("expanded", this.onRegionExpanded, this);
51118     },
51119
51120     /**
51121      * Performs a layout update.
51122      */
51123     layout : function(){
51124         if(this.updating) {
51125             return;
51126         }
51127         var size = this.getViewSize();
51128         var w = size.width;
51129         var h = size.height;
51130         var centerW = w;
51131         var centerH = h;
51132         var centerY = 0;
51133         var centerX = 0;
51134         //var x = 0, y = 0;
51135
51136         var rs = this.regions;
51137         var north = rs["north"];
51138         var south = rs["south"]; 
51139         var west = rs["west"];
51140         var east = rs["east"];
51141         var center = rs["center"];
51142         //if(this.hideOnLayout){ // not supported anymore
51143             //c.el.setStyle("display", "none");
51144         //}
51145         if(north && north.isVisible()){
51146             var b = north.getBox();
51147             var m = north.getMargins();
51148             b.width = w - (m.left+m.right);
51149             b.x = m.left;
51150             b.y = m.top;
51151             centerY = b.height + b.y + m.bottom;
51152             centerH -= centerY;
51153             north.updateBox(this.safeBox(b));
51154         }
51155         if(south && south.isVisible()){
51156             var b = south.getBox();
51157             var m = south.getMargins();
51158             b.width = w - (m.left+m.right);
51159             b.x = m.left;
51160             var totalHeight = (b.height + m.top + m.bottom);
51161             b.y = h - totalHeight + m.top;
51162             centerH -= totalHeight;
51163             south.updateBox(this.safeBox(b));
51164         }
51165         if(west && west.isVisible()){
51166             var b = west.getBox();
51167             var m = west.getMargins();
51168             b.height = centerH - (m.top+m.bottom);
51169             b.x = m.left;
51170             b.y = centerY + m.top;
51171             var totalWidth = (b.width + m.left + m.right);
51172             centerX += totalWidth;
51173             centerW -= totalWidth;
51174             west.updateBox(this.safeBox(b));
51175         }
51176         if(east && east.isVisible()){
51177             var b = east.getBox();
51178             var m = east.getMargins();
51179             b.height = centerH - (m.top+m.bottom);
51180             var totalWidth = (b.width + m.left + m.right);
51181             b.x = w - totalWidth + m.left;
51182             b.y = centerY + m.top;
51183             centerW -= totalWidth;
51184             east.updateBox(this.safeBox(b));
51185         }
51186         if(center){
51187             var m = center.getMargins();
51188             var centerBox = {
51189                 x: centerX + m.left,
51190                 y: centerY + m.top,
51191                 width: centerW - (m.left+m.right),
51192                 height: centerH - (m.top+m.bottom)
51193             };
51194             //if(this.hideOnLayout){
51195                 //center.el.setStyle("display", "block");
51196             //}
51197             center.updateBox(this.safeBox(centerBox));
51198         }
51199         this.el.repaint();
51200         this.fireEvent("layout", this);
51201     },
51202
51203     // private
51204     safeBox : function(box){
51205         box.width = Math.max(0, box.width);
51206         box.height = Math.max(0, box.height);
51207         return box;
51208     },
51209
51210     /**
51211      * Adds a ContentPanel (or subclass) to this layout.
51212      * @param {String} target The target region key (north, south, east, west or center).
51213      * @param {Roo.ContentPanel} panel The panel to add
51214      * @return {Roo.ContentPanel} The added panel
51215      */
51216     add : function(target, panel){
51217          
51218         target = target.toLowerCase();
51219         return this.regions[target].add(panel);
51220     },
51221
51222     /**
51223      * Remove a ContentPanel (or subclass) to this layout.
51224      * @param {String} target The target region key (north, south, east, west or center).
51225      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
51226      * @return {Roo.ContentPanel} The removed panel
51227      */
51228     remove : function(target, panel){
51229         target = target.toLowerCase();
51230         return this.regions[target].remove(panel);
51231     },
51232
51233     /**
51234      * Searches all regions for a panel with the specified id
51235      * @param {String} panelId
51236      * @return {Roo.ContentPanel} The panel or null if it wasn't found
51237      */
51238     findPanel : function(panelId){
51239         var rs = this.regions;
51240         for(var target in rs){
51241             if(typeof rs[target] != "function"){
51242                 var p = rs[target].getPanel(panelId);
51243                 if(p){
51244                     return p;
51245                 }
51246             }
51247         }
51248         return null;
51249     },
51250
51251     /**
51252      * Searches all regions for a panel with the specified id and activates (shows) it.
51253      * @param {String/ContentPanel} panelId The panels id or the panel itself
51254      * @return {Roo.ContentPanel} The shown panel or null
51255      */
51256     showPanel : function(panelId) {
51257       var rs = this.regions;
51258       for(var target in rs){
51259          var r = rs[target];
51260          if(typeof r != "function"){
51261             if(r.hasPanel(panelId)){
51262                return r.showPanel(panelId);
51263             }
51264          }
51265       }
51266       return null;
51267    },
51268
51269    /**
51270      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
51271      * @param {Roo.state.Provider} provider (optional) An alternate state provider
51272      */
51273     restoreState : function(provider){
51274         if(!provider){
51275             provider = Roo.state.Manager;
51276         }
51277         var sm = new Roo.LayoutStateManager();
51278         sm.init(this, provider);
51279     },
51280
51281     /**
51282      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
51283      * object should contain properties for each region to add ContentPanels to, and each property's value should be
51284      * a valid ContentPanel config object.  Example:
51285      * <pre><code>
51286 // Create the main layout
51287 var layout = new Roo.BorderLayout('main-ct', {
51288     west: {
51289         split:true,
51290         minSize: 175,
51291         titlebar: true
51292     },
51293     center: {
51294         title:'Components'
51295     }
51296 }, 'main-ct');
51297
51298 // Create and add multiple ContentPanels at once via configs
51299 layout.batchAdd({
51300    west: {
51301        id: 'source-files',
51302        autoCreate:true,
51303        title:'Ext Source Files',
51304        autoScroll:true,
51305        fitToFrame:true
51306    },
51307    center : {
51308        el: cview,
51309        autoScroll:true,
51310        fitToFrame:true,
51311        toolbar: tb,
51312        resizeEl:'cbody'
51313    }
51314 });
51315 </code></pre>
51316      * @param {Object} regions An object containing ContentPanel configs by region name
51317      */
51318     batchAdd : function(regions){
51319         this.beginUpdate();
51320         for(var rname in regions){
51321             var lr = this.regions[rname];
51322             if(lr){
51323                 this.addTypedPanels(lr, regions[rname]);
51324             }
51325         }
51326         this.endUpdate();
51327     },
51328
51329     // private
51330     addTypedPanels : function(lr, ps){
51331         if(typeof ps == 'string'){
51332             lr.add(new Roo.ContentPanel(ps));
51333         }
51334         else if(ps instanceof Array){
51335             for(var i =0, len = ps.length; i < len; i++){
51336                 this.addTypedPanels(lr, ps[i]);
51337             }
51338         }
51339         else if(!ps.events){ // raw config?
51340             var el = ps.el;
51341             delete ps.el; // prevent conflict
51342             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
51343         }
51344         else {  // panel object assumed!
51345             lr.add(ps);
51346         }
51347     },
51348     /**
51349      * Adds a xtype elements to the layout.
51350      * <pre><code>
51351
51352 layout.addxtype({
51353        xtype : 'ContentPanel',
51354        region: 'west',
51355        items: [ .... ]
51356    }
51357 );
51358
51359 layout.addxtype({
51360         xtype : 'NestedLayoutPanel',
51361         region: 'west',
51362         layout: {
51363            center: { },
51364            west: { }   
51365         },
51366         items : [ ... list of content panels or nested layout panels.. ]
51367    }
51368 );
51369 </code></pre>
51370      * @param {Object} cfg Xtype definition of item to add.
51371      */
51372     addxtype : function(cfg)
51373     {
51374         // basically accepts a pannel...
51375         // can accept a layout region..!?!?
51376         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
51377         
51378         if (!cfg.xtype.match(/Panel$/)) {
51379             return false;
51380         }
51381         var ret = false;
51382         
51383         if (typeof(cfg.region) == 'undefined') {
51384             Roo.log("Failed to add Panel, region was not set");
51385             Roo.log(cfg);
51386             return false;
51387         }
51388         var region = cfg.region;
51389         delete cfg.region;
51390         
51391           
51392         var xitems = [];
51393         if (cfg.items) {
51394             xitems = cfg.items;
51395             delete cfg.items;
51396         }
51397         var nb = false;
51398         
51399         switch(cfg.xtype) 
51400         {
51401             case 'ContentPanel':  // ContentPanel (el, cfg)
51402             case 'ScrollPanel':  // ContentPanel (el, cfg)
51403             case 'ViewPanel': 
51404                 if(cfg.autoCreate) {
51405                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51406                 } else {
51407                     var el = this.el.createChild();
51408                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
51409                 }
51410                 
51411                 this.add(region, ret);
51412                 break;
51413             
51414             
51415             case 'TreePanel': // our new panel!
51416                 cfg.el = this.el.createChild();
51417                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51418                 this.add(region, ret);
51419                 break;
51420             
51421             case 'NestedLayoutPanel': 
51422                 // create a new Layout (which is  a Border Layout...
51423                 var el = this.el.createChild();
51424                 var clayout = cfg.layout;
51425                 delete cfg.layout;
51426                 clayout.items   = clayout.items  || [];
51427                 // replace this exitems with the clayout ones..
51428                 xitems = clayout.items;
51429                  
51430                 
51431                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
51432                     cfg.background = false;
51433                 }
51434                 var layout = new Roo.BorderLayout(el, clayout);
51435                 
51436                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
51437                 //console.log('adding nested layout panel '  + cfg.toSource());
51438                 this.add(region, ret);
51439                 nb = {}; /// find first...
51440                 break;
51441                 
51442             case 'GridPanel': 
51443             
51444                 // needs grid and region
51445                 
51446                 //var el = this.getRegion(region).el.createChild();
51447                 var el = this.el.createChild();
51448                 // create the grid first...
51449                 
51450                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
51451                 delete cfg.grid;
51452                 if (region == 'center' && this.active ) {
51453                     cfg.background = false;
51454                 }
51455                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
51456                 
51457                 this.add(region, ret);
51458                 if (cfg.background) {
51459                     ret.on('activate', function(gp) {
51460                         if (!gp.grid.rendered) {
51461                             gp.grid.render();
51462                         }
51463                     });
51464                 } else {
51465                     grid.render();
51466                 }
51467                 break;
51468            
51469            
51470            
51471                 
51472                 
51473                 
51474             default:
51475                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
51476                     
51477                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51478                     this.add(region, ret);
51479                 } else {
51480                 
51481                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
51482                     return null;
51483                 }
51484                 
51485              // GridPanel (grid, cfg)
51486             
51487         }
51488         this.beginUpdate();
51489         // add children..
51490         var region = '';
51491         var abn = {};
51492         Roo.each(xitems, function(i)  {
51493             region = nb && i.region ? i.region : false;
51494             
51495             var add = ret.addxtype(i);
51496            
51497             if (region) {
51498                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
51499                 if (!i.background) {
51500                     abn[region] = nb[region] ;
51501                 }
51502             }
51503             
51504         });
51505         this.endUpdate();
51506
51507         // make the last non-background panel active..
51508         //if (nb) { Roo.log(abn); }
51509         if (nb) {
51510             
51511             for(var r in abn) {
51512                 region = this.getRegion(r);
51513                 if (region) {
51514                     // tried using nb[r], but it does not work..
51515                      
51516                     region.showPanel(abn[r]);
51517                    
51518                 }
51519             }
51520         }
51521         return ret;
51522         
51523     }
51524 });
51525
51526 /**
51527  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
51528  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
51529  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
51530  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
51531  * <pre><code>
51532 // shorthand
51533 var CP = Roo.ContentPanel;
51534
51535 var layout = Roo.BorderLayout.create({
51536     north: {
51537         initialSize: 25,
51538         titlebar: false,
51539         panels: [new CP("north", "North")]
51540     },
51541     west: {
51542         split:true,
51543         initialSize: 200,
51544         minSize: 175,
51545         maxSize: 400,
51546         titlebar: true,
51547         collapsible: true,
51548         panels: [new CP("west", {title: "West"})]
51549     },
51550     east: {
51551         split:true,
51552         initialSize: 202,
51553         minSize: 175,
51554         maxSize: 400,
51555         titlebar: true,
51556         collapsible: true,
51557         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
51558     },
51559     south: {
51560         split:true,
51561         initialSize: 100,
51562         minSize: 100,
51563         maxSize: 200,
51564         titlebar: true,
51565         collapsible: true,
51566         panels: [new CP("south", {title: "South", closable: true})]
51567     },
51568     center: {
51569         titlebar: true,
51570         autoScroll:true,
51571         resizeTabs: true,
51572         minTabWidth: 50,
51573         preferredTabWidth: 150,
51574         panels: [
51575             new CP("center1", {title: "Close Me", closable: true}),
51576             new CP("center2", {title: "Center Panel", closable: false})
51577         ]
51578     }
51579 }, document.body);
51580
51581 layout.getRegion("center").showPanel("center1");
51582 </code></pre>
51583  * @param config
51584  * @param targetEl
51585  */
51586 Roo.BorderLayout.create = function(config, targetEl){
51587     var layout = new Roo.BorderLayout(targetEl || document.body, config);
51588     layout.beginUpdate();
51589     var regions = Roo.BorderLayout.RegionFactory.validRegions;
51590     for(var j = 0, jlen = regions.length; j < jlen; j++){
51591         var lr = regions[j];
51592         if(layout.regions[lr] && config[lr].panels){
51593             var r = layout.regions[lr];
51594             var ps = config[lr].panels;
51595             layout.addTypedPanels(r, ps);
51596         }
51597     }
51598     layout.endUpdate();
51599     return layout;
51600 };
51601
51602 // private
51603 Roo.BorderLayout.RegionFactory = {
51604     // private
51605     validRegions : ["north","south","east","west","center"],
51606
51607     // private
51608     create : function(target, mgr, config){
51609         target = target.toLowerCase();
51610         if(config.lightweight || config.basic){
51611             return new Roo.BasicLayoutRegion(mgr, config, target);
51612         }
51613         switch(target){
51614             case "north":
51615                 return new Roo.NorthLayoutRegion(mgr, config);
51616             case "south":
51617                 return new Roo.SouthLayoutRegion(mgr, config);
51618             case "east":
51619                 return new Roo.EastLayoutRegion(mgr, config);
51620             case "west":
51621                 return new Roo.WestLayoutRegion(mgr, config);
51622             case "center":
51623                 return new Roo.CenterLayoutRegion(mgr, config);
51624         }
51625         throw 'Layout region "'+target+'" not supported.';
51626     }
51627 };/*
51628  * Based on:
51629  * Ext JS Library 1.1.1
51630  * Copyright(c) 2006-2007, Ext JS, LLC.
51631  *
51632  * Originally Released Under LGPL - original licence link has changed is not relivant.
51633  *
51634  * Fork - LGPL
51635  * <script type="text/javascript">
51636  */
51637  
51638 /**
51639  * @class Roo.BasicLayoutRegion
51640  * @extends Roo.util.Observable
51641  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
51642  * and does not have a titlebar, tabs or any other features. All it does is size and position 
51643  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
51644  */
51645 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
51646     this.mgr = mgr;
51647     this.position  = pos;
51648     this.events = {
51649         /**
51650          * @scope Roo.BasicLayoutRegion
51651          */
51652         
51653         /**
51654          * @event beforeremove
51655          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
51656          * @param {Roo.LayoutRegion} this
51657          * @param {Roo.ContentPanel} panel The panel
51658          * @param {Object} e The cancel event object
51659          */
51660         "beforeremove" : true,
51661         /**
51662          * @event invalidated
51663          * Fires when the layout for this region is changed.
51664          * @param {Roo.LayoutRegion} this
51665          */
51666         "invalidated" : true,
51667         /**
51668          * @event visibilitychange
51669          * Fires when this region is shown or hidden 
51670          * @param {Roo.LayoutRegion} this
51671          * @param {Boolean} visibility true or false
51672          */
51673         "visibilitychange" : true,
51674         /**
51675          * @event paneladded
51676          * Fires when a panel is added. 
51677          * @param {Roo.LayoutRegion} this
51678          * @param {Roo.ContentPanel} panel The panel
51679          */
51680         "paneladded" : true,
51681         /**
51682          * @event panelremoved
51683          * Fires when a panel is removed. 
51684          * @param {Roo.LayoutRegion} this
51685          * @param {Roo.ContentPanel} panel The panel
51686          */
51687         "panelremoved" : true,
51688         /**
51689          * @event beforecollapse
51690          * Fires when this region before collapse.
51691          * @param {Roo.LayoutRegion} this
51692          */
51693         "beforecollapse" : true,
51694         /**
51695          * @event collapsed
51696          * Fires when this region is collapsed.
51697          * @param {Roo.LayoutRegion} this
51698          */
51699         "collapsed" : true,
51700         /**
51701          * @event expanded
51702          * Fires when this region is expanded.
51703          * @param {Roo.LayoutRegion} this
51704          */
51705         "expanded" : true,
51706         /**
51707          * @event slideshow
51708          * Fires when this region is slid into view.
51709          * @param {Roo.LayoutRegion} this
51710          */
51711         "slideshow" : true,
51712         /**
51713          * @event slidehide
51714          * Fires when this region slides out of view. 
51715          * @param {Roo.LayoutRegion} this
51716          */
51717         "slidehide" : true,
51718         /**
51719          * @event panelactivated
51720          * Fires when a panel is activated. 
51721          * @param {Roo.LayoutRegion} this
51722          * @param {Roo.ContentPanel} panel The activated panel
51723          */
51724         "panelactivated" : true,
51725         /**
51726          * @event resized
51727          * Fires when the user resizes this region. 
51728          * @param {Roo.LayoutRegion} this
51729          * @param {Number} newSize The new size (width for east/west, height for north/south)
51730          */
51731         "resized" : true
51732     };
51733     /** A collection of panels in this region. @type Roo.util.MixedCollection */
51734     this.panels = new Roo.util.MixedCollection();
51735     this.panels.getKey = this.getPanelId.createDelegate(this);
51736     this.box = null;
51737     this.activePanel = null;
51738     // ensure listeners are added...
51739     
51740     if (config.listeners || config.events) {
51741         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
51742             listeners : config.listeners || {},
51743             events : config.events || {}
51744         });
51745     }
51746     
51747     if(skipConfig !== true){
51748         this.applyConfig(config);
51749     }
51750 };
51751
51752 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
51753     getPanelId : function(p){
51754         return p.getId();
51755     },
51756     
51757     applyConfig : function(config){
51758         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51759         this.config = config;
51760         
51761     },
51762     
51763     /**
51764      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
51765      * the width, for horizontal (north, south) the height.
51766      * @param {Number} newSize The new width or height
51767      */
51768     resizeTo : function(newSize){
51769         var el = this.el ? this.el :
51770                  (this.activePanel ? this.activePanel.getEl() : null);
51771         if(el){
51772             switch(this.position){
51773                 case "east":
51774                 case "west":
51775                     el.setWidth(newSize);
51776                     this.fireEvent("resized", this, newSize);
51777                 break;
51778                 case "north":
51779                 case "south":
51780                     el.setHeight(newSize);
51781                     this.fireEvent("resized", this, newSize);
51782                 break;                
51783             }
51784         }
51785     },
51786     
51787     getBox : function(){
51788         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
51789     },
51790     
51791     getMargins : function(){
51792         return this.margins;
51793     },
51794     
51795     updateBox : function(box){
51796         this.box = box;
51797         var el = this.activePanel.getEl();
51798         el.dom.style.left = box.x + "px";
51799         el.dom.style.top = box.y + "px";
51800         this.activePanel.setSize(box.width, box.height);
51801     },
51802     
51803     /**
51804      * Returns the container element for this region.
51805      * @return {Roo.Element}
51806      */
51807     getEl : function(){
51808         return this.activePanel;
51809     },
51810     
51811     /**
51812      * Returns true if this region is currently visible.
51813      * @return {Boolean}
51814      */
51815     isVisible : function(){
51816         return this.activePanel ? true : false;
51817     },
51818     
51819     setActivePanel : function(panel){
51820         panel = this.getPanel(panel);
51821         if(this.activePanel && this.activePanel != panel){
51822             this.activePanel.setActiveState(false);
51823             this.activePanel.getEl().setLeftTop(-10000,-10000);
51824         }
51825         this.activePanel = panel;
51826         panel.setActiveState(true);
51827         if(this.box){
51828             panel.setSize(this.box.width, this.box.height);
51829         }
51830         this.fireEvent("panelactivated", this, panel);
51831         this.fireEvent("invalidated");
51832     },
51833     
51834     /**
51835      * Show the specified panel.
51836      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
51837      * @return {Roo.ContentPanel} The shown panel or null
51838      */
51839     showPanel : function(panel){
51840         if(panel = this.getPanel(panel)){
51841             this.setActivePanel(panel);
51842         }
51843         return panel;
51844     },
51845     
51846     /**
51847      * Get the active panel for this region.
51848      * @return {Roo.ContentPanel} The active panel or null
51849      */
51850     getActivePanel : function(){
51851         return this.activePanel;
51852     },
51853     
51854     /**
51855      * Add the passed ContentPanel(s)
51856      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
51857      * @return {Roo.ContentPanel} The panel added (if only one was added)
51858      */
51859     add : function(panel){
51860         if(arguments.length > 1){
51861             for(var i = 0, len = arguments.length; i < len; i++) {
51862                 this.add(arguments[i]);
51863             }
51864             return null;
51865         }
51866         if(this.hasPanel(panel)){
51867             this.showPanel(panel);
51868             return panel;
51869         }
51870         var el = panel.getEl();
51871         if(el.dom.parentNode != this.mgr.el.dom){
51872             this.mgr.el.dom.appendChild(el.dom);
51873         }
51874         if(panel.setRegion){
51875             panel.setRegion(this);
51876         }
51877         this.panels.add(panel);
51878         el.setStyle("position", "absolute");
51879         if(!panel.background){
51880             this.setActivePanel(panel);
51881             if(this.config.initialSize && this.panels.getCount()==1){
51882                 this.resizeTo(this.config.initialSize);
51883             }
51884         }
51885         this.fireEvent("paneladded", this, panel);
51886         return panel;
51887     },
51888     
51889     /**
51890      * Returns true if the panel is in this region.
51891      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51892      * @return {Boolean}
51893      */
51894     hasPanel : function(panel){
51895         if(typeof panel == "object"){ // must be panel obj
51896             panel = panel.getId();
51897         }
51898         return this.getPanel(panel) ? true : false;
51899     },
51900     
51901     /**
51902      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
51903      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51904      * @param {Boolean} preservePanel Overrides the config preservePanel option
51905      * @return {Roo.ContentPanel} The panel that was removed
51906      */
51907     remove : function(panel, preservePanel){
51908         panel = this.getPanel(panel);
51909         if(!panel){
51910             return null;
51911         }
51912         var e = {};
51913         this.fireEvent("beforeremove", this, panel, e);
51914         if(e.cancel === true){
51915             return null;
51916         }
51917         var panelId = panel.getId();
51918         this.panels.removeKey(panelId);
51919         return panel;
51920     },
51921     
51922     /**
51923      * Returns the panel specified or null if it's not in this region.
51924      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51925      * @return {Roo.ContentPanel}
51926      */
51927     getPanel : function(id){
51928         if(typeof id == "object"){ // must be panel obj
51929             return id;
51930         }
51931         return this.panels.get(id);
51932     },
51933     
51934     /**
51935      * Returns this regions position (north/south/east/west/center).
51936      * @return {String} 
51937      */
51938     getPosition: function(){
51939         return this.position;    
51940     }
51941 });/*
51942  * Based on:
51943  * Ext JS Library 1.1.1
51944  * Copyright(c) 2006-2007, Ext JS, LLC.
51945  *
51946  * Originally Released Under LGPL - original licence link has changed is not relivant.
51947  *
51948  * Fork - LGPL
51949  * <script type="text/javascript">
51950  */
51951  
51952 /**
51953  * @class Roo.LayoutRegion
51954  * @extends Roo.BasicLayoutRegion
51955  * This class represents a region in a layout manager.
51956  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
51957  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
51958  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
51959  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
51960  * @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})
51961  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
51962  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
51963  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
51964  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
51965  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
51966  * @cfg {String}    title           The title for the region (overrides panel titles)
51967  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
51968  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
51969  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
51970  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
51971  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
51972  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
51973  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
51974  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
51975  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
51976  * @cfg {Boolean}   showPin         True to show a pin button
51977  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
51978  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
51979  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
51980  * @cfg {Number}    width           For East/West panels
51981  * @cfg {Number}    height          For North/South panels
51982  * @cfg {Boolean}   split           To show the splitter
51983  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
51984  */
51985 Roo.LayoutRegion = function(mgr, config, pos){
51986     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
51987     var dh = Roo.DomHelper;
51988     /** This region's container element 
51989     * @type Roo.Element */
51990     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
51991     /** This region's title element 
51992     * @type Roo.Element */
51993
51994     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
51995         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
51996         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
51997     ]}, true);
51998     this.titleEl.enableDisplayMode();
51999     /** This region's title text element 
52000     * @type HTMLElement */
52001     this.titleTextEl = this.titleEl.dom.firstChild;
52002     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
52003     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
52004     this.closeBtn.enableDisplayMode();
52005     this.closeBtn.on("click", this.closeClicked, this);
52006     this.closeBtn.hide();
52007
52008     this.createBody(config);
52009     this.visible = true;
52010     this.collapsed = false;
52011
52012     if(config.hideWhenEmpty){
52013         this.hide();
52014         this.on("paneladded", this.validateVisibility, this);
52015         this.on("panelremoved", this.validateVisibility, this);
52016     }
52017     this.applyConfig(config);
52018 };
52019
52020 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
52021
52022     createBody : function(){
52023         /** This region's body element 
52024         * @type Roo.Element */
52025         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
52026     },
52027
52028     applyConfig : function(c){
52029         if(c.collapsible && this.position != "center" && !this.collapsedEl){
52030             var dh = Roo.DomHelper;
52031             if(c.titlebar !== false){
52032                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
52033                 this.collapseBtn.on("click", this.collapse, this);
52034                 this.collapseBtn.enableDisplayMode();
52035
52036                 if(c.showPin === true || this.showPin){
52037                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
52038                     this.stickBtn.enableDisplayMode();
52039                     this.stickBtn.on("click", this.expand, this);
52040                     this.stickBtn.hide();
52041                 }
52042             }
52043             /** This region's collapsed element
52044             * @type Roo.Element */
52045             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
52046                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
52047             ]}, true);
52048             if(c.floatable !== false){
52049                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
52050                this.collapsedEl.on("click", this.collapseClick, this);
52051             }
52052
52053             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
52054                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
52055                    id: "message", unselectable: "on", style:{"float":"left"}});
52056                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
52057              }
52058             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
52059             this.expandBtn.on("click", this.expand, this);
52060         }
52061         if(this.collapseBtn){
52062             this.collapseBtn.setVisible(c.collapsible == true);
52063         }
52064         this.cmargins = c.cmargins || this.cmargins ||
52065                          (this.position == "west" || this.position == "east" ?
52066                              {top: 0, left: 2, right:2, bottom: 0} :
52067                              {top: 2, left: 0, right:0, bottom: 2});
52068         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52069         this.bottomTabs = c.tabPosition != "top";
52070         this.autoScroll = c.autoScroll || false;
52071         if(this.autoScroll){
52072             this.bodyEl.setStyle("overflow", "auto");
52073         }else{
52074             this.bodyEl.setStyle("overflow", "hidden");
52075         }
52076         //if(c.titlebar !== false){
52077             if((!c.titlebar && !c.title) || c.titlebar === false){
52078                 this.titleEl.hide();
52079             }else{
52080                 this.titleEl.show();
52081                 if(c.title){
52082                     this.titleTextEl.innerHTML = c.title;
52083                 }
52084             }
52085         //}
52086         this.duration = c.duration || .30;
52087         this.slideDuration = c.slideDuration || .45;
52088         this.config = c;
52089         if(c.collapsed){
52090             this.collapse(true);
52091         }
52092         if(c.hidden){
52093             this.hide();
52094         }
52095     },
52096     /**
52097      * Returns true if this region is currently visible.
52098      * @return {Boolean}
52099      */
52100     isVisible : function(){
52101         return this.visible;
52102     },
52103
52104     /**
52105      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
52106      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
52107      */
52108     setCollapsedTitle : function(title){
52109         title = title || "&#160;";
52110         if(this.collapsedTitleTextEl){
52111             this.collapsedTitleTextEl.innerHTML = title;
52112         }
52113     },
52114
52115     getBox : function(){
52116         var b;
52117         if(!this.collapsed){
52118             b = this.el.getBox(false, true);
52119         }else{
52120             b = this.collapsedEl.getBox(false, true);
52121         }
52122         return b;
52123     },
52124
52125     getMargins : function(){
52126         return this.collapsed ? this.cmargins : this.margins;
52127     },
52128
52129     highlight : function(){
52130         this.el.addClass("x-layout-panel-dragover");
52131     },
52132
52133     unhighlight : function(){
52134         this.el.removeClass("x-layout-panel-dragover");
52135     },
52136
52137     updateBox : function(box){
52138         this.box = box;
52139         if(!this.collapsed){
52140             this.el.dom.style.left = box.x + "px";
52141             this.el.dom.style.top = box.y + "px";
52142             this.updateBody(box.width, box.height);
52143         }else{
52144             this.collapsedEl.dom.style.left = box.x + "px";
52145             this.collapsedEl.dom.style.top = box.y + "px";
52146             this.collapsedEl.setSize(box.width, box.height);
52147         }
52148         if(this.tabs){
52149             this.tabs.autoSizeTabs();
52150         }
52151     },
52152
52153     updateBody : function(w, h){
52154         if(w !== null){
52155             this.el.setWidth(w);
52156             w -= this.el.getBorderWidth("rl");
52157             if(this.config.adjustments){
52158                 w += this.config.adjustments[0];
52159             }
52160         }
52161         if(h !== null){
52162             this.el.setHeight(h);
52163             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
52164             h -= this.el.getBorderWidth("tb");
52165             if(this.config.adjustments){
52166                 h += this.config.adjustments[1];
52167             }
52168             this.bodyEl.setHeight(h);
52169             if(this.tabs){
52170                 h = this.tabs.syncHeight(h);
52171             }
52172         }
52173         if(this.panelSize){
52174             w = w !== null ? w : this.panelSize.width;
52175             h = h !== null ? h : this.panelSize.height;
52176         }
52177         if(this.activePanel){
52178             var el = this.activePanel.getEl();
52179             w = w !== null ? w : el.getWidth();
52180             h = h !== null ? h : el.getHeight();
52181             this.panelSize = {width: w, height: h};
52182             this.activePanel.setSize(w, h);
52183         }
52184         if(Roo.isIE && this.tabs){
52185             this.tabs.el.repaint();
52186         }
52187     },
52188
52189     /**
52190      * Returns the container element for this region.
52191      * @return {Roo.Element}
52192      */
52193     getEl : function(){
52194         return this.el;
52195     },
52196
52197     /**
52198      * Hides this region.
52199      */
52200     hide : function(){
52201         if(!this.collapsed){
52202             this.el.dom.style.left = "-2000px";
52203             this.el.hide();
52204         }else{
52205             this.collapsedEl.dom.style.left = "-2000px";
52206             this.collapsedEl.hide();
52207         }
52208         this.visible = false;
52209         this.fireEvent("visibilitychange", this, false);
52210     },
52211
52212     /**
52213      * Shows this region if it was previously hidden.
52214      */
52215     show : function(){
52216         if(!this.collapsed){
52217             this.el.show();
52218         }else{
52219             this.collapsedEl.show();
52220         }
52221         this.visible = true;
52222         this.fireEvent("visibilitychange", this, true);
52223     },
52224
52225     closeClicked : function(){
52226         if(this.activePanel){
52227             this.remove(this.activePanel);
52228         }
52229     },
52230
52231     collapseClick : function(e){
52232         if(this.isSlid){
52233            e.stopPropagation();
52234            this.slideIn();
52235         }else{
52236            e.stopPropagation();
52237            this.slideOut();
52238         }
52239     },
52240
52241     /**
52242      * Collapses this region.
52243      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
52244      */
52245     collapse : function(skipAnim, skipCheck = false){
52246         if(this.collapsed) {
52247             return;
52248         }
52249         
52250         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
52251             
52252             this.collapsed = true;
52253             if(this.split){
52254                 this.split.el.hide();
52255             }
52256             if(this.config.animate && skipAnim !== true){
52257                 this.fireEvent("invalidated", this);
52258                 this.animateCollapse();
52259             }else{
52260                 this.el.setLocation(-20000,-20000);
52261                 this.el.hide();
52262                 this.collapsedEl.show();
52263                 this.fireEvent("collapsed", this);
52264                 this.fireEvent("invalidated", this);
52265             }
52266         }
52267         
52268     },
52269
52270     animateCollapse : function(){
52271         // overridden
52272     },
52273
52274     /**
52275      * Expands this region if it was previously collapsed.
52276      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
52277      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
52278      */
52279     expand : function(e, skipAnim){
52280         if(e) {
52281             e.stopPropagation();
52282         }
52283         if(!this.collapsed || this.el.hasActiveFx()) {
52284             return;
52285         }
52286         if(this.isSlid){
52287             this.afterSlideIn();
52288             skipAnim = true;
52289         }
52290         this.collapsed = false;
52291         if(this.config.animate && skipAnim !== true){
52292             this.animateExpand();
52293         }else{
52294             this.el.show();
52295             if(this.split){
52296                 this.split.el.show();
52297             }
52298             this.collapsedEl.setLocation(-2000,-2000);
52299             this.collapsedEl.hide();
52300             this.fireEvent("invalidated", this);
52301             this.fireEvent("expanded", this);
52302         }
52303     },
52304
52305     animateExpand : function(){
52306         // overridden
52307     },
52308
52309     initTabs : function()
52310     {
52311         this.bodyEl.setStyle("overflow", "hidden");
52312         var ts = new Roo.TabPanel(
52313                 this.bodyEl.dom,
52314                 {
52315                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
52316                     disableTooltips: this.config.disableTabTips,
52317                     toolbar : this.config.toolbar
52318                 }
52319         );
52320         if(this.config.hideTabs){
52321             ts.stripWrap.setDisplayed(false);
52322         }
52323         this.tabs = ts;
52324         ts.resizeTabs = this.config.resizeTabs === true;
52325         ts.minTabWidth = this.config.minTabWidth || 40;
52326         ts.maxTabWidth = this.config.maxTabWidth || 250;
52327         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
52328         ts.monitorResize = false;
52329         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52330         ts.bodyEl.addClass('x-layout-tabs-body');
52331         this.panels.each(this.initPanelAsTab, this);
52332     },
52333
52334     initPanelAsTab : function(panel){
52335         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
52336                     this.config.closeOnTab && panel.isClosable());
52337         if(panel.tabTip !== undefined){
52338             ti.setTooltip(panel.tabTip);
52339         }
52340         ti.on("activate", function(){
52341               this.setActivePanel(panel);
52342         }, this);
52343         if(this.config.closeOnTab){
52344             ti.on("beforeclose", function(t, e){
52345                 e.cancel = true;
52346                 this.remove(panel);
52347             }, this);
52348         }
52349         return ti;
52350     },
52351
52352     updatePanelTitle : function(panel, title){
52353         if(this.activePanel == panel){
52354             this.updateTitle(title);
52355         }
52356         if(this.tabs){
52357             var ti = this.tabs.getTab(panel.getEl().id);
52358             ti.setText(title);
52359             if(panel.tabTip !== undefined){
52360                 ti.setTooltip(panel.tabTip);
52361             }
52362         }
52363     },
52364
52365     updateTitle : function(title){
52366         if(this.titleTextEl && !this.config.title){
52367             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
52368         }
52369     },
52370
52371     setActivePanel : function(panel){
52372         panel = this.getPanel(panel);
52373         if(this.activePanel && this.activePanel != panel){
52374             this.activePanel.setActiveState(false);
52375         }
52376         this.activePanel = panel;
52377         panel.setActiveState(true);
52378         if(this.panelSize){
52379             panel.setSize(this.panelSize.width, this.panelSize.height);
52380         }
52381         if(this.closeBtn){
52382             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
52383         }
52384         this.updateTitle(panel.getTitle());
52385         if(this.tabs){
52386             this.fireEvent("invalidated", this);
52387         }
52388         this.fireEvent("panelactivated", this, panel);
52389     },
52390
52391     /**
52392      * Shows the specified panel.
52393      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
52394      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
52395      */
52396     showPanel : function(panel)
52397     {
52398         panel = this.getPanel(panel);
52399         if(panel){
52400             if(this.tabs){
52401                 var tab = this.tabs.getTab(panel.getEl().id);
52402                 if(tab.isHidden()){
52403                     this.tabs.unhideTab(tab.id);
52404                 }
52405                 tab.activate();
52406             }else{
52407                 this.setActivePanel(panel);
52408             }
52409         }
52410         return panel;
52411     },
52412
52413     /**
52414      * Get the active panel for this region.
52415      * @return {Roo.ContentPanel} The active panel or null
52416      */
52417     getActivePanel : function(){
52418         return this.activePanel;
52419     },
52420
52421     validateVisibility : function(){
52422         if(this.panels.getCount() < 1){
52423             this.updateTitle("&#160;");
52424             this.closeBtn.hide();
52425             this.hide();
52426         }else{
52427             if(!this.isVisible()){
52428                 this.show();
52429             }
52430         }
52431     },
52432
52433     /**
52434      * Adds the passed ContentPanel(s) to this region.
52435      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52436      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
52437      */
52438     add : function(panel){
52439         if(arguments.length > 1){
52440             for(var i = 0, len = arguments.length; i < len; i++) {
52441                 this.add(arguments[i]);
52442             }
52443             return null;
52444         }
52445         if(this.hasPanel(panel)){
52446             this.showPanel(panel);
52447             return panel;
52448         }
52449         panel.setRegion(this);
52450         this.panels.add(panel);
52451         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
52452             this.bodyEl.dom.appendChild(panel.getEl().dom);
52453             if(panel.background !== true){
52454                 this.setActivePanel(panel);
52455             }
52456             this.fireEvent("paneladded", this, panel);
52457             return panel;
52458         }
52459         if(!this.tabs){
52460             this.initTabs();
52461         }else{
52462             this.initPanelAsTab(panel);
52463         }
52464         if(panel.background !== true){
52465             this.tabs.activate(panel.getEl().id);
52466         }
52467         this.fireEvent("paneladded", this, panel);
52468         return panel;
52469     },
52470
52471     /**
52472      * Hides the tab for the specified panel.
52473      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52474      */
52475     hidePanel : function(panel){
52476         if(this.tabs && (panel = this.getPanel(panel))){
52477             this.tabs.hideTab(panel.getEl().id);
52478         }
52479     },
52480
52481     /**
52482      * Unhides the tab for a previously hidden panel.
52483      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52484      */
52485     unhidePanel : function(panel){
52486         if(this.tabs && (panel = this.getPanel(panel))){
52487             this.tabs.unhideTab(panel.getEl().id);
52488         }
52489     },
52490
52491     clearPanels : function(){
52492         while(this.panels.getCount() > 0){
52493              this.remove(this.panels.first());
52494         }
52495     },
52496
52497     /**
52498      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52499      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52500      * @param {Boolean} preservePanel Overrides the config preservePanel option
52501      * @return {Roo.ContentPanel} The panel that was removed
52502      */
52503     remove : function(panel, preservePanel){
52504         panel = this.getPanel(panel);
52505         if(!panel){
52506             return null;
52507         }
52508         var e = {};
52509         this.fireEvent("beforeremove", this, panel, e);
52510         if(e.cancel === true){
52511             return null;
52512         }
52513         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
52514         var panelId = panel.getId();
52515         this.panels.removeKey(panelId);
52516         if(preservePanel){
52517             document.body.appendChild(panel.getEl().dom);
52518         }
52519         if(this.tabs){
52520             this.tabs.removeTab(panel.getEl().id);
52521         }else if (!preservePanel){
52522             this.bodyEl.dom.removeChild(panel.getEl().dom);
52523         }
52524         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
52525             var p = this.panels.first();
52526             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
52527             tempEl.appendChild(p.getEl().dom);
52528             this.bodyEl.update("");
52529             this.bodyEl.dom.appendChild(p.getEl().dom);
52530             tempEl = null;
52531             this.updateTitle(p.getTitle());
52532             this.tabs = null;
52533             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52534             this.setActivePanel(p);
52535         }
52536         panel.setRegion(null);
52537         if(this.activePanel == panel){
52538             this.activePanel = null;
52539         }
52540         if(this.config.autoDestroy !== false && preservePanel !== true){
52541             try{panel.destroy();}catch(e){}
52542         }
52543         this.fireEvent("panelremoved", this, panel);
52544         return panel;
52545     },
52546
52547     /**
52548      * Returns the TabPanel component used by this region
52549      * @return {Roo.TabPanel}
52550      */
52551     getTabs : function(){
52552         return this.tabs;
52553     },
52554
52555     createTool : function(parentEl, className){
52556         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
52557             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
52558         btn.addClassOnOver("x-layout-tools-button-over");
52559         return btn;
52560     }
52561 });/*
52562  * Based on:
52563  * Ext JS Library 1.1.1
52564  * Copyright(c) 2006-2007, Ext JS, LLC.
52565  *
52566  * Originally Released Under LGPL - original licence link has changed is not relivant.
52567  *
52568  * Fork - LGPL
52569  * <script type="text/javascript">
52570  */
52571  
52572
52573
52574 /**
52575  * @class Roo.SplitLayoutRegion
52576  * @extends Roo.LayoutRegion
52577  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
52578  */
52579 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
52580     this.cursor = cursor;
52581     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
52582 };
52583
52584 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
52585     splitTip : "Drag to resize.",
52586     collapsibleSplitTip : "Drag to resize. Double click to hide.",
52587     useSplitTips : false,
52588
52589     applyConfig : function(config){
52590         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
52591         if(config.split){
52592             if(!this.split){
52593                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
52594                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
52595                 /** The SplitBar for this region 
52596                 * @type Roo.SplitBar */
52597                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
52598                 this.split.on("moved", this.onSplitMove, this);
52599                 this.split.useShim = config.useShim === true;
52600                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
52601                 if(this.useSplitTips){
52602                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
52603                 }
52604                 if(config.collapsible){
52605                     this.split.el.on("dblclick", this.collapse,  this);
52606                 }
52607             }
52608             if(typeof config.minSize != "undefined"){
52609                 this.split.minSize = config.minSize;
52610             }
52611             if(typeof config.maxSize != "undefined"){
52612                 this.split.maxSize = config.maxSize;
52613             }
52614             if(config.hideWhenEmpty || config.hidden || config.collapsed){
52615                 this.hideSplitter();
52616             }
52617         }
52618     },
52619
52620     getHMaxSize : function(){
52621          var cmax = this.config.maxSize || 10000;
52622          var center = this.mgr.getRegion("center");
52623          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
52624     },
52625
52626     getVMaxSize : function(){
52627          var cmax = this.config.maxSize || 10000;
52628          var center = this.mgr.getRegion("center");
52629          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
52630     },
52631
52632     onSplitMove : function(split, newSize){
52633         this.fireEvent("resized", this, newSize);
52634     },
52635     
52636     /** 
52637      * Returns the {@link Roo.SplitBar} for this region.
52638      * @return {Roo.SplitBar}
52639      */
52640     getSplitBar : function(){
52641         return this.split;
52642     },
52643     
52644     hide : function(){
52645         this.hideSplitter();
52646         Roo.SplitLayoutRegion.superclass.hide.call(this);
52647     },
52648
52649     hideSplitter : function(){
52650         if(this.split){
52651             this.split.el.setLocation(-2000,-2000);
52652             this.split.el.hide();
52653         }
52654     },
52655
52656     show : function(){
52657         if(this.split){
52658             this.split.el.show();
52659         }
52660         Roo.SplitLayoutRegion.superclass.show.call(this);
52661     },
52662     
52663     beforeSlide: function(){
52664         if(Roo.isGecko){// firefox overflow auto bug workaround
52665             this.bodyEl.clip();
52666             if(this.tabs) {
52667                 this.tabs.bodyEl.clip();
52668             }
52669             if(this.activePanel){
52670                 this.activePanel.getEl().clip();
52671                 
52672                 if(this.activePanel.beforeSlide){
52673                     this.activePanel.beforeSlide();
52674                 }
52675             }
52676         }
52677     },
52678     
52679     afterSlide : function(){
52680         if(Roo.isGecko){// firefox overflow auto bug workaround
52681             this.bodyEl.unclip();
52682             if(this.tabs) {
52683                 this.tabs.bodyEl.unclip();
52684             }
52685             if(this.activePanel){
52686                 this.activePanel.getEl().unclip();
52687                 if(this.activePanel.afterSlide){
52688                     this.activePanel.afterSlide();
52689                 }
52690             }
52691         }
52692     },
52693
52694     initAutoHide : function(){
52695         if(this.autoHide !== false){
52696             if(!this.autoHideHd){
52697                 var st = new Roo.util.DelayedTask(this.slideIn, this);
52698                 this.autoHideHd = {
52699                     "mouseout": function(e){
52700                         if(!e.within(this.el, true)){
52701                             st.delay(500);
52702                         }
52703                     },
52704                     "mouseover" : function(e){
52705                         st.cancel();
52706                     },
52707                     scope : this
52708                 };
52709             }
52710             this.el.on(this.autoHideHd);
52711         }
52712     },
52713
52714     clearAutoHide : function(){
52715         if(this.autoHide !== false){
52716             this.el.un("mouseout", this.autoHideHd.mouseout);
52717             this.el.un("mouseover", this.autoHideHd.mouseover);
52718         }
52719     },
52720
52721     clearMonitor : function(){
52722         Roo.get(document).un("click", this.slideInIf, this);
52723     },
52724
52725     // these names are backwards but not changed for compat
52726     slideOut : function(){
52727         if(this.isSlid || this.el.hasActiveFx()){
52728             return;
52729         }
52730         this.isSlid = true;
52731         if(this.collapseBtn){
52732             this.collapseBtn.hide();
52733         }
52734         this.closeBtnState = this.closeBtn.getStyle('display');
52735         this.closeBtn.hide();
52736         if(this.stickBtn){
52737             this.stickBtn.show();
52738         }
52739         this.el.show();
52740         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
52741         this.beforeSlide();
52742         this.el.setStyle("z-index", 10001);
52743         this.el.slideIn(this.getSlideAnchor(), {
52744             callback: function(){
52745                 this.afterSlide();
52746                 this.initAutoHide();
52747                 Roo.get(document).on("click", this.slideInIf, this);
52748                 this.fireEvent("slideshow", this);
52749             },
52750             scope: this,
52751             block: true
52752         });
52753     },
52754
52755     afterSlideIn : function(){
52756         this.clearAutoHide();
52757         this.isSlid = false;
52758         this.clearMonitor();
52759         this.el.setStyle("z-index", "");
52760         if(this.collapseBtn){
52761             this.collapseBtn.show();
52762         }
52763         this.closeBtn.setStyle('display', this.closeBtnState);
52764         if(this.stickBtn){
52765             this.stickBtn.hide();
52766         }
52767         this.fireEvent("slidehide", this);
52768     },
52769
52770     slideIn : function(cb){
52771         if(!this.isSlid || this.el.hasActiveFx()){
52772             Roo.callback(cb);
52773             return;
52774         }
52775         this.isSlid = false;
52776         this.beforeSlide();
52777         this.el.slideOut(this.getSlideAnchor(), {
52778             callback: function(){
52779                 this.el.setLeftTop(-10000, -10000);
52780                 this.afterSlide();
52781                 this.afterSlideIn();
52782                 Roo.callback(cb);
52783             },
52784             scope: this,
52785             block: true
52786         });
52787     },
52788     
52789     slideInIf : function(e){
52790         if(!e.within(this.el)){
52791             this.slideIn();
52792         }
52793     },
52794
52795     animateCollapse : function(){
52796         this.beforeSlide();
52797         this.el.setStyle("z-index", 20000);
52798         var anchor = this.getSlideAnchor();
52799         this.el.slideOut(anchor, {
52800             callback : function(){
52801                 this.el.setStyle("z-index", "");
52802                 this.collapsedEl.slideIn(anchor, {duration:.3});
52803                 this.afterSlide();
52804                 this.el.setLocation(-10000,-10000);
52805                 this.el.hide();
52806                 this.fireEvent("collapsed", this);
52807             },
52808             scope: this,
52809             block: true
52810         });
52811     },
52812
52813     animateExpand : function(){
52814         this.beforeSlide();
52815         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
52816         this.el.setStyle("z-index", 20000);
52817         this.collapsedEl.hide({
52818             duration:.1
52819         });
52820         this.el.slideIn(this.getSlideAnchor(), {
52821             callback : function(){
52822                 this.el.setStyle("z-index", "");
52823                 this.afterSlide();
52824                 if(this.split){
52825                     this.split.el.show();
52826                 }
52827                 this.fireEvent("invalidated", this);
52828                 this.fireEvent("expanded", this);
52829             },
52830             scope: this,
52831             block: true
52832         });
52833     },
52834
52835     anchors : {
52836         "west" : "left",
52837         "east" : "right",
52838         "north" : "top",
52839         "south" : "bottom"
52840     },
52841
52842     sanchors : {
52843         "west" : "l",
52844         "east" : "r",
52845         "north" : "t",
52846         "south" : "b"
52847     },
52848
52849     canchors : {
52850         "west" : "tl-tr",
52851         "east" : "tr-tl",
52852         "north" : "tl-bl",
52853         "south" : "bl-tl"
52854     },
52855
52856     getAnchor : function(){
52857         return this.anchors[this.position];
52858     },
52859
52860     getCollapseAnchor : function(){
52861         return this.canchors[this.position];
52862     },
52863
52864     getSlideAnchor : function(){
52865         return this.sanchors[this.position];
52866     },
52867
52868     getAlignAdj : function(){
52869         var cm = this.cmargins;
52870         switch(this.position){
52871             case "west":
52872                 return [0, 0];
52873             break;
52874             case "east":
52875                 return [0, 0];
52876             break;
52877             case "north":
52878                 return [0, 0];
52879             break;
52880             case "south":
52881                 return [0, 0];
52882             break;
52883         }
52884     },
52885
52886     getExpandAdj : function(){
52887         var c = this.collapsedEl, cm = this.cmargins;
52888         switch(this.position){
52889             case "west":
52890                 return [-(cm.right+c.getWidth()+cm.left), 0];
52891             break;
52892             case "east":
52893                 return [cm.right+c.getWidth()+cm.left, 0];
52894             break;
52895             case "north":
52896                 return [0, -(cm.top+cm.bottom+c.getHeight())];
52897             break;
52898             case "south":
52899                 return [0, cm.top+cm.bottom+c.getHeight()];
52900             break;
52901         }
52902     }
52903 });/*
52904  * Based on:
52905  * Ext JS Library 1.1.1
52906  * Copyright(c) 2006-2007, Ext JS, LLC.
52907  *
52908  * Originally Released Under LGPL - original licence link has changed is not relivant.
52909  *
52910  * Fork - LGPL
52911  * <script type="text/javascript">
52912  */
52913 /*
52914  * These classes are private internal classes
52915  */
52916 Roo.CenterLayoutRegion = function(mgr, config){
52917     Roo.LayoutRegion.call(this, mgr, config, "center");
52918     this.visible = true;
52919     this.minWidth = config.minWidth || 20;
52920     this.minHeight = config.minHeight || 20;
52921 };
52922
52923 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
52924     hide : function(){
52925         // center panel can't be hidden
52926     },
52927     
52928     show : function(){
52929         // center panel can't be hidden
52930     },
52931     
52932     getMinWidth: function(){
52933         return this.minWidth;
52934     },
52935     
52936     getMinHeight: function(){
52937         return this.minHeight;
52938     }
52939 });
52940
52941
52942 Roo.NorthLayoutRegion = function(mgr, config){
52943     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
52944     if(this.split){
52945         this.split.placement = Roo.SplitBar.TOP;
52946         this.split.orientation = Roo.SplitBar.VERTICAL;
52947         this.split.el.addClass("x-layout-split-v");
52948     }
52949     var size = config.initialSize || config.height;
52950     if(typeof size != "undefined"){
52951         this.el.setHeight(size);
52952     }
52953 };
52954 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
52955     orientation: Roo.SplitBar.VERTICAL,
52956     getBox : function(){
52957         if(this.collapsed){
52958             return this.collapsedEl.getBox();
52959         }
52960         var box = this.el.getBox();
52961         if(this.split){
52962             box.height += this.split.el.getHeight();
52963         }
52964         return box;
52965     },
52966     
52967     updateBox : function(box){
52968         if(this.split && !this.collapsed){
52969             box.height -= this.split.el.getHeight();
52970             this.split.el.setLeft(box.x);
52971             this.split.el.setTop(box.y+box.height);
52972             this.split.el.setWidth(box.width);
52973         }
52974         if(this.collapsed){
52975             this.updateBody(box.width, null);
52976         }
52977         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52978     }
52979 });
52980
52981 Roo.SouthLayoutRegion = function(mgr, config){
52982     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
52983     if(this.split){
52984         this.split.placement = Roo.SplitBar.BOTTOM;
52985         this.split.orientation = Roo.SplitBar.VERTICAL;
52986         this.split.el.addClass("x-layout-split-v");
52987     }
52988     var size = config.initialSize || config.height;
52989     if(typeof size != "undefined"){
52990         this.el.setHeight(size);
52991     }
52992 };
52993 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
52994     orientation: Roo.SplitBar.VERTICAL,
52995     getBox : function(){
52996         if(this.collapsed){
52997             return this.collapsedEl.getBox();
52998         }
52999         var box = this.el.getBox();
53000         if(this.split){
53001             var sh = this.split.el.getHeight();
53002             box.height += sh;
53003             box.y -= sh;
53004         }
53005         return box;
53006     },
53007     
53008     updateBox : function(box){
53009         if(this.split && !this.collapsed){
53010             var sh = this.split.el.getHeight();
53011             box.height -= sh;
53012             box.y += sh;
53013             this.split.el.setLeft(box.x);
53014             this.split.el.setTop(box.y-sh);
53015             this.split.el.setWidth(box.width);
53016         }
53017         if(this.collapsed){
53018             this.updateBody(box.width, null);
53019         }
53020         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53021     }
53022 });
53023
53024 Roo.EastLayoutRegion = function(mgr, config){
53025     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
53026     if(this.split){
53027         this.split.placement = Roo.SplitBar.RIGHT;
53028         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53029         this.split.el.addClass("x-layout-split-h");
53030     }
53031     var size = config.initialSize || config.width;
53032     if(typeof size != "undefined"){
53033         this.el.setWidth(size);
53034     }
53035 };
53036 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
53037     orientation: Roo.SplitBar.HORIZONTAL,
53038     getBox : function(){
53039         if(this.collapsed){
53040             return this.collapsedEl.getBox();
53041         }
53042         var box = this.el.getBox();
53043         if(this.split){
53044             var sw = this.split.el.getWidth();
53045             box.width += sw;
53046             box.x -= sw;
53047         }
53048         return box;
53049     },
53050
53051     updateBox : function(box){
53052         if(this.split && !this.collapsed){
53053             var sw = this.split.el.getWidth();
53054             box.width -= sw;
53055             this.split.el.setLeft(box.x);
53056             this.split.el.setTop(box.y);
53057             this.split.el.setHeight(box.height);
53058             box.x += sw;
53059         }
53060         if(this.collapsed){
53061             this.updateBody(null, box.height);
53062         }
53063         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53064     }
53065 });
53066
53067 Roo.WestLayoutRegion = function(mgr, config){
53068     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
53069     if(this.split){
53070         this.split.placement = Roo.SplitBar.LEFT;
53071         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53072         this.split.el.addClass("x-layout-split-h");
53073     }
53074     var size = config.initialSize || config.width;
53075     if(typeof size != "undefined"){
53076         this.el.setWidth(size);
53077     }
53078 };
53079 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
53080     orientation: Roo.SplitBar.HORIZONTAL,
53081     getBox : function(){
53082         if(this.collapsed){
53083             return this.collapsedEl.getBox();
53084         }
53085         var box = this.el.getBox();
53086         if(this.split){
53087             box.width += this.split.el.getWidth();
53088         }
53089         return box;
53090     },
53091     
53092     updateBox : function(box){
53093         if(this.split && !this.collapsed){
53094             var sw = this.split.el.getWidth();
53095             box.width -= sw;
53096             this.split.el.setLeft(box.x+box.width);
53097             this.split.el.setTop(box.y);
53098             this.split.el.setHeight(box.height);
53099         }
53100         if(this.collapsed){
53101             this.updateBody(null, box.height);
53102         }
53103         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53104     }
53105 });
53106 /*
53107  * Based on:
53108  * Ext JS Library 1.1.1
53109  * Copyright(c) 2006-2007, Ext JS, LLC.
53110  *
53111  * Originally Released Under LGPL - original licence link has changed is not relivant.
53112  *
53113  * Fork - LGPL
53114  * <script type="text/javascript">
53115  */
53116  
53117  
53118 /*
53119  * Private internal class for reading and applying state
53120  */
53121 Roo.LayoutStateManager = function(layout){
53122      // default empty state
53123      this.state = {
53124         north: {},
53125         south: {},
53126         east: {},
53127         west: {}       
53128     };
53129 };
53130
53131 Roo.LayoutStateManager.prototype = {
53132     init : function(layout, provider){
53133         this.provider = provider;
53134         var state = provider.get(layout.id+"-layout-state");
53135         if(state){
53136             var wasUpdating = layout.isUpdating();
53137             if(!wasUpdating){
53138                 layout.beginUpdate();
53139             }
53140             for(var key in state){
53141                 if(typeof state[key] != "function"){
53142                     var rstate = state[key];
53143                     var r = layout.getRegion(key);
53144                     if(r && rstate){
53145                         if(rstate.size){
53146                             r.resizeTo(rstate.size);
53147                         }
53148                         if(rstate.collapsed == true){
53149                             r.collapse(true);
53150                         }else{
53151                             r.expand(null, true);
53152                         }
53153                     }
53154                 }
53155             }
53156             if(!wasUpdating){
53157                 layout.endUpdate();
53158             }
53159             this.state = state; 
53160         }
53161         this.layout = layout;
53162         layout.on("regionresized", this.onRegionResized, this);
53163         layout.on("regioncollapsed", this.onRegionCollapsed, this);
53164         layout.on("regionexpanded", this.onRegionExpanded, this);
53165     },
53166     
53167     storeState : function(){
53168         this.provider.set(this.layout.id+"-layout-state", this.state);
53169     },
53170     
53171     onRegionResized : function(region, newSize){
53172         this.state[region.getPosition()].size = newSize;
53173         this.storeState();
53174     },
53175     
53176     onRegionCollapsed : function(region){
53177         this.state[region.getPosition()].collapsed = true;
53178         this.storeState();
53179     },
53180     
53181     onRegionExpanded : function(region){
53182         this.state[region.getPosition()].collapsed = false;
53183         this.storeState();
53184     }
53185 };/*
53186  * Based on:
53187  * Ext JS Library 1.1.1
53188  * Copyright(c) 2006-2007, Ext JS, LLC.
53189  *
53190  * Originally Released Under LGPL - original licence link has changed is not relivant.
53191  *
53192  * Fork - LGPL
53193  * <script type="text/javascript">
53194  */
53195 /**
53196  * @class Roo.ContentPanel
53197  * @extends Roo.util.Observable
53198  * A basic ContentPanel element.
53199  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
53200  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
53201  * @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
53202  * @cfg {Boolean}   closable      True if the panel can be closed/removed
53203  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
53204  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
53205  * @cfg {Toolbar}   toolbar       A toolbar for this panel
53206  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
53207  * @cfg {String} title          The title for this panel
53208  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
53209  * @cfg {String} url            Calls {@link #setUrl} with this value
53210  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
53211  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
53212  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
53213  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
53214
53215  * @constructor
53216  * Create a new ContentPanel.
53217  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
53218  * @param {String/Object} config A string to set only the title or a config object
53219  * @param {String} content (optional) Set the HTML content for this panel
53220  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
53221  */
53222 Roo.ContentPanel = function(el, config, content){
53223     
53224      
53225     /*
53226     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
53227         config = el;
53228         el = Roo.id();
53229     }
53230     if (config && config.parentLayout) { 
53231         el = config.parentLayout.el.createChild(); 
53232     }
53233     */
53234     if(el.autoCreate){ // xtype is available if this is called from factory
53235         config = el;
53236         el = Roo.id();
53237     }
53238     this.el = Roo.get(el);
53239     if(!this.el && config && config.autoCreate){
53240         if(typeof config.autoCreate == "object"){
53241             if(!config.autoCreate.id){
53242                 config.autoCreate.id = config.id||el;
53243             }
53244             this.el = Roo.DomHelper.append(document.body,
53245                         config.autoCreate, true);
53246         }else{
53247             this.el = Roo.DomHelper.append(document.body,
53248                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
53249         }
53250     }
53251     this.closable = false;
53252     this.loaded = false;
53253     this.active = false;
53254     if(typeof config == "string"){
53255         this.title = config;
53256     }else{
53257         Roo.apply(this, config);
53258     }
53259     
53260     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
53261         this.wrapEl = this.el.wrap();
53262         this.toolbar.container = this.el.insertSibling(false, 'before');
53263         this.toolbar = new Roo.Toolbar(this.toolbar);
53264     }
53265     
53266     // xtype created footer. - not sure if will work as we normally have to render first..
53267     if (this.footer && !this.footer.el && this.footer.xtype) {
53268         if (!this.wrapEl) {
53269             this.wrapEl = this.el.wrap();
53270         }
53271     
53272         this.footer.container = this.wrapEl.createChild();
53273          
53274         this.footer = Roo.factory(this.footer, Roo);
53275         
53276     }
53277     
53278     if(this.resizeEl){
53279         this.resizeEl = Roo.get(this.resizeEl, true);
53280     }else{
53281         this.resizeEl = this.el;
53282     }
53283     // handle view.xtype
53284     
53285  
53286     
53287     
53288     this.addEvents({
53289         /**
53290          * @event activate
53291          * Fires when this panel is activated. 
53292          * @param {Roo.ContentPanel} this
53293          */
53294         "activate" : true,
53295         /**
53296          * @event deactivate
53297          * Fires when this panel is activated. 
53298          * @param {Roo.ContentPanel} this
53299          */
53300         "deactivate" : true,
53301
53302         /**
53303          * @event resize
53304          * Fires when this panel is resized if fitToFrame is true.
53305          * @param {Roo.ContentPanel} this
53306          * @param {Number} width The width after any component adjustments
53307          * @param {Number} height The height after any component adjustments
53308          */
53309         "resize" : true,
53310         
53311          /**
53312          * @event render
53313          * Fires when this tab is created
53314          * @param {Roo.ContentPanel} this
53315          */
53316         "render" : true
53317         
53318         
53319         
53320     });
53321     
53322
53323     
53324     
53325     if(this.autoScroll){
53326         this.resizeEl.setStyle("overflow", "auto");
53327     } else {
53328         // fix randome scrolling
53329         this.el.on('scroll', function() {
53330             Roo.log('fix random scolling');
53331             this.scrollTo('top',0); 
53332         });
53333     }
53334     content = content || this.content;
53335     if(content){
53336         this.setContent(content);
53337     }
53338     if(config && config.url){
53339         this.setUrl(this.url, this.params, this.loadOnce);
53340     }
53341     
53342     
53343     
53344     Roo.ContentPanel.superclass.constructor.call(this);
53345     
53346     if (this.view && typeof(this.view.xtype) != 'undefined') {
53347         this.view.el = this.el.appendChild(document.createElement("div"));
53348         this.view = Roo.factory(this.view); 
53349         this.view.render  &&  this.view.render(false, '');  
53350     }
53351     
53352     
53353     this.fireEvent('render', this);
53354 };
53355
53356 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
53357     tabTip:'',
53358     setRegion : function(region){
53359         this.region = region;
53360         if(region){
53361            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
53362         }else{
53363            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
53364         } 
53365     },
53366     
53367     /**
53368      * Returns the toolbar for this Panel if one was configured. 
53369      * @return {Roo.Toolbar} 
53370      */
53371     getToolbar : function(){
53372         return this.toolbar;
53373     },
53374     
53375     setActiveState : function(active){
53376         this.active = active;
53377         if(!active){
53378             this.fireEvent("deactivate", this);
53379         }else{
53380             this.fireEvent("activate", this);
53381         }
53382     },
53383     /**
53384      * Updates this panel's element
53385      * @param {String} content The new content
53386      * @param {Boolean} loadScripts (optional) true to look for and process scripts
53387     */
53388     setContent : function(content, loadScripts){
53389         this.el.update(content, loadScripts);
53390     },
53391
53392     ignoreResize : function(w, h){
53393         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
53394             return true;
53395         }else{
53396             this.lastSize = {width: w, height: h};
53397             return false;
53398         }
53399     },
53400     /**
53401      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
53402      * @return {Roo.UpdateManager} The UpdateManager
53403      */
53404     getUpdateManager : function(){
53405         return this.el.getUpdateManager();
53406     },
53407      /**
53408      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
53409      * @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:
53410 <pre><code>
53411 panel.load({
53412     url: "your-url.php",
53413     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
53414     callback: yourFunction,
53415     scope: yourObject, //(optional scope)
53416     discardUrl: false,
53417     nocache: false,
53418     text: "Loading...",
53419     timeout: 30,
53420     scripts: false
53421 });
53422 </code></pre>
53423      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
53424      * 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.
53425      * @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}
53426      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
53427      * @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.
53428      * @return {Roo.ContentPanel} this
53429      */
53430     load : function(){
53431         var um = this.el.getUpdateManager();
53432         um.update.apply(um, arguments);
53433         return this;
53434     },
53435
53436
53437     /**
53438      * 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.
53439      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
53440      * @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)
53441      * @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)
53442      * @return {Roo.UpdateManager} The UpdateManager
53443      */
53444     setUrl : function(url, params, loadOnce){
53445         if(this.refreshDelegate){
53446             this.removeListener("activate", this.refreshDelegate);
53447         }
53448         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
53449         this.on("activate", this.refreshDelegate);
53450         return this.el.getUpdateManager();
53451     },
53452     
53453     _handleRefresh : function(url, params, loadOnce){
53454         if(!loadOnce || !this.loaded){
53455             var updater = this.el.getUpdateManager();
53456             updater.update(url, params, this._setLoaded.createDelegate(this));
53457         }
53458     },
53459     
53460     _setLoaded : function(){
53461         this.loaded = true;
53462     }, 
53463     
53464     /**
53465      * Returns this panel's id
53466      * @return {String} 
53467      */
53468     getId : function(){
53469         return this.el.id;
53470     },
53471     
53472     /** 
53473      * Returns this panel's element - used by regiosn to add.
53474      * @return {Roo.Element} 
53475      */
53476     getEl : function(){
53477         return this.wrapEl || this.el;
53478     },
53479     
53480     adjustForComponents : function(width, height)
53481     {
53482         //Roo.log('adjustForComponents ');
53483         if(this.resizeEl != this.el){
53484             width -= this.el.getFrameWidth('lr');
53485             height -= this.el.getFrameWidth('tb');
53486         }
53487         if(this.toolbar){
53488             var te = this.toolbar.getEl();
53489             height -= te.getHeight();
53490             te.setWidth(width);
53491         }
53492         if(this.footer){
53493             var te = this.footer.getEl();
53494             Roo.log("footer:" + te.getHeight());
53495             
53496             height -= te.getHeight();
53497             te.setWidth(width);
53498         }
53499         
53500         
53501         if(this.adjustments){
53502             width += this.adjustments[0];
53503             height += this.adjustments[1];
53504         }
53505         return {"width": width, "height": height};
53506     },
53507     
53508     setSize : function(width, height){
53509         if(this.fitToFrame && !this.ignoreResize(width, height)){
53510             if(this.fitContainer && this.resizeEl != this.el){
53511                 this.el.setSize(width, height);
53512             }
53513             var size = this.adjustForComponents(width, height);
53514             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
53515             this.fireEvent('resize', this, size.width, size.height);
53516         }
53517     },
53518     
53519     /**
53520      * Returns this panel's title
53521      * @return {String} 
53522      */
53523     getTitle : function(){
53524         return this.title;
53525     },
53526     
53527     /**
53528      * Set this panel's title
53529      * @param {String} title
53530      */
53531     setTitle : function(title){
53532         this.title = title;
53533         if(this.region){
53534             this.region.updatePanelTitle(this, title);
53535         }
53536     },
53537     
53538     /**
53539      * Returns true is this panel was configured to be closable
53540      * @return {Boolean} 
53541      */
53542     isClosable : function(){
53543         return this.closable;
53544     },
53545     
53546     beforeSlide : function(){
53547         this.el.clip();
53548         this.resizeEl.clip();
53549     },
53550     
53551     afterSlide : function(){
53552         this.el.unclip();
53553         this.resizeEl.unclip();
53554     },
53555     
53556     /**
53557      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
53558      *   Will fail silently if the {@link #setUrl} method has not been called.
53559      *   This does not activate the panel, just updates its content.
53560      */
53561     refresh : function(){
53562         if(this.refreshDelegate){
53563            this.loaded = false;
53564            this.refreshDelegate();
53565         }
53566     },
53567     
53568     /**
53569      * Destroys this panel
53570      */
53571     destroy : function(){
53572         this.el.removeAllListeners();
53573         var tempEl = document.createElement("span");
53574         tempEl.appendChild(this.el.dom);
53575         tempEl.innerHTML = "";
53576         this.el.remove();
53577         this.el = null;
53578     },
53579     
53580     /**
53581      * form - if the content panel contains a form - this is a reference to it.
53582      * @type {Roo.form.Form}
53583      */
53584     form : false,
53585     /**
53586      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
53587      *    This contains a reference to it.
53588      * @type {Roo.View}
53589      */
53590     view : false,
53591     
53592       /**
53593      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
53594      * <pre><code>
53595
53596 layout.addxtype({
53597        xtype : 'Form',
53598        items: [ .... ]
53599    }
53600 );
53601
53602 </code></pre>
53603      * @param {Object} cfg Xtype definition of item to add.
53604      */
53605     
53606     addxtype : function(cfg) {
53607         // add form..
53608         if (cfg.xtype.match(/^Form$/)) {
53609             
53610             var el;
53611             //if (this.footer) {
53612             //    el = this.footer.container.insertSibling(false, 'before');
53613             //} else {
53614                 el = this.el.createChild();
53615             //}
53616
53617             this.form = new  Roo.form.Form(cfg);
53618             
53619             
53620             if ( this.form.allItems.length) {
53621                 this.form.render(el.dom);
53622             }
53623             return this.form;
53624         }
53625         // should only have one of theses..
53626         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
53627             // views.. should not be just added - used named prop 'view''
53628             
53629             cfg.el = this.el.appendChild(document.createElement("div"));
53630             // factory?
53631             
53632             var ret = new Roo.factory(cfg);
53633              
53634              ret.render && ret.render(false, ''); // render blank..
53635             this.view = ret;
53636             return ret;
53637         }
53638         return false;
53639     }
53640 });
53641
53642 /**
53643  * @class Roo.GridPanel
53644  * @extends Roo.ContentPanel
53645  * @constructor
53646  * Create a new GridPanel.
53647  * @param {Roo.grid.Grid} grid The grid for this panel
53648  * @param {String/Object} config A string to set only the panel's title, or a config object
53649  */
53650 Roo.GridPanel = function(grid, config){
53651     
53652   
53653     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
53654         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
53655         
53656     this.wrapper.dom.appendChild(grid.getGridEl().dom);
53657     
53658     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
53659     
53660     if(this.toolbar){
53661         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
53662     }
53663     // xtype created footer. - not sure if will work as we normally have to render first..
53664     if (this.footer && !this.footer.el && this.footer.xtype) {
53665         
53666         this.footer.container = this.grid.getView().getFooterPanel(true);
53667         this.footer.dataSource = this.grid.dataSource;
53668         this.footer = Roo.factory(this.footer, Roo);
53669         
53670     }
53671     
53672     grid.monitorWindowResize = false; // turn off autosizing
53673     grid.autoHeight = false;
53674     grid.autoWidth = false;
53675     this.grid = grid;
53676     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
53677 };
53678
53679 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
53680     getId : function(){
53681         return this.grid.id;
53682     },
53683     
53684     /**
53685      * Returns the grid for this panel
53686      * @return {Roo.grid.Grid} 
53687      */
53688     getGrid : function(){
53689         return this.grid;    
53690     },
53691     
53692     setSize : function(width, height){
53693         if(!this.ignoreResize(width, height)){
53694             var grid = this.grid;
53695             var size = this.adjustForComponents(width, height);
53696             grid.getGridEl().setSize(size.width, size.height);
53697             grid.autoSize();
53698         }
53699     },
53700     
53701     beforeSlide : function(){
53702         this.grid.getView().scroller.clip();
53703     },
53704     
53705     afterSlide : function(){
53706         this.grid.getView().scroller.unclip();
53707     },
53708     
53709     destroy : function(){
53710         this.grid.destroy();
53711         delete this.grid;
53712         Roo.GridPanel.superclass.destroy.call(this); 
53713     }
53714 });
53715
53716
53717 /**
53718  * @class Roo.NestedLayoutPanel
53719  * @extends Roo.ContentPanel
53720  * @constructor
53721  * Create a new NestedLayoutPanel.
53722  * 
53723  * 
53724  * @param {Roo.BorderLayout} layout The layout for this panel
53725  * @param {String/Object} config A string to set only the title or a config object
53726  */
53727 Roo.NestedLayoutPanel = function(layout, config)
53728 {
53729     // construct with only one argument..
53730     /* FIXME - implement nicer consturctors
53731     if (layout.layout) {
53732         config = layout;
53733         layout = config.layout;
53734         delete config.layout;
53735     }
53736     if (layout.xtype && !layout.getEl) {
53737         // then layout needs constructing..
53738         layout = Roo.factory(layout, Roo);
53739     }
53740     */
53741     
53742     
53743     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
53744     
53745     layout.monitorWindowResize = false; // turn off autosizing
53746     this.layout = layout;
53747     this.layout.getEl().addClass("x-layout-nested-layout");
53748     
53749     
53750     
53751     
53752 };
53753
53754 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
53755
53756     setSize : function(width, height){
53757         if(!this.ignoreResize(width, height)){
53758             var size = this.adjustForComponents(width, height);
53759             var el = this.layout.getEl();
53760             el.setSize(size.width, size.height);
53761             var touch = el.dom.offsetWidth;
53762             this.layout.layout();
53763             // ie requires a double layout on the first pass
53764             if(Roo.isIE && !this.initialized){
53765                 this.initialized = true;
53766                 this.layout.layout();
53767             }
53768         }
53769     },
53770     
53771     // activate all subpanels if not currently active..
53772     
53773     setActiveState : function(active){
53774         this.active = active;
53775         if(!active){
53776             this.fireEvent("deactivate", this);
53777             return;
53778         }
53779         
53780         this.fireEvent("activate", this);
53781         // not sure if this should happen before or after..
53782         if (!this.layout) {
53783             return; // should not happen..
53784         }
53785         var reg = false;
53786         for (var r in this.layout.regions) {
53787             reg = this.layout.getRegion(r);
53788             if (reg.getActivePanel()) {
53789                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
53790                 reg.setActivePanel(reg.getActivePanel());
53791                 continue;
53792             }
53793             if (!reg.panels.length) {
53794                 continue;
53795             }
53796             reg.showPanel(reg.getPanel(0));
53797         }
53798         
53799         
53800         
53801         
53802     },
53803     
53804     /**
53805      * Returns the nested BorderLayout for this panel
53806      * @return {Roo.BorderLayout} 
53807      */
53808     getLayout : function(){
53809         return this.layout;
53810     },
53811     
53812      /**
53813      * Adds a xtype elements to the layout of the nested panel
53814      * <pre><code>
53815
53816 panel.addxtype({
53817        xtype : 'ContentPanel',
53818        region: 'west',
53819        items: [ .... ]
53820    }
53821 );
53822
53823 panel.addxtype({
53824         xtype : 'NestedLayoutPanel',
53825         region: 'west',
53826         layout: {
53827            center: { },
53828            west: { }   
53829         },
53830         items : [ ... list of content panels or nested layout panels.. ]
53831    }
53832 );
53833 </code></pre>
53834      * @param {Object} cfg Xtype definition of item to add.
53835      */
53836     addxtype : function(cfg) {
53837         return this.layout.addxtype(cfg);
53838     
53839     }
53840 });
53841
53842 Roo.ScrollPanel = function(el, config, content){
53843     config = config || {};
53844     config.fitToFrame = true;
53845     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
53846     
53847     this.el.dom.style.overflow = "hidden";
53848     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
53849     this.el.removeClass("x-layout-inactive-content");
53850     this.el.on("mousewheel", this.onWheel, this);
53851
53852     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
53853     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
53854     up.unselectable(); down.unselectable();
53855     up.on("click", this.scrollUp, this);
53856     down.on("click", this.scrollDown, this);
53857     up.addClassOnOver("x-scroller-btn-over");
53858     down.addClassOnOver("x-scroller-btn-over");
53859     up.addClassOnClick("x-scroller-btn-click");
53860     down.addClassOnClick("x-scroller-btn-click");
53861     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
53862
53863     this.resizeEl = this.el;
53864     this.el = wrap; this.up = up; this.down = down;
53865 };
53866
53867 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
53868     increment : 100,
53869     wheelIncrement : 5,
53870     scrollUp : function(){
53871         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
53872     },
53873
53874     scrollDown : function(){
53875         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
53876     },
53877
53878     afterScroll : function(){
53879         var el = this.resizeEl;
53880         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
53881         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53882         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53883     },
53884
53885     setSize : function(){
53886         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
53887         this.afterScroll();
53888     },
53889
53890     onWheel : function(e){
53891         var d = e.getWheelDelta();
53892         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
53893         this.afterScroll();
53894         e.stopEvent();
53895     },
53896
53897     setContent : function(content, loadScripts){
53898         this.resizeEl.update(content, loadScripts);
53899     }
53900
53901 });
53902
53903
53904
53905
53906
53907
53908
53909
53910
53911 /**
53912  * @class Roo.TreePanel
53913  * @extends Roo.ContentPanel
53914  * @constructor
53915  * Create a new TreePanel. - defaults to fit/scoll contents.
53916  * @param {String/Object} config A string to set only the panel's title, or a config object
53917  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
53918  */
53919 Roo.TreePanel = function(config){
53920     var el = config.el;
53921     var tree = config.tree;
53922     delete config.tree; 
53923     delete config.el; // hopefull!
53924     
53925     // wrapper for IE7 strict & safari scroll issue
53926     
53927     var treeEl = el.createChild();
53928     config.resizeEl = treeEl;
53929     
53930     
53931     
53932     Roo.TreePanel.superclass.constructor.call(this, el, config);
53933  
53934  
53935     this.tree = new Roo.tree.TreePanel(treeEl , tree);
53936     //console.log(tree);
53937     this.on('activate', function()
53938     {
53939         if (this.tree.rendered) {
53940             return;
53941         }
53942         //console.log('render tree');
53943         this.tree.render();
53944     });
53945     // this should not be needed.. - it's actually the 'el' that resizes?
53946     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
53947     
53948     //this.on('resize',  function (cp, w, h) {
53949     //        this.tree.innerCt.setWidth(w);
53950     //        this.tree.innerCt.setHeight(h);
53951     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
53952     //});
53953
53954         
53955     
53956 };
53957
53958 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
53959     fitToFrame : true,
53960     autoScroll : true
53961 });
53962
53963
53964
53965
53966
53967
53968
53969
53970
53971
53972
53973 /*
53974  * Based on:
53975  * Ext JS Library 1.1.1
53976  * Copyright(c) 2006-2007, Ext JS, LLC.
53977  *
53978  * Originally Released Under LGPL - original licence link has changed is not relivant.
53979  *
53980  * Fork - LGPL
53981  * <script type="text/javascript">
53982  */
53983  
53984
53985 /**
53986  * @class Roo.ReaderLayout
53987  * @extends Roo.BorderLayout
53988  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
53989  * center region containing two nested regions (a top one for a list view and one for item preview below),
53990  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
53991  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
53992  * expedites the setup of the overall layout and regions for this common application style.
53993  * Example:
53994  <pre><code>
53995 var reader = new Roo.ReaderLayout();
53996 var CP = Roo.ContentPanel;  // shortcut for adding
53997
53998 reader.beginUpdate();
53999 reader.add("north", new CP("north", "North"));
54000 reader.add("west", new CP("west", {title: "West"}));
54001 reader.add("east", new CP("east", {title: "East"}));
54002
54003 reader.regions.listView.add(new CP("listView", "List"));
54004 reader.regions.preview.add(new CP("preview", "Preview"));
54005 reader.endUpdate();
54006 </code></pre>
54007 * @constructor
54008 * Create a new ReaderLayout
54009 * @param {Object} config Configuration options
54010 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
54011 * document.body if omitted)
54012 */
54013 Roo.ReaderLayout = function(config, renderTo){
54014     var c = config || {size:{}};
54015     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
54016         north: c.north !== false ? Roo.apply({
54017             split:false,
54018             initialSize: 32,
54019             titlebar: false
54020         }, c.north) : false,
54021         west: c.west !== false ? Roo.apply({
54022             split:true,
54023             initialSize: 200,
54024             minSize: 175,
54025             maxSize: 400,
54026             titlebar: true,
54027             collapsible: true,
54028             animate: true,
54029             margins:{left:5,right:0,bottom:5,top:5},
54030             cmargins:{left:5,right:5,bottom:5,top:5}
54031         }, c.west) : false,
54032         east: c.east !== false ? Roo.apply({
54033             split:true,
54034             initialSize: 200,
54035             minSize: 175,
54036             maxSize: 400,
54037             titlebar: true,
54038             collapsible: true,
54039             animate: true,
54040             margins:{left:0,right:5,bottom:5,top:5},
54041             cmargins:{left:5,right:5,bottom:5,top:5}
54042         }, c.east) : false,
54043         center: Roo.apply({
54044             tabPosition: 'top',
54045             autoScroll:false,
54046             closeOnTab: true,
54047             titlebar:false,
54048             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
54049         }, c.center)
54050     });
54051
54052     this.el.addClass('x-reader');
54053
54054     this.beginUpdate();
54055
54056     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
54057         south: c.preview !== false ? Roo.apply({
54058             split:true,
54059             initialSize: 200,
54060             minSize: 100,
54061             autoScroll:true,
54062             collapsible:true,
54063             titlebar: true,
54064             cmargins:{top:5,left:0, right:0, bottom:0}
54065         }, c.preview) : false,
54066         center: Roo.apply({
54067             autoScroll:false,
54068             titlebar:false,
54069             minHeight:200
54070         }, c.listView)
54071     });
54072     this.add('center', new Roo.NestedLayoutPanel(inner,
54073             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
54074
54075     this.endUpdate();
54076
54077     this.regions.preview = inner.getRegion('south');
54078     this.regions.listView = inner.getRegion('center');
54079 };
54080
54081 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
54082  * Based on:
54083  * Ext JS Library 1.1.1
54084  * Copyright(c) 2006-2007, Ext JS, LLC.
54085  *
54086  * Originally Released Under LGPL - original licence link has changed is not relivant.
54087  *
54088  * Fork - LGPL
54089  * <script type="text/javascript">
54090  */
54091  
54092 /**
54093  * @class Roo.grid.Grid
54094  * @extends Roo.util.Observable
54095  * This class represents the primary interface of a component based grid control.
54096  * <br><br>Usage:<pre><code>
54097  var grid = new Roo.grid.Grid("my-container-id", {
54098      ds: myDataStore,
54099      cm: myColModel,
54100      selModel: mySelectionModel,
54101      autoSizeColumns: true,
54102      monitorWindowResize: false,
54103      trackMouseOver: true
54104  });
54105  // set any options
54106  grid.render();
54107  * </code></pre>
54108  * <b>Common Problems:</b><br/>
54109  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
54110  * element will correct this<br/>
54111  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
54112  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
54113  * are unpredictable.<br/>
54114  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
54115  * grid to calculate dimensions/offsets.<br/>
54116   * @constructor
54117  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
54118  * The container MUST have some type of size defined for the grid to fill. The container will be
54119  * automatically set to position relative if it isn't already.
54120  * @param {Object} config A config object that sets properties on this grid.
54121  */
54122 Roo.grid.Grid = function(container, config){
54123         // initialize the container
54124         this.container = Roo.get(container);
54125         this.container.update("");
54126         this.container.setStyle("overflow", "hidden");
54127     this.container.addClass('x-grid-container');
54128
54129     this.id = this.container.id;
54130
54131     Roo.apply(this, config);
54132     // check and correct shorthanded configs
54133     if(this.ds){
54134         this.dataSource = this.ds;
54135         delete this.ds;
54136     }
54137     if(this.cm){
54138         this.colModel = this.cm;
54139         delete this.cm;
54140     }
54141     if(this.sm){
54142         this.selModel = this.sm;
54143         delete this.sm;
54144     }
54145
54146     if (this.selModel) {
54147         this.selModel = Roo.factory(this.selModel, Roo.grid);
54148         this.sm = this.selModel;
54149         this.sm.xmodule = this.xmodule || false;
54150     }
54151     if (typeof(this.colModel.config) == 'undefined') {
54152         this.colModel = new Roo.grid.ColumnModel(this.colModel);
54153         this.cm = this.colModel;
54154         this.cm.xmodule = this.xmodule || false;
54155     }
54156     if (this.dataSource) {
54157         this.dataSource= Roo.factory(this.dataSource, Roo.data);
54158         this.ds = this.dataSource;
54159         this.ds.xmodule = this.xmodule || false;
54160          
54161     }
54162     
54163     
54164     
54165     if(this.width){
54166         this.container.setWidth(this.width);
54167     }
54168
54169     if(this.height){
54170         this.container.setHeight(this.height);
54171     }
54172     /** @private */
54173         this.addEvents({
54174         // raw events
54175         /**
54176          * @event click
54177          * The raw click event for the entire grid.
54178          * @param {Roo.EventObject} e
54179          */
54180         "click" : true,
54181         /**
54182          * @event dblclick
54183          * The raw dblclick event for the entire grid.
54184          * @param {Roo.EventObject} e
54185          */
54186         "dblclick" : true,
54187         /**
54188          * @event contextmenu
54189          * The raw contextmenu event for the entire grid.
54190          * @param {Roo.EventObject} e
54191          */
54192         "contextmenu" : true,
54193         /**
54194          * @event mousedown
54195          * The raw mousedown event for the entire grid.
54196          * @param {Roo.EventObject} e
54197          */
54198         "mousedown" : true,
54199         /**
54200          * @event mouseup
54201          * The raw mouseup event for the entire grid.
54202          * @param {Roo.EventObject} e
54203          */
54204         "mouseup" : true,
54205         /**
54206          * @event mouseover
54207          * The raw mouseover event for the entire grid.
54208          * @param {Roo.EventObject} e
54209          */
54210         "mouseover" : true,
54211         /**
54212          * @event mouseout
54213          * The raw mouseout event for the entire grid.
54214          * @param {Roo.EventObject} e
54215          */
54216         "mouseout" : true,
54217         /**
54218          * @event keypress
54219          * The raw keypress event for the entire grid.
54220          * @param {Roo.EventObject} e
54221          */
54222         "keypress" : true,
54223         /**
54224          * @event keydown
54225          * The raw keydown event for the entire grid.
54226          * @param {Roo.EventObject} e
54227          */
54228         "keydown" : true,
54229
54230         // custom events
54231
54232         /**
54233          * @event cellclick
54234          * Fires when a cell is clicked
54235          * @param {Grid} this
54236          * @param {Number} rowIndex
54237          * @param {Number} columnIndex
54238          * @param {Roo.EventObject} e
54239          */
54240         "cellclick" : true,
54241         /**
54242          * @event celldblclick
54243          * Fires when a cell is double clicked
54244          * @param {Grid} this
54245          * @param {Number} rowIndex
54246          * @param {Number} columnIndex
54247          * @param {Roo.EventObject} e
54248          */
54249         "celldblclick" : true,
54250         /**
54251          * @event rowclick
54252          * Fires when a row is clicked
54253          * @param {Grid} this
54254          * @param {Number} rowIndex
54255          * @param {Roo.EventObject} e
54256          */
54257         "rowclick" : true,
54258         /**
54259          * @event rowdblclick
54260          * Fires when a row is double clicked
54261          * @param {Grid} this
54262          * @param {Number} rowIndex
54263          * @param {Roo.EventObject} e
54264          */
54265         "rowdblclick" : true,
54266         /**
54267          * @event headerclick
54268          * Fires when a header is clicked
54269          * @param {Grid} this
54270          * @param {Number} columnIndex
54271          * @param {Roo.EventObject} e
54272          */
54273         "headerclick" : true,
54274         /**
54275          * @event headerdblclick
54276          * Fires when a header cell is double clicked
54277          * @param {Grid} this
54278          * @param {Number} columnIndex
54279          * @param {Roo.EventObject} e
54280          */
54281         "headerdblclick" : true,
54282         /**
54283          * @event rowcontextmenu
54284          * Fires when a row is right clicked
54285          * @param {Grid} this
54286          * @param {Number} rowIndex
54287          * @param {Roo.EventObject} e
54288          */
54289         "rowcontextmenu" : true,
54290         /**
54291          * @event cellcontextmenu
54292          * Fires when a cell is right clicked
54293          * @param {Grid} this
54294          * @param {Number} rowIndex
54295          * @param {Number} cellIndex
54296          * @param {Roo.EventObject} e
54297          */
54298          "cellcontextmenu" : true,
54299         /**
54300          * @event headercontextmenu
54301          * Fires when a header is right clicked
54302          * @param {Grid} this
54303          * @param {Number} columnIndex
54304          * @param {Roo.EventObject} e
54305          */
54306         "headercontextmenu" : true,
54307         /**
54308          * @event bodyscroll
54309          * Fires when the body element is scrolled
54310          * @param {Number} scrollLeft
54311          * @param {Number} scrollTop
54312          */
54313         "bodyscroll" : true,
54314         /**
54315          * @event columnresize
54316          * Fires when the user resizes a column
54317          * @param {Number} columnIndex
54318          * @param {Number} newSize
54319          */
54320         "columnresize" : true,
54321         /**
54322          * @event columnmove
54323          * Fires when the user moves a column
54324          * @param {Number} oldIndex
54325          * @param {Number} newIndex
54326          */
54327         "columnmove" : true,
54328         /**
54329          * @event startdrag
54330          * Fires when row(s) start being dragged
54331          * @param {Grid} this
54332          * @param {Roo.GridDD} dd The drag drop object
54333          * @param {event} e The raw browser event
54334          */
54335         "startdrag" : true,
54336         /**
54337          * @event enddrag
54338          * Fires when a drag operation is complete
54339          * @param {Grid} this
54340          * @param {Roo.GridDD} dd The drag drop object
54341          * @param {event} e The raw browser event
54342          */
54343         "enddrag" : true,
54344         /**
54345          * @event dragdrop
54346          * Fires when dragged row(s) are dropped on a valid DD target
54347          * @param {Grid} this
54348          * @param {Roo.GridDD} dd The drag drop object
54349          * @param {String} targetId The target drag drop object
54350          * @param {event} e The raw browser event
54351          */
54352         "dragdrop" : true,
54353         /**
54354          * @event dragover
54355          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
54356          * @param {Grid} this
54357          * @param {Roo.GridDD} dd The drag drop object
54358          * @param {String} targetId The target drag drop object
54359          * @param {event} e The raw browser event
54360          */
54361         "dragover" : true,
54362         /**
54363          * @event dragenter
54364          *  Fires when the dragged row(s) first cross another DD target while being dragged
54365          * @param {Grid} this
54366          * @param {Roo.GridDD} dd The drag drop object
54367          * @param {String} targetId The target drag drop object
54368          * @param {event} e The raw browser event
54369          */
54370         "dragenter" : true,
54371         /**
54372          * @event dragout
54373          * Fires when the dragged row(s) leave another DD target while being dragged
54374          * @param {Grid} this
54375          * @param {Roo.GridDD} dd The drag drop object
54376          * @param {String} targetId The target drag drop object
54377          * @param {event} e The raw browser event
54378          */
54379         "dragout" : true,
54380         /**
54381          * @event rowclass
54382          * Fires when a row is rendered, so you can change add a style to it.
54383          * @param {GridView} gridview   The grid view
54384          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
54385          */
54386         'rowclass' : true,
54387
54388         /**
54389          * @event render
54390          * Fires when the grid is rendered
54391          * @param {Grid} grid
54392          */
54393         'render' : true
54394     });
54395
54396     Roo.grid.Grid.superclass.constructor.call(this);
54397 };
54398 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
54399     
54400     /**
54401      * @cfg {String} ddGroup - drag drop group.
54402      */
54403
54404     /**
54405      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
54406      */
54407     minColumnWidth : 25,
54408
54409     /**
54410      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
54411      * <b>on initial render.</b> It is more efficient to explicitly size the columns
54412      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
54413      */
54414     autoSizeColumns : false,
54415
54416     /**
54417      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
54418      */
54419     autoSizeHeaders : true,
54420
54421     /**
54422      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
54423      */
54424     monitorWindowResize : true,
54425
54426     /**
54427      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
54428      * rows measured to get a columns size. Default is 0 (all rows).
54429      */
54430     maxRowsToMeasure : 0,
54431
54432     /**
54433      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
54434      */
54435     trackMouseOver : true,
54436
54437     /**
54438     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
54439     */
54440     
54441     /**
54442     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
54443     */
54444     enableDragDrop : false,
54445     
54446     /**
54447     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
54448     */
54449     enableColumnMove : true,
54450     
54451     /**
54452     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
54453     */
54454     enableColumnHide : true,
54455     
54456     /**
54457     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
54458     */
54459     enableRowHeightSync : false,
54460     
54461     /**
54462     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
54463     */
54464     stripeRows : true,
54465     
54466     /**
54467     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
54468     */
54469     autoHeight : false,
54470
54471     /**
54472      * @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.
54473      */
54474     autoExpandColumn : false,
54475
54476     /**
54477     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
54478     * Default is 50.
54479     */
54480     autoExpandMin : 50,
54481
54482     /**
54483     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
54484     */
54485     autoExpandMax : 1000,
54486
54487     /**
54488     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
54489     */
54490     view : null,
54491
54492     /**
54493     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
54494     */
54495     loadMask : false,
54496     /**
54497     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
54498     */
54499     dropTarget: false,
54500     
54501    
54502     
54503     // private
54504     rendered : false,
54505
54506     /**
54507     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
54508     * of a fixed width. Default is false.
54509     */
54510     /**
54511     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
54512     */
54513     /**
54514      * Called once after all setup has been completed and the grid is ready to be rendered.
54515      * @return {Roo.grid.Grid} this
54516      */
54517     render : function()
54518     {
54519         var c = this.container;
54520         // try to detect autoHeight/width mode
54521         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
54522             this.autoHeight = true;
54523         }
54524         var view = this.getView();
54525         view.init(this);
54526
54527         c.on("click", this.onClick, this);
54528         c.on("dblclick", this.onDblClick, this);
54529         c.on("contextmenu", this.onContextMenu, this);
54530         c.on("keydown", this.onKeyDown, this);
54531         if (Roo.isTouch) {
54532             c.on("touchstart", this.onTouchStart, this);
54533         }
54534
54535         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
54536
54537         this.getSelectionModel().init(this);
54538
54539         view.render();
54540
54541         if(this.loadMask){
54542             this.loadMask = new Roo.LoadMask(this.container,
54543                     Roo.apply({store:this.dataSource}, this.loadMask));
54544         }
54545         
54546         
54547         if (this.toolbar && this.toolbar.xtype) {
54548             this.toolbar.container = this.getView().getHeaderPanel(true);
54549             this.toolbar = new Roo.Toolbar(this.toolbar);
54550         }
54551         if (this.footer && this.footer.xtype) {
54552             this.footer.dataSource = this.getDataSource();
54553             this.footer.container = this.getView().getFooterPanel(true);
54554             this.footer = Roo.factory(this.footer, Roo);
54555         }
54556         if (this.dropTarget && this.dropTarget.xtype) {
54557             delete this.dropTarget.xtype;
54558             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
54559         }
54560         
54561         
54562         this.rendered = true;
54563         this.fireEvent('render', this);
54564         return this;
54565     },
54566
54567         /**
54568          * Reconfigures the grid to use a different Store and Column Model.
54569          * The View will be bound to the new objects and refreshed.
54570          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
54571          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
54572          */
54573     reconfigure : function(dataSource, colModel){
54574         if(this.loadMask){
54575             this.loadMask.destroy();
54576             this.loadMask = new Roo.LoadMask(this.container,
54577                     Roo.apply({store:dataSource}, this.loadMask));
54578         }
54579         this.view.bind(dataSource, colModel);
54580         this.dataSource = dataSource;
54581         this.colModel = colModel;
54582         this.view.refresh(true);
54583     },
54584
54585     // private
54586     onKeyDown : function(e){
54587         this.fireEvent("keydown", e);
54588     },
54589
54590     /**
54591      * Destroy this grid.
54592      * @param {Boolean} removeEl True to remove the element
54593      */
54594     destroy : function(removeEl, keepListeners){
54595         if(this.loadMask){
54596             this.loadMask.destroy();
54597         }
54598         var c = this.container;
54599         c.removeAllListeners();
54600         this.view.destroy();
54601         this.colModel.purgeListeners();
54602         if(!keepListeners){
54603             this.purgeListeners();
54604         }
54605         c.update("");
54606         if(removeEl === true){
54607             c.remove();
54608         }
54609     },
54610
54611     // private
54612     processEvent : function(name, e){
54613         // does this fire select???
54614         //Roo.log('grid:processEvent '  + name);
54615         
54616         if (name != 'touchstart' ) {
54617             this.fireEvent(name, e);    
54618         }
54619         
54620         var t = e.getTarget();
54621         var v = this.view;
54622         var header = v.findHeaderIndex(t);
54623         if(header !== false){
54624             var ename = name == 'touchstart' ? 'click' : name;
54625              
54626             this.fireEvent("header" + ename, this, header, e);
54627         }else{
54628             var row = v.findRowIndex(t);
54629             var cell = v.findCellIndex(t);
54630             if (name == 'touchstart') {
54631                 // first touch is always a click.
54632                 // hopefull this happens after selection is updated.?
54633                 name = false;
54634                 
54635                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
54636                     var cs = this.selModel.getSelectedCell();
54637                     if (row == cs[0] && cell == cs[1]){
54638                         name = 'dblclick';
54639                     }
54640                 }
54641                 if (typeof(this.selModel.getSelections) != 'undefined') {
54642                     var cs = this.selModel.getSelections();
54643                     var ds = this.dataSource;
54644                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
54645                         name = 'dblclick';
54646                     }
54647                 }
54648                 if (!name) {
54649                     return;
54650                 }
54651             }
54652             
54653             
54654             if(row !== false){
54655                 this.fireEvent("row" + name, this, row, e);
54656                 if(cell !== false){
54657                     this.fireEvent("cell" + name, this, row, cell, e);
54658                 }
54659             }
54660         }
54661     },
54662
54663     // private
54664     onClick : function(e){
54665         this.processEvent("click", e);
54666     },
54667    // private
54668     onTouchStart : function(e){
54669         this.processEvent("touchstart", e);
54670     },
54671
54672     // private
54673     onContextMenu : function(e, t){
54674         this.processEvent("contextmenu", e);
54675     },
54676
54677     // private
54678     onDblClick : function(e){
54679         this.processEvent("dblclick", e);
54680     },
54681
54682     // private
54683     walkCells : function(row, col, step, fn, scope){
54684         var cm = this.colModel, clen = cm.getColumnCount();
54685         var ds = this.dataSource, rlen = ds.getCount(), first = true;
54686         if(step < 0){
54687             if(col < 0){
54688                 row--;
54689                 first = false;
54690             }
54691             while(row >= 0){
54692                 if(!first){
54693                     col = clen-1;
54694                 }
54695                 first = false;
54696                 while(col >= 0){
54697                     if(fn.call(scope || this, row, col, cm) === true){
54698                         return [row, col];
54699                     }
54700                     col--;
54701                 }
54702                 row--;
54703             }
54704         } else {
54705             if(col >= clen){
54706                 row++;
54707                 first = false;
54708             }
54709             while(row < rlen){
54710                 if(!first){
54711                     col = 0;
54712                 }
54713                 first = false;
54714                 while(col < clen){
54715                     if(fn.call(scope || this, row, col, cm) === true){
54716                         return [row, col];
54717                     }
54718                     col++;
54719                 }
54720                 row++;
54721             }
54722         }
54723         return null;
54724     },
54725
54726     // private
54727     getSelections : function(){
54728         return this.selModel.getSelections();
54729     },
54730
54731     /**
54732      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
54733      * but if manual update is required this method will initiate it.
54734      */
54735     autoSize : function(){
54736         if(this.rendered){
54737             this.view.layout();
54738             if(this.view.adjustForScroll){
54739                 this.view.adjustForScroll();
54740             }
54741         }
54742     },
54743
54744     /**
54745      * Returns the grid's underlying element.
54746      * @return {Element} The element
54747      */
54748     getGridEl : function(){
54749         return this.container;
54750     },
54751
54752     // private for compatibility, overridden by editor grid
54753     stopEditing : function(){},
54754
54755     /**
54756      * Returns the grid's SelectionModel.
54757      * @return {SelectionModel}
54758      */
54759     getSelectionModel : function(){
54760         if(!this.selModel){
54761             this.selModel = new Roo.grid.RowSelectionModel();
54762         }
54763         return this.selModel;
54764     },
54765
54766     /**
54767      * Returns the grid's DataSource.
54768      * @return {DataSource}
54769      */
54770     getDataSource : function(){
54771         return this.dataSource;
54772     },
54773
54774     /**
54775      * Returns the grid's ColumnModel.
54776      * @return {ColumnModel}
54777      */
54778     getColumnModel : function(){
54779         return this.colModel;
54780     },
54781
54782     /**
54783      * Returns the grid's GridView object.
54784      * @return {GridView}
54785      */
54786     getView : function(){
54787         if(!this.view){
54788             this.view = new Roo.grid.GridView(this.viewConfig);
54789         }
54790         return this.view;
54791     },
54792     /**
54793      * Called to get grid's drag proxy text, by default returns this.ddText.
54794      * @return {String}
54795      */
54796     getDragDropText : function(){
54797         var count = this.selModel.getCount();
54798         return String.format(this.ddText, count, count == 1 ? '' : 's');
54799     }
54800 });
54801 /**
54802  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
54803  * %0 is replaced with the number of selected rows.
54804  * @type String
54805  */
54806 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
54807  * Based on:
54808  * Ext JS Library 1.1.1
54809  * Copyright(c) 2006-2007, Ext JS, LLC.
54810  *
54811  * Originally Released Under LGPL - original licence link has changed is not relivant.
54812  *
54813  * Fork - LGPL
54814  * <script type="text/javascript">
54815  */
54816  
54817 Roo.grid.AbstractGridView = function(){
54818         this.grid = null;
54819         
54820         this.events = {
54821             "beforerowremoved" : true,
54822             "beforerowsinserted" : true,
54823             "beforerefresh" : true,
54824             "rowremoved" : true,
54825             "rowsinserted" : true,
54826             "rowupdated" : true,
54827             "refresh" : true
54828         };
54829     Roo.grid.AbstractGridView.superclass.constructor.call(this);
54830 };
54831
54832 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
54833     rowClass : "x-grid-row",
54834     cellClass : "x-grid-cell",
54835     tdClass : "x-grid-td",
54836     hdClass : "x-grid-hd",
54837     splitClass : "x-grid-hd-split",
54838     
54839     init: function(grid){
54840         this.grid = grid;
54841                 var cid = this.grid.getGridEl().id;
54842         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
54843         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
54844         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
54845         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
54846         },
54847         
54848     getColumnRenderers : function(){
54849         var renderers = [];
54850         var cm = this.grid.colModel;
54851         var colCount = cm.getColumnCount();
54852         for(var i = 0; i < colCount; i++){
54853             renderers[i] = cm.getRenderer(i);
54854         }
54855         return renderers;
54856     },
54857     
54858     getColumnIds : function(){
54859         var ids = [];
54860         var cm = this.grid.colModel;
54861         var colCount = cm.getColumnCount();
54862         for(var i = 0; i < colCount; i++){
54863             ids[i] = cm.getColumnId(i);
54864         }
54865         return ids;
54866     },
54867     
54868     getDataIndexes : function(){
54869         if(!this.indexMap){
54870             this.indexMap = this.buildIndexMap();
54871         }
54872         return this.indexMap.colToData;
54873     },
54874     
54875     getColumnIndexByDataIndex : function(dataIndex){
54876         if(!this.indexMap){
54877             this.indexMap = this.buildIndexMap();
54878         }
54879         return this.indexMap.dataToCol[dataIndex];
54880     },
54881     
54882     /**
54883      * Set a css style for a column dynamically. 
54884      * @param {Number} colIndex The index of the column
54885      * @param {String} name The css property name
54886      * @param {String} value The css value
54887      */
54888     setCSSStyle : function(colIndex, name, value){
54889         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
54890         Roo.util.CSS.updateRule(selector, name, value);
54891     },
54892     
54893     generateRules : function(cm){
54894         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
54895         Roo.util.CSS.removeStyleSheet(rulesId);
54896         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54897             var cid = cm.getColumnId(i);
54898             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
54899                          this.tdSelector, cid, " {\n}\n",
54900                          this.hdSelector, cid, " {\n}\n",
54901                          this.splitSelector, cid, " {\n}\n");
54902         }
54903         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54904     }
54905 });/*
54906  * Based on:
54907  * Ext JS Library 1.1.1
54908  * Copyright(c) 2006-2007, Ext JS, LLC.
54909  *
54910  * Originally Released Under LGPL - original licence link has changed is not relivant.
54911  *
54912  * Fork - LGPL
54913  * <script type="text/javascript">
54914  */
54915
54916 // private
54917 // This is a support class used internally by the Grid components
54918 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
54919     this.grid = grid;
54920     this.view = grid.getView();
54921     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54922     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
54923     if(hd2){
54924         this.setHandleElId(Roo.id(hd));
54925         this.setOuterHandleElId(Roo.id(hd2));
54926     }
54927     this.scroll = false;
54928 };
54929 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
54930     maxDragWidth: 120,
54931     getDragData : function(e){
54932         var t = Roo.lib.Event.getTarget(e);
54933         var h = this.view.findHeaderCell(t);
54934         if(h){
54935             return {ddel: h.firstChild, header:h};
54936         }
54937         return false;
54938     },
54939
54940     onInitDrag : function(e){
54941         this.view.headersDisabled = true;
54942         var clone = this.dragData.ddel.cloneNode(true);
54943         clone.id = Roo.id();
54944         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
54945         this.proxy.update(clone);
54946         return true;
54947     },
54948
54949     afterValidDrop : function(){
54950         var v = this.view;
54951         setTimeout(function(){
54952             v.headersDisabled = false;
54953         }, 50);
54954     },
54955
54956     afterInvalidDrop : function(){
54957         var v = this.view;
54958         setTimeout(function(){
54959             v.headersDisabled = false;
54960         }, 50);
54961     }
54962 });
54963 /*
54964  * Based on:
54965  * Ext JS Library 1.1.1
54966  * Copyright(c) 2006-2007, Ext JS, LLC.
54967  *
54968  * Originally Released Under LGPL - original licence link has changed is not relivant.
54969  *
54970  * Fork - LGPL
54971  * <script type="text/javascript">
54972  */
54973 // private
54974 // This is a support class used internally by the Grid components
54975 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
54976     this.grid = grid;
54977     this.view = grid.getView();
54978     // split the proxies so they don't interfere with mouse events
54979     this.proxyTop = Roo.DomHelper.append(document.body, {
54980         cls:"col-move-top", html:"&#160;"
54981     }, true);
54982     this.proxyBottom = Roo.DomHelper.append(document.body, {
54983         cls:"col-move-bottom", html:"&#160;"
54984     }, true);
54985     this.proxyTop.hide = this.proxyBottom.hide = function(){
54986         this.setLeftTop(-100,-100);
54987         this.setStyle("visibility", "hidden");
54988     };
54989     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54990     // temporarily disabled
54991     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
54992     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
54993 };
54994 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
54995     proxyOffsets : [-4, -9],
54996     fly: Roo.Element.fly,
54997
54998     getTargetFromEvent : function(e){
54999         var t = Roo.lib.Event.getTarget(e);
55000         var cindex = this.view.findCellIndex(t);
55001         if(cindex !== false){
55002             return this.view.getHeaderCell(cindex);
55003         }
55004         return null;
55005     },
55006
55007     nextVisible : function(h){
55008         var v = this.view, cm = this.grid.colModel;
55009         h = h.nextSibling;
55010         while(h){
55011             if(!cm.isHidden(v.getCellIndex(h))){
55012                 return h;
55013             }
55014             h = h.nextSibling;
55015         }
55016         return null;
55017     },
55018
55019     prevVisible : function(h){
55020         var v = this.view, cm = this.grid.colModel;
55021         h = h.prevSibling;
55022         while(h){
55023             if(!cm.isHidden(v.getCellIndex(h))){
55024                 return h;
55025             }
55026             h = h.prevSibling;
55027         }
55028         return null;
55029     },
55030
55031     positionIndicator : function(h, n, e){
55032         var x = Roo.lib.Event.getPageX(e);
55033         var r = Roo.lib.Dom.getRegion(n.firstChild);
55034         var px, pt, py = r.top + this.proxyOffsets[1];
55035         if((r.right - x) <= (r.right-r.left)/2){
55036             px = r.right+this.view.borderWidth;
55037             pt = "after";
55038         }else{
55039             px = r.left;
55040             pt = "before";
55041         }
55042         var oldIndex = this.view.getCellIndex(h);
55043         var newIndex = this.view.getCellIndex(n);
55044
55045         if(this.grid.colModel.isFixed(newIndex)){
55046             return false;
55047         }
55048
55049         var locked = this.grid.colModel.isLocked(newIndex);
55050
55051         if(pt == "after"){
55052             newIndex++;
55053         }
55054         if(oldIndex < newIndex){
55055             newIndex--;
55056         }
55057         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
55058             return false;
55059         }
55060         px +=  this.proxyOffsets[0];
55061         this.proxyTop.setLeftTop(px, py);
55062         this.proxyTop.show();
55063         if(!this.bottomOffset){
55064             this.bottomOffset = this.view.mainHd.getHeight();
55065         }
55066         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
55067         this.proxyBottom.show();
55068         return pt;
55069     },
55070
55071     onNodeEnter : function(n, dd, e, data){
55072         if(data.header != n){
55073             this.positionIndicator(data.header, n, e);
55074         }
55075     },
55076
55077     onNodeOver : function(n, dd, e, data){
55078         var result = false;
55079         if(data.header != n){
55080             result = this.positionIndicator(data.header, n, e);
55081         }
55082         if(!result){
55083             this.proxyTop.hide();
55084             this.proxyBottom.hide();
55085         }
55086         return result ? this.dropAllowed : this.dropNotAllowed;
55087     },
55088
55089     onNodeOut : function(n, dd, e, data){
55090         this.proxyTop.hide();
55091         this.proxyBottom.hide();
55092     },
55093
55094     onNodeDrop : function(n, dd, e, data){
55095         var h = data.header;
55096         if(h != n){
55097             var cm = this.grid.colModel;
55098             var x = Roo.lib.Event.getPageX(e);
55099             var r = Roo.lib.Dom.getRegion(n.firstChild);
55100             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
55101             var oldIndex = this.view.getCellIndex(h);
55102             var newIndex = this.view.getCellIndex(n);
55103             var locked = cm.isLocked(newIndex);
55104             if(pt == "after"){
55105                 newIndex++;
55106             }
55107             if(oldIndex < newIndex){
55108                 newIndex--;
55109             }
55110             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
55111                 return false;
55112             }
55113             cm.setLocked(oldIndex, locked, true);
55114             cm.moveColumn(oldIndex, newIndex);
55115             this.grid.fireEvent("columnmove", oldIndex, newIndex);
55116             return true;
55117         }
55118         return false;
55119     }
55120 });
55121 /*
55122  * Based on:
55123  * Ext JS Library 1.1.1
55124  * Copyright(c) 2006-2007, Ext JS, LLC.
55125  *
55126  * Originally Released Under LGPL - original licence link has changed is not relivant.
55127  *
55128  * Fork - LGPL
55129  * <script type="text/javascript">
55130  */
55131   
55132 /**
55133  * @class Roo.grid.GridView
55134  * @extends Roo.util.Observable
55135  *
55136  * @constructor
55137  * @param {Object} config
55138  */
55139 Roo.grid.GridView = function(config){
55140     Roo.grid.GridView.superclass.constructor.call(this);
55141     this.el = null;
55142
55143     Roo.apply(this, config);
55144 };
55145
55146 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
55147
55148     unselectable :  'unselectable="on"',
55149     unselectableCls :  'x-unselectable',
55150     
55151     
55152     rowClass : "x-grid-row",
55153
55154     cellClass : "x-grid-col",
55155
55156     tdClass : "x-grid-td",
55157
55158     hdClass : "x-grid-hd",
55159
55160     splitClass : "x-grid-split",
55161
55162     sortClasses : ["sort-asc", "sort-desc"],
55163
55164     enableMoveAnim : false,
55165
55166     hlColor: "C3DAF9",
55167
55168     dh : Roo.DomHelper,
55169
55170     fly : Roo.Element.fly,
55171
55172     css : Roo.util.CSS,
55173
55174     borderWidth: 1,
55175
55176     splitOffset: 3,
55177
55178     scrollIncrement : 22,
55179
55180     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
55181
55182     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
55183
55184     bind : function(ds, cm){
55185         if(this.ds){
55186             this.ds.un("load", this.onLoad, this);
55187             this.ds.un("datachanged", this.onDataChange, this);
55188             this.ds.un("add", this.onAdd, this);
55189             this.ds.un("remove", this.onRemove, this);
55190             this.ds.un("update", this.onUpdate, this);
55191             this.ds.un("clear", this.onClear, this);
55192         }
55193         if(ds){
55194             ds.on("load", this.onLoad, this);
55195             ds.on("datachanged", this.onDataChange, this);
55196             ds.on("add", this.onAdd, this);
55197             ds.on("remove", this.onRemove, this);
55198             ds.on("update", this.onUpdate, this);
55199             ds.on("clear", this.onClear, this);
55200         }
55201         this.ds = ds;
55202
55203         if(this.cm){
55204             this.cm.un("widthchange", this.onColWidthChange, this);
55205             this.cm.un("headerchange", this.onHeaderChange, this);
55206             this.cm.un("hiddenchange", this.onHiddenChange, this);
55207             this.cm.un("columnmoved", this.onColumnMove, this);
55208             this.cm.un("columnlockchange", this.onColumnLock, this);
55209         }
55210         if(cm){
55211             this.generateRules(cm);
55212             cm.on("widthchange", this.onColWidthChange, this);
55213             cm.on("headerchange", this.onHeaderChange, this);
55214             cm.on("hiddenchange", this.onHiddenChange, this);
55215             cm.on("columnmoved", this.onColumnMove, this);
55216             cm.on("columnlockchange", this.onColumnLock, this);
55217         }
55218         this.cm = cm;
55219     },
55220
55221     init: function(grid){
55222         Roo.grid.GridView.superclass.init.call(this, grid);
55223
55224         this.bind(grid.dataSource, grid.colModel);
55225
55226         grid.on("headerclick", this.handleHeaderClick, this);
55227
55228         if(grid.trackMouseOver){
55229             grid.on("mouseover", this.onRowOver, this);
55230             grid.on("mouseout", this.onRowOut, this);
55231         }
55232         grid.cancelTextSelection = function(){};
55233         this.gridId = grid.id;
55234
55235         var tpls = this.templates || {};
55236
55237         if(!tpls.master){
55238             tpls.master = new Roo.Template(
55239                '<div class="x-grid" hidefocus="true">',
55240                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
55241                   '<div class="x-grid-topbar"></div>',
55242                   '<div class="x-grid-scroller"><div></div></div>',
55243                   '<div class="x-grid-locked">',
55244                       '<div class="x-grid-header">{lockedHeader}</div>',
55245                       '<div class="x-grid-body">{lockedBody}</div>',
55246                   "</div>",
55247                   '<div class="x-grid-viewport">',
55248                       '<div class="x-grid-header">{header}</div>',
55249                       '<div class="x-grid-body">{body}</div>',
55250                   "</div>",
55251                   '<div class="x-grid-bottombar"></div>',
55252                  
55253                   '<div class="x-grid-resize-proxy">&#160;</div>',
55254                "</div>"
55255             );
55256             tpls.master.disableformats = true;
55257         }
55258
55259         if(!tpls.header){
55260             tpls.header = new Roo.Template(
55261                '<table border="0" cellspacing="0" cellpadding="0">',
55262                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
55263                "</table>{splits}"
55264             );
55265             tpls.header.disableformats = true;
55266         }
55267         tpls.header.compile();
55268
55269         if(!tpls.hcell){
55270             tpls.hcell = new Roo.Template(
55271                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
55272                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
55273                 "</div></td>"
55274              );
55275              tpls.hcell.disableFormats = true;
55276         }
55277         tpls.hcell.compile();
55278
55279         if(!tpls.hsplit){
55280             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
55281                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
55282             tpls.hsplit.disableFormats = true;
55283         }
55284         tpls.hsplit.compile();
55285
55286         if(!tpls.body){
55287             tpls.body = new Roo.Template(
55288                '<table border="0" cellspacing="0" cellpadding="0">',
55289                "<tbody>{rows}</tbody>",
55290                "</table>"
55291             );
55292             tpls.body.disableFormats = true;
55293         }
55294         tpls.body.compile();
55295
55296         if(!tpls.row){
55297             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
55298             tpls.row.disableFormats = true;
55299         }
55300         tpls.row.compile();
55301
55302         if(!tpls.cell){
55303             tpls.cell = new Roo.Template(
55304                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
55305                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
55306                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
55307                 "</td>"
55308             );
55309             tpls.cell.disableFormats = true;
55310         }
55311         tpls.cell.compile();
55312
55313         this.templates = tpls;
55314     },
55315
55316     // remap these for backwards compat
55317     onColWidthChange : function(){
55318         this.updateColumns.apply(this, arguments);
55319     },
55320     onHeaderChange : function(){
55321         this.updateHeaders.apply(this, arguments);
55322     }, 
55323     onHiddenChange : function(){
55324         this.handleHiddenChange.apply(this, arguments);
55325     },
55326     onColumnMove : function(){
55327         this.handleColumnMove.apply(this, arguments);
55328     },
55329     onColumnLock : function(){
55330         this.handleLockChange.apply(this, arguments);
55331     },
55332
55333     onDataChange : function(){
55334         this.refresh();
55335         this.updateHeaderSortState();
55336     },
55337
55338     onClear : function(){
55339         this.refresh();
55340     },
55341
55342     onUpdate : function(ds, record){
55343         this.refreshRow(record);
55344     },
55345
55346     refreshRow : function(record){
55347         var ds = this.ds, index;
55348         if(typeof record == 'number'){
55349             index = record;
55350             record = ds.getAt(index);
55351         }else{
55352             index = ds.indexOf(record);
55353         }
55354         this.insertRows(ds, index, index, true);
55355         this.onRemove(ds, record, index+1, true);
55356         this.syncRowHeights(index, index);
55357         this.layout();
55358         this.fireEvent("rowupdated", this, index, record);
55359     },
55360
55361     onAdd : function(ds, records, index){
55362         this.insertRows(ds, index, index + (records.length-1));
55363     },
55364
55365     onRemove : function(ds, record, index, isUpdate){
55366         if(isUpdate !== true){
55367             this.fireEvent("beforerowremoved", this, index, record);
55368         }
55369         var bt = this.getBodyTable(), lt = this.getLockedTable();
55370         if(bt.rows[index]){
55371             bt.firstChild.removeChild(bt.rows[index]);
55372         }
55373         if(lt.rows[index]){
55374             lt.firstChild.removeChild(lt.rows[index]);
55375         }
55376         if(isUpdate !== true){
55377             this.stripeRows(index);
55378             this.syncRowHeights(index, index);
55379             this.layout();
55380             this.fireEvent("rowremoved", this, index, record);
55381         }
55382     },
55383
55384     onLoad : function(){
55385         this.scrollToTop();
55386     },
55387
55388     /**
55389      * Scrolls the grid to the top
55390      */
55391     scrollToTop : function(){
55392         if(this.scroller){
55393             this.scroller.dom.scrollTop = 0;
55394             this.syncScroll();
55395         }
55396     },
55397
55398     /**
55399      * Gets a panel in the header of the grid that can be used for toolbars etc.
55400      * After modifying the contents of this panel a call to grid.autoSize() may be
55401      * required to register any changes in size.
55402      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
55403      * @return Roo.Element
55404      */
55405     getHeaderPanel : function(doShow){
55406         if(doShow){
55407             this.headerPanel.show();
55408         }
55409         return this.headerPanel;
55410     },
55411
55412     /**
55413      * Gets a panel in the footer of the grid that can be used for toolbars etc.
55414      * After modifying the contents of this panel a call to grid.autoSize() may be
55415      * required to register any changes in size.
55416      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
55417      * @return Roo.Element
55418      */
55419     getFooterPanel : function(doShow){
55420         if(doShow){
55421             this.footerPanel.show();
55422         }
55423         return this.footerPanel;
55424     },
55425
55426     initElements : function(){
55427         var E = Roo.Element;
55428         var el = this.grid.getGridEl().dom.firstChild;
55429         var cs = el.childNodes;
55430
55431         this.el = new E(el);
55432         
55433          this.focusEl = new E(el.firstChild);
55434         this.focusEl.swallowEvent("click", true);
55435         
55436         this.headerPanel = new E(cs[1]);
55437         this.headerPanel.enableDisplayMode("block");
55438
55439         this.scroller = new E(cs[2]);
55440         this.scrollSizer = new E(this.scroller.dom.firstChild);
55441
55442         this.lockedWrap = new E(cs[3]);
55443         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
55444         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
55445
55446         this.mainWrap = new E(cs[4]);
55447         this.mainHd = new E(this.mainWrap.dom.firstChild);
55448         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
55449
55450         this.footerPanel = new E(cs[5]);
55451         this.footerPanel.enableDisplayMode("block");
55452
55453         this.resizeProxy = new E(cs[6]);
55454
55455         this.headerSelector = String.format(
55456            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
55457            this.lockedHd.id, this.mainHd.id
55458         );
55459
55460         this.splitterSelector = String.format(
55461            '#{0} div.x-grid-split, #{1} div.x-grid-split',
55462            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
55463         );
55464     },
55465     idToCssName : function(s)
55466     {
55467         return s.replace(/[^a-z0-9]+/ig, '-');
55468     },
55469
55470     getHeaderCell : function(index){
55471         return Roo.DomQuery.select(this.headerSelector)[index];
55472     },
55473
55474     getHeaderCellMeasure : function(index){
55475         return this.getHeaderCell(index).firstChild;
55476     },
55477
55478     getHeaderCellText : function(index){
55479         return this.getHeaderCell(index).firstChild.firstChild;
55480     },
55481
55482     getLockedTable : function(){
55483         return this.lockedBody.dom.firstChild;
55484     },
55485
55486     getBodyTable : function(){
55487         return this.mainBody.dom.firstChild;
55488     },
55489
55490     getLockedRow : function(index){
55491         return this.getLockedTable().rows[index];
55492     },
55493
55494     getRow : function(index){
55495         return this.getBodyTable().rows[index];
55496     },
55497
55498     getRowComposite : function(index){
55499         if(!this.rowEl){
55500             this.rowEl = new Roo.CompositeElementLite();
55501         }
55502         var els = [], lrow, mrow;
55503         if(lrow = this.getLockedRow(index)){
55504             els.push(lrow);
55505         }
55506         if(mrow = this.getRow(index)){
55507             els.push(mrow);
55508         }
55509         this.rowEl.elements = els;
55510         return this.rowEl;
55511     },
55512     /**
55513      * Gets the 'td' of the cell
55514      * 
55515      * @param {Integer} rowIndex row to select
55516      * @param {Integer} colIndex column to select
55517      * 
55518      * @return {Object} 
55519      */
55520     getCell : function(rowIndex, colIndex){
55521         var locked = this.cm.getLockedCount();
55522         var source;
55523         if(colIndex < locked){
55524             source = this.lockedBody.dom.firstChild;
55525         }else{
55526             source = this.mainBody.dom.firstChild;
55527             colIndex -= locked;
55528         }
55529         return source.rows[rowIndex].childNodes[colIndex];
55530     },
55531
55532     getCellText : function(rowIndex, colIndex){
55533         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
55534     },
55535
55536     getCellBox : function(cell){
55537         var b = this.fly(cell).getBox();
55538         if(Roo.isOpera){ // opera fails to report the Y
55539             b.y = cell.offsetTop + this.mainBody.getY();
55540         }
55541         return b;
55542     },
55543
55544     getCellIndex : function(cell){
55545         var id = String(cell.className).match(this.cellRE);
55546         if(id){
55547             return parseInt(id[1], 10);
55548         }
55549         return 0;
55550     },
55551
55552     findHeaderIndex : function(n){
55553         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55554         return r ? this.getCellIndex(r) : false;
55555     },
55556
55557     findHeaderCell : function(n){
55558         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55559         return r ? r : false;
55560     },
55561
55562     findRowIndex : function(n){
55563         if(!n){
55564             return false;
55565         }
55566         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
55567         return r ? r.rowIndex : false;
55568     },
55569
55570     findCellIndex : function(node){
55571         var stop = this.el.dom;
55572         while(node && node != stop){
55573             if(this.findRE.test(node.className)){
55574                 return this.getCellIndex(node);
55575             }
55576             node = node.parentNode;
55577         }
55578         return false;
55579     },
55580
55581     getColumnId : function(index){
55582         return this.cm.getColumnId(index);
55583     },
55584
55585     getSplitters : function()
55586     {
55587         if(this.splitterSelector){
55588            return Roo.DomQuery.select(this.splitterSelector);
55589         }else{
55590             return null;
55591       }
55592     },
55593
55594     getSplitter : function(index){
55595         return this.getSplitters()[index];
55596     },
55597
55598     onRowOver : function(e, t){
55599         var row;
55600         if((row = this.findRowIndex(t)) !== false){
55601             this.getRowComposite(row).addClass("x-grid-row-over");
55602         }
55603     },
55604
55605     onRowOut : function(e, t){
55606         var row;
55607         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
55608             this.getRowComposite(row).removeClass("x-grid-row-over");
55609         }
55610     },
55611
55612     renderHeaders : function(){
55613         var cm = this.cm;
55614         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
55615         var cb = [], lb = [], sb = [], lsb = [], p = {};
55616         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55617             p.cellId = "x-grid-hd-0-" + i;
55618             p.splitId = "x-grid-csplit-0-" + i;
55619             p.id = cm.getColumnId(i);
55620             p.value = cm.getColumnHeader(i) || "";
55621             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
55622             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
55623             if(!cm.isLocked(i)){
55624                 cb[cb.length] = ct.apply(p);
55625                 sb[sb.length] = st.apply(p);
55626             }else{
55627                 lb[lb.length] = ct.apply(p);
55628                 lsb[lsb.length] = st.apply(p);
55629             }
55630         }
55631         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
55632                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
55633     },
55634
55635     updateHeaders : function(){
55636         var html = this.renderHeaders();
55637         this.lockedHd.update(html[0]);
55638         this.mainHd.update(html[1]);
55639     },
55640
55641     /**
55642      * Focuses the specified row.
55643      * @param {Number} row The row index
55644      */
55645     focusRow : function(row)
55646     {
55647         //Roo.log('GridView.focusRow');
55648         var x = this.scroller.dom.scrollLeft;
55649         this.focusCell(row, 0, false);
55650         this.scroller.dom.scrollLeft = x;
55651     },
55652
55653     /**
55654      * Focuses the specified cell.
55655      * @param {Number} row The row index
55656      * @param {Number} col The column index
55657      * @param {Boolean} hscroll false to disable horizontal scrolling
55658      */
55659     focusCell : function(row, col, hscroll)
55660     {
55661         //Roo.log('GridView.focusCell');
55662         var el = this.ensureVisible(row, col, hscroll);
55663         this.focusEl.alignTo(el, "tl-tl");
55664         if(Roo.isGecko){
55665             this.focusEl.focus();
55666         }else{
55667             this.focusEl.focus.defer(1, this.focusEl);
55668         }
55669     },
55670
55671     /**
55672      * Scrolls the specified cell into view
55673      * @param {Number} row The row index
55674      * @param {Number} col The column index
55675      * @param {Boolean} hscroll false to disable horizontal scrolling
55676      */
55677     ensureVisible : function(row, col, hscroll)
55678     {
55679         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
55680         //return null; //disable for testing.
55681         if(typeof row != "number"){
55682             row = row.rowIndex;
55683         }
55684         if(row < 0 && row >= this.ds.getCount()){
55685             return  null;
55686         }
55687         col = (col !== undefined ? col : 0);
55688         var cm = this.grid.colModel;
55689         while(cm.isHidden(col)){
55690             col++;
55691         }
55692
55693         var el = this.getCell(row, col);
55694         if(!el){
55695             return null;
55696         }
55697         var c = this.scroller.dom;
55698
55699         var ctop = parseInt(el.offsetTop, 10);
55700         var cleft = parseInt(el.offsetLeft, 10);
55701         var cbot = ctop + el.offsetHeight;
55702         var cright = cleft + el.offsetWidth;
55703         
55704         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
55705         var stop = parseInt(c.scrollTop, 10);
55706         var sleft = parseInt(c.scrollLeft, 10);
55707         var sbot = stop + ch;
55708         var sright = sleft + c.clientWidth;
55709         /*
55710         Roo.log('GridView.ensureVisible:' +
55711                 ' ctop:' + ctop +
55712                 ' c.clientHeight:' + c.clientHeight +
55713                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
55714                 ' stop:' + stop +
55715                 ' cbot:' + cbot +
55716                 ' sbot:' + sbot +
55717                 ' ch:' + ch  
55718                 );
55719         */
55720         if(ctop < stop){
55721              c.scrollTop = ctop;
55722             //Roo.log("set scrolltop to ctop DISABLE?");
55723         }else if(cbot > sbot){
55724             //Roo.log("set scrolltop to cbot-ch");
55725             c.scrollTop = cbot-ch;
55726         }
55727         
55728         if(hscroll !== false){
55729             if(cleft < sleft){
55730                 c.scrollLeft = cleft;
55731             }else if(cright > sright){
55732                 c.scrollLeft = cright-c.clientWidth;
55733             }
55734         }
55735          
55736         return el;
55737     },
55738
55739     updateColumns : function(){
55740         this.grid.stopEditing();
55741         var cm = this.grid.colModel, colIds = this.getColumnIds();
55742         //var totalWidth = cm.getTotalWidth();
55743         var pos = 0;
55744         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55745             //if(cm.isHidden(i)) continue;
55746             var w = cm.getColumnWidth(i);
55747             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55748             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55749         }
55750         this.updateSplitters();
55751     },
55752
55753     generateRules : function(cm){
55754         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
55755         Roo.util.CSS.removeStyleSheet(rulesId);
55756         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55757             var cid = cm.getColumnId(i);
55758             var align = '';
55759             if(cm.config[i].align){
55760                 align = 'text-align:'+cm.config[i].align+';';
55761             }
55762             var hidden = '';
55763             if(cm.isHidden(i)){
55764                 hidden = 'display:none;';
55765             }
55766             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
55767             ruleBuf.push(
55768                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
55769                     this.hdSelector, cid, " {\n", align, width, "}\n",
55770                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
55771                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
55772         }
55773         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55774     },
55775
55776     updateSplitters : function(){
55777         var cm = this.cm, s = this.getSplitters();
55778         if(s){ // splitters not created yet
55779             var pos = 0, locked = true;
55780             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55781                 if(cm.isHidden(i)) {
55782                     continue;
55783                 }
55784                 var w = cm.getColumnWidth(i); // make sure it's a number
55785                 if(!cm.isLocked(i) && locked){
55786                     pos = 0;
55787                     locked = false;
55788                 }
55789                 pos += w;
55790                 s[i].style.left = (pos-this.splitOffset) + "px";
55791             }
55792         }
55793     },
55794
55795     handleHiddenChange : function(colModel, colIndex, hidden){
55796         if(hidden){
55797             this.hideColumn(colIndex);
55798         }else{
55799             this.unhideColumn(colIndex);
55800         }
55801     },
55802
55803     hideColumn : function(colIndex){
55804         var cid = this.getColumnId(colIndex);
55805         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
55806         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
55807         if(Roo.isSafari){
55808             this.updateHeaders();
55809         }
55810         this.updateSplitters();
55811         this.layout();
55812     },
55813
55814     unhideColumn : function(colIndex){
55815         var cid = this.getColumnId(colIndex);
55816         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
55817         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
55818
55819         if(Roo.isSafari){
55820             this.updateHeaders();
55821         }
55822         this.updateSplitters();
55823         this.layout();
55824     },
55825
55826     insertRows : function(dm, firstRow, lastRow, isUpdate){
55827         if(firstRow == 0 && lastRow == dm.getCount()-1){
55828             this.refresh();
55829         }else{
55830             if(!isUpdate){
55831                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
55832             }
55833             var s = this.getScrollState();
55834             var markup = this.renderRows(firstRow, lastRow);
55835             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
55836             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
55837             this.restoreScroll(s);
55838             if(!isUpdate){
55839                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
55840                 this.syncRowHeights(firstRow, lastRow);
55841                 this.stripeRows(firstRow);
55842                 this.layout();
55843             }
55844         }
55845     },
55846
55847     bufferRows : function(markup, target, index){
55848         var before = null, trows = target.rows, tbody = target.tBodies[0];
55849         if(index < trows.length){
55850             before = trows[index];
55851         }
55852         var b = document.createElement("div");
55853         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
55854         var rows = b.firstChild.rows;
55855         for(var i = 0, len = rows.length; i < len; i++){
55856             if(before){
55857                 tbody.insertBefore(rows[0], before);
55858             }else{
55859                 tbody.appendChild(rows[0]);
55860             }
55861         }
55862         b.innerHTML = "";
55863         b = null;
55864     },
55865
55866     deleteRows : function(dm, firstRow, lastRow){
55867         if(dm.getRowCount()<1){
55868             this.fireEvent("beforerefresh", this);
55869             this.mainBody.update("");
55870             this.lockedBody.update("");
55871             this.fireEvent("refresh", this);
55872         }else{
55873             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
55874             var bt = this.getBodyTable();
55875             var tbody = bt.firstChild;
55876             var rows = bt.rows;
55877             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
55878                 tbody.removeChild(rows[firstRow]);
55879             }
55880             this.stripeRows(firstRow);
55881             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
55882         }
55883     },
55884
55885     updateRows : function(dataSource, firstRow, lastRow){
55886         var s = this.getScrollState();
55887         this.refresh();
55888         this.restoreScroll(s);
55889     },
55890
55891     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
55892         if(!noRefresh){
55893            this.refresh();
55894         }
55895         this.updateHeaderSortState();
55896     },
55897
55898     getScrollState : function(){
55899         
55900         var sb = this.scroller.dom;
55901         return {left: sb.scrollLeft, top: sb.scrollTop};
55902     },
55903
55904     stripeRows : function(startRow){
55905         if(!this.grid.stripeRows || this.ds.getCount() < 1){
55906             return;
55907         }
55908         startRow = startRow || 0;
55909         var rows = this.getBodyTable().rows;
55910         var lrows = this.getLockedTable().rows;
55911         var cls = ' x-grid-row-alt ';
55912         for(var i = startRow, len = rows.length; i < len; i++){
55913             var row = rows[i], lrow = lrows[i];
55914             var isAlt = ((i+1) % 2 == 0);
55915             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
55916             if(isAlt == hasAlt){
55917                 continue;
55918             }
55919             if(isAlt){
55920                 row.className += " x-grid-row-alt";
55921             }else{
55922                 row.className = row.className.replace("x-grid-row-alt", "");
55923             }
55924             if(lrow){
55925                 lrow.className = row.className;
55926             }
55927         }
55928     },
55929
55930     restoreScroll : function(state){
55931         //Roo.log('GridView.restoreScroll');
55932         var sb = this.scroller.dom;
55933         sb.scrollLeft = state.left;
55934         sb.scrollTop = state.top;
55935         this.syncScroll();
55936     },
55937
55938     syncScroll : function(){
55939         //Roo.log('GridView.syncScroll');
55940         var sb = this.scroller.dom;
55941         var sh = this.mainHd.dom;
55942         var bs = this.mainBody.dom;
55943         var lv = this.lockedBody.dom;
55944         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
55945         lv.scrollTop = bs.scrollTop = sb.scrollTop;
55946     },
55947
55948     handleScroll : function(e){
55949         this.syncScroll();
55950         var sb = this.scroller.dom;
55951         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
55952         e.stopEvent();
55953     },
55954
55955     handleWheel : function(e){
55956         var d = e.getWheelDelta();
55957         this.scroller.dom.scrollTop -= d*22;
55958         // set this here to prevent jumpy scrolling on large tables
55959         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
55960         e.stopEvent();
55961     },
55962
55963     renderRows : function(startRow, endRow){
55964         // pull in all the crap needed to render rows
55965         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
55966         var colCount = cm.getColumnCount();
55967
55968         if(ds.getCount() < 1){
55969             return ["", ""];
55970         }
55971
55972         // build a map for all the columns
55973         var cs = [];
55974         for(var i = 0; i < colCount; i++){
55975             var name = cm.getDataIndex(i);
55976             cs[i] = {
55977                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
55978                 renderer : cm.getRenderer(i),
55979                 id : cm.getColumnId(i),
55980                 locked : cm.isLocked(i),
55981                 has_editor : cm.isCellEditable(i)
55982             };
55983         }
55984
55985         startRow = startRow || 0;
55986         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
55987
55988         // records to render
55989         var rs = ds.getRange(startRow, endRow);
55990
55991         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
55992     },
55993
55994     // As much as I hate to duplicate code, this was branched because FireFox really hates
55995     // [].join("") on strings. The performance difference was substantial enough to
55996     // branch this function
55997     doRender : Roo.isGecko ?
55998             function(cs, rs, ds, startRow, colCount, stripe){
55999                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56000                 // buffers
56001                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56002                 
56003                 var hasListener = this.grid.hasListener('rowclass');
56004                 var rowcfg = {};
56005                 for(var j = 0, len = rs.length; j < len; j++){
56006                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
56007                     for(var i = 0; i < colCount; i++){
56008                         c = cs[i];
56009                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56010                         p.id = c.id;
56011                         p.css = p.attr = "";
56012                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56013                         if(p.value == undefined || p.value === "") {
56014                             p.value = "&#160;";
56015                         }
56016                         if(c.has_editor){
56017                             p.css += ' x-grid-editable-cell';
56018                         }
56019                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
56020                             p.css +=  ' x-grid-dirty-cell';
56021                         }
56022                         var markup = ct.apply(p);
56023                         if(!c.locked){
56024                             cb+= markup;
56025                         }else{
56026                             lcb+= markup;
56027                         }
56028                     }
56029                     var alt = [];
56030                     if(stripe && ((rowIndex+1) % 2 == 0)){
56031                         alt.push("x-grid-row-alt")
56032                     }
56033                     if(r.dirty){
56034                         alt.push(  " x-grid-dirty-row");
56035                     }
56036                     rp.cells = lcb;
56037                     if(this.getRowClass){
56038                         alt.push(this.getRowClass(r, rowIndex));
56039                     }
56040                     if (hasListener) {
56041                         rowcfg = {
56042                              
56043                             record: r,
56044                             rowIndex : rowIndex,
56045                             rowClass : ''
56046                         };
56047                         this.grid.fireEvent('rowclass', this, rowcfg);
56048                         alt.push(rowcfg.rowClass);
56049                     }
56050                     rp.alt = alt.join(" ");
56051                     lbuf+= rt.apply(rp);
56052                     rp.cells = cb;
56053                     buf+=  rt.apply(rp);
56054                 }
56055                 return [lbuf, buf];
56056             } :
56057             function(cs, rs, ds, startRow, colCount, stripe){
56058                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56059                 // buffers
56060                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56061                 var hasListener = this.grid.hasListener('rowclass');
56062  
56063                 var rowcfg = {};
56064                 for(var j = 0, len = rs.length; j < len; j++){
56065                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
56066                     for(var i = 0; i < colCount; i++){
56067                         c = cs[i];
56068                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56069                         p.id = c.id;
56070                         p.css = p.attr = "";
56071                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56072                         if(p.value == undefined || p.value === "") {
56073                             p.value = "&#160;";
56074                         }
56075                         //Roo.log(c);
56076                          if(c.has_editor){
56077                             p.css += ' x-grid-editable-cell';
56078                         }
56079                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
56080                             p.css += ' x-grid-dirty-cell' 
56081                         }
56082                         
56083                         var markup = ct.apply(p);
56084                         if(!c.locked){
56085                             cb[cb.length] = markup;
56086                         }else{
56087                             lcb[lcb.length] = markup;
56088                         }
56089                     }
56090                     var alt = [];
56091                     if(stripe && ((rowIndex+1) % 2 == 0)){
56092                         alt.push( "x-grid-row-alt");
56093                     }
56094                     if(r.dirty){
56095                         alt.push(" x-grid-dirty-row");
56096                     }
56097                     rp.cells = lcb;
56098                     if(this.getRowClass){
56099                         alt.push( this.getRowClass(r, rowIndex));
56100                     }
56101                     if (hasListener) {
56102                         rowcfg = {
56103                              
56104                             record: r,
56105                             rowIndex : rowIndex,
56106                             rowClass : ''
56107                         };
56108                         this.grid.fireEvent('rowclass', this, rowcfg);
56109                         alt.push(rowcfg.rowClass);
56110                     }
56111                     
56112                     rp.alt = alt.join(" ");
56113                     rp.cells = lcb.join("");
56114                     lbuf[lbuf.length] = rt.apply(rp);
56115                     rp.cells = cb.join("");
56116                     buf[buf.length] =  rt.apply(rp);
56117                 }
56118                 return [lbuf.join(""), buf.join("")];
56119             },
56120
56121     renderBody : function(){
56122         var markup = this.renderRows();
56123         var bt = this.templates.body;
56124         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
56125     },
56126
56127     /**
56128      * Refreshes the grid
56129      * @param {Boolean} headersToo
56130      */
56131     refresh : function(headersToo){
56132         this.fireEvent("beforerefresh", this);
56133         this.grid.stopEditing();
56134         var result = this.renderBody();
56135         this.lockedBody.update(result[0]);
56136         this.mainBody.update(result[1]);
56137         if(headersToo === true){
56138             this.updateHeaders();
56139             this.updateColumns();
56140             this.updateSplitters();
56141             this.updateHeaderSortState();
56142         }
56143         this.syncRowHeights();
56144         this.layout();
56145         this.fireEvent("refresh", this);
56146     },
56147
56148     handleColumnMove : function(cm, oldIndex, newIndex){
56149         this.indexMap = null;
56150         var s = this.getScrollState();
56151         this.refresh(true);
56152         this.restoreScroll(s);
56153         this.afterMove(newIndex);
56154     },
56155
56156     afterMove : function(colIndex){
56157         if(this.enableMoveAnim && Roo.enableFx){
56158             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
56159         }
56160         // if multisort - fix sortOrder, and reload..
56161         if (this.grid.dataSource.multiSort) {
56162             // the we can call sort again..
56163             var dm = this.grid.dataSource;
56164             var cm = this.grid.colModel;
56165             var so = [];
56166             for(var i = 0; i < cm.config.length; i++ ) {
56167                 
56168                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
56169                     continue; // dont' bother, it's not in sort list or being set.
56170                 }
56171                 
56172                 so.push(cm.config[i].dataIndex);
56173             };
56174             dm.sortOrder = so;
56175             dm.load(dm.lastOptions);
56176             
56177             
56178         }
56179         
56180     },
56181
56182     updateCell : function(dm, rowIndex, dataIndex){
56183         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
56184         if(typeof colIndex == "undefined"){ // not present in grid
56185             return;
56186         }
56187         var cm = this.grid.colModel;
56188         var cell = this.getCell(rowIndex, colIndex);
56189         var cellText = this.getCellText(rowIndex, colIndex);
56190
56191         var p = {
56192             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
56193             id : cm.getColumnId(colIndex),
56194             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
56195         };
56196         var renderer = cm.getRenderer(colIndex);
56197         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
56198         if(typeof val == "undefined" || val === "") {
56199             val = "&#160;";
56200         }
56201         cellText.innerHTML = val;
56202         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
56203         this.syncRowHeights(rowIndex, rowIndex);
56204     },
56205
56206     calcColumnWidth : function(colIndex, maxRowsToMeasure){
56207         var maxWidth = 0;
56208         if(this.grid.autoSizeHeaders){
56209             var h = this.getHeaderCellMeasure(colIndex);
56210             maxWidth = Math.max(maxWidth, h.scrollWidth);
56211         }
56212         var tb, index;
56213         if(this.cm.isLocked(colIndex)){
56214             tb = this.getLockedTable();
56215             index = colIndex;
56216         }else{
56217             tb = this.getBodyTable();
56218             index = colIndex - this.cm.getLockedCount();
56219         }
56220         if(tb && tb.rows){
56221             var rows = tb.rows;
56222             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
56223             for(var i = 0; i < stopIndex; i++){
56224                 var cell = rows[i].childNodes[index].firstChild;
56225                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
56226             }
56227         }
56228         return maxWidth + /*margin for error in IE*/ 5;
56229     },
56230     /**
56231      * Autofit a column to its content.
56232      * @param {Number} colIndex
56233      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
56234      */
56235      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
56236          if(this.cm.isHidden(colIndex)){
56237              return; // can't calc a hidden column
56238          }
56239         if(forceMinSize){
56240             var cid = this.cm.getColumnId(colIndex);
56241             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
56242            if(this.grid.autoSizeHeaders){
56243                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
56244            }
56245         }
56246         var newWidth = this.calcColumnWidth(colIndex);
56247         this.cm.setColumnWidth(colIndex,
56248             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
56249         if(!suppressEvent){
56250             this.grid.fireEvent("columnresize", colIndex, newWidth);
56251         }
56252     },
56253
56254     /**
56255      * Autofits all columns to their content and then expands to fit any extra space in the grid
56256      */
56257      autoSizeColumns : function(){
56258         var cm = this.grid.colModel;
56259         var colCount = cm.getColumnCount();
56260         for(var i = 0; i < colCount; i++){
56261             this.autoSizeColumn(i, true, true);
56262         }
56263         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
56264             this.fitColumns();
56265         }else{
56266             this.updateColumns();
56267             this.layout();
56268         }
56269     },
56270
56271     /**
56272      * Autofits all columns to the grid's width proportionate with their current size
56273      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
56274      */
56275     fitColumns : function(reserveScrollSpace){
56276         var cm = this.grid.colModel;
56277         var colCount = cm.getColumnCount();
56278         var cols = [];
56279         var width = 0;
56280         var i, w;
56281         for (i = 0; i < colCount; i++){
56282             if(!cm.isHidden(i) && !cm.isFixed(i)){
56283                 w = cm.getColumnWidth(i);
56284                 cols.push(i);
56285                 cols.push(w);
56286                 width += w;
56287             }
56288         }
56289         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
56290         if(reserveScrollSpace){
56291             avail -= 17;
56292         }
56293         var frac = (avail - cm.getTotalWidth())/width;
56294         while (cols.length){
56295             w = cols.pop();
56296             i = cols.pop();
56297             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
56298         }
56299         this.updateColumns();
56300         this.layout();
56301     },
56302
56303     onRowSelect : function(rowIndex){
56304         var row = this.getRowComposite(rowIndex);
56305         row.addClass("x-grid-row-selected");
56306     },
56307
56308     onRowDeselect : function(rowIndex){
56309         var row = this.getRowComposite(rowIndex);
56310         row.removeClass("x-grid-row-selected");
56311     },
56312
56313     onCellSelect : function(row, col){
56314         var cell = this.getCell(row, col);
56315         if(cell){
56316             Roo.fly(cell).addClass("x-grid-cell-selected");
56317         }
56318     },
56319
56320     onCellDeselect : function(row, col){
56321         var cell = this.getCell(row, col);
56322         if(cell){
56323             Roo.fly(cell).removeClass("x-grid-cell-selected");
56324         }
56325     },
56326
56327     updateHeaderSortState : function(){
56328         
56329         // sort state can be single { field: xxx, direction : yyy}
56330         // or   { xxx=>ASC , yyy : DESC ..... }
56331         
56332         var mstate = {};
56333         if (!this.ds.multiSort) { 
56334             var state = this.ds.getSortState();
56335             if(!state){
56336                 return;
56337             }
56338             mstate[state.field] = state.direction;
56339             // FIXME... - this is not used here.. but might be elsewhere..
56340             this.sortState = state;
56341             
56342         } else {
56343             mstate = this.ds.sortToggle;
56344         }
56345         //remove existing sort classes..
56346         
56347         var sc = this.sortClasses;
56348         var hds = this.el.select(this.headerSelector).removeClass(sc);
56349         
56350         for(var f in mstate) {
56351         
56352             var sortColumn = this.cm.findColumnIndex(f);
56353             
56354             if(sortColumn != -1){
56355                 var sortDir = mstate[f];        
56356                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
56357             }
56358         }
56359         
56360          
56361         
56362     },
56363
56364
56365     handleHeaderClick : function(g, index,e){
56366         
56367         Roo.log("header click");
56368         
56369         if (Roo.isTouch) {
56370             // touch events on header are handled by context
56371             this.handleHdCtx(g,index,e);
56372             return;
56373         }
56374         
56375         
56376         if(this.headersDisabled){
56377             return;
56378         }
56379         var dm = g.dataSource, cm = g.colModel;
56380         if(!cm.isSortable(index)){
56381             return;
56382         }
56383         g.stopEditing();
56384         
56385         if (dm.multiSort) {
56386             // update the sortOrder
56387             var so = [];
56388             for(var i = 0; i < cm.config.length; i++ ) {
56389                 
56390                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
56391                     continue; // dont' bother, it's not in sort list or being set.
56392                 }
56393                 
56394                 so.push(cm.config[i].dataIndex);
56395             };
56396             dm.sortOrder = so;
56397         }
56398         
56399         
56400         dm.sort(cm.getDataIndex(index));
56401     },
56402
56403
56404     destroy : function(){
56405         if(this.colMenu){
56406             this.colMenu.removeAll();
56407             Roo.menu.MenuMgr.unregister(this.colMenu);
56408             this.colMenu.getEl().remove();
56409             delete this.colMenu;
56410         }
56411         if(this.hmenu){
56412             this.hmenu.removeAll();
56413             Roo.menu.MenuMgr.unregister(this.hmenu);
56414             this.hmenu.getEl().remove();
56415             delete this.hmenu;
56416         }
56417         if(this.grid.enableColumnMove){
56418             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56419             if(dds){
56420                 for(var dd in dds){
56421                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
56422                         var elid = dds[dd].dragElId;
56423                         dds[dd].unreg();
56424                         Roo.get(elid).remove();
56425                     } else if(dds[dd].config.isTarget){
56426                         dds[dd].proxyTop.remove();
56427                         dds[dd].proxyBottom.remove();
56428                         dds[dd].unreg();
56429                     }
56430                     if(Roo.dd.DDM.locationCache[dd]){
56431                         delete Roo.dd.DDM.locationCache[dd];
56432                     }
56433                 }
56434                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56435             }
56436         }
56437         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
56438         this.bind(null, null);
56439         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
56440     },
56441
56442     handleLockChange : function(){
56443         this.refresh(true);
56444     },
56445
56446     onDenyColumnLock : function(){
56447
56448     },
56449
56450     onDenyColumnHide : function(){
56451
56452     },
56453
56454     handleHdMenuClick : function(item){
56455         var index = this.hdCtxIndex;
56456         var cm = this.cm, ds = this.ds;
56457         switch(item.id){
56458             case "asc":
56459                 ds.sort(cm.getDataIndex(index), "ASC");
56460                 break;
56461             case "desc":
56462                 ds.sort(cm.getDataIndex(index), "DESC");
56463                 break;
56464             case "lock":
56465                 var lc = cm.getLockedCount();
56466                 if(cm.getColumnCount(true) <= lc+1){
56467                     this.onDenyColumnLock();
56468                     return;
56469                 }
56470                 if(lc != index){
56471                     cm.setLocked(index, true, true);
56472                     cm.moveColumn(index, lc);
56473                     this.grid.fireEvent("columnmove", index, lc);
56474                 }else{
56475                     cm.setLocked(index, true);
56476                 }
56477             break;
56478             case "unlock":
56479                 var lc = cm.getLockedCount();
56480                 if((lc-1) != index){
56481                     cm.setLocked(index, false, true);
56482                     cm.moveColumn(index, lc-1);
56483                     this.grid.fireEvent("columnmove", index, lc-1);
56484                 }else{
56485                     cm.setLocked(index, false);
56486                 }
56487             break;
56488             case 'wider': // used to expand cols on touch..
56489             case 'narrow':
56490                 var cw = cm.getColumnWidth(index);
56491                 cw += (item.id == 'wider' ? 1 : -1) * 50;
56492                 cw = Math.max(0, cw);
56493                 cw = Math.min(cw,4000);
56494                 cm.setColumnWidth(index, cw);
56495                 break;
56496                 
56497             default:
56498                 index = cm.getIndexById(item.id.substr(4));
56499                 if(index != -1){
56500                     if(item.checked && cm.getColumnCount(true) <= 1){
56501                         this.onDenyColumnHide();
56502                         return false;
56503                     }
56504                     cm.setHidden(index, item.checked);
56505                 }
56506         }
56507         return true;
56508     },
56509
56510     beforeColMenuShow : function(){
56511         var cm = this.cm,  colCount = cm.getColumnCount();
56512         this.colMenu.removeAll();
56513         for(var i = 0; i < colCount; i++){
56514             this.colMenu.add(new Roo.menu.CheckItem({
56515                 id: "col-"+cm.getColumnId(i),
56516                 text: cm.getColumnHeader(i),
56517                 checked: !cm.isHidden(i),
56518                 hideOnClick:false
56519             }));
56520         }
56521     },
56522
56523     handleHdCtx : function(g, index, e){
56524         e.stopEvent();
56525         var hd = this.getHeaderCell(index);
56526         this.hdCtxIndex = index;
56527         var ms = this.hmenu.items, cm = this.cm;
56528         ms.get("asc").setDisabled(!cm.isSortable(index));
56529         ms.get("desc").setDisabled(!cm.isSortable(index));
56530         if(this.grid.enableColLock !== false){
56531             ms.get("lock").setDisabled(cm.isLocked(index));
56532             ms.get("unlock").setDisabled(!cm.isLocked(index));
56533         }
56534         this.hmenu.show(hd, "tl-bl");
56535     },
56536
56537     handleHdOver : function(e){
56538         var hd = this.findHeaderCell(e.getTarget());
56539         if(hd && !this.headersDisabled){
56540             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
56541                this.fly(hd).addClass("x-grid-hd-over");
56542             }
56543         }
56544     },
56545
56546     handleHdOut : function(e){
56547         var hd = this.findHeaderCell(e.getTarget());
56548         if(hd){
56549             this.fly(hd).removeClass("x-grid-hd-over");
56550         }
56551     },
56552
56553     handleSplitDblClick : function(e, t){
56554         var i = this.getCellIndex(t);
56555         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
56556             this.autoSizeColumn(i, true);
56557             this.layout();
56558         }
56559     },
56560
56561     render : function(){
56562
56563         var cm = this.cm;
56564         var colCount = cm.getColumnCount();
56565
56566         if(this.grid.monitorWindowResize === true){
56567             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
56568         }
56569         var header = this.renderHeaders();
56570         var body = this.templates.body.apply({rows:""});
56571         var html = this.templates.master.apply({
56572             lockedBody: body,
56573             body: body,
56574             lockedHeader: header[0],
56575             header: header[1]
56576         });
56577
56578         //this.updateColumns();
56579
56580         this.grid.getGridEl().dom.innerHTML = html;
56581
56582         this.initElements();
56583         
56584         // a kludge to fix the random scolling effect in webkit
56585         this.el.on("scroll", function() {
56586             this.el.dom.scrollTop=0; // hopefully not recursive..
56587         },this);
56588
56589         this.scroller.on("scroll", this.handleScroll, this);
56590         this.lockedBody.on("mousewheel", this.handleWheel, this);
56591         this.mainBody.on("mousewheel", this.handleWheel, this);
56592
56593         this.mainHd.on("mouseover", this.handleHdOver, this);
56594         this.mainHd.on("mouseout", this.handleHdOut, this);
56595         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
56596                 {delegate: "."+this.splitClass});
56597
56598         this.lockedHd.on("mouseover", this.handleHdOver, this);
56599         this.lockedHd.on("mouseout", this.handleHdOut, this);
56600         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
56601                 {delegate: "."+this.splitClass});
56602
56603         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
56604             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56605         }
56606
56607         this.updateSplitters();
56608
56609         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
56610             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56611             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56612         }
56613
56614         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
56615             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
56616             this.hmenu.add(
56617                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
56618                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
56619             );
56620             if(this.grid.enableColLock !== false){
56621                 this.hmenu.add('-',
56622                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
56623                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
56624                 );
56625             }
56626             if (Roo.isTouch) {
56627                  this.hmenu.add('-',
56628                     {id:"wider", text: this.columnsWiderText},
56629                     {id:"narrow", text: this.columnsNarrowText }
56630                 );
56631                 
56632                  
56633             }
56634             
56635             if(this.grid.enableColumnHide !== false){
56636
56637                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
56638                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
56639                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
56640
56641                 this.hmenu.add('-',
56642                     {id:"columns", text: this.columnsText, menu: this.colMenu}
56643                 );
56644             }
56645             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
56646
56647             this.grid.on("headercontextmenu", this.handleHdCtx, this);
56648         }
56649
56650         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
56651             this.dd = new Roo.grid.GridDragZone(this.grid, {
56652                 ddGroup : this.grid.ddGroup || 'GridDD'
56653             });
56654             
56655         }
56656
56657         /*
56658         for(var i = 0; i < colCount; i++){
56659             if(cm.isHidden(i)){
56660                 this.hideColumn(i);
56661             }
56662             if(cm.config[i].align){
56663                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
56664                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
56665             }
56666         }*/
56667         
56668         this.updateHeaderSortState();
56669
56670         this.beforeInitialResize();
56671         this.layout(true);
56672
56673         // two part rendering gives faster view to the user
56674         this.renderPhase2.defer(1, this);
56675     },
56676
56677     renderPhase2 : function(){
56678         // render the rows now
56679         this.refresh();
56680         if(this.grid.autoSizeColumns){
56681             this.autoSizeColumns();
56682         }
56683     },
56684
56685     beforeInitialResize : function(){
56686
56687     },
56688
56689     onColumnSplitterMoved : function(i, w){
56690         this.userResized = true;
56691         var cm = this.grid.colModel;
56692         cm.setColumnWidth(i, w, true);
56693         var cid = cm.getColumnId(i);
56694         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56695         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56696         this.updateSplitters();
56697         this.layout();
56698         this.grid.fireEvent("columnresize", i, w);
56699     },
56700
56701     syncRowHeights : function(startIndex, endIndex){
56702         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
56703             startIndex = startIndex || 0;
56704             var mrows = this.getBodyTable().rows;
56705             var lrows = this.getLockedTable().rows;
56706             var len = mrows.length-1;
56707             endIndex = Math.min(endIndex || len, len);
56708             for(var i = startIndex; i <= endIndex; i++){
56709                 var m = mrows[i], l = lrows[i];
56710                 var h = Math.max(m.offsetHeight, l.offsetHeight);
56711                 m.style.height = l.style.height = h + "px";
56712             }
56713         }
56714     },
56715
56716     layout : function(initialRender, is2ndPass){
56717         var g = this.grid;
56718         var auto = g.autoHeight;
56719         var scrollOffset = 16;
56720         var c = g.getGridEl(), cm = this.cm,
56721                 expandCol = g.autoExpandColumn,
56722                 gv = this;
56723         //c.beginMeasure();
56724
56725         if(!c.dom.offsetWidth){ // display:none?
56726             if(initialRender){
56727                 this.lockedWrap.show();
56728                 this.mainWrap.show();
56729             }
56730             return;
56731         }
56732
56733         var hasLock = this.cm.isLocked(0);
56734
56735         var tbh = this.headerPanel.getHeight();
56736         var bbh = this.footerPanel.getHeight();
56737
56738         if(auto){
56739             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
56740             var newHeight = ch + c.getBorderWidth("tb");
56741             if(g.maxHeight){
56742                 newHeight = Math.min(g.maxHeight, newHeight);
56743             }
56744             c.setHeight(newHeight);
56745         }
56746
56747         if(g.autoWidth){
56748             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
56749         }
56750
56751         var s = this.scroller;
56752
56753         var csize = c.getSize(true);
56754
56755         this.el.setSize(csize.width, csize.height);
56756
56757         this.headerPanel.setWidth(csize.width);
56758         this.footerPanel.setWidth(csize.width);
56759
56760         var hdHeight = this.mainHd.getHeight();
56761         var vw = csize.width;
56762         var vh = csize.height - (tbh + bbh);
56763
56764         s.setSize(vw, vh);
56765
56766         var bt = this.getBodyTable();
56767         
56768         if(cm.getLockedCount() == cm.config.length){
56769             bt = this.getLockedTable();
56770         }
56771         
56772         var ltWidth = hasLock ?
56773                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
56774
56775         var scrollHeight = bt.offsetHeight;
56776         var scrollWidth = ltWidth + bt.offsetWidth;
56777         var vscroll = false, hscroll = false;
56778
56779         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
56780
56781         var lw = this.lockedWrap, mw = this.mainWrap;
56782         var lb = this.lockedBody, mb = this.mainBody;
56783
56784         setTimeout(function(){
56785             var t = s.dom.offsetTop;
56786             var w = s.dom.clientWidth,
56787                 h = s.dom.clientHeight;
56788
56789             lw.setTop(t);
56790             lw.setSize(ltWidth, h);
56791
56792             mw.setLeftTop(ltWidth, t);
56793             mw.setSize(w-ltWidth, h);
56794
56795             lb.setHeight(h-hdHeight);
56796             mb.setHeight(h-hdHeight);
56797
56798             if(is2ndPass !== true && !gv.userResized && expandCol){
56799                 // high speed resize without full column calculation
56800                 
56801                 var ci = cm.getIndexById(expandCol);
56802                 if (ci < 0) {
56803                     ci = cm.findColumnIndex(expandCol);
56804                 }
56805                 ci = Math.max(0, ci); // make sure it's got at least the first col.
56806                 var expandId = cm.getColumnId(ci);
56807                 var  tw = cm.getTotalWidth(false);
56808                 var currentWidth = cm.getColumnWidth(ci);
56809                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
56810                 if(currentWidth != cw){
56811                     cm.setColumnWidth(ci, cw, true);
56812                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56813                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56814                     gv.updateSplitters();
56815                     gv.layout(false, true);
56816                 }
56817             }
56818
56819             if(initialRender){
56820                 lw.show();
56821                 mw.show();
56822             }
56823             //c.endMeasure();
56824         }, 10);
56825     },
56826
56827     onWindowResize : function(){
56828         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
56829             return;
56830         }
56831         this.layout();
56832     },
56833
56834     appendFooter : function(parentEl){
56835         return null;
56836     },
56837
56838     sortAscText : "Sort Ascending",
56839     sortDescText : "Sort Descending",
56840     lockText : "Lock Column",
56841     unlockText : "Unlock Column",
56842     columnsText : "Columns",
56843  
56844     columnsWiderText : "Wider",
56845     columnsNarrowText : "Thinner"
56846 });
56847
56848
56849 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
56850     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
56851     this.proxy.el.addClass('x-grid3-col-dd');
56852 };
56853
56854 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
56855     handleMouseDown : function(e){
56856
56857     },
56858
56859     callHandleMouseDown : function(e){
56860         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
56861     }
56862 });
56863 /*
56864  * Based on:
56865  * Ext JS Library 1.1.1
56866  * Copyright(c) 2006-2007, Ext JS, LLC.
56867  *
56868  * Originally Released Under LGPL - original licence link has changed is not relivant.
56869  *
56870  * Fork - LGPL
56871  * <script type="text/javascript">
56872  */
56873  
56874 // private
56875 // This is a support class used internally by the Grid components
56876 Roo.grid.SplitDragZone = function(grid, hd, hd2){
56877     this.grid = grid;
56878     this.view = grid.getView();
56879     this.proxy = this.view.resizeProxy;
56880     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
56881         "gridSplitters" + this.grid.getGridEl().id, {
56882         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
56883     });
56884     this.setHandleElId(Roo.id(hd));
56885     this.setOuterHandleElId(Roo.id(hd2));
56886     this.scroll = false;
56887 };
56888 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
56889     fly: Roo.Element.fly,
56890
56891     b4StartDrag : function(x, y){
56892         this.view.headersDisabled = true;
56893         this.proxy.setHeight(this.view.mainWrap.getHeight());
56894         var w = this.cm.getColumnWidth(this.cellIndex);
56895         var minw = Math.max(w-this.grid.minColumnWidth, 0);
56896         this.resetConstraints();
56897         this.setXConstraint(minw, 1000);
56898         this.setYConstraint(0, 0);
56899         this.minX = x - minw;
56900         this.maxX = x + 1000;
56901         this.startPos = x;
56902         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
56903     },
56904
56905
56906     handleMouseDown : function(e){
56907         ev = Roo.EventObject.setEvent(e);
56908         var t = this.fly(ev.getTarget());
56909         if(t.hasClass("x-grid-split")){
56910             this.cellIndex = this.view.getCellIndex(t.dom);
56911             this.split = t.dom;
56912             this.cm = this.grid.colModel;
56913             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
56914                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
56915             }
56916         }
56917     },
56918
56919     endDrag : function(e){
56920         this.view.headersDisabled = false;
56921         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
56922         var diff = endX - this.startPos;
56923         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
56924     },
56925
56926     autoOffset : function(){
56927         this.setDelta(0,0);
56928     }
56929 });/*
56930  * Based on:
56931  * Ext JS Library 1.1.1
56932  * Copyright(c) 2006-2007, Ext JS, LLC.
56933  *
56934  * Originally Released Under LGPL - original licence link has changed is not relivant.
56935  *
56936  * Fork - LGPL
56937  * <script type="text/javascript">
56938  */
56939  
56940 // private
56941 // This is a support class used internally by the Grid components
56942 Roo.grid.GridDragZone = function(grid, config){
56943     this.view = grid.getView();
56944     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
56945     if(this.view.lockedBody){
56946         this.setHandleElId(Roo.id(this.view.mainBody.dom));
56947         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
56948     }
56949     this.scroll = false;
56950     this.grid = grid;
56951     this.ddel = document.createElement('div');
56952     this.ddel.className = 'x-grid-dd-wrap';
56953 };
56954
56955 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
56956     ddGroup : "GridDD",
56957
56958     getDragData : function(e){
56959         var t = Roo.lib.Event.getTarget(e);
56960         var rowIndex = this.view.findRowIndex(t);
56961         var sm = this.grid.selModel;
56962             
56963         //Roo.log(rowIndex);
56964         
56965         if (sm.getSelectedCell) {
56966             // cell selection..
56967             if (!sm.getSelectedCell()) {
56968                 return false;
56969             }
56970             if (rowIndex != sm.getSelectedCell()[0]) {
56971                 return false;
56972             }
56973         
56974         }
56975         
56976         if(rowIndex !== false){
56977             
56978             // if editorgrid.. 
56979             
56980             
56981             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
56982                
56983             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
56984               //  
56985             //}
56986             if (e.hasModifier()){
56987                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
56988             }
56989             
56990             Roo.log("getDragData");
56991             
56992             return {
56993                 grid: this.grid,
56994                 ddel: this.ddel,
56995                 rowIndex: rowIndex,
56996                 selections:sm.getSelections ? sm.getSelections() : (
56997                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
56998                 )
56999             };
57000         }
57001         return false;
57002     },
57003
57004     onInitDrag : function(e){
57005         var data = this.dragData;
57006         this.ddel.innerHTML = this.grid.getDragDropText();
57007         this.proxy.update(this.ddel);
57008         // fire start drag?
57009     },
57010
57011     afterRepair : function(){
57012         this.dragging = false;
57013     },
57014
57015     getRepairXY : function(e, data){
57016         return false;
57017     },
57018
57019     onEndDrag : function(data, e){
57020         // fire end drag?
57021     },
57022
57023     onValidDrop : function(dd, e, id){
57024         // fire drag drop?
57025         this.hideProxy();
57026     },
57027
57028     beforeInvalidDrop : function(e, id){
57029
57030     }
57031 });/*
57032  * Based on:
57033  * Ext JS Library 1.1.1
57034  * Copyright(c) 2006-2007, Ext JS, LLC.
57035  *
57036  * Originally Released Under LGPL - original licence link has changed is not relivant.
57037  *
57038  * Fork - LGPL
57039  * <script type="text/javascript">
57040  */
57041  
57042
57043 /**
57044  * @class Roo.grid.ColumnModel
57045  * @extends Roo.util.Observable
57046  * This is the default implementation of a ColumnModel used by the Grid. It defines
57047  * the columns in the grid.
57048  * <br>Usage:<br>
57049  <pre><code>
57050  var colModel = new Roo.grid.ColumnModel([
57051         {header: "Ticker", width: 60, sortable: true, locked: true},
57052         {header: "Company Name", width: 150, sortable: true},
57053         {header: "Market Cap.", width: 100, sortable: true},
57054         {header: "$ Sales", width: 100, sortable: true, renderer: money},
57055         {header: "Employees", width: 100, sortable: true, resizable: false}
57056  ]);
57057  </code></pre>
57058  * <p>
57059  
57060  * The config options listed for this class are options which may appear in each
57061  * individual column definition.
57062  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
57063  * @constructor
57064  * @param {Object} config An Array of column config objects. See this class's
57065  * config objects for details.
57066 */
57067 Roo.grid.ColumnModel = function(config){
57068         /**
57069      * The config passed into the constructor
57070      */
57071     this.config = config;
57072     this.lookup = {};
57073
57074     // if no id, create one
57075     // if the column does not have a dataIndex mapping,
57076     // map it to the order it is in the config
57077     for(var i = 0, len = config.length; i < len; i++){
57078         var c = config[i];
57079         if(typeof c.dataIndex == "undefined"){
57080             c.dataIndex = i;
57081         }
57082         if(typeof c.renderer == "string"){
57083             c.renderer = Roo.util.Format[c.renderer];
57084         }
57085         if(typeof c.id == "undefined"){
57086             c.id = Roo.id();
57087         }
57088         if(c.editor && c.editor.xtype){
57089             c.editor  = Roo.factory(c.editor, Roo.grid);
57090         }
57091         if(c.editor && c.editor.isFormField){
57092             c.editor = new Roo.grid.GridEditor(c.editor);
57093         }
57094         this.lookup[c.id] = c;
57095     }
57096
57097     /**
57098      * The width of columns which have no width specified (defaults to 100)
57099      * @type Number
57100      */
57101     this.defaultWidth = 100;
57102
57103     /**
57104      * Default sortable of columns which have no sortable specified (defaults to false)
57105      * @type Boolean
57106      */
57107     this.defaultSortable = false;
57108
57109     this.addEvents({
57110         /**
57111              * @event widthchange
57112              * Fires when the width of a column changes.
57113              * @param {ColumnModel} this
57114              * @param {Number} columnIndex The column index
57115              * @param {Number} newWidth The new width
57116              */
57117             "widthchange": true,
57118         /**
57119              * @event headerchange
57120              * Fires when the text of a header changes.
57121              * @param {ColumnModel} this
57122              * @param {Number} columnIndex The column index
57123              * @param {Number} newText The new header text
57124              */
57125             "headerchange": true,
57126         /**
57127              * @event hiddenchange
57128              * Fires when a column is hidden or "unhidden".
57129              * @param {ColumnModel} this
57130              * @param {Number} columnIndex The column index
57131              * @param {Boolean} hidden true if hidden, false otherwise
57132              */
57133             "hiddenchange": true,
57134             /**
57135          * @event columnmoved
57136          * Fires when a column is moved.
57137          * @param {ColumnModel} this
57138          * @param {Number} oldIndex
57139          * @param {Number} newIndex
57140          */
57141         "columnmoved" : true,
57142         /**
57143          * @event columlockchange
57144          * Fires when a column's locked state is changed
57145          * @param {ColumnModel} this
57146          * @param {Number} colIndex
57147          * @param {Boolean} locked true if locked
57148          */
57149         "columnlockchange" : true
57150     });
57151     Roo.grid.ColumnModel.superclass.constructor.call(this);
57152 };
57153 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
57154     /**
57155      * @cfg {String} header The header text to display in the Grid view.
57156      */
57157     /**
57158      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
57159      * {@link Roo.data.Record} definition from which to draw the column's value. If not
57160      * specified, the column's index is used as an index into the Record's data Array.
57161      */
57162     /**
57163      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
57164      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
57165      */
57166     /**
57167      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
57168      * Defaults to the value of the {@link #defaultSortable} property.
57169      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
57170      */
57171     /**
57172      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
57173      */
57174     /**
57175      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
57176      */
57177     /**
57178      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
57179      */
57180     /**
57181      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
57182      */
57183     /**
57184      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
57185      * given the cell's data value. See {@link #setRenderer}. If not specified, the
57186      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
57187      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
57188      */
57189        /**
57190      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
57191      */
57192     /**
57193      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
57194      */
57195     /**
57196      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
57197      */
57198     /**
57199      * @cfg {String} cursor (Optional)
57200      */
57201     /**
57202      * @cfg {String} tooltip (Optional)
57203      */
57204     /**
57205      * @cfg {Number} xs (Optional)
57206      */
57207     /**
57208      * @cfg {Number} sm (Optional)
57209      */
57210     /**
57211      * @cfg {Number} md (Optional)
57212      */
57213     /**
57214      * @cfg {Number} lg (Optional)
57215      */
57216     /**
57217      * Returns the id of the column at the specified index.
57218      * @param {Number} index The column index
57219      * @return {String} the id
57220      */
57221     getColumnId : function(index){
57222         return this.config[index].id;
57223     },
57224
57225     /**
57226      * Returns the column for a specified id.
57227      * @param {String} id The column id
57228      * @return {Object} the column
57229      */
57230     getColumnById : function(id){
57231         return this.lookup[id];
57232     },
57233
57234     
57235     /**
57236      * Returns the column for a specified dataIndex.
57237      * @param {String} dataIndex The column dataIndex
57238      * @return {Object|Boolean} the column or false if not found
57239      */
57240     getColumnByDataIndex: function(dataIndex){
57241         var index = this.findColumnIndex(dataIndex);
57242         return index > -1 ? this.config[index] : false;
57243     },
57244     
57245     /**
57246      * Returns the index for a specified column id.
57247      * @param {String} id The column id
57248      * @return {Number} the index, or -1 if not found
57249      */
57250     getIndexById : function(id){
57251         for(var i = 0, len = this.config.length; i < len; i++){
57252             if(this.config[i].id == id){
57253                 return i;
57254             }
57255         }
57256         return -1;
57257     },
57258     
57259     /**
57260      * Returns the index for a specified column dataIndex.
57261      * @param {String} dataIndex The column dataIndex
57262      * @return {Number} the index, or -1 if not found
57263      */
57264     
57265     findColumnIndex : function(dataIndex){
57266         for(var i = 0, len = this.config.length; i < len; i++){
57267             if(this.config[i].dataIndex == dataIndex){
57268                 return i;
57269             }
57270         }
57271         return -1;
57272     },
57273     
57274     
57275     moveColumn : function(oldIndex, newIndex){
57276         var c = this.config[oldIndex];
57277         this.config.splice(oldIndex, 1);
57278         this.config.splice(newIndex, 0, c);
57279         this.dataMap = null;
57280         this.fireEvent("columnmoved", this, oldIndex, newIndex);
57281     },
57282
57283     isLocked : function(colIndex){
57284         return this.config[colIndex].locked === true;
57285     },
57286
57287     setLocked : function(colIndex, value, suppressEvent){
57288         if(this.isLocked(colIndex) == value){
57289             return;
57290         }
57291         this.config[colIndex].locked = value;
57292         if(!suppressEvent){
57293             this.fireEvent("columnlockchange", this, colIndex, value);
57294         }
57295     },
57296
57297     getTotalLockedWidth : function(){
57298         var totalWidth = 0;
57299         for(var i = 0; i < this.config.length; i++){
57300             if(this.isLocked(i) && !this.isHidden(i)){
57301                 this.totalWidth += this.getColumnWidth(i);
57302             }
57303         }
57304         return totalWidth;
57305     },
57306
57307     getLockedCount : function(){
57308         for(var i = 0, len = this.config.length; i < len; i++){
57309             if(!this.isLocked(i)){
57310                 return i;
57311             }
57312         }
57313         
57314         return this.config.length;
57315     },
57316
57317     /**
57318      * Returns the number of columns.
57319      * @return {Number}
57320      */
57321     getColumnCount : function(visibleOnly){
57322         if(visibleOnly === true){
57323             var c = 0;
57324             for(var i = 0, len = this.config.length; i < len; i++){
57325                 if(!this.isHidden(i)){
57326                     c++;
57327                 }
57328             }
57329             return c;
57330         }
57331         return this.config.length;
57332     },
57333
57334     /**
57335      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
57336      * @param {Function} fn
57337      * @param {Object} scope (optional)
57338      * @return {Array} result
57339      */
57340     getColumnsBy : function(fn, scope){
57341         var r = [];
57342         for(var i = 0, len = this.config.length; i < len; i++){
57343             var c = this.config[i];
57344             if(fn.call(scope||this, c, i) === true){
57345                 r[r.length] = c;
57346             }
57347         }
57348         return r;
57349     },
57350
57351     /**
57352      * Returns true if the specified column is sortable.
57353      * @param {Number} col The column index
57354      * @return {Boolean}
57355      */
57356     isSortable : function(col){
57357         if(typeof this.config[col].sortable == "undefined"){
57358             return this.defaultSortable;
57359         }
57360         return this.config[col].sortable;
57361     },
57362
57363     /**
57364      * Returns the rendering (formatting) function defined for the column.
57365      * @param {Number} col The column index.
57366      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
57367      */
57368     getRenderer : function(col){
57369         if(!this.config[col].renderer){
57370             return Roo.grid.ColumnModel.defaultRenderer;
57371         }
57372         return this.config[col].renderer;
57373     },
57374
57375     /**
57376      * Sets the rendering (formatting) function for a column.
57377      * @param {Number} col The column index
57378      * @param {Function} fn The function to use to process the cell's raw data
57379      * to return HTML markup for the grid view. The render function is called with
57380      * the following parameters:<ul>
57381      * <li>Data value.</li>
57382      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
57383      * <li>css A CSS style string to apply to the table cell.</li>
57384      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
57385      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
57386      * <li>Row index</li>
57387      * <li>Column index</li>
57388      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
57389      */
57390     setRenderer : function(col, fn){
57391         this.config[col].renderer = fn;
57392     },
57393
57394     /**
57395      * Returns the width for the specified column.
57396      * @param {Number} col The column index
57397      * @return {Number}
57398      */
57399     getColumnWidth : function(col){
57400         return this.config[col].width * 1 || this.defaultWidth;
57401     },
57402
57403     /**
57404      * Sets the width for a column.
57405      * @param {Number} col The column index
57406      * @param {Number} width The new width
57407      */
57408     setColumnWidth : function(col, width, suppressEvent){
57409         this.config[col].width = width;
57410         this.totalWidth = null;
57411         if(!suppressEvent){
57412              this.fireEvent("widthchange", this, col, width);
57413         }
57414     },
57415
57416     /**
57417      * Returns the total width of all columns.
57418      * @param {Boolean} includeHidden True to include hidden column widths
57419      * @return {Number}
57420      */
57421     getTotalWidth : function(includeHidden){
57422         if(!this.totalWidth){
57423             this.totalWidth = 0;
57424             for(var i = 0, len = this.config.length; i < len; i++){
57425                 if(includeHidden || !this.isHidden(i)){
57426                     this.totalWidth += this.getColumnWidth(i);
57427                 }
57428             }
57429         }
57430         return this.totalWidth;
57431     },
57432
57433     /**
57434      * Returns the header for the specified column.
57435      * @param {Number} col The column index
57436      * @return {String}
57437      */
57438     getColumnHeader : function(col){
57439         return this.config[col].header;
57440     },
57441
57442     /**
57443      * Sets the header for a column.
57444      * @param {Number} col The column index
57445      * @param {String} header The new header
57446      */
57447     setColumnHeader : function(col, header){
57448         this.config[col].header = header;
57449         this.fireEvent("headerchange", this, col, header);
57450     },
57451
57452     /**
57453      * Returns the tooltip for the specified column.
57454      * @param {Number} col The column index
57455      * @return {String}
57456      */
57457     getColumnTooltip : function(col){
57458             return this.config[col].tooltip;
57459     },
57460     /**
57461      * Sets the tooltip for a column.
57462      * @param {Number} col The column index
57463      * @param {String} tooltip The new tooltip
57464      */
57465     setColumnTooltip : function(col, tooltip){
57466             this.config[col].tooltip = tooltip;
57467     },
57468
57469     /**
57470      * Returns the dataIndex for the specified column.
57471      * @param {Number} col The column index
57472      * @return {Number}
57473      */
57474     getDataIndex : function(col){
57475         return this.config[col].dataIndex;
57476     },
57477
57478     /**
57479      * Sets the dataIndex for a column.
57480      * @param {Number} col The column index
57481      * @param {Number} dataIndex The new dataIndex
57482      */
57483     setDataIndex : function(col, dataIndex){
57484         this.config[col].dataIndex = dataIndex;
57485     },
57486
57487     
57488     
57489     /**
57490      * Returns true if the cell is editable.
57491      * @param {Number} colIndex The column index
57492      * @param {Number} rowIndex The row index - this is nto actually used..?
57493      * @return {Boolean}
57494      */
57495     isCellEditable : function(colIndex, rowIndex){
57496         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
57497     },
57498
57499     /**
57500      * Returns the editor defined for the cell/column.
57501      * return false or null to disable editing.
57502      * @param {Number} colIndex The column index
57503      * @param {Number} rowIndex The row index
57504      * @return {Object}
57505      */
57506     getCellEditor : function(colIndex, rowIndex){
57507         return this.config[colIndex].editor;
57508     },
57509
57510     /**
57511      * Sets if a column is editable.
57512      * @param {Number} col The column index
57513      * @param {Boolean} editable True if the column is editable
57514      */
57515     setEditable : function(col, editable){
57516         this.config[col].editable = editable;
57517     },
57518
57519
57520     /**
57521      * Returns true if the column is hidden.
57522      * @param {Number} colIndex The column index
57523      * @return {Boolean}
57524      */
57525     isHidden : function(colIndex){
57526         return this.config[colIndex].hidden;
57527     },
57528
57529
57530     /**
57531      * Returns true if the column width cannot be changed
57532      */
57533     isFixed : function(colIndex){
57534         return this.config[colIndex].fixed;
57535     },
57536
57537     /**
57538      * Returns true if the column can be resized
57539      * @return {Boolean}
57540      */
57541     isResizable : function(colIndex){
57542         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
57543     },
57544     /**
57545      * Sets if a column is hidden.
57546      * @param {Number} colIndex The column index
57547      * @param {Boolean} hidden True if the column is hidden
57548      */
57549     setHidden : function(colIndex, hidden){
57550         this.config[colIndex].hidden = hidden;
57551         this.totalWidth = null;
57552         this.fireEvent("hiddenchange", this, colIndex, hidden);
57553     },
57554
57555     /**
57556      * Sets the editor for a column.
57557      * @param {Number} col The column index
57558      * @param {Object} editor The editor object
57559      */
57560     setEditor : function(col, editor){
57561         this.config[col].editor = editor;
57562     }
57563 });
57564
57565 Roo.grid.ColumnModel.defaultRenderer = function(value)
57566 {
57567     if(typeof value == "object") {
57568         return value;
57569     }
57570         if(typeof value == "string" && value.length < 1){
57571             return "&#160;";
57572         }
57573     
57574         return String.format("{0}", value);
57575 };
57576
57577 // Alias for backwards compatibility
57578 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
57579 /*
57580  * Based on:
57581  * Ext JS Library 1.1.1
57582  * Copyright(c) 2006-2007, Ext JS, LLC.
57583  *
57584  * Originally Released Under LGPL - original licence link has changed is not relivant.
57585  *
57586  * Fork - LGPL
57587  * <script type="text/javascript">
57588  */
57589
57590 /**
57591  * @class Roo.grid.AbstractSelectionModel
57592  * @extends Roo.util.Observable
57593  * Abstract base class for grid SelectionModels.  It provides the interface that should be
57594  * implemented by descendant classes.  This class should not be directly instantiated.
57595  * @constructor
57596  */
57597 Roo.grid.AbstractSelectionModel = function(){
57598     this.locked = false;
57599     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
57600 };
57601
57602 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
57603     /** @ignore Called by the grid automatically. Do not call directly. */
57604     init : function(grid){
57605         this.grid = grid;
57606         this.initEvents();
57607     },
57608
57609     /**
57610      * Locks the selections.
57611      */
57612     lock : function(){
57613         this.locked = true;
57614     },
57615
57616     /**
57617      * Unlocks the selections.
57618      */
57619     unlock : function(){
57620         this.locked = false;
57621     },
57622
57623     /**
57624      * Returns true if the selections are locked.
57625      * @return {Boolean}
57626      */
57627     isLocked : function(){
57628         return this.locked;
57629     }
57630 });/*
57631  * Based on:
57632  * Ext JS Library 1.1.1
57633  * Copyright(c) 2006-2007, Ext JS, LLC.
57634  *
57635  * Originally Released Under LGPL - original licence link has changed is not relivant.
57636  *
57637  * Fork - LGPL
57638  * <script type="text/javascript">
57639  */
57640 /**
57641  * @extends Roo.grid.AbstractSelectionModel
57642  * @class Roo.grid.RowSelectionModel
57643  * The default SelectionModel used by {@link Roo.grid.Grid}.
57644  * It supports multiple selections and keyboard selection/navigation. 
57645  * @constructor
57646  * @param {Object} config
57647  */
57648 Roo.grid.RowSelectionModel = function(config){
57649     Roo.apply(this, config);
57650     this.selections = new Roo.util.MixedCollection(false, function(o){
57651         return o.id;
57652     });
57653
57654     this.last = false;
57655     this.lastActive = false;
57656
57657     this.addEvents({
57658         /**
57659              * @event selectionchange
57660              * Fires when the selection changes
57661              * @param {SelectionModel} this
57662              */
57663             "selectionchange" : true,
57664         /**
57665              * @event afterselectionchange
57666              * Fires after the selection changes (eg. by key press or clicking)
57667              * @param {SelectionModel} this
57668              */
57669             "afterselectionchange" : true,
57670         /**
57671              * @event beforerowselect
57672              * Fires when a row is selected being selected, return false to cancel.
57673              * @param {SelectionModel} this
57674              * @param {Number} rowIndex The selected index
57675              * @param {Boolean} keepExisting False if other selections will be cleared
57676              */
57677             "beforerowselect" : true,
57678         /**
57679              * @event rowselect
57680              * Fires when a row is selected.
57681              * @param {SelectionModel} this
57682              * @param {Number} rowIndex The selected index
57683              * @param {Roo.data.Record} r The record
57684              */
57685             "rowselect" : true,
57686         /**
57687              * @event rowdeselect
57688              * Fires when a row is deselected.
57689              * @param {SelectionModel} this
57690              * @param {Number} rowIndex The selected index
57691              */
57692         "rowdeselect" : true
57693     });
57694     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
57695     this.locked = false;
57696 };
57697
57698 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
57699     /**
57700      * @cfg {Boolean} singleSelect
57701      * True to allow selection of only one row at a time (defaults to false)
57702      */
57703     singleSelect : false,
57704
57705     // private
57706     initEvents : function(){
57707
57708         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
57709             this.grid.on("mousedown", this.handleMouseDown, this);
57710         }else{ // allow click to work like normal
57711             this.grid.on("rowclick", this.handleDragableRowClick, this);
57712         }
57713
57714         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
57715             "up" : function(e){
57716                 if(!e.shiftKey){
57717                     this.selectPrevious(e.shiftKey);
57718                 }else if(this.last !== false && this.lastActive !== false){
57719                     var last = this.last;
57720                     this.selectRange(this.last,  this.lastActive-1);
57721                     this.grid.getView().focusRow(this.lastActive);
57722                     if(last !== false){
57723                         this.last = last;
57724                     }
57725                 }else{
57726                     this.selectFirstRow();
57727                 }
57728                 this.fireEvent("afterselectionchange", this);
57729             },
57730             "down" : function(e){
57731                 if(!e.shiftKey){
57732                     this.selectNext(e.shiftKey);
57733                 }else if(this.last !== false && this.lastActive !== false){
57734                     var last = this.last;
57735                     this.selectRange(this.last,  this.lastActive+1);
57736                     this.grid.getView().focusRow(this.lastActive);
57737                     if(last !== false){
57738                         this.last = last;
57739                     }
57740                 }else{
57741                     this.selectFirstRow();
57742                 }
57743                 this.fireEvent("afterselectionchange", this);
57744             },
57745             scope: this
57746         });
57747
57748         var view = this.grid.view;
57749         view.on("refresh", this.onRefresh, this);
57750         view.on("rowupdated", this.onRowUpdated, this);
57751         view.on("rowremoved", this.onRemove, this);
57752     },
57753
57754     // private
57755     onRefresh : function(){
57756         var ds = this.grid.dataSource, i, v = this.grid.view;
57757         var s = this.selections;
57758         s.each(function(r){
57759             if((i = ds.indexOfId(r.id)) != -1){
57760                 v.onRowSelect(i);
57761                 s.add(ds.getAt(i)); // updating the selection relate data
57762             }else{
57763                 s.remove(r);
57764             }
57765         });
57766     },
57767
57768     // private
57769     onRemove : function(v, index, r){
57770         this.selections.remove(r);
57771     },
57772
57773     // private
57774     onRowUpdated : function(v, index, r){
57775         if(this.isSelected(r)){
57776             v.onRowSelect(index);
57777         }
57778     },
57779
57780     /**
57781      * Select records.
57782      * @param {Array} records The records to select
57783      * @param {Boolean} keepExisting (optional) True to keep existing selections
57784      */
57785     selectRecords : function(records, keepExisting){
57786         if(!keepExisting){
57787             this.clearSelections();
57788         }
57789         var ds = this.grid.dataSource;
57790         for(var i = 0, len = records.length; i < len; i++){
57791             this.selectRow(ds.indexOf(records[i]), true);
57792         }
57793     },
57794
57795     /**
57796      * Gets the number of selected rows.
57797      * @return {Number}
57798      */
57799     getCount : function(){
57800         return this.selections.length;
57801     },
57802
57803     /**
57804      * Selects the first row in the grid.
57805      */
57806     selectFirstRow : function(){
57807         this.selectRow(0);
57808     },
57809
57810     /**
57811      * Select the last row.
57812      * @param {Boolean} keepExisting (optional) True to keep existing selections
57813      */
57814     selectLastRow : function(keepExisting){
57815         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
57816     },
57817
57818     /**
57819      * Selects the row immediately following the last selected row.
57820      * @param {Boolean} keepExisting (optional) True to keep existing selections
57821      */
57822     selectNext : function(keepExisting){
57823         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
57824             this.selectRow(this.last+1, keepExisting);
57825             this.grid.getView().focusRow(this.last);
57826         }
57827     },
57828
57829     /**
57830      * Selects the row that precedes the last selected row.
57831      * @param {Boolean} keepExisting (optional) True to keep existing selections
57832      */
57833     selectPrevious : function(keepExisting){
57834         if(this.last){
57835             this.selectRow(this.last-1, keepExisting);
57836             this.grid.getView().focusRow(this.last);
57837         }
57838     },
57839
57840     /**
57841      * Returns the selected records
57842      * @return {Array} Array of selected records
57843      */
57844     getSelections : function(){
57845         return [].concat(this.selections.items);
57846     },
57847
57848     /**
57849      * Returns the first selected record.
57850      * @return {Record}
57851      */
57852     getSelected : function(){
57853         return this.selections.itemAt(0);
57854     },
57855
57856
57857     /**
57858      * Clears all selections.
57859      */
57860     clearSelections : function(fast){
57861         if(this.locked) {
57862             return;
57863         }
57864         if(fast !== true){
57865             var ds = this.grid.dataSource;
57866             var s = this.selections;
57867             s.each(function(r){
57868                 this.deselectRow(ds.indexOfId(r.id));
57869             }, this);
57870             s.clear();
57871         }else{
57872             this.selections.clear();
57873         }
57874         this.last = false;
57875     },
57876
57877
57878     /**
57879      * Selects all rows.
57880      */
57881     selectAll : function(){
57882         if(this.locked) {
57883             return;
57884         }
57885         this.selections.clear();
57886         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
57887             this.selectRow(i, true);
57888         }
57889     },
57890
57891     /**
57892      * Returns True if there is a selection.
57893      * @return {Boolean}
57894      */
57895     hasSelection : function(){
57896         return this.selections.length > 0;
57897     },
57898
57899     /**
57900      * Returns True if the specified row is selected.
57901      * @param {Number/Record} record The record or index of the record to check
57902      * @return {Boolean}
57903      */
57904     isSelected : function(index){
57905         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
57906         return (r && this.selections.key(r.id) ? true : false);
57907     },
57908
57909     /**
57910      * Returns True if the specified record id is selected.
57911      * @param {String} id The id of record to check
57912      * @return {Boolean}
57913      */
57914     isIdSelected : function(id){
57915         return (this.selections.key(id) ? true : false);
57916     },
57917
57918     // private
57919     handleMouseDown : function(e, t){
57920         var view = this.grid.getView(), rowIndex;
57921         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
57922             return;
57923         };
57924         if(e.shiftKey && this.last !== false){
57925             var last = this.last;
57926             this.selectRange(last, rowIndex, e.ctrlKey);
57927             this.last = last; // reset the last
57928             view.focusRow(rowIndex);
57929         }else{
57930             var isSelected = this.isSelected(rowIndex);
57931             if(e.button !== 0 && isSelected){
57932                 view.focusRow(rowIndex);
57933             }else if(e.ctrlKey && isSelected){
57934                 this.deselectRow(rowIndex);
57935             }else if(!isSelected){
57936                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
57937                 view.focusRow(rowIndex);
57938             }
57939         }
57940         this.fireEvent("afterselectionchange", this);
57941     },
57942     // private
57943     handleDragableRowClick :  function(grid, rowIndex, e) 
57944     {
57945         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
57946             this.selectRow(rowIndex, false);
57947             grid.view.focusRow(rowIndex);
57948              this.fireEvent("afterselectionchange", this);
57949         }
57950     },
57951     
57952     /**
57953      * Selects multiple rows.
57954      * @param {Array} rows Array of the indexes of the row to select
57955      * @param {Boolean} keepExisting (optional) True to keep existing selections
57956      */
57957     selectRows : function(rows, keepExisting){
57958         if(!keepExisting){
57959             this.clearSelections();
57960         }
57961         for(var i = 0, len = rows.length; i < len; i++){
57962             this.selectRow(rows[i], true);
57963         }
57964     },
57965
57966     /**
57967      * Selects a range of rows. All rows in between startRow and endRow are also selected.
57968      * @param {Number} startRow The index of the first row in the range
57969      * @param {Number} endRow The index of the last row in the range
57970      * @param {Boolean} keepExisting (optional) True to retain existing selections
57971      */
57972     selectRange : function(startRow, endRow, keepExisting){
57973         if(this.locked) {
57974             return;
57975         }
57976         if(!keepExisting){
57977             this.clearSelections();
57978         }
57979         if(startRow <= endRow){
57980             for(var i = startRow; i <= endRow; i++){
57981                 this.selectRow(i, true);
57982             }
57983         }else{
57984             for(var i = startRow; i >= endRow; i--){
57985                 this.selectRow(i, true);
57986             }
57987         }
57988     },
57989
57990     /**
57991      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
57992      * @param {Number} startRow The index of the first row in the range
57993      * @param {Number} endRow The index of the last row in the range
57994      */
57995     deselectRange : function(startRow, endRow, preventViewNotify){
57996         if(this.locked) {
57997             return;
57998         }
57999         for(var i = startRow; i <= endRow; i++){
58000             this.deselectRow(i, preventViewNotify);
58001         }
58002     },
58003
58004     /**
58005      * Selects a row.
58006      * @param {Number} row The index of the row to select
58007      * @param {Boolean} keepExisting (optional) True to keep existing selections
58008      */
58009     selectRow : function(index, keepExisting, preventViewNotify){
58010         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
58011             return;
58012         }
58013         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
58014             if(!keepExisting || this.singleSelect){
58015                 this.clearSelections();
58016             }
58017             var r = this.grid.dataSource.getAt(index);
58018             this.selections.add(r);
58019             this.last = this.lastActive = index;
58020             if(!preventViewNotify){
58021                 this.grid.getView().onRowSelect(index);
58022             }
58023             this.fireEvent("rowselect", this, index, r);
58024             this.fireEvent("selectionchange", this);
58025         }
58026     },
58027
58028     /**
58029      * Deselects a row.
58030      * @param {Number} row The index of the row to deselect
58031      */
58032     deselectRow : function(index, preventViewNotify){
58033         if(this.locked) {
58034             return;
58035         }
58036         if(this.last == index){
58037             this.last = false;
58038         }
58039         if(this.lastActive == index){
58040             this.lastActive = false;
58041         }
58042         var r = this.grid.dataSource.getAt(index);
58043         this.selections.remove(r);
58044         if(!preventViewNotify){
58045             this.grid.getView().onRowDeselect(index);
58046         }
58047         this.fireEvent("rowdeselect", this, index);
58048         this.fireEvent("selectionchange", this);
58049     },
58050
58051     // private
58052     restoreLast : function(){
58053         if(this._last){
58054             this.last = this._last;
58055         }
58056     },
58057
58058     // private
58059     acceptsNav : function(row, col, cm){
58060         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58061     },
58062
58063     // private
58064     onEditorKey : function(field, e){
58065         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
58066         if(k == e.TAB){
58067             e.stopEvent();
58068             ed.completeEdit();
58069             if(e.shiftKey){
58070                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58071             }else{
58072                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58073             }
58074         }else if(k == e.ENTER && !e.ctrlKey){
58075             e.stopEvent();
58076             ed.completeEdit();
58077             if(e.shiftKey){
58078                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
58079             }else{
58080                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
58081             }
58082         }else if(k == e.ESC){
58083             ed.cancelEdit();
58084         }
58085         if(newCell){
58086             g.startEditing(newCell[0], newCell[1]);
58087         }
58088     }
58089 });/*
58090  * Based on:
58091  * Ext JS Library 1.1.1
58092  * Copyright(c) 2006-2007, Ext JS, LLC.
58093  *
58094  * Originally Released Under LGPL - original licence link has changed is not relivant.
58095  *
58096  * Fork - LGPL
58097  * <script type="text/javascript">
58098  */
58099 /**
58100  * @class Roo.grid.CellSelectionModel
58101  * @extends Roo.grid.AbstractSelectionModel
58102  * This class provides the basic implementation for cell selection in a grid.
58103  * @constructor
58104  * @param {Object} config The object containing the configuration of this model.
58105  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
58106  */
58107 Roo.grid.CellSelectionModel = function(config){
58108     Roo.apply(this, config);
58109
58110     this.selection = null;
58111
58112     this.addEvents({
58113         /**
58114              * @event beforerowselect
58115              * Fires before a cell is selected.
58116              * @param {SelectionModel} this
58117              * @param {Number} rowIndex The selected row index
58118              * @param {Number} colIndex The selected cell index
58119              */
58120             "beforecellselect" : true,
58121         /**
58122              * @event cellselect
58123              * Fires when a cell is selected.
58124              * @param {SelectionModel} this
58125              * @param {Number} rowIndex The selected row index
58126              * @param {Number} colIndex The selected cell index
58127              */
58128             "cellselect" : true,
58129         /**
58130              * @event selectionchange
58131              * Fires when the active selection changes.
58132              * @param {SelectionModel} this
58133              * @param {Object} selection null for no selection or an object (o) with two properties
58134                 <ul>
58135                 <li>o.record: the record object for the row the selection is in</li>
58136                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
58137                 </ul>
58138              */
58139             "selectionchange" : true,
58140         /**
58141              * @event tabend
58142              * Fires when the tab (or enter) was pressed on the last editable cell
58143              * You can use this to trigger add new row.
58144              * @param {SelectionModel} this
58145              */
58146             "tabend" : true,
58147          /**
58148              * @event beforeeditnext
58149              * Fires before the next editable sell is made active
58150              * You can use this to skip to another cell or fire the tabend
58151              *    if you set cell to false
58152              * @param {Object} eventdata object : { cell : [ row, col ] } 
58153              */
58154             "beforeeditnext" : true
58155     });
58156     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
58157 };
58158
58159 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
58160     
58161     enter_is_tab: false,
58162
58163     /** @ignore */
58164     initEvents : function(){
58165         this.grid.on("mousedown", this.handleMouseDown, this);
58166         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
58167         var view = this.grid.view;
58168         view.on("refresh", this.onViewChange, this);
58169         view.on("rowupdated", this.onRowUpdated, this);
58170         view.on("beforerowremoved", this.clearSelections, this);
58171         view.on("beforerowsinserted", this.clearSelections, this);
58172         if(this.grid.isEditor){
58173             this.grid.on("beforeedit", this.beforeEdit,  this);
58174         }
58175     },
58176
58177         //private
58178     beforeEdit : function(e){
58179         this.select(e.row, e.column, false, true, e.record);
58180     },
58181
58182         //private
58183     onRowUpdated : function(v, index, r){
58184         if(this.selection && this.selection.record == r){
58185             v.onCellSelect(index, this.selection.cell[1]);
58186         }
58187     },
58188
58189         //private
58190     onViewChange : function(){
58191         this.clearSelections(true);
58192     },
58193
58194         /**
58195          * Returns the currently selected cell,.
58196          * @return {Array} The selected cell (row, column) or null if none selected.
58197          */
58198     getSelectedCell : function(){
58199         return this.selection ? this.selection.cell : null;
58200     },
58201
58202     /**
58203      * Clears all selections.
58204      * @param {Boolean} true to prevent the gridview from being notified about the change.
58205      */
58206     clearSelections : function(preventNotify){
58207         var s = this.selection;
58208         if(s){
58209             if(preventNotify !== true){
58210                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
58211             }
58212             this.selection = null;
58213             this.fireEvent("selectionchange", this, null);
58214         }
58215     },
58216
58217     /**
58218      * Returns true if there is a selection.
58219      * @return {Boolean}
58220      */
58221     hasSelection : function(){
58222         return this.selection ? true : false;
58223     },
58224
58225     /** @ignore */
58226     handleMouseDown : function(e, t){
58227         var v = this.grid.getView();
58228         if(this.isLocked()){
58229             return;
58230         };
58231         var row = v.findRowIndex(t);
58232         var cell = v.findCellIndex(t);
58233         if(row !== false && cell !== false){
58234             this.select(row, cell);
58235         }
58236     },
58237
58238     /**
58239      * Selects a cell.
58240      * @param {Number} rowIndex
58241      * @param {Number} collIndex
58242      */
58243     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
58244         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
58245             this.clearSelections();
58246             r = r || this.grid.dataSource.getAt(rowIndex);
58247             this.selection = {
58248                 record : r,
58249                 cell : [rowIndex, colIndex]
58250             };
58251             if(!preventViewNotify){
58252                 var v = this.grid.getView();
58253                 v.onCellSelect(rowIndex, colIndex);
58254                 if(preventFocus !== true){
58255                     v.focusCell(rowIndex, colIndex);
58256                 }
58257             }
58258             this.fireEvent("cellselect", this, rowIndex, colIndex);
58259             this.fireEvent("selectionchange", this, this.selection);
58260         }
58261     },
58262
58263         //private
58264     isSelectable : function(rowIndex, colIndex, cm){
58265         return !cm.isHidden(colIndex);
58266     },
58267
58268     /** @ignore */
58269     handleKeyDown : function(e){
58270         //Roo.log('Cell Sel Model handleKeyDown');
58271         if(!e.isNavKeyPress()){
58272             return;
58273         }
58274         var g = this.grid, s = this.selection;
58275         if(!s){
58276             e.stopEvent();
58277             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
58278             if(cell){
58279                 this.select(cell[0], cell[1]);
58280             }
58281             return;
58282         }
58283         var sm = this;
58284         var walk = function(row, col, step){
58285             return g.walkCells(row, col, step, sm.isSelectable,  sm);
58286         };
58287         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
58288         var newCell;
58289
58290       
58291
58292         switch(k){
58293             case e.TAB:
58294                 // handled by onEditorKey
58295                 if (g.isEditor && g.editing) {
58296                     return;
58297                 }
58298                 if(e.shiftKey) {
58299                     newCell = walk(r, c-1, -1);
58300                 } else {
58301                     newCell = walk(r, c+1, 1);
58302                 }
58303                 break;
58304             
58305             case e.DOWN:
58306                newCell = walk(r+1, c, 1);
58307                 break;
58308             
58309             case e.UP:
58310                 newCell = walk(r-1, c, -1);
58311                 break;
58312             
58313             case e.RIGHT:
58314                 newCell = walk(r, c+1, 1);
58315                 break;
58316             
58317             case e.LEFT:
58318                 newCell = walk(r, c-1, -1);
58319                 break;
58320             
58321             case e.ENTER:
58322                 
58323                 if(g.isEditor && !g.editing){
58324                    g.startEditing(r, c);
58325                    e.stopEvent();
58326                    return;
58327                 }
58328                 
58329                 
58330              break;
58331         };
58332         if(newCell){
58333             this.select(newCell[0], newCell[1]);
58334             e.stopEvent();
58335             
58336         }
58337     },
58338
58339     acceptsNav : function(row, col, cm){
58340         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58341     },
58342     /**
58343      * Selects a cell.
58344      * @param {Number} field (not used) - as it's normally used as a listener
58345      * @param {Number} e - event - fake it by using
58346      *
58347      * var e = Roo.EventObjectImpl.prototype;
58348      * e.keyCode = e.TAB
58349      *
58350      * 
58351      */
58352     onEditorKey : function(field, e){
58353         
58354         var k = e.getKey(),
58355             newCell,
58356             g = this.grid,
58357             ed = g.activeEditor,
58358             forward = false;
58359         ///Roo.log('onEditorKey' + k);
58360         
58361         
58362         if (this.enter_is_tab && k == e.ENTER) {
58363             k = e.TAB;
58364         }
58365         
58366         if(k == e.TAB){
58367             if(e.shiftKey){
58368                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58369             }else{
58370                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58371                 forward = true;
58372             }
58373             
58374             e.stopEvent();
58375             
58376         } else if(k == e.ENTER &&  !e.ctrlKey){
58377             ed.completeEdit();
58378             e.stopEvent();
58379             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58380         
58381                 } else if(k == e.ESC){
58382             ed.cancelEdit();
58383         }
58384                 
58385         if (newCell) {
58386             var ecall = { cell : newCell, forward : forward };
58387             this.fireEvent('beforeeditnext', ecall );
58388             newCell = ecall.cell;
58389                         forward = ecall.forward;
58390         }
58391                 
58392         if(newCell){
58393             //Roo.log('next cell after edit');
58394             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
58395         } else if (forward) {
58396             // tabbed past last
58397             this.fireEvent.defer(100, this, ['tabend',this]);
58398         }
58399     }
58400 });/*
58401  * Based on:
58402  * Ext JS Library 1.1.1
58403  * Copyright(c) 2006-2007, Ext JS, LLC.
58404  *
58405  * Originally Released Under LGPL - original licence link has changed is not relivant.
58406  *
58407  * Fork - LGPL
58408  * <script type="text/javascript">
58409  */
58410  
58411 /**
58412  * @class Roo.grid.EditorGrid
58413  * @extends Roo.grid.Grid
58414  * Class for creating and editable grid.
58415  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
58416  * The container MUST have some type of size defined for the grid to fill. The container will be 
58417  * automatically set to position relative if it isn't already.
58418  * @param {Object} dataSource The data model to bind to
58419  * @param {Object} colModel The column model with info about this grid's columns
58420  */
58421 Roo.grid.EditorGrid = function(container, config){
58422     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
58423     this.getGridEl().addClass("xedit-grid");
58424
58425     if(!this.selModel){
58426         this.selModel = new Roo.grid.CellSelectionModel();
58427     }
58428
58429     this.activeEditor = null;
58430
58431         this.addEvents({
58432             /**
58433              * @event beforeedit
58434              * Fires before cell editing is triggered. The edit event object has the following properties <br />
58435              * <ul style="padding:5px;padding-left:16px;">
58436              * <li>grid - This grid</li>
58437              * <li>record - The record being edited</li>
58438              * <li>field - The field name being edited</li>
58439              * <li>value - The value for the field being edited.</li>
58440              * <li>row - The grid row index</li>
58441              * <li>column - The grid column index</li>
58442              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58443              * </ul>
58444              * @param {Object} e An edit event (see above for description)
58445              */
58446             "beforeedit" : true,
58447             /**
58448              * @event afteredit
58449              * Fires after a cell is edited. <br />
58450              * <ul style="padding:5px;padding-left:16px;">
58451              * <li>grid - This grid</li>
58452              * <li>record - The record being edited</li>
58453              * <li>field - The field name being edited</li>
58454              * <li>value - The value being set</li>
58455              * <li>originalValue - The original value for the field, before the edit.</li>
58456              * <li>row - The grid row index</li>
58457              * <li>column - The grid column index</li>
58458              * </ul>
58459              * @param {Object} e An edit event (see above for description)
58460              */
58461             "afteredit" : true,
58462             /**
58463              * @event validateedit
58464              * Fires after a cell is edited, but before the value is set in the record. 
58465          * You can use this to modify the value being set in the field, Return false
58466              * to cancel the change. The edit event object has the following properties <br />
58467              * <ul style="padding:5px;padding-left:16px;">
58468          * <li>editor - This editor</li>
58469              * <li>grid - This grid</li>
58470              * <li>record - The record being edited</li>
58471              * <li>field - The field name being edited</li>
58472              * <li>value - The value being set</li>
58473              * <li>originalValue - The original value for the field, before the edit.</li>
58474              * <li>row - The grid row index</li>
58475              * <li>column - The grid column index</li>
58476              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58477              * </ul>
58478              * @param {Object} e An edit event (see above for description)
58479              */
58480             "validateedit" : true
58481         });
58482     this.on("bodyscroll", this.stopEditing,  this);
58483     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
58484 };
58485
58486 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
58487     /**
58488      * @cfg {Number} clicksToEdit
58489      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
58490      */
58491     clicksToEdit: 2,
58492
58493     // private
58494     isEditor : true,
58495     // private
58496     trackMouseOver: false, // causes very odd FF errors
58497
58498     onCellDblClick : function(g, row, col){
58499         this.startEditing(row, col);
58500     },
58501
58502     onEditComplete : function(ed, value, startValue){
58503         this.editing = false;
58504         this.activeEditor = null;
58505         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
58506         var r = ed.record;
58507         var field = this.colModel.getDataIndex(ed.col);
58508         var e = {
58509             grid: this,
58510             record: r,
58511             field: field,
58512             originalValue: startValue,
58513             value: value,
58514             row: ed.row,
58515             column: ed.col,
58516             cancel:false,
58517             editor: ed
58518         };
58519         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
58520         cell.show();
58521           
58522         if(String(value) !== String(startValue)){
58523             
58524             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
58525                 r.set(field, e.value);
58526                 // if we are dealing with a combo box..
58527                 // then we also set the 'name' colum to be the displayField
58528                 if (ed.field.displayField && ed.field.name) {
58529                     r.set(ed.field.name, ed.field.el.dom.value);
58530                 }
58531                 
58532                 delete e.cancel; //?? why!!!
58533                 this.fireEvent("afteredit", e);
58534             }
58535         } else {
58536             this.fireEvent("afteredit", e); // always fire it!
58537         }
58538         this.view.focusCell(ed.row, ed.col);
58539     },
58540
58541     /**
58542      * Starts editing the specified for the specified row/column
58543      * @param {Number} rowIndex
58544      * @param {Number} colIndex
58545      */
58546     startEditing : function(row, col){
58547         this.stopEditing();
58548         if(this.colModel.isCellEditable(col, row)){
58549             this.view.ensureVisible(row, col, true);
58550           
58551             var r = this.dataSource.getAt(row);
58552             var field = this.colModel.getDataIndex(col);
58553             var cell = Roo.get(this.view.getCell(row,col));
58554             var e = {
58555                 grid: this,
58556                 record: r,
58557                 field: field,
58558                 value: r.data[field],
58559                 row: row,
58560                 column: col,
58561                 cancel:false 
58562             };
58563             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
58564                 this.editing = true;
58565                 var ed = this.colModel.getCellEditor(col, row);
58566                 
58567                 if (!ed) {
58568                     return;
58569                 }
58570                 if(!ed.rendered){
58571                     ed.render(ed.parentEl || document.body);
58572                 }
58573                 ed.field.reset();
58574                
58575                 cell.hide();
58576                 
58577                 (function(){ // complex but required for focus issues in safari, ie and opera
58578                     ed.row = row;
58579                     ed.col = col;
58580                     ed.record = r;
58581                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
58582                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
58583                     this.activeEditor = ed;
58584                     var v = r.data[field];
58585                     ed.startEdit(this.view.getCell(row, col), v);
58586                     // combo's with 'displayField and name set
58587                     if (ed.field.displayField && ed.field.name) {
58588                         ed.field.el.dom.value = r.data[ed.field.name];
58589                     }
58590                     
58591                     
58592                 }).defer(50, this);
58593             }
58594         }
58595     },
58596         
58597     /**
58598      * Stops any active editing
58599      */
58600     stopEditing : function(){
58601         if(this.activeEditor){
58602             this.activeEditor.completeEdit();
58603         }
58604         this.activeEditor = null;
58605     },
58606         
58607          /**
58608      * Called to get grid's drag proxy text, by default returns this.ddText.
58609      * @return {String}
58610      */
58611     getDragDropText : function(){
58612         var count = this.selModel.getSelectedCell() ? 1 : 0;
58613         return String.format(this.ddText, count, count == 1 ? '' : 's');
58614     }
58615         
58616 });/*
58617  * Based on:
58618  * Ext JS Library 1.1.1
58619  * Copyright(c) 2006-2007, Ext JS, LLC.
58620  *
58621  * Originally Released Under LGPL - original licence link has changed is not relivant.
58622  *
58623  * Fork - LGPL
58624  * <script type="text/javascript">
58625  */
58626
58627 // private - not really -- you end up using it !
58628 // This is a support class used internally by the Grid components
58629
58630 /**
58631  * @class Roo.grid.GridEditor
58632  * @extends Roo.Editor
58633  * Class for creating and editable grid elements.
58634  * @param {Object} config any settings (must include field)
58635  */
58636 Roo.grid.GridEditor = function(field, config){
58637     if (!config && field.field) {
58638         config = field;
58639         field = Roo.factory(config.field, Roo.form);
58640     }
58641     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
58642     field.monitorTab = false;
58643 };
58644
58645 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
58646     
58647     /**
58648      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
58649      */
58650     
58651     alignment: "tl-tl",
58652     autoSize: "width",
58653     hideEl : false,
58654     cls: "x-small-editor x-grid-editor",
58655     shim:false,
58656     shadow:"frame"
58657 });/*
58658  * Based on:
58659  * Ext JS Library 1.1.1
58660  * Copyright(c) 2006-2007, Ext JS, LLC.
58661  *
58662  * Originally Released Under LGPL - original licence link has changed is not relivant.
58663  *
58664  * Fork - LGPL
58665  * <script type="text/javascript">
58666  */
58667   
58668
58669   
58670 Roo.grid.PropertyRecord = Roo.data.Record.create([
58671     {name:'name',type:'string'},  'value'
58672 ]);
58673
58674
58675 Roo.grid.PropertyStore = function(grid, source){
58676     this.grid = grid;
58677     this.store = new Roo.data.Store({
58678         recordType : Roo.grid.PropertyRecord
58679     });
58680     this.store.on('update', this.onUpdate,  this);
58681     if(source){
58682         this.setSource(source);
58683     }
58684     Roo.grid.PropertyStore.superclass.constructor.call(this);
58685 };
58686
58687
58688
58689 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
58690     setSource : function(o){
58691         this.source = o;
58692         this.store.removeAll();
58693         var data = [];
58694         for(var k in o){
58695             if(this.isEditableValue(o[k])){
58696                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
58697             }
58698         }
58699         this.store.loadRecords({records: data}, {}, true);
58700     },
58701
58702     onUpdate : function(ds, record, type){
58703         if(type == Roo.data.Record.EDIT){
58704             var v = record.data['value'];
58705             var oldValue = record.modified['value'];
58706             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
58707                 this.source[record.id] = v;
58708                 record.commit();
58709                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
58710             }else{
58711                 record.reject();
58712             }
58713         }
58714     },
58715
58716     getProperty : function(row){
58717        return this.store.getAt(row);
58718     },
58719
58720     isEditableValue: function(val){
58721         if(val && val instanceof Date){
58722             return true;
58723         }else if(typeof val == 'object' || typeof val == 'function'){
58724             return false;
58725         }
58726         return true;
58727     },
58728
58729     setValue : function(prop, value){
58730         this.source[prop] = value;
58731         this.store.getById(prop).set('value', value);
58732     },
58733
58734     getSource : function(){
58735         return this.source;
58736     }
58737 });
58738
58739 Roo.grid.PropertyColumnModel = function(grid, store){
58740     this.grid = grid;
58741     var g = Roo.grid;
58742     g.PropertyColumnModel.superclass.constructor.call(this, [
58743         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
58744         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
58745     ]);
58746     this.store = store;
58747     this.bselect = Roo.DomHelper.append(document.body, {
58748         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
58749             {tag: 'option', value: 'true', html: 'true'},
58750             {tag: 'option', value: 'false', html: 'false'}
58751         ]
58752     });
58753     Roo.id(this.bselect);
58754     var f = Roo.form;
58755     this.editors = {
58756         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
58757         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
58758         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
58759         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
58760         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
58761     };
58762     this.renderCellDelegate = this.renderCell.createDelegate(this);
58763     this.renderPropDelegate = this.renderProp.createDelegate(this);
58764 };
58765
58766 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
58767     
58768     
58769     nameText : 'Name',
58770     valueText : 'Value',
58771     
58772     dateFormat : 'm/j/Y',
58773     
58774     
58775     renderDate : function(dateVal){
58776         return dateVal.dateFormat(this.dateFormat);
58777     },
58778
58779     renderBool : function(bVal){
58780         return bVal ? 'true' : 'false';
58781     },
58782
58783     isCellEditable : function(colIndex, rowIndex){
58784         return colIndex == 1;
58785     },
58786
58787     getRenderer : function(col){
58788         return col == 1 ?
58789             this.renderCellDelegate : this.renderPropDelegate;
58790     },
58791
58792     renderProp : function(v){
58793         return this.getPropertyName(v);
58794     },
58795
58796     renderCell : function(val){
58797         var rv = val;
58798         if(val instanceof Date){
58799             rv = this.renderDate(val);
58800         }else if(typeof val == 'boolean'){
58801             rv = this.renderBool(val);
58802         }
58803         return Roo.util.Format.htmlEncode(rv);
58804     },
58805
58806     getPropertyName : function(name){
58807         var pn = this.grid.propertyNames;
58808         return pn && pn[name] ? pn[name] : name;
58809     },
58810
58811     getCellEditor : function(colIndex, rowIndex){
58812         var p = this.store.getProperty(rowIndex);
58813         var n = p.data['name'], val = p.data['value'];
58814         
58815         if(typeof(this.grid.customEditors[n]) == 'string'){
58816             return this.editors[this.grid.customEditors[n]];
58817         }
58818         if(typeof(this.grid.customEditors[n]) != 'undefined'){
58819             return this.grid.customEditors[n];
58820         }
58821         if(val instanceof Date){
58822             return this.editors['date'];
58823         }else if(typeof val == 'number'){
58824             return this.editors['number'];
58825         }else if(typeof val == 'boolean'){
58826             return this.editors['boolean'];
58827         }else{
58828             return this.editors['string'];
58829         }
58830     }
58831 });
58832
58833 /**
58834  * @class Roo.grid.PropertyGrid
58835  * @extends Roo.grid.EditorGrid
58836  * This class represents the  interface of a component based property grid control.
58837  * <br><br>Usage:<pre><code>
58838  var grid = new Roo.grid.PropertyGrid("my-container-id", {
58839       
58840  });
58841  // set any options
58842  grid.render();
58843  * </code></pre>
58844   
58845  * @constructor
58846  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58847  * The container MUST have some type of size defined for the grid to fill. The container will be
58848  * automatically set to position relative if it isn't already.
58849  * @param {Object} config A config object that sets properties on this grid.
58850  */
58851 Roo.grid.PropertyGrid = function(container, config){
58852     config = config || {};
58853     var store = new Roo.grid.PropertyStore(this);
58854     this.store = store;
58855     var cm = new Roo.grid.PropertyColumnModel(this, store);
58856     store.store.sort('name', 'ASC');
58857     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
58858         ds: store.store,
58859         cm: cm,
58860         enableColLock:false,
58861         enableColumnMove:false,
58862         stripeRows:false,
58863         trackMouseOver: false,
58864         clicksToEdit:1
58865     }, config));
58866     this.getGridEl().addClass('x-props-grid');
58867     this.lastEditRow = null;
58868     this.on('columnresize', this.onColumnResize, this);
58869     this.addEvents({
58870          /**
58871              * @event beforepropertychange
58872              * Fires before a property changes (return false to stop?)
58873              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58874              * @param {String} id Record Id
58875              * @param {String} newval New Value
58876          * @param {String} oldval Old Value
58877              */
58878         "beforepropertychange": true,
58879         /**
58880              * @event propertychange
58881              * Fires after a property changes
58882              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58883              * @param {String} id Record Id
58884              * @param {String} newval New Value
58885          * @param {String} oldval Old Value
58886              */
58887         "propertychange": true
58888     });
58889     this.customEditors = this.customEditors || {};
58890 };
58891 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
58892     
58893      /**
58894      * @cfg {Object} customEditors map of colnames=> custom editors.
58895      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
58896      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
58897      * false disables editing of the field.
58898          */
58899     
58900       /**
58901      * @cfg {Object} propertyNames map of property Names to their displayed value
58902          */
58903     
58904     render : function(){
58905         Roo.grid.PropertyGrid.superclass.render.call(this);
58906         this.autoSize.defer(100, this);
58907     },
58908
58909     autoSize : function(){
58910         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
58911         if(this.view){
58912             this.view.fitColumns();
58913         }
58914     },
58915
58916     onColumnResize : function(){
58917         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
58918         this.autoSize();
58919     },
58920     /**
58921      * Sets the data for the Grid
58922      * accepts a Key => Value object of all the elements avaiable.
58923      * @param {Object} data  to appear in grid.
58924      */
58925     setSource : function(source){
58926         this.store.setSource(source);
58927         //this.autoSize();
58928     },
58929     /**
58930      * Gets all the data from the grid.
58931      * @return {Object} data  data stored in grid
58932      */
58933     getSource : function(){
58934         return this.store.getSource();
58935     }
58936 });/*
58937   
58938  * Licence LGPL
58939  
58940  */
58941  
58942 /**
58943  * @class Roo.grid.Calendar
58944  * @extends Roo.util.Grid
58945  * This class extends the Grid to provide a calendar widget
58946  * <br><br>Usage:<pre><code>
58947  var grid = new Roo.grid.Calendar("my-container-id", {
58948      ds: myDataStore,
58949      cm: myColModel,
58950      selModel: mySelectionModel,
58951      autoSizeColumns: true,
58952      monitorWindowResize: false,
58953      trackMouseOver: true
58954      eventstore : real data store..
58955  });
58956  // set any options
58957  grid.render();
58958   
58959   * @constructor
58960  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58961  * The container MUST have some type of size defined for the grid to fill. The container will be
58962  * automatically set to position relative if it isn't already.
58963  * @param {Object} config A config object that sets properties on this grid.
58964  */
58965 Roo.grid.Calendar = function(container, config){
58966         // initialize the container
58967         this.container = Roo.get(container);
58968         this.container.update("");
58969         this.container.setStyle("overflow", "hidden");
58970     this.container.addClass('x-grid-container');
58971
58972     this.id = this.container.id;
58973
58974     Roo.apply(this, config);
58975     // check and correct shorthanded configs
58976     
58977     var rows = [];
58978     var d =1;
58979     for (var r = 0;r < 6;r++) {
58980         
58981         rows[r]=[];
58982         for (var c =0;c < 7;c++) {
58983             rows[r][c]= '';
58984         }
58985     }
58986     if (this.eventStore) {
58987         this.eventStore= Roo.factory(this.eventStore, Roo.data);
58988         this.eventStore.on('load',this.onLoad, this);
58989         this.eventStore.on('beforeload',this.clearEvents, this);
58990          
58991     }
58992     
58993     this.dataSource = new Roo.data.Store({
58994             proxy: new Roo.data.MemoryProxy(rows),
58995             reader: new Roo.data.ArrayReader({}, [
58996                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
58997     });
58998
58999     this.dataSource.load();
59000     this.ds = this.dataSource;
59001     this.ds.xmodule = this.xmodule || false;
59002     
59003     
59004     var cellRender = function(v,x,r)
59005     {
59006         return String.format(
59007             '<div class="fc-day  fc-widget-content"><div>' +
59008                 '<div class="fc-event-container"></div>' +
59009                 '<div class="fc-day-number">{0}</div>'+
59010                 
59011                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
59012             '</div></div>', v);
59013     
59014     }
59015     
59016     
59017     this.colModel = new Roo.grid.ColumnModel( [
59018         {
59019             xtype: 'ColumnModel',
59020             xns: Roo.grid,
59021             dataIndex : 'weekday0',
59022             header : 'Sunday',
59023             renderer : cellRender
59024         },
59025         {
59026             xtype: 'ColumnModel',
59027             xns: Roo.grid,
59028             dataIndex : 'weekday1',
59029             header : 'Monday',
59030             renderer : cellRender
59031         },
59032         {
59033             xtype: 'ColumnModel',
59034             xns: Roo.grid,
59035             dataIndex : 'weekday2',
59036             header : 'Tuesday',
59037             renderer : cellRender
59038         },
59039         {
59040             xtype: 'ColumnModel',
59041             xns: Roo.grid,
59042             dataIndex : 'weekday3',
59043             header : 'Wednesday',
59044             renderer : cellRender
59045         },
59046         {
59047             xtype: 'ColumnModel',
59048             xns: Roo.grid,
59049             dataIndex : 'weekday4',
59050             header : 'Thursday',
59051             renderer : cellRender
59052         },
59053         {
59054             xtype: 'ColumnModel',
59055             xns: Roo.grid,
59056             dataIndex : 'weekday5',
59057             header : 'Friday',
59058             renderer : cellRender
59059         },
59060         {
59061             xtype: 'ColumnModel',
59062             xns: Roo.grid,
59063             dataIndex : 'weekday6',
59064             header : 'Saturday',
59065             renderer : cellRender
59066         }
59067     ]);
59068     this.cm = this.colModel;
59069     this.cm.xmodule = this.xmodule || false;
59070  
59071         
59072           
59073     //this.selModel = new Roo.grid.CellSelectionModel();
59074     //this.sm = this.selModel;
59075     //this.selModel.init(this);
59076     
59077     
59078     if(this.width){
59079         this.container.setWidth(this.width);
59080     }
59081
59082     if(this.height){
59083         this.container.setHeight(this.height);
59084     }
59085     /** @private */
59086         this.addEvents({
59087         // raw events
59088         /**
59089          * @event click
59090          * The raw click event for the entire grid.
59091          * @param {Roo.EventObject} e
59092          */
59093         "click" : true,
59094         /**
59095          * @event dblclick
59096          * The raw dblclick event for the entire grid.
59097          * @param {Roo.EventObject} e
59098          */
59099         "dblclick" : true,
59100         /**
59101          * @event contextmenu
59102          * The raw contextmenu event for the entire grid.
59103          * @param {Roo.EventObject} e
59104          */
59105         "contextmenu" : true,
59106         /**
59107          * @event mousedown
59108          * The raw mousedown event for the entire grid.
59109          * @param {Roo.EventObject} e
59110          */
59111         "mousedown" : true,
59112         /**
59113          * @event mouseup
59114          * The raw mouseup event for the entire grid.
59115          * @param {Roo.EventObject} e
59116          */
59117         "mouseup" : true,
59118         /**
59119          * @event mouseover
59120          * The raw mouseover event for the entire grid.
59121          * @param {Roo.EventObject} e
59122          */
59123         "mouseover" : true,
59124         /**
59125          * @event mouseout
59126          * The raw mouseout event for the entire grid.
59127          * @param {Roo.EventObject} e
59128          */
59129         "mouseout" : true,
59130         /**
59131          * @event keypress
59132          * The raw keypress event for the entire grid.
59133          * @param {Roo.EventObject} e
59134          */
59135         "keypress" : true,
59136         /**
59137          * @event keydown
59138          * The raw keydown event for the entire grid.
59139          * @param {Roo.EventObject} e
59140          */
59141         "keydown" : true,
59142
59143         // custom events
59144
59145         /**
59146          * @event cellclick
59147          * Fires when a cell is clicked
59148          * @param {Grid} this
59149          * @param {Number} rowIndex
59150          * @param {Number} columnIndex
59151          * @param {Roo.EventObject} e
59152          */
59153         "cellclick" : true,
59154         /**
59155          * @event celldblclick
59156          * Fires when a cell is double clicked
59157          * @param {Grid} this
59158          * @param {Number} rowIndex
59159          * @param {Number} columnIndex
59160          * @param {Roo.EventObject} e
59161          */
59162         "celldblclick" : true,
59163         /**
59164          * @event rowclick
59165          * Fires when a row is clicked
59166          * @param {Grid} this
59167          * @param {Number} rowIndex
59168          * @param {Roo.EventObject} e
59169          */
59170         "rowclick" : true,
59171         /**
59172          * @event rowdblclick
59173          * Fires when a row is double clicked
59174          * @param {Grid} this
59175          * @param {Number} rowIndex
59176          * @param {Roo.EventObject} e
59177          */
59178         "rowdblclick" : true,
59179         /**
59180          * @event headerclick
59181          * Fires when a header is clicked
59182          * @param {Grid} this
59183          * @param {Number} columnIndex
59184          * @param {Roo.EventObject} e
59185          */
59186         "headerclick" : true,
59187         /**
59188          * @event headerdblclick
59189          * Fires when a header cell is double clicked
59190          * @param {Grid} this
59191          * @param {Number} columnIndex
59192          * @param {Roo.EventObject} e
59193          */
59194         "headerdblclick" : true,
59195         /**
59196          * @event rowcontextmenu
59197          * Fires when a row is right clicked
59198          * @param {Grid} this
59199          * @param {Number} rowIndex
59200          * @param {Roo.EventObject} e
59201          */
59202         "rowcontextmenu" : true,
59203         /**
59204          * @event cellcontextmenu
59205          * Fires when a cell is right clicked
59206          * @param {Grid} this
59207          * @param {Number} rowIndex
59208          * @param {Number} cellIndex
59209          * @param {Roo.EventObject} e
59210          */
59211          "cellcontextmenu" : true,
59212         /**
59213          * @event headercontextmenu
59214          * Fires when a header is right clicked
59215          * @param {Grid} this
59216          * @param {Number} columnIndex
59217          * @param {Roo.EventObject} e
59218          */
59219         "headercontextmenu" : true,
59220         /**
59221          * @event bodyscroll
59222          * Fires when the body element is scrolled
59223          * @param {Number} scrollLeft
59224          * @param {Number} scrollTop
59225          */
59226         "bodyscroll" : true,
59227         /**
59228          * @event columnresize
59229          * Fires when the user resizes a column
59230          * @param {Number} columnIndex
59231          * @param {Number} newSize
59232          */
59233         "columnresize" : true,
59234         /**
59235          * @event columnmove
59236          * Fires when the user moves a column
59237          * @param {Number} oldIndex
59238          * @param {Number} newIndex
59239          */
59240         "columnmove" : true,
59241         /**
59242          * @event startdrag
59243          * Fires when row(s) start being dragged
59244          * @param {Grid} this
59245          * @param {Roo.GridDD} dd The drag drop object
59246          * @param {event} e The raw browser event
59247          */
59248         "startdrag" : true,
59249         /**
59250          * @event enddrag
59251          * Fires when a drag operation is complete
59252          * @param {Grid} this
59253          * @param {Roo.GridDD} dd The drag drop object
59254          * @param {event} e The raw browser event
59255          */
59256         "enddrag" : true,
59257         /**
59258          * @event dragdrop
59259          * Fires when dragged row(s) are dropped on a valid DD target
59260          * @param {Grid} this
59261          * @param {Roo.GridDD} dd The drag drop object
59262          * @param {String} targetId The target drag drop object
59263          * @param {event} e The raw browser event
59264          */
59265         "dragdrop" : true,
59266         /**
59267          * @event dragover
59268          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
59269          * @param {Grid} this
59270          * @param {Roo.GridDD} dd The drag drop object
59271          * @param {String} targetId The target drag drop object
59272          * @param {event} e The raw browser event
59273          */
59274         "dragover" : true,
59275         /**
59276          * @event dragenter
59277          *  Fires when the dragged row(s) first cross another DD target while being dragged
59278          * @param {Grid} this
59279          * @param {Roo.GridDD} dd The drag drop object
59280          * @param {String} targetId The target drag drop object
59281          * @param {event} e The raw browser event
59282          */
59283         "dragenter" : true,
59284         /**
59285          * @event dragout
59286          * Fires when the dragged row(s) leave another DD target while being dragged
59287          * @param {Grid} this
59288          * @param {Roo.GridDD} dd The drag drop object
59289          * @param {String} targetId The target drag drop object
59290          * @param {event} e The raw browser event
59291          */
59292         "dragout" : true,
59293         /**
59294          * @event rowclass
59295          * Fires when a row is rendered, so you can change add a style to it.
59296          * @param {GridView} gridview   The grid view
59297          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
59298          */
59299         'rowclass' : true,
59300
59301         /**
59302          * @event render
59303          * Fires when the grid is rendered
59304          * @param {Grid} grid
59305          */
59306         'render' : true,
59307             /**
59308              * @event select
59309              * Fires when a date is selected
59310              * @param {DatePicker} this
59311              * @param {Date} date The selected date
59312              */
59313         'select': true,
59314         /**
59315              * @event monthchange
59316              * Fires when the displayed month changes 
59317              * @param {DatePicker} this
59318              * @param {Date} date The selected month
59319              */
59320         'monthchange': true,
59321         /**
59322              * @event evententer
59323              * Fires when mouse over an event
59324              * @param {Calendar} this
59325              * @param {event} Event
59326              */
59327         'evententer': true,
59328         /**
59329              * @event eventleave
59330              * Fires when the mouse leaves an
59331              * @param {Calendar} this
59332              * @param {event}
59333              */
59334         'eventleave': true,
59335         /**
59336              * @event eventclick
59337              * Fires when the mouse click an
59338              * @param {Calendar} this
59339              * @param {event}
59340              */
59341         'eventclick': true,
59342         /**
59343              * @event eventrender
59344              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
59345              * @param {Calendar} this
59346              * @param {data} data to be modified
59347              */
59348         'eventrender': true
59349         
59350     });
59351
59352     Roo.grid.Grid.superclass.constructor.call(this);
59353     this.on('render', function() {
59354         this.view.el.addClass('x-grid-cal'); 
59355         
59356         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
59357
59358     },this);
59359     
59360     if (!Roo.grid.Calendar.style) {
59361         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
59362             
59363             
59364             '.x-grid-cal .x-grid-col' :  {
59365                 height: 'auto !important',
59366                 'vertical-align': 'top'
59367             },
59368             '.x-grid-cal  .fc-event-hori' : {
59369                 height: '14px'
59370             }
59371              
59372             
59373         }, Roo.id());
59374     }
59375
59376     
59377     
59378 };
59379 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
59380     /**
59381      * @cfg {Store} eventStore The store that loads events.
59382      */
59383     eventStore : 25,
59384
59385      
59386     activeDate : false,
59387     startDay : 0,
59388     autoWidth : true,
59389     monitorWindowResize : false,
59390
59391     
59392     resizeColumns : function() {
59393         var col = (this.view.el.getWidth() / 7) - 3;
59394         // loop through cols, and setWidth
59395         for(var i =0 ; i < 7 ; i++){
59396             this.cm.setColumnWidth(i, col);
59397         }
59398     },
59399      setDate :function(date) {
59400         
59401         Roo.log('setDate?');
59402         
59403         this.resizeColumns();
59404         var vd = this.activeDate;
59405         this.activeDate = date;
59406 //        if(vd && this.el){
59407 //            var t = date.getTime();
59408 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
59409 //                Roo.log('using add remove');
59410 //                
59411 //                this.fireEvent('monthchange', this, date);
59412 //                
59413 //                this.cells.removeClass("fc-state-highlight");
59414 //                this.cells.each(function(c){
59415 //                   if(c.dateValue == t){
59416 //                       c.addClass("fc-state-highlight");
59417 //                       setTimeout(function(){
59418 //                            try{c.dom.firstChild.focus();}catch(e){}
59419 //                       }, 50);
59420 //                       return false;
59421 //                   }
59422 //                   return true;
59423 //                });
59424 //                return;
59425 //            }
59426 //        }
59427         
59428         var days = date.getDaysInMonth();
59429         
59430         var firstOfMonth = date.getFirstDateOfMonth();
59431         var startingPos = firstOfMonth.getDay()-this.startDay;
59432         
59433         if(startingPos < this.startDay){
59434             startingPos += 7;
59435         }
59436         
59437         var pm = date.add(Date.MONTH, -1);
59438         var prevStart = pm.getDaysInMonth()-startingPos;
59439 //        
59440         
59441         
59442         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59443         
59444         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
59445         //this.cells.addClassOnOver('fc-state-hover');
59446         
59447         var cells = this.cells.elements;
59448         var textEls = this.textNodes;
59449         
59450         //Roo.each(cells, function(cell){
59451         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
59452         //});
59453         
59454         days += startingPos;
59455
59456         // convert everything to numbers so it's fast
59457         var day = 86400000;
59458         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
59459         //Roo.log(d);
59460         //Roo.log(pm);
59461         //Roo.log(prevStart);
59462         
59463         var today = new Date().clearTime().getTime();
59464         var sel = date.clearTime().getTime();
59465         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
59466         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
59467         var ddMatch = this.disabledDatesRE;
59468         var ddText = this.disabledDatesText;
59469         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
59470         var ddaysText = this.disabledDaysText;
59471         var format = this.format;
59472         
59473         var setCellClass = function(cal, cell){
59474             
59475             //Roo.log('set Cell Class');
59476             cell.title = "";
59477             var t = d.getTime();
59478             
59479             //Roo.log(d);
59480             
59481             
59482             cell.dateValue = t;
59483             if(t == today){
59484                 cell.className += " fc-today";
59485                 cell.className += " fc-state-highlight";
59486                 cell.title = cal.todayText;
59487             }
59488             if(t == sel){
59489                 // disable highlight in other month..
59490                 cell.className += " fc-state-highlight";
59491                 
59492             }
59493             // disabling
59494             if(t < min) {
59495                 //cell.className = " fc-state-disabled";
59496                 cell.title = cal.minText;
59497                 return;
59498             }
59499             if(t > max) {
59500                 //cell.className = " fc-state-disabled";
59501                 cell.title = cal.maxText;
59502                 return;
59503             }
59504             if(ddays){
59505                 if(ddays.indexOf(d.getDay()) != -1){
59506                     // cell.title = ddaysText;
59507                    // cell.className = " fc-state-disabled";
59508                 }
59509             }
59510             if(ddMatch && format){
59511                 var fvalue = d.dateFormat(format);
59512                 if(ddMatch.test(fvalue)){
59513                     cell.title = ddText.replace("%0", fvalue);
59514                    cell.className = " fc-state-disabled";
59515                 }
59516             }
59517             
59518             if (!cell.initialClassName) {
59519                 cell.initialClassName = cell.dom.className;
59520             }
59521             
59522             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
59523         };
59524
59525         var i = 0;
59526         
59527         for(; i < startingPos; i++) {
59528             cells[i].dayName =  (++prevStart);
59529             Roo.log(textEls[i]);
59530             d.setDate(d.getDate()+1);
59531             
59532             //cells[i].className = "fc-past fc-other-month";
59533             setCellClass(this, cells[i]);
59534         }
59535         
59536         var intDay = 0;
59537         
59538         for(; i < days; i++){
59539             intDay = i - startingPos + 1;
59540             cells[i].dayName =  (intDay);
59541             d.setDate(d.getDate()+1);
59542             
59543             cells[i].className = ''; // "x-date-active";
59544             setCellClass(this, cells[i]);
59545         }
59546         var extraDays = 0;
59547         
59548         for(; i < 42; i++) {
59549             //textEls[i].innerHTML = (++extraDays);
59550             
59551             d.setDate(d.getDate()+1);
59552             cells[i].dayName = (++extraDays);
59553             cells[i].className = "fc-future fc-other-month";
59554             setCellClass(this, cells[i]);
59555         }
59556         
59557         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
59558         
59559         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
59560         
59561         // this will cause all the cells to mis
59562         var rows= [];
59563         var i =0;
59564         for (var r = 0;r < 6;r++) {
59565             for (var c =0;c < 7;c++) {
59566                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
59567             }    
59568         }
59569         
59570         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59571         for(i=0;i<cells.length;i++) {
59572             
59573             this.cells.elements[i].dayName = cells[i].dayName ;
59574             this.cells.elements[i].className = cells[i].className;
59575             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
59576             this.cells.elements[i].title = cells[i].title ;
59577             this.cells.elements[i].dateValue = cells[i].dateValue ;
59578         }
59579         
59580         
59581         
59582         
59583         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
59584         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
59585         
59586         ////if(totalRows != 6){
59587             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
59588            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
59589        // }
59590         
59591         this.fireEvent('monthchange', this, date);
59592         
59593         
59594     },
59595  /**
59596      * Returns the grid's SelectionModel.
59597      * @return {SelectionModel}
59598      */
59599     getSelectionModel : function(){
59600         if(!this.selModel){
59601             this.selModel = new Roo.grid.CellSelectionModel();
59602         }
59603         return this.selModel;
59604     },
59605
59606     load: function() {
59607         this.eventStore.load()
59608         
59609         
59610         
59611     },
59612     
59613     findCell : function(dt) {
59614         dt = dt.clearTime().getTime();
59615         var ret = false;
59616         this.cells.each(function(c){
59617             //Roo.log("check " +c.dateValue + '?=' + dt);
59618             if(c.dateValue == dt){
59619                 ret = c;
59620                 return false;
59621             }
59622             return true;
59623         });
59624         
59625         return ret;
59626     },
59627     
59628     findCells : function(rec) {
59629         var s = rec.data.start_dt.clone().clearTime().getTime();
59630        // Roo.log(s);
59631         var e= rec.data.end_dt.clone().clearTime().getTime();
59632        // Roo.log(e);
59633         var ret = [];
59634         this.cells.each(function(c){
59635              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
59636             
59637             if(c.dateValue > e){
59638                 return ;
59639             }
59640             if(c.dateValue < s){
59641                 return ;
59642             }
59643             ret.push(c);
59644         });
59645         
59646         return ret;    
59647     },
59648     
59649     findBestRow: function(cells)
59650     {
59651         var ret = 0;
59652         
59653         for (var i =0 ; i < cells.length;i++) {
59654             ret  = Math.max(cells[i].rows || 0,ret);
59655         }
59656         return ret;
59657         
59658     },
59659     
59660     
59661     addItem : function(rec)
59662     {
59663         // look for vertical location slot in
59664         var cells = this.findCells(rec);
59665         
59666         rec.row = this.findBestRow(cells);
59667         
59668         // work out the location.
59669         
59670         var crow = false;
59671         var rows = [];
59672         for(var i =0; i < cells.length; i++) {
59673             if (!crow) {
59674                 crow = {
59675                     start : cells[i],
59676                     end :  cells[i]
59677                 };
59678                 continue;
59679             }
59680             if (crow.start.getY() == cells[i].getY()) {
59681                 // on same row.
59682                 crow.end = cells[i];
59683                 continue;
59684             }
59685             // different row.
59686             rows.push(crow);
59687             crow = {
59688                 start: cells[i],
59689                 end : cells[i]
59690             };
59691             
59692         }
59693         
59694         rows.push(crow);
59695         rec.els = [];
59696         rec.rows = rows;
59697         rec.cells = cells;
59698         for (var i = 0; i < cells.length;i++) {
59699             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
59700             
59701         }
59702         
59703         
59704     },
59705     
59706     clearEvents: function() {
59707         
59708         if (!this.eventStore.getCount()) {
59709             return;
59710         }
59711         // reset number of rows in cells.
59712         Roo.each(this.cells.elements, function(c){
59713             c.rows = 0;
59714         });
59715         
59716         this.eventStore.each(function(e) {
59717             this.clearEvent(e);
59718         },this);
59719         
59720     },
59721     
59722     clearEvent : function(ev)
59723     {
59724         if (ev.els) {
59725             Roo.each(ev.els, function(el) {
59726                 el.un('mouseenter' ,this.onEventEnter, this);
59727                 el.un('mouseleave' ,this.onEventLeave, this);
59728                 el.remove();
59729             },this);
59730             ev.els = [];
59731         }
59732     },
59733     
59734     
59735     renderEvent : function(ev,ctr) {
59736         if (!ctr) {
59737              ctr = this.view.el.select('.fc-event-container',true).first();
59738         }
59739         
59740          
59741         this.clearEvent(ev);
59742             //code
59743        
59744         
59745         
59746         ev.els = [];
59747         var cells = ev.cells;
59748         var rows = ev.rows;
59749         this.fireEvent('eventrender', this, ev);
59750         
59751         for(var i =0; i < rows.length; i++) {
59752             
59753             cls = '';
59754             if (i == 0) {
59755                 cls += ' fc-event-start';
59756             }
59757             if ((i+1) == rows.length) {
59758                 cls += ' fc-event-end';
59759             }
59760             
59761             //Roo.log(ev.data);
59762             // how many rows should it span..
59763             var cg = this.eventTmpl.append(ctr,Roo.apply({
59764                 fccls : cls
59765                 
59766             }, ev.data) , true);
59767             
59768             
59769             cg.on('mouseenter' ,this.onEventEnter, this, ev);
59770             cg.on('mouseleave' ,this.onEventLeave, this, ev);
59771             cg.on('click', this.onEventClick, this, ev);
59772             
59773             ev.els.push(cg);
59774             
59775             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
59776             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
59777             //Roo.log(cg);
59778              
59779             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
59780             cg.setWidth(ebox.right - sbox.x -2);
59781         }
59782     },
59783     
59784     renderEvents: function()
59785     {   
59786         // first make sure there is enough space..
59787         
59788         if (!this.eventTmpl) {
59789             this.eventTmpl = new Roo.Template(
59790                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
59791                     '<div class="fc-event-inner">' +
59792                         '<span class="fc-event-time">{time}</span>' +
59793                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
59794                     '</div>' +
59795                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
59796                 '</div>'
59797             );
59798                 
59799         }
59800                
59801         
59802         
59803         this.cells.each(function(c) {
59804             //Roo.log(c.select('.fc-day-content div',true).first());
59805             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
59806         });
59807         
59808         var ctr = this.view.el.select('.fc-event-container',true).first();
59809         
59810         var cls;
59811         this.eventStore.each(function(ev){
59812             
59813             this.renderEvent(ev);
59814              
59815              
59816         }, this);
59817         this.view.layout();
59818         
59819     },
59820     
59821     onEventEnter: function (e, el,event,d) {
59822         this.fireEvent('evententer', this, el, event);
59823     },
59824     
59825     onEventLeave: function (e, el,event,d) {
59826         this.fireEvent('eventleave', this, el, event);
59827     },
59828     
59829     onEventClick: function (e, el,event,d) {
59830         this.fireEvent('eventclick', this, el, event);
59831     },
59832     
59833     onMonthChange: function () {
59834         this.store.load();
59835     },
59836     
59837     onLoad: function () {
59838         
59839         //Roo.log('calendar onload');
59840 //         
59841         if(this.eventStore.getCount() > 0){
59842             
59843            
59844             
59845             this.eventStore.each(function(d){
59846                 
59847                 
59848                 // FIXME..
59849                 var add =   d.data;
59850                 if (typeof(add.end_dt) == 'undefined')  {
59851                     Roo.log("Missing End time in calendar data: ");
59852                     Roo.log(d);
59853                     return;
59854                 }
59855                 if (typeof(add.start_dt) == 'undefined')  {
59856                     Roo.log("Missing Start time in calendar data: ");
59857                     Roo.log(d);
59858                     return;
59859                 }
59860                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
59861                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
59862                 add.id = add.id || d.id;
59863                 add.title = add.title || '??';
59864                 
59865                 this.addItem(d);
59866                 
59867              
59868             },this);
59869         }
59870         
59871         this.renderEvents();
59872     }
59873     
59874
59875 });
59876 /*
59877  grid : {
59878                 xtype: 'Grid',
59879                 xns: Roo.grid,
59880                 listeners : {
59881                     render : function ()
59882                     {
59883                         _this.grid = this;
59884                         
59885                         if (!this.view.el.hasClass('course-timesheet')) {
59886                             this.view.el.addClass('course-timesheet');
59887                         }
59888                         if (this.tsStyle) {
59889                             this.ds.load({});
59890                             return; 
59891                         }
59892                         Roo.log('width');
59893                         Roo.log(_this.grid.view.el.getWidth());
59894                         
59895                         
59896                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
59897                             '.course-timesheet .x-grid-row' : {
59898                                 height: '80px'
59899                             },
59900                             '.x-grid-row td' : {
59901                                 'vertical-align' : 0
59902                             },
59903                             '.course-edit-link' : {
59904                                 'color' : 'blue',
59905                                 'text-overflow' : 'ellipsis',
59906                                 'overflow' : 'hidden',
59907                                 'white-space' : 'nowrap',
59908                                 'cursor' : 'pointer'
59909                             },
59910                             '.sub-link' : {
59911                                 'color' : 'green'
59912                             },
59913                             '.de-act-sup-link' : {
59914                                 'color' : 'purple',
59915                                 'text-decoration' : 'line-through'
59916                             },
59917                             '.de-act-link' : {
59918                                 'color' : 'red',
59919                                 'text-decoration' : 'line-through'
59920                             },
59921                             '.course-timesheet .course-highlight' : {
59922                                 'border-top-style': 'dashed !important',
59923                                 'border-bottom-bottom': 'dashed !important'
59924                             },
59925                             '.course-timesheet .course-item' : {
59926                                 'font-family'   : 'tahoma, arial, helvetica',
59927                                 'font-size'     : '11px',
59928                                 'overflow'      : 'hidden',
59929                                 'padding-left'  : '10px',
59930                                 'padding-right' : '10px',
59931                                 'padding-top' : '10px' 
59932                             }
59933                             
59934                         }, Roo.id());
59935                                 this.ds.load({});
59936                     }
59937                 },
59938                 autoWidth : true,
59939                 monitorWindowResize : false,
59940                 cellrenderer : function(v,x,r)
59941                 {
59942                     return v;
59943                 },
59944                 sm : {
59945                     xtype: 'CellSelectionModel',
59946                     xns: Roo.grid
59947                 },
59948                 dataSource : {
59949                     xtype: 'Store',
59950                     xns: Roo.data,
59951                     listeners : {
59952                         beforeload : function (_self, options)
59953                         {
59954                             options.params = options.params || {};
59955                             options.params._month = _this.monthField.getValue();
59956                             options.params.limit = 9999;
59957                             options.params['sort'] = 'when_dt';    
59958                             options.params['dir'] = 'ASC';    
59959                             this.proxy.loadResponse = this.loadResponse;
59960                             Roo.log("load?");
59961                             //this.addColumns();
59962                         },
59963                         load : function (_self, records, options)
59964                         {
59965                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
59966                                 // if you click on the translation.. you can edit it...
59967                                 var el = Roo.get(this);
59968                                 var id = el.dom.getAttribute('data-id');
59969                                 var d = el.dom.getAttribute('data-date');
59970                                 var t = el.dom.getAttribute('data-time');
59971                                 //var id = this.child('span').dom.textContent;
59972                                 
59973                                 //Roo.log(this);
59974                                 Pman.Dialog.CourseCalendar.show({
59975                                     id : id,
59976                                     when_d : d,
59977                                     when_t : t,
59978                                     productitem_active : id ? 1 : 0
59979                                 }, function() {
59980                                     _this.grid.ds.load({});
59981                                 });
59982                            
59983                            });
59984                            
59985                            _this.panel.fireEvent('resize', [ '', '' ]);
59986                         }
59987                     },
59988                     loadResponse : function(o, success, response){
59989                             // this is overridden on before load..
59990                             
59991                             Roo.log("our code?");       
59992                             //Roo.log(success);
59993                             //Roo.log(response)
59994                             delete this.activeRequest;
59995                             if(!success){
59996                                 this.fireEvent("loadexception", this, o, response);
59997                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59998                                 return;
59999                             }
60000                             var result;
60001                             try {
60002                                 result = o.reader.read(response);
60003                             }catch(e){
60004                                 Roo.log("load exception?");
60005                                 this.fireEvent("loadexception", this, o, response, e);
60006                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60007                                 return;
60008                             }
60009                             Roo.log("ready...");        
60010                             // loop through result.records;
60011                             // and set this.tdate[date] = [] << array of records..
60012                             _this.tdata  = {};
60013                             Roo.each(result.records, function(r){
60014                                 //Roo.log(r.data);
60015                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
60016                                     _this.tdata[r.data.when_dt.format('j')] = [];
60017                                 }
60018                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
60019                             });
60020                             
60021                             //Roo.log(_this.tdata);
60022                             
60023                             result.records = [];
60024                             result.totalRecords = 6;
60025                     
60026                             // let's generate some duumy records for the rows.
60027                             //var st = _this.dateField.getValue();
60028                             
60029                             // work out monday..
60030                             //st = st.add(Date.DAY, -1 * st.format('w'));
60031                             
60032                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60033                             
60034                             var firstOfMonth = date.getFirstDayOfMonth();
60035                             var days = date.getDaysInMonth();
60036                             var d = 1;
60037                             var firstAdded = false;
60038                             for (var i = 0; i < result.totalRecords ; i++) {
60039                                 //var d= st.add(Date.DAY, i);
60040                                 var row = {};
60041                                 var added = 0;
60042                                 for(var w = 0 ; w < 7 ; w++){
60043                                     if(!firstAdded && firstOfMonth != w){
60044                                         continue;
60045                                     }
60046                                     if(d > days){
60047                                         continue;
60048                                     }
60049                                     firstAdded = true;
60050                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
60051                                     row['weekday'+w] = String.format(
60052                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
60053                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
60054                                                     d,
60055                                                     date.format('Y-m-')+dd
60056                                                 );
60057                                     added++;
60058                                     if(typeof(_this.tdata[d]) != 'undefined'){
60059                                         Roo.each(_this.tdata[d], function(r){
60060                                             var is_sub = '';
60061                                             var deactive = '';
60062                                             var id = r.id;
60063                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
60064                                             if(r.parent_id*1>0){
60065                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
60066                                                 id = r.parent_id;
60067                                             }
60068                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
60069                                                 deactive = 'de-act-link';
60070                                             }
60071                                             
60072                                             row['weekday'+w] += String.format(
60073                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
60074                                                     id, //0
60075                                                     r.product_id_name, //1
60076                                                     r.when_dt.format('h:ia'), //2
60077                                                     is_sub, //3
60078                                                     deactive, //4
60079                                                     desc // 5
60080                                             );
60081                                         });
60082                                     }
60083                                     d++;
60084                                 }
60085                                 
60086                                 // only do this if something added..
60087                                 if(added > 0){ 
60088                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
60089                                 }
60090                                 
60091                                 
60092                                 // push it twice. (second one with an hour..
60093                                 
60094                             }
60095                             //Roo.log(result);
60096                             this.fireEvent("load", this, o, o.request.arg);
60097                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
60098                         },
60099                     sortInfo : {field: 'when_dt', direction : 'ASC' },
60100                     proxy : {
60101                         xtype: 'HttpProxy',
60102                         xns: Roo.data,
60103                         method : 'GET',
60104                         url : baseURL + '/Roo/Shop_course.php'
60105                     },
60106                     reader : {
60107                         xtype: 'JsonReader',
60108                         xns: Roo.data,
60109                         id : 'id',
60110                         fields : [
60111                             {
60112                                 'name': 'id',
60113                                 'type': 'int'
60114                             },
60115                             {
60116                                 'name': 'when_dt',
60117                                 'type': 'string'
60118                             },
60119                             {
60120                                 'name': 'end_dt',
60121                                 'type': 'string'
60122                             },
60123                             {
60124                                 'name': 'parent_id',
60125                                 'type': 'int'
60126                             },
60127                             {
60128                                 'name': 'product_id',
60129                                 'type': 'int'
60130                             },
60131                             {
60132                                 'name': 'productitem_id',
60133                                 'type': 'int'
60134                             },
60135                             {
60136                                 'name': 'guid',
60137                                 'type': 'int'
60138                             }
60139                         ]
60140                     }
60141                 },
60142                 toolbar : {
60143                     xtype: 'Toolbar',
60144                     xns: Roo,
60145                     items : [
60146                         {
60147                             xtype: 'Button',
60148                             xns: Roo.Toolbar,
60149                             listeners : {
60150                                 click : function (_self, e)
60151                                 {
60152                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60153                                     sd.setMonth(sd.getMonth()-1);
60154                                     _this.monthField.setValue(sd.format('Y-m-d'));
60155                                     _this.grid.ds.load({});
60156                                 }
60157                             },
60158                             text : "Back"
60159                         },
60160                         {
60161                             xtype: 'Separator',
60162                             xns: Roo.Toolbar
60163                         },
60164                         {
60165                             xtype: 'MonthField',
60166                             xns: Roo.form,
60167                             listeners : {
60168                                 render : function (_self)
60169                                 {
60170                                     _this.monthField = _self;
60171                                    // _this.monthField.set  today
60172                                 },
60173                                 select : function (combo, date)
60174                                 {
60175                                     _this.grid.ds.load({});
60176                                 }
60177                             },
60178                             value : (function() { return new Date(); })()
60179                         },
60180                         {
60181                             xtype: 'Separator',
60182                             xns: Roo.Toolbar
60183                         },
60184                         {
60185                             xtype: 'TextItem',
60186                             xns: Roo.Toolbar,
60187                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
60188                         },
60189                         {
60190                             xtype: 'Fill',
60191                             xns: Roo.Toolbar
60192                         },
60193                         {
60194                             xtype: 'Button',
60195                             xns: Roo.Toolbar,
60196                             listeners : {
60197                                 click : function (_self, e)
60198                                 {
60199                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60200                                     sd.setMonth(sd.getMonth()+1);
60201                                     _this.monthField.setValue(sd.format('Y-m-d'));
60202                                     _this.grid.ds.load({});
60203                                 }
60204                             },
60205                             text : "Next"
60206                         }
60207                     ]
60208                 },
60209                  
60210             }
60211         };
60212         
60213         *//*
60214  * Based on:
60215  * Ext JS Library 1.1.1
60216  * Copyright(c) 2006-2007, Ext JS, LLC.
60217  *
60218  * Originally Released Under LGPL - original licence link has changed is not relivant.
60219  *
60220  * Fork - LGPL
60221  * <script type="text/javascript">
60222  */
60223  
60224 /**
60225  * @class Roo.LoadMask
60226  * A simple utility class for generically masking elements while loading data.  If the element being masked has
60227  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
60228  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
60229  * element's UpdateManager load indicator and will be destroyed after the initial load.
60230  * @constructor
60231  * Create a new LoadMask
60232  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
60233  * @param {Object} config The config object
60234  */
60235 Roo.LoadMask = function(el, config){
60236     this.el = Roo.get(el);
60237     Roo.apply(this, config);
60238     if(this.store){
60239         this.store.on('beforeload', this.onBeforeLoad, this);
60240         this.store.on('load', this.onLoad, this);
60241         this.store.on('loadexception', this.onLoadException, this);
60242         this.removeMask = false;
60243     }else{
60244         var um = this.el.getUpdateManager();
60245         um.showLoadIndicator = false; // disable the default indicator
60246         um.on('beforeupdate', this.onBeforeLoad, this);
60247         um.on('update', this.onLoad, this);
60248         um.on('failure', this.onLoad, this);
60249         this.removeMask = true;
60250     }
60251 };
60252
60253 Roo.LoadMask.prototype = {
60254     /**
60255      * @cfg {Boolean} removeMask
60256      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
60257      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
60258      */
60259     /**
60260      * @cfg {String} msg
60261      * The text to display in a centered loading message box (defaults to 'Loading...')
60262      */
60263     msg : 'Loading...',
60264     /**
60265      * @cfg {String} msgCls
60266      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
60267      */
60268     msgCls : 'x-mask-loading',
60269
60270     /**
60271      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
60272      * @type Boolean
60273      */
60274     disabled: false,
60275
60276     /**
60277      * Disables the mask to prevent it from being displayed
60278      */
60279     disable : function(){
60280        this.disabled = true;
60281     },
60282
60283     /**
60284      * Enables the mask so that it can be displayed
60285      */
60286     enable : function(){
60287         this.disabled = false;
60288     },
60289     
60290     onLoadException : function()
60291     {
60292         Roo.log(arguments);
60293         
60294         if (typeof(arguments[3]) != 'undefined') {
60295             Roo.MessageBox.alert("Error loading",arguments[3]);
60296         } 
60297         /*
60298         try {
60299             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
60300                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
60301             }   
60302         } catch(e) {
60303             
60304         }
60305         */
60306     
60307         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60308     },
60309     // private
60310     onLoad : function()
60311     {
60312         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60313     },
60314
60315     // private
60316     onBeforeLoad : function(){
60317         if(!this.disabled){
60318             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
60319         }
60320     },
60321
60322     // private
60323     destroy : function(){
60324         if(this.store){
60325             this.store.un('beforeload', this.onBeforeLoad, this);
60326             this.store.un('load', this.onLoad, this);
60327             this.store.un('loadexception', this.onLoadException, this);
60328         }else{
60329             var um = this.el.getUpdateManager();
60330             um.un('beforeupdate', this.onBeforeLoad, this);
60331             um.un('update', this.onLoad, this);
60332             um.un('failure', this.onLoad, this);
60333         }
60334     }
60335 };/*
60336  * Based on:
60337  * Ext JS Library 1.1.1
60338  * Copyright(c) 2006-2007, Ext JS, LLC.
60339  *
60340  * Originally Released Under LGPL - original licence link has changed is not relivant.
60341  *
60342  * Fork - LGPL
60343  * <script type="text/javascript">
60344  */
60345
60346
60347 /**
60348  * @class Roo.XTemplate
60349  * @extends Roo.Template
60350  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
60351 <pre><code>
60352 var t = new Roo.XTemplate(
60353         '&lt;select name="{name}"&gt;',
60354                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
60355         '&lt;/select&gt;'
60356 );
60357  
60358 // then append, applying the master template values
60359  </code></pre>
60360  *
60361  * Supported features:
60362  *
60363  *  Tags:
60364
60365 <pre><code>
60366       {a_variable} - output encoded.
60367       {a_variable.format:("Y-m-d")} - call a method on the variable
60368       {a_variable:raw} - unencoded output
60369       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
60370       {a_variable:this.method_on_template(...)} - call a method on the template object.
60371  
60372 </code></pre>
60373  *  The tpl tag:
60374 <pre><code>
60375         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
60376         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
60377         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
60378         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
60379   
60380         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
60381         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
60382 </code></pre>
60383  *      
60384  */
60385 Roo.XTemplate = function()
60386 {
60387     Roo.XTemplate.superclass.constructor.apply(this, arguments);
60388     if (this.html) {
60389         this.compile();
60390     }
60391 };
60392
60393
60394 Roo.extend(Roo.XTemplate, Roo.Template, {
60395
60396     /**
60397      * The various sub templates
60398      */
60399     tpls : false,
60400     /**
60401      *
60402      * basic tag replacing syntax
60403      * WORD:WORD()
60404      *
60405      * // you can fake an object call by doing this
60406      *  x.t:(test,tesT) 
60407      * 
60408      */
60409     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
60410
60411     /**
60412      * compile the template
60413      *
60414      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
60415      *
60416      */
60417     compile: function()
60418     {
60419         var s = this.html;
60420      
60421         s = ['<tpl>', s, '</tpl>'].join('');
60422     
60423         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
60424             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
60425             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
60426             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
60427             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
60428             m,
60429             id     = 0,
60430             tpls   = [];
60431     
60432         while(true == !!(m = s.match(re))){
60433             var forMatch   = m[0].match(nameRe),
60434                 ifMatch   = m[0].match(ifRe),
60435                 execMatch   = m[0].match(execRe),
60436                 namedMatch   = m[0].match(namedRe),
60437                 
60438                 exp  = null, 
60439                 fn   = null,
60440                 exec = null,
60441                 name = forMatch && forMatch[1] ? forMatch[1] : '';
60442                 
60443             if (ifMatch) {
60444                 // if - puts fn into test..
60445                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
60446                 if(exp){
60447                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
60448                 }
60449             }
60450             
60451             if (execMatch) {
60452                 // exec - calls a function... returns empty if true is  returned.
60453                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
60454                 if(exp){
60455                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
60456                 }
60457             }
60458             
60459             
60460             if (name) {
60461                 // for = 
60462                 switch(name){
60463                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
60464                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
60465                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
60466                 }
60467             }
60468             var uid = namedMatch ? namedMatch[1] : id;
60469             
60470             
60471             tpls.push({
60472                 id:     namedMatch ? namedMatch[1] : id,
60473                 target: name,
60474                 exec:   exec,
60475                 test:   fn,
60476                 body:   m[1] || ''
60477             });
60478             if (namedMatch) {
60479                 s = s.replace(m[0], '');
60480             } else { 
60481                 s = s.replace(m[0], '{xtpl'+ id + '}');
60482             }
60483             ++id;
60484         }
60485         this.tpls = [];
60486         for(var i = tpls.length-1; i >= 0; --i){
60487             this.compileTpl(tpls[i]);
60488             this.tpls[tpls[i].id] = tpls[i];
60489         }
60490         this.master = tpls[tpls.length-1];
60491         return this;
60492     },
60493     /**
60494      * same as applyTemplate, except it's done to one of the subTemplates
60495      * when using named templates, you can do:
60496      *
60497      * var str = pl.applySubTemplate('your-name', values);
60498      *
60499      * 
60500      * @param {Number} id of the template
60501      * @param {Object} values to apply to template
60502      * @param {Object} parent (normaly the instance of this object)
60503      */
60504     applySubTemplate : function(id, values, parent)
60505     {
60506         
60507         
60508         var t = this.tpls[id];
60509         
60510         
60511         try { 
60512             if(t.test && !t.test.call(this, values, parent)){
60513                 return '';
60514             }
60515         } catch(e) {
60516             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
60517             Roo.log(e.toString());
60518             Roo.log(t.test);
60519             return ''
60520         }
60521         try { 
60522             
60523             if(t.exec && t.exec.call(this, values, parent)){
60524                 return '';
60525             }
60526         } catch(e) {
60527             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
60528             Roo.log(e.toString());
60529             Roo.log(t.exec);
60530             return ''
60531         }
60532         try {
60533             var vs = t.target ? t.target.call(this, values, parent) : values;
60534             parent = t.target ? values : parent;
60535             if(t.target && vs instanceof Array){
60536                 var buf = [];
60537                 for(var i = 0, len = vs.length; i < len; i++){
60538                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
60539                 }
60540                 return buf.join('');
60541             }
60542             return t.compiled.call(this, vs, parent);
60543         } catch (e) {
60544             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
60545             Roo.log(e.toString());
60546             Roo.log(t.compiled);
60547             return '';
60548         }
60549     },
60550
60551     compileTpl : function(tpl)
60552     {
60553         var fm = Roo.util.Format;
60554         var useF = this.disableFormats !== true;
60555         var sep = Roo.isGecko ? "+" : ",";
60556         var undef = function(str) {
60557             Roo.log("Property not found :"  + str);
60558             return '';
60559         };
60560         
60561         var fn = function(m, name, format, args)
60562         {
60563             //Roo.log(arguments);
60564             args = args ? args.replace(/\\'/g,"'") : args;
60565             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
60566             if (typeof(format) == 'undefined') {
60567                 format= 'htmlEncode';
60568             }
60569             if (format == 'raw' ) {
60570                 format = false;
60571             }
60572             
60573             if(name.substr(0, 4) == 'xtpl'){
60574                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
60575             }
60576             
60577             // build an array of options to determine if value is undefined..
60578             
60579             // basically get 'xxxx.yyyy' then do
60580             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
60581             //    (function () { Roo.log("Property not found"); return ''; })() :
60582             //    ......
60583             
60584             var udef_ar = [];
60585             var lookfor = '';
60586             Roo.each(name.split('.'), function(st) {
60587                 lookfor += (lookfor.length ? '.': '') + st;
60588                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
60589             });
60590             
60591             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
60592             
60593             
60594             if(format && useF){
60595                 
60596                 args = args ? ',' + args : "";
60597                  
60598                 if(format.substr(0, 5) != "this."){
60599                     format = "fm." + format + '(';
60600                 }else{
60601                     format = 'this.call("'+ format.substr(5) + '", ';
60602                     args = ", values";
60603                 }
60604                 
60605                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
60606             }
60607              
60608             if (args.length) {
60609                 // called with xxyx.yuu:(test,test)
60610                 // change to ()
60611                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
60612             }
60613             // raw.. - :raw modifier..
60614             return "'"+ sep + udef_st  + name + ")"+sep+"'";
60615             
60616         };
60617         var body;
60618         // branched to use + in gecko and [].join() in others
60619         if(Roo.isGecko){
60620             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
60621                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
60622                     "';};};";
60623         }else{
60624             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
60625             body.push(tpl.body.replace(/(\r\n|\n)/g,
60626                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
60627             body.push("'].join('');};};");
60628             body = body.join('');
60629         }
60630         
60631         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
60632        
60633         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
60634         eval(body);
60635         
60636         return this;
60637     },
60638
60639     applyTemplate : function(values){
60640         return this.master.compiled.call(this, values, {});
60641         //var s = this.subs;
60642     },
60643
60644     apply : function(){
60645         return this.applyTemplate.apply(this, arguments);
60646     }
60647
60648  });
60649
60650 Roo.XTemplate.from = function(el){
60651     el = Roo.getDom(el);
60652     return new Roo.XTemplate(el.value || el.innerHTML);
60653 };