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             console.log(s);
346             
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  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
892  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
893  * they are already different, the first value passed in is returned.  Note that this method returns the new value
894  * but does not change the current string.
895  * <pre><code>
896 // alternate sort directions
897 sort = sort.toggle('ASC', 'DESC');
898
899 // instead of conditional logic:
900 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
901 </code></pre>
902  * @param {String} value The value to compare to the current string
903  * @param {String} other The new value to use if the string already equals the first value passed in
904  * @return {String} The new value
905  */
906  
907 String.prototype.toggle = function(value, other){
908     return this == value ? other : value;
909 };/*
910  * Based on:
911  * Ext JS Library 1.1.1
912  * Copyright(c) 2006-2007, Ext JS, LLC.
913  *
914  * Originally Released Under LGPL - original licence link has changed is not relivant.
915  *
916  * Fork - LGPL
917  * <script type="text/javascript">
918  */
919
920  /**
921  * @class Number
922  */
923 Roo.applyIf(Number.prototype, {
924     /**
925      * Checks whether or not the current number is within a desired range.  If the number is already within the
926      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
927      * exceeded.  Note that this method returns the constrained value but does not change the current number.
928      * @param {Number} min The minimum number in the range
929      * @param {Number} max The maximum number in the range
930      * @return {Number} The constrained value if outside the range, otherwise the current value
931      */
932     constrain : function(min, max){
933         return Math.min(Math.max(this, min), max);
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  * @class Array
947  */
948 Roo.applyIf(Array.prototype, {
949     /**
950      * 
951      * Checks whether or not the specified object exists in the array.
952      * @param {Object} o The object to check for
953      * @return {Number} The index of o in the array (or -1 if it is not found)
954      */
955     indexOf : function(o){
956        for (var i = 0, len = this.length; i < len; i++){
957               if(this[i] == o) { return i; }
958        }
959            return -1;
960     },
961
962     /**
963      * Removes the specified object from the array.  If the object is not found nothing happens.
964      * @param {Object} o The object to remove
965      */
966     remove : function(o){
967        var index = this.indexOf(o);
968        if(index != -1){
969            this.splice(index, 1);
970        }
971     },
972     /**
973      * Map (JS 1.6 compatibility)
974      * @param {Function} function  to call
975      */
976     map : function(fun )
977     {
978         var len = this.length >>> 0;
979         if (typeof fun != "function") {
980             throw new TypeError();
981         }
982         var res = new Array(len);
983         var thisp = arguments[1];
984         for (var i = 0; i < len; i++)
985         {
986             if (i in this) {
987                 res[i] = fun.call(thisp, this[i], i, this);
988             }
989         }
990
991         return res;
992     }
993     
994 });
995
996
997  
998 /*
999  * Based on:
1000  * Ext JS Library 1.1.1
1001  * Copyright(c) 2006-2007, Ext JS, LLC.
1002  *
1003  * Originally Released Under LGPL - original licence link has changed is not relivant.
1004  *
1005  * Fork - LGPL
1006  * <script type="text/javascript">
1007  */
1008
1009 /**
1010  * @class Date
1011  *
1012  * The date parsing and format syntax is a subset of
1013  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1014  * supported will provide results equivalent to their PHP versions.
1015  *
1016  * Following is the list of all currently supported formats:
1017  *<pre>
1018 Sample date:
1019 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1020
1021 Format  Output      Description
1022 ------  ----------  --------------------------------------------------------------
1023   d      10         Day of the month, 2 digits with leading zeros
1024   D      Wed        A textual representation of a day, three letters
1025   j      10         Day of the month without leading zeros
1026   l      Wednesday  A full textual representation of the day of the week
1027   S      th         English ordinal day of month suffix, 2 chars (use with j)
1028   w      3          Numeric representation of the day of the week
1029   z      9          The julian date, or day of the year (0-365)
1030   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1031   F      January    A full textual representation of the month
1032   m      01         Numeric representation of a month, with leading zeros
1033   M      Jan        Month name abbreviation, three letters
1034   n      1          Numeric representation of a month, without leading zeros
1035   t      31         Number of days in the given month
1036   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1037   Y      2007       A full numeric representation of a year, 4 digits
1038   y      07         A two digit representation of a year
1039   a      pm         Lowercase Ante meridiem and Post meridiem
1040   A      PM         Uppercase Ante meridiem and Post meridiem
1041   g      3          12-hour format of an hour without leading zeros
1042   G      15         24-hour format of an hour without leading zeros
1043   h      03         12-hour format of an hour with leading zeros
1044   H      15         24-hour format of an hour with leading zeros
1045   i      05         Minutes with leading zeros
1046   s      01         Seconds, with leading zeros
1047   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1048   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1049   T      CST        Timezone setting of the machine running the code
1050   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1051 </pre>
1052  *
1053  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1054  * <pre><code>
1055 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1056 document.write(dt.format('Y-m-d'));                         //2007-01-10
1057 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1058 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
1059  </code></pre>
1060  *
1061  * Here are some standard date/time patterns that you might find helpful.  They
1062  * are not part of the source of Date.js, but to use them you can simply copy this
1063  * block of code into any script that is included after Date.js and they will also become
1064  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1065  * <pre><code>
1066 Date.patterns = {
1067     ISO8601Long:"Y-m-d H:i:s",
1068     ISO8601Short:"Y-m-d",
1069     ShortDate: "n/j/Y",
1070     LongDate: "l, F d, Y",
1071     FullDateTime: "l, F d, Y g:i:s A",
1072     MonthDay: "F d",
1073     ShortTime: "g:i A",
1074     LongTime: "g:i:s A",
1075     SortableDateTime: "Y-m-d\\TH:i:s",
1076     UniversalSortableDateTime: "Y-m-d H:i:sO",
1077     YearMonth: "F, Y"
1078 };
1079 </code></pre>
1080  *
1081  * Example usage:
1082  * <pre><code>
1083 var dt = new Date();
1084 document.write(dt.format(Date.patterns.ShortDate));
1085  </code></pre>
1086  */
1087
1088 /*
1089  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1090  * They generate precompiled functions from date formats instead of parsing and
1091  * processing the pattern every time you format a date.  These functions are available
1092  * on every Date object (any javascript function).
1093  *
1094  * The original article and download are here:
1095  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1096  *
1097  */
1098  
1099  
1100  // was in core
1101 /**
1102  Returns the number of milliseconds between this date and date
1103  @param {Date} date (optional) Defaults to now
1104  @return {Number} The diff in milliseconds
1105  @member Date getElapsed
1106  */
1107 Date.prototype.getElapsed = function(date) {
1108         return Math.abs((date || new Date()).getTime()-this.getTime());
1109 };
1110 // was in date file..
1111
1112
1113 // private
1114 Date.parseFunctions = {count:0};
1115 // private
1116 Date.parseRegexes = [];
1117 // private
1118 Date.formatFunctions = {count:0};
1119
1120 // private
1121 Date.prototype.dateFormat = function(format) {
1122     if (Date.formatFunctions[format] == null) {
1123         Date.createNewFormat(format);
1124     }
1125     var func = Date.formatFunctions[format];
1126     return this[func]();
1127 };
1128
1129
1130 /**
1131  * Formats a date given the supplied format string
1132  * @param {String} format The format string
1133  * @return {String} The formatted date
1134  * @method
1135  */
1136 Date.prototype.format = Date.prototype.dateFormat;
1137
1138 // private
1139 Date.createNewFormat = function(format) {
1140     var funcName = "format" + Date.formatFunctions.count++;
1141     Date.formatFunctions[format] = funcName;
1142     var code = "Date.prototype." + funcName + " = function(){return ";
1143     var special = false;
1144     var ch = '';
1145     for (var i = 0; i < format.length; ++i) {
1146         ch = format.charAt(i);
1147         if (!special && ch == "\\") {
1148             special = true;
1149         }
1150         else if (special) {
1151             special = false;
1152             code += "'" + String.escape(ch) + "' + ";
1153         }
1154         else {
1155             code += Date.getFormatCode(ch);
1156         }
1157     }
1158     /** eval:var:zzzzzzzzzzzzz */
1159     eval(code.substring(0, code.length - 3) + ";}");
1160 };
1161
1162 // private
1163 Date.getFormatCode = function(character) {
1164     switch (character) {
1165     case "d":
1166         return "String.leftPad(this.getDate(), 2, '0') + ";
1167     case "D":
1168         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1169     case "j":
1170         return "this.getDate() + ";
1171     case "l":
1172         return "Date.dayNames[this.getDay()] + ";
1173     case "S":
1174         return "this.getSuffix() + ";
1175     case "w":
1176         return "this.getDay() + ";
1177     case "z":
1178         return "this.getDayOfYear() + ";
1179     case "W":
1180         return "this.getWeekOfYear() + ";
1181     case "F":
1182         return "Date.monthNames[this.getMonth()] + ";
1183     case "m":
1184         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1185     case "M":
1186         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1187     case "n":
1188         return "(this.getMonth() + 1) + ";
1189     case "t":
1190         return "this.getDaysInMonth() + ";
1191     case "L":
1192         return "(this.isLeapYear() ? 1 : 0) + ";
1193     case "Y":
1194         return "this.getFullYear() + ";
1195     case "y":
1196         return "('' + this.getFullYear()).substring(2, 4) + ";
1197     case "a":
1198         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1199     case "A":
1200         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1201     case "g":
1202         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1203     case "G":
1204         return "this.getHours() + ";
1205     case "h":
1206         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1207     case "H":
1208         return "String.leftPad(this.getHours(), 2, '0') + ";
1209     case "i":
1210         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1211     case "s":
1212         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1213     case "O":
1214         return "this.getGMTOffset() + ";
1215     case "P":
1216         return "this.getGMTColonOffset() + ";
1217     case "T":
1218         return "this.getTimezone() + ";
1219     case "Z":
1220         return "(this.getTimezoneOffset() * -60) + ";
1221     default:
1222         return "'" + String.escape(character) + "' + ";
1223     }
1224 };
1225
1226 /**
1227  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1228  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1229  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1230  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1231  * string or the parse operation will fail.
1232  * Example Usage:
1233 <pre><code>
1234 //dt = Fri May 25 2007 (current date)
1235 var dt = new Date();
1236
1237 //dt = Thu May 25 2006 (today's month/day in 2006)
1238 dt = Date.parseDate("2006", "Y");
1239
1240 //dt = Sun Jan 15 2006 (all date parts specified)
1241 dt = Date.parseDate("2006-1-15", "Y-m-d");
1242
1243 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1244 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1245 </code></pre>
1246  * @param {String} input The unparsed date as a string
1247  * @param {String} format The format the date is in
1248  * @return {Date} The parsed date
1249  * @static
1250  */
1251 Date.parseDate = function(input, format) {
1252     if (Date.parseFunctions[format] == null) {
1253         Date.createParser(format);
1254     }
1255     var func = Date.parseFunctions[format];
1256     return Date[func](input);
1257 };
1258 /**
1259  * @private
1260  */
1261
1262 Date.createParser = function(format) {
1263     var funcName = "parse" + Date.parseFunctions.count++;
1264     var regexNum = Date.parseRegexes.length;
1265     var currentGroup = 1;
1266     Date.parseFunctions[format] = funcName;
1267
1268     var code = "Date." + funcName + " = function(input){\n"
1269         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1270         + "var d = new Date();\n"
1271         + "y = d.getFullYear();\n"
1272         + "m = d.getMonth();\n"
1273         + "d = d.getDate();\n"
1274         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1275         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1276         + "if (results && results.length > 0) {";
1277     var regex = "";
1278
1279     var special = false;
1280     var ch = '';
1281     for (var i = 0; i < format.length; ++i) {
1282         ch = format.charAt(i);
1283         if (!special && ch == "\\") {
1284             special = true;
1285         }
1286         else if (special) {
1287             special = false;
1288             regex += String.escape(ch);
1289         }
1290         else {
1291             var obj = Date.formatCodeToRegex(ch, currentGroup);
1292             currentGroup += obj.g;
1293             regex += obj.s;
1294             if (obj.g && obj.c) {
1295                 code += obj.c;
1296             }
1297         }
1298     }
1299
1300     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1301         + "{v = new Date(y, m, d, h, i, s);}\n"
1302         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1303         + "{v = new Date(y, m, d, h, i);}\n"
1304         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1305         + "{v = new Date(y, m, d, h);}\n"
1306         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1307         + "{v = new Date(y, m, d);}\n"
1308         + "else if (y >= 0 && m >= 0)\n"
1309         + "{v = new Date(y, m);}\n"
1310         + "else if (y >= 0)\n"
1311         + "{v = new Date(y);}\n"
1312         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1313         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1314         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1315         + ";}";
1316
1317     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1318     /** eval:var:zzzzzzzzzzzzz */
1319     eval(code);
1320 };
1321
1322 // private
1323 Date.formatCodeToRegex = function(character, currentGroup) {
1324     switch (character) {
1325     case "D":
1326         return {g:0,
1327         c:null,
1328         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1329     case "j":
1330         return {g:1,
1331             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1332             s:"(\\d{1,2})"}; // day of month without leading zeroes
1333     case "d":
1334         return {g:1,
1335             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1336             s:"(\\d{2})"}; // day of month with leading zeroes
1337     case "l":
1338         return {g:0,
1339             c:null,
1340             s:"(?:" + Date.dayNames.join("|") + ")"};
1341     case "S":
1342         return {g:0,
1343             c:null,
1344             s:"(?:st|nd|rd|th)"};
1345     case "w":
1346         return {g:0,
1347             c:null,
1348             s:"\\d"};
1349     case "z":
1350         return {g:0,
1351             c:null,
1352             s:"(?:\\d{1,3})"};
1353     case "W":
1354         return {g:0,
1355             c:null,
1356             s:"(?:\\d{2})"};
1357     case "F":
1358         return {g:1,
1359             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1360             s:"(" + Date.monthNames.join("|") + ")"};
1361     case "M":
1362         return {g:1,
1363             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1364             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1365     case "n":
1366         return {g:1,
1367             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1368             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1369     case "m":
1370         return {g:1,
1371             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1372             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1373     case "t":
1374         return {g:0,
1375             c:null,
1376             s:"\\d{1,2}"};
1377     case "L":
1378         return {g:0,
1379             c:null,
1380             s:"(?:1|0)"};
1381     case "Y":
1382         return {g:1,
1383             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1384             s:"(\\d{4})"};
1385     case "y":
1386         return {g:1,
1387             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1388                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1389             s:"(\\d{1,2})"};
1390     case "a":
1391         return {g:1,
1392             c:"if (results[" + currentGroup + "] == 'am') {\n"
1393                 + "if (h == 12) { h = 0; }\n"
1394                 + "} else { if (h < 12) { h += 12; }}",
1395             s:"(am|pm)"};
1396     case "A":
1397         return {g:1,
1398             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1399                 + "if (h == 12) { h = 0; }\n"
1400                 + "} else { if (h < 12) { h += 12; }}",
1401             s:"(AM|PM)"};
1402     case "g":
1403     case "G":
1404         return {g:1,
1405             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1406             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1407     case "h":
1408     case "H":
1409         return {g:1,
1410             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1411             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1412     case "i":
1413         return {g:1,
1414             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1415             s:"(\\d{2})"};
1416     case "s":
1417         return {g:1,
1418             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1419             s:"(\\d{2})"};
1420     case "O":
1421         return {g:1,
1422             c:[
1423                 "o = results[", currentGroup, "];\n",
1424                 "var sn = o.substring(0,1);\n", // get + / - sign
1425                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1426                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1427                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1428                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1429             ].join(""),
1430             s:"([+\-]\\d{2,4})"};
1431     
1432     
1433     case "P":
1434         return {g:1,
1435                 c:[
1436                    "o = results[", currentGroup, "];\n",
1437                    "var sn = o.substring(0,1);\n",
1438                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1439                    "var mn = o.substring(4,6) % 60;\n",
1440                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1441                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1442             ].join(""),
1443             s:"([+\-]\\d{4})"};
1444     case "T":
1445         return {g:0,
1446             c:null,
1447             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1448     case "Z":
1449         return {g:1,
1450             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1451                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1452             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1453     default:
1454         return {g:0,
1455             c:null,
1456             s:String.escape(character)};
1457     }
1458 };
1459
1460 /**
1461  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1462  * @return {String} The abbreviated timezone name (e.g. 'CST')
1463  */
1464 Date.prototype.getTimezone = function() {
1465     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1466 };
1467
1468 /**
1469  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1470  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1471  */
1472 Date.prototype.getGMTOffset = function() {
1473     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1474         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1475         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1476 };
1477
1478 /**
1479  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1480  * @return {String} 2-characters representing hours and 2-characters representing minutes
1481  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1482  */
1483 Date.prototype.getGMTColonOffset = function() {
1484         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1485                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1486                 + ":"
1487                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1488 }
1489
1490 /**
1491  * Get the numeric day number of the year, adjusted for leap year.
1492  * @return {Number} 0 through 364 (365 in leap years)
1493  */
1494 Date.prototype.getDayOfYear = function() {
1495     var num = 0;
1496     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1497     for (var i = 0; i < this.getMonth(); ++i) {
1498         num += Date.daysInMonth[i];
1499     }
1500     return num + this.getDate() - 1;
1501 };
1502
1503 /**
1504  * Get the string representation of the numeric week number of the year
1505  * (equivalent to the format specifier 'W').
1506  * @return {String} '00' through '52'
1507  */
1508 Date.prototype.getWeekOfYear = function() {
1509     // Skip to Thursday of this week
1510     var now = this.getDayOfYear() + (4 - this.getDay());
1511     // Find the first Thursday of the year
1512     var jan1 = new Date(this.getFullYear(), 0, 1);
1513     var then = (7 - jan1.getDay() + 4);
1514     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1515 };
1516
1517 /**
1518  * Whether or not the current date is in a leap year.
1519  * @return {Boolean} True if the current date is in a leap year, else false
1520  */
1521 Date.prototype.isLeapYear = function() {
1522     var year = this.getFullYear();
1523     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1524 };
1525
1526 /**
1527  * Get the first day of the current month, adjusted for leap year.  The returned value
1528  * is the numeric day index within the week (0-6) which can be used in conjunction with
1529  * the {@link #monthNames} array to retrieve the textual day name.
1530  * Example:
1531  *<pre><code>
1532 var dt = new Date('1/10/2007');
1533 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1534 </code></pre>
1535  * @return {Number} The day number (0-6)
1536  */
1537 Date.prototype.getFirstDayOfMonth = function() {
1538     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1539     return (day < 0) ? (day + 7) : day;
1540 };
1541
1542 /**
1543  * Get the last day of the current month, adjusted for leap year.  The returned value
1544  * is the numeric day index within the week (0-6) which can be used in conjunction with
1545  * the {@link #monthNames} array to retrieve the textual day name.
1546  * Example:
1547  *<pre><code>
1548 var dt = new Date('1/10/2007');
1549 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1550 </code></pre>
1551  * @return {Number} The day number (0-6)
1552  */
1553 Date.prototype.getLastDayOfMonth = function() {
1554     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1555     return (day < 0) ? (day + 7) : day;
1556 };
1557
1558
1559 /**
1560  * Get the first date of this date's month
1561  * @return {Date}
1562  */
1563 Date.prototype.getFirstDateOfMonth = function() {
1564     return new Date(this.getFullYear(), this.getMonth(), 1);
1565 };
1566
1567 /**
1568  * Get the last date of this date's month
1569  * @return {Date}
1570  */
1571 Date.prototype.getLastDateOfMonth = function() {
1572     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1573 };
1574 /**
1575  * Get the number of days in the current month, adjusted for leap year.
1576  * @return {Number} The number of days in the month
1577  */
1578 Date.prototype.getDaysInMonth = function() {
1579     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1580     return Date.daysInMonth[this.getMonth()];
1581 };
1582
1583 /**
1584  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1585  * @return {String} 'st, 'nd', 'rd' or 'th'
1586  */
1587 Date.prototype.getSuffix = function() {
1588     switch (this.getDate()) {
1589         case 1:
1590         case 21:
1591         case 31:
1592             return "st";
1593         case 2:
1594         case 22:
1595             return "nd";
1596         case 3:
1597         case 23:
1598             return "rd";
1599         default:
1600             return "th";
1601     }
1602 };
1603
1604 // private
1605 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1606
1607 /**
1608  * An array of textual month names.
1609  * Override these values for international dates, for example...
1610  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1611  * @type Array
1612  * @static
1613  */
1614 Date.monthNames =
1615    ["January",
1616     "February",
1617     "March",
1618     "April",
1619     "May",
1620     "June",
1621     "July",
1622     "August",
1623     "September",
1624     "October",
1625     "November",
1626     "December"];
1627
1628 /**
1629  * An array of textual day names.
1630  * Override these values for international dates, for example...
1631  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1632  * @type Array
1633  * @static
1634  */
1635 Date.dayNames =
1636    ["Sunday",
1637     "Monday",
1638     "Tuesday",
1639     "Wednesday",
1640     "Thursday",
1641     "Friday",
1642     "Saturday"];
1643
1644 // private
1645 Date.y2kYear = 50;
1646 // private
1647 Date.monthNumbers = {
1648     Jan:0,
1649     Feb:1,
1650     Mar:2,
1651     Apr:3,
1652     May:4,
1653     Jun:5,
1654     Jul:6,
1655     Aug:7,
1656     Sep:8,
1657     Oct:9,
1658     Nov:10,
1659     Dec:11};
1660
1661 /**
1662  * Creates and returns a new Date instance with the exact same date value as the called instance.
1663  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1664  * variable will also be changed.  When the intention is to create a new variable that will not
1665  * modify the original instance, you should create a clone.
1666  *
1667  * Example of correctly cloning a date:
1668  * <pre><code>
1669 //wrong way:
1670 var orig = new Date('10/1/2006');
1671 var copy = orig;
1672 copy.setDate(5);
1673 document.write(orig);  //returns 'Thu Oct 05 2006'!
1674
1675 //correct way:
1676 var orig = new Date('10/1/2006');
1677 var copy = orig.clone();
1678 copy.setDate(5);
1679 document.write(orig);  //returns 'Thu Oct 01 2006'
1680 </code></pre>
1681  * @return {Date} The new Date instance
1682  */
1683 Date.prototype.clone = function() {
1684         return new Date(this.getTime());
1685 };
1686
1687 /**
1688  * Clears any time information from this date
1689  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1690  @return {Date} this or the clone
1691  */
1692 Date.prototype.clearTime = function(clone){
1693     if(clone){
1694         return this.clone().clearTime();
1695     }
1696     this.setHours(0);
1697     this.setMinutes(0);
1698     this.setSeconds(0);
1699     this.setMilliseconds(0);
1700     return this;
1701 };
1702
1703 // private
1704 // safari setMonth is broken -- check that this is only donw once...
1705 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1706     Date.brokenSetMonth = Date.prototype.setMonth;
1707         Date.prototype.setMonth = function(num){
1708                 if(num <= -1){
1709                         var n = Math.ceil(-num);
1710                         var back_year = Math.ceil(n/12);
1711                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1712                         this.setFullYear(this.getFullYear() - back_year);
1713                         return Date.brokenSetMonth.call(this, month);
1714                 } else {
1715                         return Date.brokenSetMonth.apply(this, arguments);
1716                 }
1717         };
1718 }
1719
1720 /** Date interval constant 
1721 * @static 
1722 * @type String */
1723 Date.MILLI = "ms";
1724 /** Date interval constant 
1725 * @static 
1726 * @type String */
1727 Date.SECOND = "s";
1728 /** Date interval constant 
1729 * @static 
1730 * @type String */
1731 Date.MINUTE = "mi";
1732 /** Date interval constant 
1733 * @static 
1734 * @type String */
1735 Date.HOUR = "h";
1736 /** Date interval constant 
1737 * @static 
1738 * @type String */
1739 Date.DAY = "d";
1740 /** Date interval constant 
1741 * @static 
1742 * @type String */
1743 Date.MONTH = "mo";
1744 /** Date interval constant 
1745 * @static 
1746 * @type String */
1747 Date.YEAR = "y";
1748
1749 /**
1750  * Provides a convenient method of performing basic date arithmetic.  This method
1751  * does not modify the Date instance being called - it creates and returns
1752  * a new Date instance containing the resulting date value.
1753  *
1754  * Examples:
1755  * <pre><code>
1756 //Basic usage:
1757 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1758 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1759
1760 //Negative values will subtract correctly:
1761 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1762 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1763
1764 //You can even chain several calls together in one line!
1765 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1766 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1767  </code></pre>
1768  *
1769  * @param {String} interval   A valid date interval enum value
1770  * @param {Number} value      The amount to add to the current date
1771  * @return {Date} The new Date instance
1772  */
1773 Date.prototype.add = function(interval, value){
1774   var d = this.clone();
1775   if (!interval || value === 0) { return d; }
1776   switch(interval.toLowerCase()){
1777     case Date.MILLI:
1778       d.setMilliseconds(this.getMilliseconds() + value);
1779       break;
1780     case Date.SECOND:
1781       d.setSeconds(this.getSeconds() + value);
1782       break;
1783     case Date.MINUTE:
1784       d.setMinutes(this.getMinutes() + value);
1785       break;
1786     case Date.HOUR:
1787       d.setHours(this.getHours() + value);
1788       break;
1789     case Date.DAY:
1790       d.setDate(this.getDate() + value);
1791       break;
1792     case Date.MONTH:
1793       var day = this.getDate();
1794       if(day > 28){
1795           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1796       }
1797       d.setDate(day);
1798       d.setMonth(this.getMonth() + value);
1799       break;
1800     case Date.YEAR:
1801       d.setFullYear(this.getFullYear() + value);
1802       break;
1803   }
1804   return d;
1805 };
1806 /*
1807  * Based on:
1808  * Ext JS Library 1.1.1
1809  * Copyright(c) 2006-2007, Ext JS, LLC.
1810  *
1811  * Originally Released Under LGPL - original licence link has changed is not relivant.
1812  *
1813  * Fork - LGPL
1814  * <script type="text/javascript">
1815  */
1816
1817 /**
1818  * @class Roo.lib.Dom
1819  * @static
1820  * 
1821  * Dom utils (from YIU afaik)
1822  * 
1823  **/
1824 Roo.lib.Dom = {
1825     /**
1826      * Get the view width
1827      * @param {Boolean} full True will get the full document, otherwise it's the view width
1828      * @return {Number} The width
1829      */
1830      
1831     getViewWidth : function(full) {
1832         return full ? this.getDocumentWidth() : this.getViewportWidth();
1833     },
1834     /**
1835      * Get the view height
1836      * @param {Boolean} full True will get the full document, otherwise it's the view height
1837      * @return {Number} The height
1838      */
1839     getViewHeight : function(full) {
1840         return full ? this.getDocumentHeight() : this.getViewportHeight();
1841     },
1842
1843     getDocumentHeight: function() {
1844         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1845         return Math.max(scrollHeight, this.getViewportHeight());
1846     },
1847
1848     getDocumentWidth: function() {
1849         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1850         return Math.max(scrollWidth, this.getViewportWidth());
1851     },
1852
1853     getViewportHeight: function() {
1854         var height = self.innerHeight;
1855         var mode = document.compatMode;
1856
1857         if ((mode || Roo.isIE) && !Roo.isOpera) {
1858             height = (mode == "CSS1Compat") ?
1859                      document.documentElement.clientHeight :
1860                      document.body.clientHeight;
1861         }
1862
1863         return height;
1864     },
1865
1866     getViewportWidth: function() {
1867         var width = self.innerWidth;
1868         var mode = document.compatMode;
1869
1870         if (mode || Roo.isIE) {
1871             width = (mode == "CSS1Compat") ?
1872                     document.documentElement.clientWidth :
1873                     document.body.clientWidth;
1874         }
1875         return width;
1876     },
1877
1878     isAncestor : function(p, c) {
1879         p = Roo.getDom(p);
1880         c = Roo.getDom(c);
1881         if (!p || !c) {
1882             return false;
1883         }
1884
1885         if (p.contains && !Roo.isSafari) {
1886             return p.contains(c);
1887         } else if (p.compareDocumentPosition) {
1888             return !!(p.compareDocumentPosition(c) & 16);
1889         } else {
1890             var parent = c.parentNode;
1891             while (parent) {
1892                 if (parent == p) {
1893                     return true;
1894                 }
1895                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1896                     return false;
1897                 }
1898                 parent = parent.parentNode;
1899             }
1900             return false;
1901         }
1902     },
1903
1904     getRegion : function(el) {
1905         return Roo.lib.Region.getRegion(el);
1906     },
1907
1908     getY : function(el) {
1909         return this.getXY(el)[1];
1910     },
1911
1912     getX : function(el) {
1913         return this.getXY(el)[0];
1914     },
1915
1916     getXY : function(el) {
1917         var p, pe, b, scroll, bd = document.body;
1918         el = Roo.getDom(el);
1919         var fly = Roo.lib.AnimBase.fly;
1920         if (el.getBoundingClientRect) {
1921             b = el.getBoundingClientRect();
1922             scroll = fly(document).getScroll();
1923             return [b.left + scroll.left, b.top + scroll.top];
1924         }
1925         var x = 0, y = 0;
1926
1927         p = el;
1928
1929         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1930
1931         while (p) {
1932
1933             x += p.offsetLeft;
1934             y += p.offsetTop;
1935
1936             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1937                 hasAbsolute = true;
1938             }
1939
1940             if (Roo.isGecko) {
1941                 pe = fly(p);
1942
1943                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1944                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1945
1946
1947                 x += bl;
1948                 y += bt;
1949
1950
1951                 if (p != el && pe.getStyle('overflow') != 'visible') {
1952                     x += bl;
1953                     y += bt;
1954                 }
1955             }
1956             p = p.offsetParent;
1957         }
1958
1959         if (Roo.isSafari && hasAbsolute) {
1960             x -= bd.offsetLeft;
1961             y -= bd.offsetTop;
1962         }
1963
1964         if (Roo.isGecko && !hasAbsolute) {
1965             var dbd = fly(bd);
1966             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1967             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1968         }
1969
1970         p = el.parentNode;
1971         while (p && p != bd) {
1972             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1973                 x -= p.scrollLeft;
1974                 y -= p.scrollTop;
1975             }
1976             p = p.parentNode;
1977         }
1978         return [x, y];
1979     },
1980  
1981   
1982
1983
1984     setXY : function(el, xy) {
1985         el = Roo.fly(el, '_setXY');
1986         el.position();
1987         var pts = el.translatePoints(xy);
1988         if (xy[0] !== false) {
1989             el.dom.style.left = pts.left + "px";
1990         }
1991         if (xy[1] !== false) {
1992             el.dom.style.top = pts.top + "px";
1993         }
1994     },
1995
1996     setX : function(el, x) {
1997         this.setXY(el, [x, false]);
1998     },
1999
2000     setY : function(el, y) {
2001         this.setXY(el, [false, y]);
2002     }
2003 };
2004 /*
2005  * Portions of this file are based on pieces of Yahoo User Interface Library
2006  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2007  * YUI licensed under the BSD License:
2008  * http://developer.yahoo.net/yui/license.txt
2009  * <script type="text/javascript">
2010  *
2011  */
2012
2013 Roo.lib.Event = function() {
2014     var loadComplete = false;
2015     var listeners = [];
2016     var unloadListeners = [];
2017     var retryCount = 0;
2018     var onAvailStack = [];
2019     var counter = 0;
2020     var lastError = null;
2021
2022     return {
2023         POLL_RETRYS: 200,
2024         POLL_INTERVAL: 20,
2025         EL: 0,
2026         TYPE: 1,
2027         FN: 2,
2028         WFN: 3,
2029         OBJ: 3,
2030         ADJ_SCOPE: 4,
2031         _interval: null,
2032
2033         startInterval: function() {
2034             if (!this._interval) {
2035                 var self = this;
2036                 var callback = function() {
2037                     self._tryPreloadAttach();
2038                 };
2039                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2040
2041             }
2042         },
2043
2044         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2045             onAvailStack.push({ id:         p_id,
2046                 fn:         p_fn,
2047                 obj:        p_obj,
2048                 override:   p_override,
2049                 checkReady: false    });
2050
2051             retryCount = this.POLL_RETRYS;
2052             this.startInterval();
2053         },
2054
2055
2056         addListener: function(el, eventName, fn) {
2057             el = Roo.getDom(el);
2058             if (!el || !fn) {
2059                 return false;
2060             }
2061
2062             if ("unload" == eventName) {
2063                 unloadListeners[unloadListeners.length] =
2064                 [el, eventName, fn];
2065                 return true;
2066             }
2067
2068             var wrappedFn = function(e) {
2069                 return fn(Roo.lib.Event.getEvent(e));
2070             };
2071
2072             var li = [el, eventName, fn, wrappedFn];
2073
2074             var index = listeners.length;
2075             listeners[index] = li;
2076
2077             this.doAdd(el, eventName, wrappedFn, false);
2078             return true;
2079
2080         },
2081
2082
2083         removeListener: function(el, eventName, fn) {
2084             var i, len;
2085
2086             el = Roo.getDom(el);
2087
2088             if(!fn) {
2089                 return this.purgeElement(el, false, eventName);
2090             }
2091
2092
2093             if ("unload" == eventName) {
2094
2095                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2096                     var li = unloadListeners[i];
2097                     if (li &&
2098                         li[0] == el &&
2099                         li[1] == eventName &&
2100                         li[2] == fn) {
2101                         unloadListeners.splice(i, 1);
2102                         return true;
2103                     }
2104                 }
2105
2106                 return false;
2107             }
2108
2109             var cacheItem = null;
2110
2111
2112             var index = arguments[3];
2113
2114             if ("undefined" == typeof index) {
2115                 index = this._getCacheIndex(el, eventName, fn);
2116             }
2117
2118             if (index >= 0) {
2119                 cacheItem = listeners[index];
2120             }
2121
2122             if (!el || !cacheItem) {
2123                 return false;
2124             }
2125
2126             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2127
2128             delete listeners[index][this.WFN];
2129             delete listeners[index][this.FN];
2130             listeners.splice(index, 1);
2131
2132             return true;
2133
2134         },
2135
2136
2137         getTarget: function(ev, resolveTextNode) {
2138             ev = ev.browserEvent || ev;
2139             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2140             var t = ev.target || ev.srcElement;
2141             return this.resolveTextNode(t);
2142         },
2143
2144
2145         resolveTextNode: function(node) {
2146             if (Roo.isSafari && node && 3 == node.nodeType) {
2147                 return node.parentNode;
2148             } else {
2149                 return node;
2150             }
2151         },
2152
2153
2154         getPageX: function(ev) {
2155             ev = ev.browserEvent || ev;
2156             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2157             var x = ev.pageX;
2158             if (!x && 0 !== x) {
2159                 x = ev.clientX || 0;
2160
2161                 if (Roo.isIE) {
2162                     x += this.getScroll()[1];
2163                 }
2164             }
2165
2166             return x;
2167         },
2168
2169
2170         getPageY: function(ev) {
2171             ev = ev.browserEvent || ev;
2172             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2173             var y = ev.pageY;
2174             if (!y && 0 !== y) {
2175                 y = ev.clientY || 0;
2176
2177                 if (Roo.isIE) {
2178                     y += this.getScroll()[0];
2179                 }
2180             }
2181
2182
2183             return y;
2184         },
2185
2186
2187         getXY: function(ev) {
2188             ev = ev.browserEvent || ev;
2189             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2190             return [this.getPageX(ev), this.getPageY(ev)];
2191         },
2192
2193
2194         getRelatedTarget: function(ev) {
2195             ev = ev.browserEvent || ev;
2196             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2197             var t = ev.relatedTarget;
2198             if (!t) {
2199                 if (ev.type == "mouseout") {
2200                     t = ev.toElement;
2201                 } else if (ev.type == "mouseover") {
2202                     t = ev.fromElement;
2203                 }
2204             }
2205
2206             return this.resolveTextNode(t);
2207         },
2208
2209
2210         getTime: function(ev) {
2211             ev = ev.browserEvent || ev;
2212             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2213             if (!ev.time) {
2214                 var t = new Date().getTime();
2215                 try {
2216                     ev.time = t;
2217                 } catch(ex) {
2218                     this.lastError = ex;
2219                     return t;
2220                 }
2221             }
2222
2223             return ev.time;
2224         },
2225
2226
2227         stopEvent: function(ev) {
2228             this.stopPropagation(ev);
2229             this.preventDefault(ev);
2230         },
2231
2232
2233         stopPropagation: function(ev) {
2234             ev = ev.browserEvent || ev;
2235             if (ev.stopPropagation) {
2236                 ev.stopPropagation();
2237             } else {
2238                 ev.cancelBubble = true;
2239             }
2240         },
2241
2242
2243         preventDefault: function(ev) {
2244             ev = ev.browserEvent || ev;
2245             if(ev.preventDefault) {
2246                 ev.preventDefault();
2247             } else {
2248                 ev.returnValue = false;
2249             }
2250         },
2251
2252
2253         getEvent: function(e) {
2254             var ev = e || window.event;
2255             if (!ev) {
2256                 var c = this.getEvent.caller;
2257                 while (c) {
2258                     ev = c.arguments[0];
2259                     if (ev && Event == ev.constructor) {
2260                         break;
2261                     }
2262                     c = c.caller;
2263                 }
2264             }
2265             return ev;
2266         },
2267
2268
2269         getCharCode: function(ev) {
2270             ev = ev.browserEvent || ev;
2271             return ev.charCode || ev.keyCode || 0;
2272         },
2273
2274
2275         _getCacheIndex: function(el, eventName, fn) {
2276             for (var i = 0,len = listeners.length; i < len; ++i) {
2277                 var li = listeners[i];
2278                 if (li &&
2279                     li[this.FN] == fn &&
2280                     li[this.EL] == el &&
2281                     li[this.TYPE] == eventName) {
2282                     return i;
2283                 }
2284             }
2285
2286             return -1;
2287         },
2288
2289
2290         elCache: {},
2291
2292
2293         getEl: function(id) {
2294             return document.getElementById(id);
2295         },
2296
2297
2298         clearCache: function() {
2299         },
2300
2301
2302         _load: function(e) {
2303             loadComplete = true;
2304             var EU = Roo.lib.Event;
2305
2306
2307             if (Roo.isIE) {
2308                 EU.doRemove(window, "load", EU._load);
2309             }
2310         },
2311
2312
2313         _tryPreloadAttach: function() {
2314
2315             if (this.locked) {
2316                 return false;
2317             }
2318
2319             this.locked = true;
2320
2321
2322             var tryAgain = !loadComplete;
2323             if (!tryAgain) {
2324                 tryAgain = (retryCount > 0);
2325             }
2326
2327
2328             var notAvail = [];
2329             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2330                 var item = onAvailStack[i];
2331                 if (item) {
2332                     var el = this.getEl(item.id);
2333
2334                     if (el) {
2335                         if (!item.checkReady ||
2336                             loadComplete ||
2337                             el.nextSibling ||
2338                             (document && document.body)) {
2339
2340                             var scope = el;
2341                             if (item.override) {
2342                                 if (item.override === true) {
2343                                     scope = item.obj;
2344                                 } else {
2345                                     scope = item.override;
2346                                 }
2347                             }
2348                             item.fn.call(scope, item.obj);
2349                             onAvailStack[i] = null;
2350                         }
2351                     } else {
2352                         notAvail.push(item);
2353                     }
2354                 }
2355             }
2356
2357             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2358
2359             if (tryAgain) {
2360
2361                 this.startInterval();
2362             } else {
2363                 clearInterval(this._interval);
2364                 this._interval = null;
2365             }
2366
2367             this.locked = false;
2368
2369             return true;
2370
2371         },
2372
2373
2374         purgeElement: function(el, recurse, eventName) {
2375             var elListeners = this.getListeners(el, eventName);
2376             if (elListeners) {
2377                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2378                     var l = elListeners[i];
2379                     this.removeListener(el, l.type, l.fn);
2380                 }
2381             }
2382
2383             if (recurse && el && el.childNodes) {
2384                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2385                     this.purgeElement(el.childNodes[i], recurse, eventName);
2386                 }
2387             }
2388         },
2389
2390
2391         getListeners: function(el, eventName) {
2392             var results = [], searchLists;
2393             if (!eventName) {
2394                 searchLists = [listeners, unloadListeners];
2395             } else if (eventName == "unload") {
2396                 searchLists = [unloadListeners];
2397             } else {
2398                 searchLists = [listeners];
2399             }
2400
2401             for (var j = 0; j < searchLists.length; ++j) {
2402                 var searchList = searchLists[j];
2403                 if (searchList && searchList.length > 0) {
2404                     for (var i = 0,len = searchList.length; i < len; ++i) {
2405                         var l = searchList[i];
2406                         if (l && l[this.EL] === el &&
2407                             (!eventName || eventName === l[this.TYPE])) {
2408                             results.push({
2409                                 type:   l[this.TYPE],
2410                                 fn:     l[this.FN],
2411                                 obj:    l[this.OBJ],
2412                                 adjust: l[this.ADJ_SCOPE],
2413                                 index:  i
2414                             });
2415                         }
2416                     }
2417                 }
2418             }
2419
2420             return (results.length) ? results : null;
2421         },
2422
2423
2424         _unload: function(e) {
2425
2426             var EU = Roo.lib.Event, i, j, l, len, index;
2427
2428             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2429                 l = unloadListeners[i];
2430                 if (l) {
2431                     var scope = window;
2432                     if (l[EU.ADJ_SCOPE]) {
2433                         if (l[EU.ADJ_SCOPE] === true) {
2434                             scope = l[EU.OBJ];
2435                         } else {
2436                             scope = l[EU.ADJ_SCOPE];
2437                         }
2438                     }
2439                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2440                     unloadListeners[i] = null;
2441                     l = null;
2442                     scope = null;
2443                 }
2444             }
2445
2446             unloadListeners = null;
2447
2448             if (listeners && listeners.length > 0) {
2449                 j = listeners.length;
2450                 while (j) {
2451                     index = j - 1;
2452                     l = listeners[index];
2453                     if (l) {
2454                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2455                                 l[EU.FN], index);
2456                     }
2457                     j = j - 1;
2458                 }
2459                 l = null;
2460
2461                 EU.clearCache();
2462             }
2463
2464             EU.doRemove(window, "unload", EU._unload);
2465
2466         },
2467
2468
2469         getScroll: function() {
2470             var dd = document.documentElement, db = document.body;
2471             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2472                 return [dd.scrollTop, dd.scrollLeft];
2473             } else if (db) {
2474                 return [db.scrollTop, db.scrollLeft];
2475             } else {
2476                 return [0, 0];
2477             }
2478         },
2479
2480
2481         doAdd: function () {
2482             if (window.addEventListener) {
2483                 return function(el, eventName, fn, capture) {
2484                     el.addEventListener(eventName, fn, (capture));
2485                 };
2486             } else if (window.attachEvent) {
2487                 return function(el, eventName, fn, capture) {
2488                     el.attachEvent("on" + eventName, fn);
2489                 };
2490             } else {
2491                 return function() {
2492                 };
2493             }
2494         }(),
2495
2496
2497         doRemove: function() {
2498             if (window.removeEventListener) {
2499                 return function (el, eventName, fn, capture) {
2500                     el.removeEventListener(eventName, fn, (capture));
2501                 };
2502             } else if (window.detachEvent) {
2503                 return function (el, eventName, fn) {
2504                     el.detachEvent("on" + eventName, fn);
2505                 };
2506             } else {
2507                 return function() {
2508                 };
2509             }
2510         }()
2511     };
2512     
2513 }();
2514 (function() {     
2515    
2516     var E = Roo.lib.Event;
2517     E.on = E.addListener;
2518     E.un = E.removeListener;
2519
2520     if (document && document.body) {
2521         E._load();
2522     } else {
2523         E.doAdd(window, "load", E._load);
2524     }
2525     E.doAdd(window, "unload", E._unload);
2526     E._tryPreloadAttach();
2527 })();
2528
2529 /*
2530  * Portions of this file are based on pieces of Yahoo User Interface Library
2531  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2532  * YUI licensed under the BSD License:
2533  * http://developer.yahoo.net/yui/license.txt
2534  * <script type="text/javascript">
2535  *
2536  */
2537
2538 (function() {
2539     /**
2540      * @class Roo.lib.Ajax
2541      *
2542      */
2543     Roo.lib.Ajax = {
2544         /**
2545          * @static 
2546          */
2547         request : function(method, uri, cb, data, options) {
2548             if(options){
2549                 var hs = options.headers;
2550                 if(hs){
2551                     for(var h in hs){
2552                         if(hs.hasOwnProperty(h)){
2553                             this.initHeader(h, hs[h], false);
2554                         }
2555                     }
2556                 }
2557                 if(options.xmlData){
2558                     this.initHeader('Content-Type', 'text/xml', false);
2559                     method = 'POST';
2560                     data = options.xmlData;
2561                 }
2562             }
2563
2564             return this.asyncRequest(method, uri, cb, data);
2565         },
2566
2567         serializeForm : function(form) {
2568             if(typeof form == 'string') {
2569                 form = (document.getElementById(form) || document.forms[form]);
2570             }
2571
2572             var el, name, val, disabled, data = '', hasSubmit = false;
2573             for (var i = 0; i < form.elements.length; i++) {
2574                 el = form.elements[i];
2575                 disabled = form.elements[i].disabled;
2576                 name = form.elements[i].name;
2577                 val = form.elements[i].value;
2578
2579                 if (!disabled && name){
2580                     switch (el.type)
2581                             {
2582                         case 'select-one':
2583                         case 'select-multiple':
2584                             for (var j = 0; j < el.options.length; j++) {
2585                                 if (el.options[j].selected) {
2586                                     if (Roo.isIE) {
2587                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2588                                     }
2589                                     else {
2590                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2591                                     }
2592                                 }
2593                             }
2594                             break;
2595                         case 'radio':
2596                         case 'checkbox':
2597                             if (el.checked) {
2598                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2599                             }
2600                             break;
2601                         case 'file':
2602
2603                         case undefined:
2604
2605                         case 'reset':
2606
2607                         case 'button':
2608
2609                             break;
2610                         case 'submit':
2611                             if(hasSubmit == false) {
2612                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2613                                 hasSubmit = true;
2614                             }
2615                             break;
2616                         default:
2617                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2618                             break;
2619                     }
2620                 }
2621             }
2622             data = data.substr(0, data.length - 1);
2623             return data;
2624         },
2625
2626         headers:{},
2627
2628         hasHeaders:false,
2629
2630         useDefaultHeader:true,
2631
2632         defaultPostHeader:'application/x-www-form-urlencoded',
2633
2634         useDefaultXhrHeader:true,
2635
2636         defaultXhrHeader:'XMLHttpRequest',
2637
2638         hasDefaultHeaders:true,
2639
2640         defaultHeaders:{},
2641
2642         poll:{},
2643
2644         timeout:{},
2645
2646         pollInterval:50,
2647
2648         transactionId:0,
2649
2650         setProgId:function(id)
2651         {
2652             this.activeX.unshift(id);
2653         },
2654
2655         setDefaultPostHeader:function(b)
2656         {
2657             this.useDefaultHeader = b;
2658         },
2659
2660         setDefaultXhrHeader:function(b)
2661         {
2662             this.useDefaultXhrHeader = b;
2663         },
2664
2665         setPollingInterval:function(i)
2666         {
2667             if (typeof i == 'number' && isFinite(i)) {
2668                 this.pollInterval = i;
2669             }
2670         },
2671
2672         createXhrObject:function(transactionId)
2673         {
2674             var obj,http;
2675             try
2676             {
2677
2678                 http = new XMLHttpRequest();
2679
2680                 obj = { conn:http, tId:transactionId };
2681             }
2682             catch(e)
2683             {
2684                 for (var i = 0; i < this.activeX.length; ++i) {
2685                     try
2686                     {
2687
2688                         http = new ActiveXObject(this.activeX[i]);
2689
2690                         obj = { conn:http, tId:transactionId };
2691                         break;
2692                     }
2693                     catch(e) {
2694                     }
2695                 }
2696             }
2697             finally
2698             {
2699                 return obj;
2700             }
2701         },
2702
2703         getConnectionObject:function()
2704         {
2705             var o;
2706             var tId = this.transactionId;
2707
2708             try
2709             {
2710                 o = this.createXhrObject(tId);
2711                 if (o) {
2712                     this.transactionId++;
2713                 }
2714             }
2715             catch(e) {
2716             }
2717             finally
2718             {
2719                 return o;
2720             }
2721         },
2722
2723         asyncRequest:function(method, uri, callback, postData)
2724         {
2725             var o = this.getConnectionObject();
2726
2727             if (!o) {
2728                 return null;
2729             }
2730             else {
2731                 o.conn.open(method, uri, true);
2732
2733                 if (this.useDefaultXhrHeader) {
2734                     if (!this.defaultHeaders['X-Requested-With']) {
2735                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2736                     }
2737                 }
2738
2739                 if(postData && this.useDefaultHeader){
2740                     this.initHeader('Content-Type', this.defaultPostHeader);
2741                 }
2742
2743                  if (this.hasDefaultHeaders || this.hasHeaders) {
2744                     this.setHeader(o);
2745                 }
2746
2747                 this.handleReadyState(o, callback);
2748                 o.conn.send(postData || null);
2749
2750                 return o;
2751             }
2752         },
2753
2754         handleReadyState:function(o, callback)
2755         {
2756             var oConn = this;
2757
2758             if (callback && callback.timeout) {
2759                 
2760                 this.timeout[o.tId] = window.setTimeout(function() {
2761                     oConn.abort(o, callback, true);
2762                 }, callback.timeout);
2763             }
2764
2765             this.poll[o.tId] = window.setInterval(
2766                     function() {
2767                         if (o.conn && o.conn.readyState == 4) {
2768                             window.clearInterval(oConn.poll[o.tId]);
2769                             delete oConn.poll[o.tId];
2770
2771                             if(callback && callback.timeout) {
2772                                 window.clearTimeout(oConn.timeout[o.tId]);
2773                                 delete oConn.timeout[o.tId];
2774                             }
2775
2776                             oConn.handleTransactionResponse(o, callback);
2777                         }
2778                     }
2779                     , this.pollInterval);
2780         },
2781
2782         handleTransactionResponse:function(o, callback, isAbort)
2783         {
2784
2785             if (!callback) {
2786                 this.releaseObject(o);
2787                 return;
2788             }
2789
2790             var httpStatus, responseObject;
2791
2792             try
2793             {
2794                 if (o.conn.status !== undefined && o.conn.status != 0) {
2795                     httpStatus = o.conn.status;
2796                 }
2797                 else {
2798                     httpStatus = 13030;
2799                 }
2800             }
2801             catch(e) {
2802
2803
2804                 httpStatus = 13030;
2805             }
2806
2807             if (httpStatus >= 200 && httpStatus < 300) {
2808                 responseObject = this.createResponseObject(o, callback.argument);
2809                 if (callback.success) {
2810                     if (!callback.scope) {
2811                         callback.success(responseObject);
2812                     }
2813                     else {
2814
2815
2816                         callback.success.apply(callback.scope, [responseObject]);
2817                     }
2818                 }
2819             }
2820             else {
2821                 switch (httpStatus) {
2822
2823                     case 12002:
2824                     case 12029:
2825                     case 12030:
2826                     case 12031:
2827                     case 12152:
2828                     case 13030:
2829                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2830                         if (callback.failure) {
2831                             if (!callback.scope) {
2832                                 callback.failure(responseObject);
2833                             }
2834                             else {
2835                                 callback.failure.apply(callback.scope, [responseObject]);
2836                             }
2837                         }
2838                         break;
2839                     default:
2840                         responseObject = this.createResponseObject(o, callback.argument);
2841                         if (callback.failure) {
2842                             if (!callback.scope) {
2843                                 callback.failure(responseObject);
2844                             }
2845                             else {
2846                                 callback.failure.apply(callback.scope, [responseObject]);
2847                             }
2848                         }
2849                 }
2850             }
2851
2852             this.releaseObject(o);
2853             responseObject = null;
2854         },
2855
2856         createResponseObject:function(o, callbackArg)
2857         {
2858             var obj = {};
2859             var headerObj = {};
2860
2861             try
2862             {
2863                 var headerStr = o.conn.getAllResponseHeaders();
2864                 var header = headerStr.split('\n');
2865                 for (var i = 0; i < header.length; i++) {
2866                     var delimitPos = header[i].indexOf(':');
2867                     if (delimitPos != -1) {
2868                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2869                     }
2870                 }
2871             }
2872             catch(e) {
2873             }
2874
2875             obj.tId = o.tId;
2876             obj.status = o.conn.status;
2877             obj.statusText = o.conn.statusText;
2878             obj.getResponseHeader = headerObj;
2879             obj.getAllResponseHeaders = headerStr;
2880             obj.responseText = o.conn.responseText;
2881             obj.responseXML = o.conn.responseXML;
2882
2883             if (typeof callbackArg !== undefined) {
2884                 obj.argument = callbackArg;
2885             }
2886
2887             return obj;
2888         },
2889
2890         createExceptionObject:function(tId, callbackArg, isAbort)
2891         {
2892             var COMM_CODE = 0;
2893             var COMM_ERROR = 'communication failure';
2894             var ABORT_CODE = -1;
2895             var ABORT_ERROR = 'transaction aborted';
2896
2897             var obj = {};
2898
2899             obj.tId = tId;
2900             if (isAbort) {
2901                 obj.status = ABORT_CODE;
2902                 obj.statusText = ABORT_ERROR;
2903             }
2904             else {
2905                 obj.status = COMM_CODE;
2906                 obj.statusText = COMM_ERROR;
2907             }
2908
2909             if (callbackArg) {
2910                 obj.argument = callbackArg;
2911             }
2912
2913             return obj;
2914         },
2915
2916         initHeader:function(label, value, isDefault)
2917         {
2918             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2919
2920             if (headerObj[label] === undefined) {
2921                 headerObj[label] = value;
2922             }
2923             else {
2924
2925
2926                 headerObj[label] = value + "," + headerObj[label];
2927             }
2928
2929             if (isDefault) {
2930                 this.hasDefaultHeaders = true;
2931             }
2932             else {
2933                 this.hasHeaders = true;
2934             }
2935         },
2936
2937
2938         setHeader:function(o)
2939         {
2940             if (this.hasDefaultHeaders) {
2941                 for (var prop in this.defaultHeaders) {
2942                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2943                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2944                     }
2945                 }
2946             }
2947
2948             if (this.hasHeaders) {
2949                 for (var prop in this.headers) {
2950                     if (this.headers.hasOwnProperty(prop)) {
2951                         o.conn.setRequestHeader(prop, this.headers[prop]);
2952                     }
2953                 }
2954                 this.headers = {};
2955                 this.hasHeaders = false;
2956             }
2957         },
2958
2959         resetDefaultHeaders:function() {
2960             delete this.defaultHeaders;
2961             this.defaultHeaders = {};
2962             this.hasDefaultHeaders = false;
2963         },
2964
2965         abort:function(o, callback, isTimeout)
2966         {
2967             if(this.isCallInProgress(o)) {
2968                 o.conn.abort();
2969                 window.clearInterval(this.poll[o.tId]);
2970                 delete this.poll[o.tId];
2971                 if (isTimeout) {
2972                     delete this.timeout[o.tId];
2973                 }
2974
2975                 this.handleTransactionResponse(o, callback, true);
2976
2977                 return true;
2978             }
2979             else {
2980                 return false;
2981             }
2982         },
2983
2984
2985         isCallInProgress:function(o)
2986         {
2987             if (o && o.conn) {
2988                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2989             }
2990             else {
2991
2992                 return false;
2993             }
2994         },
2995
2996
2997         releaseObject:function(o)
2998         {
2999
3000             o.conn = null;
3001
3002             o = null;
3003         },
3004
3005         activeX:[
3006         'MSXML2.XMLHTTP.3.0',
3007         'MSXML2.XMLHTTP',
3008         'Microsoft.XMLHTTP'
3009         ]
3010
3011
3012     };
3013 })();/*
3014  * Portions of this file are based on pieces of Yahoo User Interface Library
3015  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3016  * YUI licensed under the BSD License:
3017  * http://developer.yahoo.net/yui/license.txt
3018  * <script type="text/javascript">
3019  *
3020  */
3021
3022 Roo.lib.Region = function(t, r, b, l) {
3023     this.top = t;
3024     this[1] = t;
3025     this.right = r;
3026     this.bottom = b;
3027     this.left = l;
3028     this[0] = l;
3029 };
3030
3031
3032 Roo.lib.Region.prototype = {
3033     contains : function(region) {
3034         return ( region.left >= this.left &&
3035                  region.right <= this.right &&
3036                  region.top >= this.top &&
3037                  region.bottom <= this.bottom    );
3038
3039     },
3040
3041     getArea : function() {
3042         return ( (this.bottom - this.top) * (this.right - this.left) );
3043     },
3044
3045     intersect : function(region) {
3046         var t = Math.max(this.top, region.top);
3047         var r = Math.min(this.right, region.right);
3048         var b = Math.min(this.bottom, region.bottom);
3049         var l = Math.max(this.left, region.left);
3050
3051         if (b >= t && r >= l) {
3052             return new Roo.lib.Region(t, r, b, l);
3053         } else {
3054             return null;
3055         }
3056     },
3057     union : function(region) {
3058         var t = Math.min(this.top, region.top);
3059         var r = Math.max(this.right, region.right);
3060         var b = Math.max(this.bottom, region.bottom);
3061         var l = Math.min(this.left, region.left);
3062
3063         return new Roo.lib.Region(t, r, b, l);
3064     },
3065
3066     adjust : function(t, l, b, r) {
3067         this.top += t;
3068         this.left += l;
3069         this.right += r;
3070         this.bottom += b;
3071         return this;
3072     }
3073 };
3074
3075 Roo.lib.Region.getRegion = function(el) {
3076     var p = Roo.lib.Dom.getXY(el);
3077
3078     var t = p[1];
3079     var r = p[0] + el.offsetWidth;
3080     var b = p[1] + el.offsetHeight;
3081     var l = p[0];
3082
3083     return new Roo.lib.Region(t, r, b, l);
3084 };
3085 /*
3086  * Portions of this file are based on pieces of Yahoo User Interface Library
3087  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3088  * YUI licensed under the BSD License:
3089  * http://developer.yahoo.net/yui/license.txt
3090  * <script type="text/javascript">
3091  *
3092  */
3093 //@@dep Roo.lib.Region
3094
3095
3096 Roo.lib.Point = function(x, y) {
3097     if (x instanceof Array) {
3098         y = x[1];
3099         x = x[0];
3100     }
3101     this.x = this.right = this.left = this[0] = x;
3102     this.y = this.top = this.bottom = this[1] = y;
3103 };
3104
3105 Roo.lib.Point.prototype = new Roo.lib.Region();
3106 /*
3107  * Portions of this file are based on pieces of Yahoo User Interface Library
3108  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3109  * YUI licensed under the BSD License:
3110  * http://developer.yahoo.net/yui/license.txt
3111  * <script type="text/javascript">
3112  *
3113  */
3114  
3115 (function() {   
3116
3117     Roo.lib.Anim = {
3118         scroll : function(el, args, duration, easing, cb, scope) {
3119             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3120         },
3121
3122         motion : function(el, args, duration, easing, cb, scope) {
3123             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3124         },
3125
3126         color : function(el, args, duration, easing, cb, scope) {
3127             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3128         },
3129
3130         run : function(el, args, duration, easing, cb, scope, type) {
3131             type = type || Roo.lib.AnimBase;
3132             if (typeof easing == "string") {
3133                 easing = Roo.lib.Easing[easing];
3134             }
3135             var anim = new type(el, args, duration, easing);
3136             anim.animateX(function() {
3137                 Roo.callback(cb, scope);
3138             });
3139             return anim;
3140         }
3141     };
3142 })();/*
3143  * Portions of this file are based on pieces of Yahoo User Interface Library
3144  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3145  * YUI licensed under the BSD License:
3146  * http://developer.yahoo.net/yui/license.txt
3147  * <script type="text/javascript">
3148  *
3149  */
3150
3151 (function() {    
3152     var libFlyweight;
3153     
3154     function fly(el) {
3155         if (!libFlyweight) {
3156             libFlyweight = new Roo.Element.Flyweight();
3157         }
3158         libFlyweight.dom = el;
3159         return libFlyweight;
3160     }
3161
3162     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3163     
3164    
3165     
3166     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3167         if (el) {
3168             this.init(el, attributes, duration, method);
3169         }
3170     };
3171
3172     Roo.lib.AnimBase.fly = fly;
3173     
3174     
3175     
3176     Roo.lib.AnimBase.prototype = {
3177
3178         toString: function() {
3179             var el = this.getEl();
3180             var id = el.id || el.tagName;
3181             return ("Anim " + id);
3182         },
3183
3184         patterns: {
3185             noNegatives:        /width|height|opacity|padding/i,
3186             offsetAttribute:  /^((width|height)|(top|left))$/,
3187             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3188             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3189         },
3190
3191
3192         doMethod: function(attr, start, end) {
3193             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3194         },
3195
3196
3197         setAttribute: function(attr, val, unit) {
3198             if (this.patterns.noNegatives.test(attr)) {
3199                 val = (val > 0) ? val : 0;
3200             }
3201
3202             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3203         },
3204
3205
3206         getAttribute: function(attr) {
3207             var el = this.getEl();
3208             var val = fly(el).getStyle(attr);
3209
3210             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3211                 return parseFloat(val);
3212             }
3213
3214             var a = this.patterns.offsetAttribute.exec(attr) || [];
3215             var pos = !!( a[3] );
3216             var box = !!( a[2] );
3217
3218
3219             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3220                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3221             } else {
3222                 val = 0;
3223             }
3224
3225             return val;
3226         },
3227
3228
3229         getDefaultUnit: function(attr) {
3230             if (this.patterns.defaultUnit.test(attr)) {
3231                 return 'px';
3232             }
3233
3234             return '';
3235         },
3236
3237         animateX : function(callback, scope) {
3238             var f = function() {
3239                 this.onComplete.removeListener(f);
3240                 if (typeof callback == "function") {
3241                     callback.call(scope || this, this);
3242                 }
3243             };
3244             this.onComplete.addListener(f, this);
3245             this.animate();
3246         },
3247
3248
3249         setRuntimeAttribute: function(attr) {
3250             var start;
3251             var end;
3252             var attributes = this.attributes;
3253
3254             this.runtimeAttributes[attr] = {};
3255
3256             var isset = function(prop) {
3257                 return (typeof prop !== 'undefined');
3258             };
3259
3260             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3261                 return false;
3262             }
3263
3264             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3265
3266
3267             if (isset(attributes[attr]['to'])) {
3268                 end = attributes[attr]['to'];
3269             } else if (isset(attributes[attr]['by'])) {
3270                 if (start.constructor == Array) {
3271                     end = [];
3272                     for (var i = 0, len = start.length; i < len; ++i) {
3273                         end[i] = start[i] + attributes[attr]['by'][i];
3274                     }
3275                 } else {
3276                     end = start + attributes[attr]['by'];
3277                 }
3278             }
3279
3280             this.runtimeAttributes[attr].start = start;
3281             this.runtimeAttributes[attr].end = end;
3282
3283
3284             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3285         },
3286
3287
3288         init: function(el, attributes, duration, method) {
3289
3290             var isAnimated = false;
3291
3292
3293             var startTime = null;
3294
3295
3296             var actualFrames = 0;
3297
3298
3299             el = Roo.getDom(el);
3300
3301
3302             this.attributes = attributes || {};
3303
3304
3305             this.duration = duration || 1;
3306
3307
3308             this.method = method || Roo.lib.Easing.easeNone;
3309
3310
3311             this.useSeconds = true;
3312
3313
3314             this.currentFrame = 0;
3315
3316
3317             this.totalFrames = Roo.lib.AnimMgr.fps;
3318
3319
3320             this.getEl = function() {
3321                 return el;
3322             };
3323
3324
3325             this.isAnimated = function() {
3326                 return isAnimated;
3327             };
3328
3329
3330             this.getStartTime = function() {
3331                 return startTime;
3332             };
3333
3334             this.runtimeAttributes = {};
3335
3336
3337             this.animate = function() {
3338                 if (this.isAnimated()) {
3339                     return false;
3340                 }
3341
3342                 this.currentFrame = 0;
3343
3344                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3345
3346                 Roo.lib.AnimMgr.registerElement(this);
3347             };
3348
3349
3350             this.stop = function(finish) {
3351                 if (finish) {
3352                     this.currentFrame = this.totalFrames;
3353                     this._onTween.fire();
3354                 }
3355                 Roo.lib.AnimMgr.stop(this);
3356             };
3357
3358             var onStart = function() {
3359                 this.onStart.fire();
3360
3361                 this.runtimeAttributes = {};
3362                 for (var attr in this.attributes) {
3363                     this.setRuntimeAttribute(attr);
3364                 }
3365
3366                 isAnimated = true;
3367                 actualFrames = 0;
3368                 startTime = new Date();
3369             };
3370
3371
3372             var onTween = function() {
3373                 var data = {
3374                     duration: new Date() - this.getStartTime(),
3375                     currentFrame: this.currentFrame
3376                 };
3377
3378                 data.toString = function() {
3379                     return (
3380                             'duration: ' + data.duration +
3381                             ', currentFrame: ' + data.currentFrame
3382                             );
3383                 };
3384
3385                 this.onTween.fire(data);
3386
3387                 var runtimeAttributes = this.runtimeAttributes;
3388
3389                 for (var attr in runtimeAttributes) {
3390                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3391                 }
3392
3393                 actualFrames += 1;
3394             };
3395
3396             var onComplete = function() {
3397                 var actual_duration = (new Date() - startTime) / 1000 ;
3398
3399                 var data = {
3400                     duration: actual_duration,
3401                     frames: actualFrames,
3402                     fps: actualFrames / actual_duration
3403                 };
3404
3405                 data.toString = function() {
3406                     return (
3407                             'duration: ' + data.duration +
3408                             ', frames: ' + data.frames +
3409                             ', fps: ' + data.fps
3410                             );
3411                 };
3412
3413                 isAnimated = false;
3414                 actualFrames = 0;
3415                 this.onComplete.fire(data);
3416             };
3417
3418
3419             this._onStart = new Roo.util.Event(this);
3420             this.onStart = new Roo.util.Event(this);
3421             this.onTween = new Roo.util.Event(this);
3422             this._onTween = new Roo.util.Event(this);
3423             this.onComplete = new Roo.util.Event(this);
3424             this._onComplete = new Roo.util.Event(this);
3425             this._onStart.addListener(onStart);
3426             this._onTween.addListener(onTween);
3427             this._onComplete.addListener(onComplete);
3428         }
3429     };
3430 })();
3431 /*
3432  * Portions of this file are based on pieces of Yahoo User Interface Library
3433  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3434  * YUI licensed under the BSD License:
3435  * http://developer.yahoo.net/yui/license.txt
3436  * <script type="text/javascript">
3437  *
3438  */
3439
3440 Roo.lib.AnimMgr = new function() {
3441
3442     var thread = null;
3443
3444
3445     var queue = [];
3446
3447
3448     var tweenCount = 0;
3449
3450
3451     this.fps = 1000;
3452
3453
3454     this.delay = 1;
3455
3456
3457     this.registerElement = function(tween) {
3458         queue[queue.length] = tween;
3459         tweenCount += 1;
3460         tween._onStart.fire();
3461         this.start();
3462     };
3463
3464
3465     this.unRegister = function(tween, index) {
3466         tween._onComplete.fire();
3467         index = index || getIndex(tween);
3468         if (index != -1) {
3469             queue.splice(index, 1);
3470         }
3471
3472         tweenCount -= 1;
3473         if (tweenCount <= 0) {
3474             this.stop();
3475         }
3476     };
3477
3478
3479     this.start = function() {
3480         if (thread === null) {
3481             thread = setInterval(this.run, this.delay);
3482         }
3483     };
3484
3485
3486     this.stop = function(tween) {
3487         if (!tween) {
3488             clearInterval(thread);
3489
3490             for (var i = 0, len = queue.length; i < len; ++i) {
3491                 if (queue[0].isAnimated()) {
3492                     this.unRegister(queue[0], 0);
3493                 }
3494             }
3495
3496             queue = [];
3497             thread = null;
3498             tweenCount = 0;
3499         }
3500         else {
3501             this.unRegister(tween);
3502         }
3503     };
3504
3505
3506     this.run = function() {
3507         for (var i = 0, len = queue.length; i < len; ++i) {
3508             var tween = queue[i];
3509             if (!tween || !tween.isAnimated()) {
3510                 continue;
3511             }
3512
3513             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3514             {
3515                 tween.currentFrame += 1;
3516
3517                 if (tween.useSeconds) {
3518                     correctFrame(tween);
3519                 }
3520                 tween._onTween.fire();
3521             }
3522             else {
3523                 Roo.lib.AnimMgr.stop(tween, i);
3524             }
3525         }
3526     };
3527
3528     var getIndex = function(anim) {
3529         for (var i = 0, len = queue.length; i < len; ++i) {
3530             if (queue[i] == anim) {
3531                 return i;
3532             }
3533         }
3534         return -1;
3535     };
3536
3537
3538     var correctFrame = function(tween) {
3539         var frames = tween.totalFrames;
3540         var frame = tween.currentFrame;
3541         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3542         var elapsed = (new Date() - tween.getStartTime());
3543         var tweak = 0;
3544
3545         if (elapsed < tween.duration * 1000) {
3546             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3547         } else {
3548             tweak = frames - (frame + 1);
3549         }
3550         if (tweak > 0 && isFinite(tweak)) {
3551             if (tween.currentFrame + tweak >= frames) {
3552                 tweak = frames - (frame + 1);
3553             }
3554
3555             tween.currentFrame += tweak;
3556         }
3557     };
3558 };
3559
3560     /*
3561  * Portions of this file are based on pieces of Yahoo User Interface Library
3562  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3563  * YUI licensed under the BSD License:
3564  * http://developer.yahoo.net/yui/license.txt
3565  * <script type="text/javascript">
3566  *
3567  */
3568 Roo.lib.Bezier = new function() {
3569
3570         this.getPosition = function(points, t) {
3571             var n = points.length;
3572             var tmp = [];
3573
3574             for (var i = 0; i < n; ++i) {
3575                 tmp[i] = [points[i][0], points[i][1]];
3576             }
3577
3578             for (var j = 1; j < n; ++j) {
3579                 for (i = 0; i < n - j; ++i) {
3580                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3581                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3582                 }
3583             }
3584
3585             return [ tmp[0][0], tmp[0][1] ];
3586
3587         };
3588     };/*
3589  * Portions of this file are based on pieces of Yahoo User Interface Library
3590  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3591  * YUI licensed under the BSD License:
3592  * http://developer.yahoo.net/yui/license.txt
3593  * <script type="text/javascript">
3594  *
3595  */
3596 (function() {
3597
3598     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3599         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3600     };
3601
3602     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3603
3604     var fly = Roo.lib.AnimBase.fly;
3605     var Y = Roo.lib;
3606     var superclass = Y.ColorAnim.superclass;
3607     var proto = Y.ColorAnim.prototype;
3608
3609     proto.toString = function() {
3610         var el = this.getEl();
3611         var id = el.id || el.tagName;
3612         return ("ColorAnim " + id);
3613     };
3614
3615     proto.patterns.color = /color$/i;
3616     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3617     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3618     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3619     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3620
3621
3622     proto.parseColor = function(s) {
3623         if (s.length == 3) {
3624             return s;
3625         }
3626
3627         var c = this.patterns.hex.exec(s);
3628         if (c && c.length == 4) {
3629             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3630         }
3631
3632         c = this.patterns.rgb.exec(s);
3633         if (c && c.length == 4) {
3634             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3635         }
3636
3637         c = this.patterns.hex3.exec(s);
3638         if (c && c.length == 4) {
3639             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3640         }
3641
3642         return null;
3643     };
3644     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3645     proto.getAttribute = function(attr) {
3646         var el = this.getEl();
3647         if (this.patterns.color.test(attr)) {
3648             var val = fly(el).getStyle(attr);
3649
3650             if (this.patterns.transparent.test(val)) {
3651                 var parent = el.parentNode;
3652                 val = fly(parent).getStyle(attr);
3653
3654                 while (parent && this.patterns.transparent.test(val)) {
3655                     parent = parent.parentNode;
3656                     val = fly(parent).getStyle(attr);
3657                     if (parent.tagName.toUpperCase() == 'HTML') {
3658                         val = '#fff';
3659                     }
3660                 }
3661             }
3662         } else {
3663             val = superclass.getAttribute.call(this, attr);
3664         }
3665
3666         return val;
3667     };
3668     proto.getAttribute = function(attr) {
3669         var el = this.getEl();
3670         if (this.patterns.color.test(attr)) {
3671             var val = fly(el).getStyle(attr);
3672
3673             if (this.patterns.transparent.test(val)) {
3674                 var parent = el.parentNode;
3675                 val = fly(parent).getStyle(attr);
3676
3677                 while (parent && this.patterns.transparent.test(val)) {
3678                     parent = parent.parentNode;
3679                     val = fly(parent).getStyle(attr);
3680                     if (parent.tagName.toUpperCase() == 'HTML') {
3681                         val = '#fff';
3682                     }
3683                 }
3684             }
3685         } else {
3686             val = superclass.getAttribute.call(this, attr);
3687         }
3688
3689         return val;
3690     };
3691
3692     proto.doMethod = function(attr, start, end) {
3693         var val;
3694
3695         if (this.patterns.color.test(attr)) {
3696             val = [];
3697             for (var i = 0, len = start.length; i < len; ++i) {
3698                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3699             }
3700
3701             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3702         }
3703         else {
3704             val = superclass.doMethod.call(this, attr, start, end);
3705         }
3706
3707         return val;
3708     };
3709
3710     proto.setRuntimeAttribute = function(attr) {
3711         superclass.setRuntimeAttribute.call(this, attr);
3712
3713         if (this.patterns.color.test(attr)) {
3714             var attributes = this.attributes;
3715             var start = this.parseColor(this.runtimeAttributes[attr].start);
3716             var end = this.parseColor(this.runtimeAttributes[attr].end);
3717
3718             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3719                 end = this.parseColor(attributes[attr].by);
3720
3721                 for (var i = 0, len = start.length; i < len; ++i) {
3722                     end[i] = start[i] + end[i];
3723                 }
3724             }
3725
3726             this.runtimeAttributes[attr].start = start;
3727             this.runtimeAttributes[attr].end = end;
3728         }
3729     };
3730 })();
3731
3732 /*
3733  * Portions of this file are based on pieces of Yahoo User Interface Library
3734  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3735  * YUI licensed under the BSD License:
3736  * http://developer.yahoo.net/yui/license.txt
3737  * <script type="text/javascript">
3738  *
3739  */
3740 Roo.lib.Easing = {
3741
3742
3743     easeNone: function (t, b, c, d) {
3744         return c * t / d + b;
3745     },
3746
3747
3748     easeIn: function (t, b, c, d) {
3749         return c * (t /= d) * t + b;
3750     },
3751
3752
3753     easeOut: function (t, b, c, d) {
3754         return -c * (t /= d) * (t - 2) + b;
3755     },
3756
3757
3758     easeBoth: function (t, b, c, d) {
3759         if ((t /= d / 2) < 1) {
3760             return c / 2 * t * t + b;
3761         }
3762
3763         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3764     },
3765
3766
3767     easeInStrong: function (t, b, c, d) {
3768         return c * (t /= d) * t * t * t + b;
3769     },
3770
3771
3772     easeOutStrong: function (t, b, c, d) {
3773         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3774     },
3775
3776
3777     easeBothStrong: function (t, b, c, d) {
3778         if ((t /= d / 2) < 1) {
3779             return c / 2 * t * t * t * t + b;
3780         }
3781
3782         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3783     },
3784
3785
3786
3787     elasticIn: function (t, b, c, d, a, p) {
3788         if (t == 0) {
3789             return b;
3790         }
3791         if ((t /= d) == 1) {
3792             return b + c;
3793         }
3794         if (!p) {
3795             p = d * .3;
3796         }
3797
3798         if (!a || a < Math.abs(c)) {
3799             a = c;
3800             var s = p / 4;
3801         }
3802         else {
3803             var s = p / (2 * Math.PI) * Math.asin(c / a);
3804         }
3805
3806         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3807     },
3808
3809
3810     elasticOut: function (t, b, c, d, a, p) {
3811         if (t == 0) {
3812             return b;
3813         }
3814         if ((t /= d) == 1) {
3815             return b + c;
3816         }
3817         if (!p) {
3818             p = d * .3;
3819         }
3820
3821         if (!a || a < Math.abs(c)) {
3822             a = c;
3823             var s = p / 4;
3824         }
3825         else {
3826             var s = p / (2 * Math.PI) * Math.asin(c / a);
3827         }
3828
3829         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3830     },
3831
3832
3833     elasticBoth: function (t, b, c, d, a, p) {
3834         if (t == 0) {
3835             return b;
3836         }
3837
3838         if ((t /= d / 2) == 2) {
3839             return b + c;
3840         }
3841
3842         if (!p) {
3843             p = d * (.3 * 1.5);
3844         }
3845
3846         if (!a || a < Math.abs(c)) {
3847             a = c;
3848             var s = p / 4;
3849         }
3850         else {
3851             var s = p / (2 * Math.PI) * Math.asin(c / a);
3852         }
3853
3854         if (t < 1) {
3855             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3856                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3857         }
3858         return a * Math.pow(2, -10 * (t -= 1)) *
3859                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3860     },
3861
3862
3863
3864     backIn: function (t, b, c, d, s) {
3865         if (typeof s == 'undefined') {
3866             s = 1.70158;
3867         }
3868         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3869     },
3870
3871
3872     backOut: function (t, b, c, d, s) {
3873         if (typeof s == 'undefined') {
3874             s = 1.70158;
3875         }
3876         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3877     },
3878
3879
3880     backBoth: function (t, b, c, d, s) {
3881         if (typeof s == 'undefined') {
3882             s = 1.70158;
3883         }
3884
3885         if ((t /= d / 2 ) < 1) {
3886             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3887         }
3888         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3889     },
3890
3891
3892     bounceIn: function (t, b, c, d) {
3893         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3894     },
3895
3896
3897     bounceOut: function (t, b, c, d) {
3898         if ((t /= d) < (1 / 2.75)) {
3899             return c * (7.5625 * t * t) + b;
3900         } else if (t < (2 / 2.75)) {
3901             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3902         } else if (t < (2.5 / 2.75)) {
3903             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3904         }
3905         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3906     },
3907
3908
3909     bounceBoth: function (t, b, c, d) {
3910         if (t < d / 2) {
3911             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3912         }
3913         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3914     }
3915 };/*
3916  * Portions of this file are based on pieces of Yahoo User Interface Library
3917  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3918  * YUI licensed under the BSD License:
3919  * http://developer.yahoo.net/yui/license.txt
3920  * <script type="text/javascript">
3921  *
3922  */
3923     (function() {
3924         Roo.lib.Motion = function(el, attributes, duration, method) {
3925             if (el) {
3926                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3927             }
3928         };
3929
3930         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3931
3932
3933         var Y = Roo.lib;
3934         var superclass = Y.Motion.superclass;
3935         var proto = Y.Motion.prototype;
3936
3937         proto.toString = function() {
3938             var el = this.getEl();
3939             var id = el.id || el.tagName;
3940             return ("Motion " + id);
3941         };
3942
3943         proto.patterns.points = /^points$/i;
3944
3945         proto.setAttribute = function(attr, val, unit) {
3946             if (this.patterns.points.test(attr)) {
3947                 unit = unit || 'px';
3948                 superclass.setAttribute.call(this, 'left', val[0], unit);
3949                 superclass.setAttribute.call(this, 'top', val[1], unit);
3950             } else {
3951                 superclass.setAttribute.call(this, attr, val, unit);
3952             }
3953         };
3954
3955         proto.getAttribute = function(attr) {
3956             if (this.patterns.points.test(attr)) {
3957                 var val = [
3958                         superclass.getAttribute.call(this, 'left'),
3959                         superclass.getAttribute.call(this, 'top')
3960                         ];
3961             } else {
3962                 val = superclass.getAttribute.call(this, attr);
3963             }
3964
3965             return val;
3966         };
3967
3968         proto.doMethod = function(attr, start, end) {
3969             var val = null;
3970
3971             if (this.patterns.points.test(attr)) {
3972                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3973                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3974             } else {
3975                 val = superclass.doMethod.call(this, attr, start, end);
3976             }
3977             return val;
3978         };
3979
3980         proto.setRuntimeAttribute = function(attr) {
3981             if (this.patterns.points.test(attr)) {
3982                 var el = this.getEl();
3983                 var attributes = this.attributes;
3984                 var start;
3985                 var control = attributes['points']['control'] || [];
3986                 var end;
3987                 var i, len;
3988
3989                 if (control.length > 0 && !(control[0] instanceof Array)) {
3990                     control = [control];
3991                 } else {
3992                     var tmp = [];
3993                     for (i = 0,len = control.length; i < len; ++i) {
3994                         tmp[i] = control[i];
3995                     }
3996                     control = tmp;
3997                 }
3998
3999                 Roo.fly(el).position();
4000
4001                 if (isset(attributes['points']['from'])) {
4002                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4003                 }
4004                 else {
4005                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4006                 }
4007
4008                 start = this.getAttribute('points');
4009
4010
4011                 if (isset(attributes['points']['to'])) {
4012                     end = translateValues.call(this, attributes['points']['to'], start);
4013
4014                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4015                     for (i = 0,len = control.length; i < len; ++i) {
4016                         control[i] = translateValues.call(this, control[i], start);
4017                     }
4018
4019
4020                 } else if (isset(attributes['points']['by'])) {
4021                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4022
4023                     for (i = 0,len = control.length; i < len; ++i) {
4024                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4025                     }
4026                 }
4027
4028                 this.runtimeAttributes[attr] = [start];
4029
4030                 if (control.length > 0) {
4031                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4032                 }
4033
4034                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4035             }
4036             else {
4037                 superclass.setRuntimeAttribute.call(this, attr);
4038             }
4039         };
4040
4041         var translateValues = function(val, start) {
4042             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4043             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4044
4045             return val;
4046         };
4047
4048         var isset = function(prop) {
4049             return (typeof prop !== 'undefined');
4050         };
4051     })();
4052 /*
4053  * Portions of this file are based on pieces of Yahoo User Interface Library
4054  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4055  * YUI licensed under the BSD License:
4056  * http://developer.yahoo.net/yui/license.txt
4057  * <script type="text/javascript">
4058  *
4059  */
4060     (function() {
4061         Roo.lib.Scroll = function(el, attributes, duration, method) {
4062             if (el) {
4063                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4064             }
4065         };
4066
4067         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4068
4069
4070         var Y = Roo.lib;
4071         var superclass = Y.Scroll.superclass;
4072         var proto = Y.Scroll.prototype;
4073
4074         proto.toString = function() {
4075             var el = this.getEl();
4076             var id = el.id || el.tagName;
4077             return ("Scroll " + id);
4078         };
4079
4080         proto.doMethod = function(attr, start, end) {
4081             var val = null;
4082
4083             if (attr == 'scroll') {
4084                 val = [
4085                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4086                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4087                         ];
4088
4089             } else {
4090                 val = superclass.doMethod.call(this, attr, start, end);
4091             }
4092             return val;
4093         };
4094
4095         proto.getAttribute = function(attr) {
4096             var val = null;
4097             var el = this.getEl();
4098
4099             if (attr == 'scroll') {
4100                 val = [ el.scrollLeft, el.scrollTop ];
4101             } else {
4102                 val = superclass.getAttribute.call(this, attr);
4103             }
4104
4105             return val;
4106         };
4107
4108         proto.setAttribute = function(attr, val, unit) {
4109             var el = this.getEl();
4110
4111             if (attr == 'scroll') {
4112                 el.scrollLeft = val[0];
4113                 el.scrollTop = val[1];
4114             } else {
4115                 superclass.setAttribute.call(this, attr, val, unit);
4116             }
4117         };
4118     })();
4119 /*
4120  * Based on:
4121  * Ext JS Library 1.1.1
4122  * Copyright(c) 2006-2007, Ext JS, LLC.
4123  *
4124  * Originally Released Under LGPL - original licence link has changed is not relivant.
4125  *
4126  * Fork - LGPL
4127  * <script type="text/javascript">
4128  */
4129
4130
4131 // nasty IE9 hack - what a pile of crap that is..
4132
4133  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4134     Range.prototype.createContextualFragment = function (html) {
4135         var doc = window.document;
4136         var container = doc.createElement("div");
4137         container.innerHTML = html;
4138         var frag = doc.createDocumentFragment(), n;
4139         while ((n = container.firstChild)) {
4140             frag.appendChild(n);
4141         }
4142         return frag;
4143     };
4144 }
4145
4146 /**
4147  * @class Roo.DomHelper
4148  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4149  * 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>.
4150  * @singleton
4151  */
4152 Roo.DomHelper = function(){
4153     var tempTableEl = null;
4154     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4155     var tableRe = /^table|tbody|tr|td$/i;
4156     var xmlns = {};
4157     // build as innerHTML where available
4158     /** @ignore */
4159     var createHtml = function(o){
4160         if(typeof o == 'string'){
4161             return o;
4162         }
4163         var b = "";
4164         if(!o.tag){
4165             o.tag = "div";
4166         }
4167         b += "<" + o.tag;
4168         for(var attr in o){
4169             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4170             if(attr == "style"){
4171                 var s = o["style"];
4172                 if(typeof s == "function"){
4173                     s = s.call();
4174                 }
4175                 if(typeof s == "string"){
4176                     b += ' style="' + s + '"';
4177                 }else if(typeof s == "object"){
4178                     b += ' style="';
4179                     for(var key in s){
4180                         if(typeof s[key] != "function"){
4181                             b += key + ":" + s[key] + ";";
4182                         }
4183                     }
4184                     b += '"';
4185                 }
4186             }else{
4187                 if(attr == "cls"){
4188                     b += ' class="' + o["cls"] + '"';
4189                 }else if(attr == "htmlFor"){
4190                     b += ' for="' + o["htmlFor"] + '"';
4191                 }else{
4192                     b += " " + attr + '="' + o[attr] + '"';
4193                 }
4194             }
4195         }
4196         if(emptyTags.test(o.tag)){
4197             b += "/>";
4198         }else{
4199             b += ">";
4200             var cn = o.children || o.cn;
4201             if(cn){
4202                 //http://bugs.kde.org/show_bug.cgi?id=71506
4203                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4204                     for(var i = 0, len = cn.length; i < len; i++) {
4205                         b += createHtml(cn[i], b);
4206                     }
4207                 }else{
4208                     b += createHtml(cn, b);
4209                 }
4210             }
4211             if(o.html){
4212                 b += o.html;
4213             }
4214             b += "</" + o.tag + ">";
4215         }
4216         return b;
4217     };
4218
4219     // build as dom
4220     /** @ignore */
4221     var createDom = function(o, parentNode){
4222          
4223         // defininition craeted..
4224         var ns = false;
4225         if (o.ns && o.ns != 'html') {
4226                
4227             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4228                 xmlns[o.ns] = o.xmlns;
4229                 ns = o.xmlns;
4230             }
4231             if (typeof(xmlns[o.ns]) == 'undefined') {
4232                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4233             }
4234             ns = xmlns[o.ns];
4235         }
4236         
4237         
4238         if (typeof(o) == 'string') {
4239             return parentNode.appendChild(document.createTextNode(o));
4240         }
4241         o.tag = o.tag || div;
4242         if (o.ns && Roo.isIE) {
4243             ns = false;
4244             o.tag = o.ns + ':' + o.tag;
4245             
4246         }
4247         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4248         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4249         for(var attr in o){
4250             
4251             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4252                     attr == "style" || typeof o[attr] == "function") { continue; }
4253                     
4254             if(attr=="cls" && Roo.isIE){
4255                 el.className = o["cls"];
4256             }else{
4257                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4258                 else { 
4259                     el[attr] = o[attr];
4260                 }
4261             }
4262         }
4263         Roo.DomHelper.applyStyles(el, o.style);
4264         var cn = o.children || o.cn;
4265         if(cn){
4266             //http://bugs.kde.org/show_bug.cgi?id=71506
4267              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4268                 for(var i = 0, len = cn.length; i < len; i++) {
4269                     createDom(cn[i], el);
4270                 }
4271             }else{
4272                 createDom(cn, el);
4273             }
4274         }
4275         if(o.html){
4276             el.innerHTML = o.html;
4277         }
4278         if(parentNode){
4279            parentNode.appendChild(el);
4280         }
4281         return el;
4282     };
4283
4284     var ieTable = function(depth, s, h, e){
4285         tempTableEl.innerHTML = [s, h, e].join('');
4286         var i = -1, el = tempTableEl;
4287         while(++i < depth){
4288             el = el.firstChild;
4289         }
4290         return el;
4291     };
4292
4293     // kill repeat to save bytes
4294     var ts = '<table>',
4295         te = '</table>',
4296         tbs = ts+'<tbody>',
4297         tbe = '</tbody>'+te,
4298         trs = tbs + '<tr>',
4299         tre = '</tr>'+tbe;
4300
4301     /**
4302      * @ignore
4303      * Nasty code for IE's broken table implementation
4304      */
4305     var insertIntoTable = function(tag, where, el, html){
4306         if(!tempTableEl){
4307             tempTableEl = document.createElement('div');
4308         }
4309         var node;
4310         var before = null;
4311         if(tag == 'td'){
4312             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4313                 return;
4314             }
4315             if(where == 'beforebegin'){
4316                 before = el;
4317                 el = el.parentNode;
4318             } else{
4319                 before = el.nextSibling;
4320                 el = el.parentNode;
4321             }
4322             node = ieTable(4, trs, html, tre);
4323         }
4324         else if(tag == 'tr'){
4325             if(where == 'beforebegin'){
4326                 before = el;
4327                 el = el.parentNode;
4328                 node = ieTable(3, tbs, html, tbe);
4329             } else if(where == 'afterend'){
4330                 before = el.nextSibling;
4331                 el = el.parentNode;
4332                 node = ieTable(3, tbs, html, tbe);
4333             } else{ // INTO a TR
4334                 if(where == 'afterbegin'){
4335                     before = el.firstChild;
4336                 }
4337                 node = ieTable(4, trs, html, tre);
4338             }
4339         } else if(tag == 'tbody'){
4340             if(where == 'beforebegin'){
4341                 before = el;
4342                 el = el.parentNode;
4343                 node = ieTable(2, ts, html, te);
4344             } else if(where == 'afterend'){
4345                 before = el.nextSibling;
4346                 el = el.parentNode;
4347                 node = ieTable(2, ts, html, te);
4348             } else{
4349                 if(where == 'afterbegin'){
4350                     before = el.firstChild;
4351                 }
4352                 node = ieTable(3, tbs, html, tbe);
4353             }
4354         } else{ // TABLE
4355             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4356                 return;
4357             }
4358             if(where == 'afterbegin'){
4359                 before = el.firstChild;
4360             }
4361             node = ieTable(2, ts, html, te);
4362         }
4363         el.insertBefore(node, before);
4364         return node;
4365     };
4366
4367     return {
4368     /** True to force the use of DOM instead of html fragments @type Boolean */
4369     useDom : false,
4370
4371     /**
4372      * Returns the markup for the passed Element(s) config
4373      * @param {Object} o The Dom object spec (and children)
4374      * @return {String}
4375      */
4376     markup : function(o){
4377         return createHtml(o);
4378     },
4379
4380     /**
4381      * Applies a style specification to an element
4382      * @param {String/HTMLElement} el The element to apply styles to
4383      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4384      * a function which returns such a specification.
4385      */
4386     applyStyles : function(el, styles){
4387         if(styles){
4388            el = Roo.fly(el);
4389            if(typeof styles == "string"){
4390                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4391                var matches;
4392                while ((matches = re.exec(styles)) != null){
4393                    el.setStyle(matches[1], matches[2]);
4394                }
4395            }else if (typeof styles == "object"){
4396                for (var style in styles){
4397                   el.setStyle(style, styles[style]);
4398                }
4399            }else if (typeof styles == "function"){
4400                 Roo.DomHelper.applyStyles(el, styles.call());
4401            }
4402         }
4403     },
4404
4405     /**
4406      * Inserts an HTML fragment into the Dom
4407      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4408      * @param {HTMLElement} el The context element
4409      * @param {String} html The HTML fragmenet
4410      * @return {HTMLElement} The new node
4411      */
4412     insertHtml : function(where, el, html){
4413         where = where.toLowerCase();
4414         if(el.insertAdjacentHTML){
4415             if(tableRe.test(el.tagName)){
4416                 var rs;
4417                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4418                     return rs;
4419                 }
4420             }
4421             switch(where){
4422                 case "beforebegin":
4423                     el.insertAdjacentHTML('BeforeBegin', html);
4424                     return el.previousSibling;
4425                 case "afterbegin":
4426                     el.insertAdjacentHTML('AfterBegin', html);
4427                     return el.firstChild;
4428                 case "beforeend":
4429                     el.insertAdjacentHTML('BeforeEnd', html);
4430                     return el.lastChild;
4431                 case "afterend":
4432                     el.insertAdjacentHTML('AfterEnd', html);
4433                     return el.nextSibling;
4434             }
4435             throw 'Illegal insertion point -> "' + where + '"';
4436         }
4437         var range = el.ownerDocument.createRange();
4438         var frag;
4439         switch(where){
4440              case "beforebegin":
4441                 range.setStartBefore(el);
4442                 frag = range.createContextualFragment(html);
4443                 el.parentNode.insertBefore(frag, el);
4444                 return el.previousSibling;
4445              case "afterbegin":
4446                 if(el.firstChild){
4447                     range.setStartBefore(el.firstChild);
4448                     frag = range.createContextualFragment(html);
4449                     el.insertBefore(frag, el.firstChild);
4450                     return el.firstChild;
4451                 }else{
4452                     el.innerHTML = html;
4453                     return el.firstChild;
4454                 }
4455             case "beforeend":
4456                 if(el.lastChild){
4457                     range.setStartAfter(el.lastChild);
4458                     frag = range.createContextualFragment(html);
4459                     el.appendChild(frag);
4460                     return el.lastChild;
4461                 }else{
4462                     el.innerHTML = html;
4463                     return el.lastChild;
4464                 }
4465             case "afterend":
4466                 range.setStartAfter(el);
4467                 frag = range.createContextualFragment(html);
4468                 el.parentNode.insertBefore(frag, el.nextSibling);
4469                 return el.nextSibling;
4470             }
4471             throw 'Illegal insertion point -> "' + where + '"';
4472     },
4473
4474     /**
4475      * Creates new Dom element(s) and inserts them before el
4476      * @param {String/HTMLElement/Element} el The context element
4477      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4478      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4479      * @return {HTMLElement/Roo.Element} The new node
4480      */
4481     insertBefore : function(el, o, returnElement){
4482         return this.doInsert(el, o, returnElement, "beforeBegin");
4483     },
4484
4485     /**
4486      * Creates new Dom element(s) and inserts them after el
4487      * @param {String/HTMLElement/Element} el The context element
4488      * @param {Object} o The Dom object spec (and children)
4489      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4490      * @return {HTMLElement/Roo.Element} The new node
4491      */
4492     insertAfter : function(el, o, returnElement){
4493         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4494     },
4495
4496     /**
4497      * Creates new Dom element(s) and inserts them as the first child of el
4498      * @param {String/HTMLElement/Element} el The context element
4499      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4500      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4501      * @return {HTMLElement/Roo.Element} The new node
4502      */
4503     insertFirst : function(el, o, returnElement){
4504         return this.doInsert(el, o, returnElement, "afterBegin");
4505     },
4506
4507     // private
4508     doInsert : function(el, o, returnElement, pos, sibling){
4509         el = Roo.getDom(el);
4510         var newNode;
4511         if(this.useDom || o.ns){
4512             newNode = createDom(o, null);
4513             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4514         }else{
4515             var html = createHtml(o);
4516             newNode = this.insertHtml(pos, el, html);
4517         }
4518         return returnElement ? Roo.get(newNode, true) : newNode;
4519     },
4520
4521     /**
4522      * Creates new Dom element(s) and appends them to el
4523      * @param {String/HTMLElement/Element} el The context element
4524      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4525      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4526      * @return {HTMLElement/Roo.Element} The new node
4527      */
4528     append : function(el, o, returnElement){
4529         el = Roo.getDom(el);
4530         var newNode;
4531         if(this.useDom || o.ns){
4532             newNode = createDom(o, null);
4533             el.appendChild(newNode);
4534         }else{
4535             var html = createHtml(o);
4536             newNode = this.insertHtml("beforeEnd", el, html);
4537         }
4538         return returnElement ? Roo.get(newNode, true) : newNode;
4539     },
4540
4541     /**
4542      * Creates new Dom element(s) and overwrites the contents of el with them
4543      * @param {String/HTMLElement/Element} el The context element
4544      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4545      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4546      * @return {HTMLElement/Roo.Element} The new node
4547      */
4548     overwrite : function(el, o, returnElement){
4549         el = Roo.getDom(el);
4550         if (o.ns) {
4551           
4552             while (el.childNodes.length) {
4553                 el.removeChild(el.firstChild);
4554             }
4555             createDom(o, el);
4556         } else {
4557             el.innerHTML = createHtml(o);   
4558         }
4559         
4560         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4561     },
4562
4563     /**
4564      * Creates a new Roo.DomHelper.Template from the Dom object spec
4565      * @param {Object} o The Dom object spec (and children)
4566      * @return {Roo.DomHelper.Template} The new template
4567      */
4568     createTemplate : function(o){
4569         var html = createHtml(o);
4570         return new Roo.Template(html);
4571     }
4572     };
4573 }();
4574 /*
4575  * Based on:
4576  * Ext JS Library 1.1.1
4577  * Copyright(c) 2006-2007, Ext JS, LLC.
4578  *
4579  * Originally Released Under LGPL - original licence link has changed is not relivant.
4580  *
4581  * Fork - LGPL
4582  * <script type="text/javascript">
4583  */
4584  
4585 /**
4586 * @class Roo.Template
4587 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4588 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4589 * Usage:
4590 <pre><code>
4591 var t = new Roo.Template({
4592     html :  '&lt;div name="{id}"&gt;' + 
4593         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4594         '&lt;/div&gt;',
4595     myformat: function (value, allValues) {
4596         return 'XX' + value;
4597     }
4598 });
4599 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4600 </code></pre>
4601 * For more information see this blog post with examples:
4602 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4603      - Create Elements using DOM, HTML fragments and Templates</a>. 
4604 * @constructor
4605 * @param {Object} cfg - Configuration object.
4606 */
4607 Roo.Template = function(cfg){
4608     // BC!
4609     if(cfg instanceof Array){
4610         cfg = cfg.join("");
4611     }else if(arguments.length > 1){
4612         cfg = Array.prototype.join.call(arguments, "");
4613     }
4614     
4615     
4616     if (typeof(cfg) == 'object') {
4617         Roo.apply(this,cfg)
4618     } else {
4619         // bc
4620         this.html = cfg;
4621     }
4622     if (this.url) {
4623         this.load();
4624     }
4625     
4626 };
4627 Roo.Template.prototype = {
4628     
4629     /**
4630      * @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..
4631      *                    it should be fixed so that template is observable...
4632      */
4633     url : false,
4634     /**
4635      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4636      */
4637     html : '',
4638     /**
4639      * Returns an HTML fragment of this template with the specified values applied.
4640      * @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'})
4641      * @return {String} The HTML fragment
4642      */
4643     applyTemplate : function(values){
4644         try {
4645            
4646             if(this.compiled){
4647                 return this.compiled(values);
4648             }
4649             var useF = this.disableFormats !== true;
4650             var fm = Roo.util.Format, tpl = this;
4651             var fn = function(m, name, format, args){
4652                 if(format && useF){
4653                     if(format.substr(0, 5) == "this."){
4654                         return tpl.call(format.substr(5), values[name], values);
4655                     }else{
4656                         if(args){
4657                             // quoted values are required for strings in compiled templates, 
4658                             // but for non compiled we need to strip them
4659                             // quoted reversed for jsmin
4660                             var re = /^\s*['"](.*)["']\s*$/;
4661                             args = args.split(',');
4662                             for(var i = 0, len = args.length; i < len; i++){
4663                                 args[i] = args[i].replace(re, "$1");
4664                             }
4665                             args = [values[name]].concat(args);
4666                         }else{
4667                             args = [values[name]];
4668                         }
4669                         return fm[format].apply(fm, args);
4670                     }
4671                 }else{
4672                     return values[name] !== undefined ? values[name] : "";
4673                 }
4674             };
4675             return this.html.replace(this.re, fn);
4676         } catch (e) {
4677             Roo.log(e);
4678             throw e;
4679         }
4680          
4681     },
4682     
4683     loading : false,
4684       
4685     load : function ()
4686     {
4687          
4688         if (this.loading) {
4689             return;
4690         }
4691         var _t = this;
4692         
4693         this.loading = true;
4694         this.compiled = false;
4695         
4696         var cx = new Roo.data.Connection();
4697         cx.request({
4698             url : this.url,
4699             method : 'GET',
4700             success : function (response) {
4701                 _t.loading = false;
4702                 _t.html = response.responseText;
4703                 _t.url = false;
4704                 _t.compile();
4705              },
4706             failure : function(response) {
4707                 Roo.log("Template failed to load from " + _t.url);
4708                 _t.loading = false;
4709             }
4710         });
4711     },
4712
4713     /**
4714      * Sets the HTML used as the template and optionally compiles it.
4715      * @param {String} html
4716      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4717      * @return {Roo.Template} this
4718      */
4719     set : function(html, compile){
4720         this.html = html;
4721         this.compiled = null;
4722         if(compile){
4723             this.compile();
4724         }
4725         return this;
4726     },
4727     
4728     /**
4729      * True to disable format functions (defaults to false)
4730      * @type Boolean
4731      */
4732     disableFormats : false,
4733     
4734     /**
4735     * The regular expression used to match template variables 
4736     * @type RegExp
4737     * @property 
4738     */
4739     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4740     
4741     /**
4742      * Compiles the template into an internal function, eliminating the RegEx overhead.
4743      * @return {Roo.Template} this
4744      */
4745     compile : function(){
4746         var fm = Roo.util.Format;
4747         var useF = this.disableFormats !== true;
4748         var sep = Roo.isGecko ? "+" : ",";
4749         var fn = function(m, name, format, args){
4750             if(format && useF){
4751                 args = args ? ',' + args : "";
4752                 if(format.substr(0, 5) != "this."){
4753                     format = "fm." + format + '(';
4754                 }else{
4755                     format = 'this.call("'+ format.substr(5) + '", ';
4756                     args = ", values";
4757                 }
4758             }else{
4759                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4760             }
4761             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4762         };
4763         var body;
4764         // branched to use + in gecko and [].join() in others
4765         if(Roo.isGecko){
4766             body = "this.compiled = function(values){ return '" +
4767                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4768                     "';};";
4769         }else{
4770             body = ["this.compiled = function(values){ return ['"];
4771             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4772             body.push("'].join('');};");
4773             body = body.join('');
4774         }
4775         /**
4776          * eval:var:values
4777          * eval:var:fm
4778          */
4779         eval(body);
4780         return this;
4781     },
4782     
4783     // private function used to call members
4784     call : function(fnName, value, allValues){
4785         return this[fnName](value, allValues);
4786     },
4787     
4788     /**
4789      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4790      * @param {String/HTMLElement/Roo.Element} el The context element
4791      * @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'})
4792      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4793      * @return {HTMLElement/Roo.Element} The new node or Element
4794      */
4795     insertFirst: function(el, values, returnElement){
4796         return this.doInsert('afterBegin', el, values, returnElement);
4797     },
4798
4799     /**
4800      * Applies the supplied values to the template and inserts the new node(s) before el.
4801      * @param {String/HTMLElement/Roo.Element} el The context element
4802      * @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'})
4803      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4804      * @return {HTMLElement/Roo.Element} The new node or Element
4805      */
4806     insertBefore: function(el, values, returnElement){
4807         return this.doInsert('beforeBegin', el, values, returnElement);
4808     },
4809
4810     /**
4811      * Applies the supplied values to the template and inserts the new node(s) after el.
4812      * @param {String/HTMLElement/Roo.Element} el The context element
4813      * @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'})
4814      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4815      * @return {HTMLElement/Roo.Element} The new node or Element
4816      */
4817     insertAfter : function(el, values, returnElement){
4818         return this.doInsert('afterEnd', el, values, returnElement);
4819     },
4820     
4821     /**
4822      * Applies the supplied values to the template and appends the new node(s) to el.
4823      * @param {String/HTMLElement/Roo.Element} el The context element
4824      * @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'})
4825      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4826      * @return {HTMLElement/Roo.Element} The new node or Element
4827      */
4828     append : function(el, values, returnElement){
4829         return this.doInsert('beforeEnd', el, values, returnElement);
4830     },
4831
4832     doInsert : function(where, el, values, returnEl){
4833         el = Roo.getDom(el);
4834         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4835         return returnEl ? Roo.get(newNode, true) : newNode;
4836     },
4837
4838     /**
4839      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4840      * @param {String/HTMLElement/Roo.Element} el The context element
4841      * @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'})
4842      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4843      * @return {HTMLElement/Roo.Element} The new node or Element
4844      */
4845     overwrite : function(el, values, returnElement){
4846         el = Roo.getDom(el);
4847         el.innerHTML = this.applyTemplate(values);
4848         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4849     }
4850 };
4851 /**
4852  * Alias for {@link #applyTemplate}
4853  * @method
4854  */
4855 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4856
4857 // backwards compat
4858 Roo.DomHelper.Template = Roo.Template;
4859
4860 /**
4861  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4862  * @param {String/HTMLElement} el A DOM element or its id
4863  * @returns {Roo.Template} The created template
4864  * @static
4865  */
4866 Roo.Template.from = function(el){
4867     el = Roo.getDom(el);
4868     return new Roo.Template(el.value || el.innerHTML);
4869 };/*
4870  * Based on:
4871  * Ext JS Library 1.1.1
4872  * Copyright(c) 2006-2007, Ext JS, LLC.
4873  *
4874  * Originally Released Under LGPL - original licence link has changed is not relivant.
4875  *
4876  * Fork - LGPL
4877  * <script type="text/javascript">
4878  */
4879  
4880
4881 /*
4882  * This is code is also distributed under MIT license for use
4883  * with jQuery and prototype JavaScript libraries.
4884  */
4885 /**
4886  * @class Roo.DomQuery
4887 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).
4888 <p>
4889 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>
4890
4891 <p>
4892 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.
4893 </p>
4894 <h4>Element Selectors:</h4>
4895 <ul class="list">
4896     <li> <b>*</b> any element</li>
4897     <li> <b>E</b> an element with the tag E</li>
4898     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4899     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4900     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4901     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4902 </ul>
4903 <h4>Attribute Selectors:</h4>
4904 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4905 <ul class="list">
4906     <li> <b>E[foo]</b> has an attribute "foo"</li>
4907     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4908     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4909     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4910     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4911     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4912     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4913 </ul>
4914 <h4>Pseudo Classes:</h4>
4915 <ul class="list">
4916     <li> <b>E:first-child</b> E is the first child of its parent</li>
4917     <li> <b>E:last-child</b> E is the last child of its parent</li>
4918     <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>
4919     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4920     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4921     <li> <b>E:only-child</b> E is the only child of its parent</li>
4922     <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>
4923     <li> <b>E:first</b> the first E in the resultset</li>
4924     <li> <b>E:last</b> the last E in the resultset</li>
4925     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4926     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4927     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4928     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4929     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4930     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4931     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4932     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4933     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4934 </ul>
4935 <h4>CSS Value Selectors:</h4>
4936 <ul class="list">
4937     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4938     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4939     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4940     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4941     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4942     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4943 </ul>
4944  * @singleton
4945  */
4946 Roo.DomQuery = function(){
4947     var cache = {}, simpleCache = {}, valueCache = {};
4948     var nonSpace = /\S/;
4949     var trimRe = /^\s+|\s+$/g;
4950     var tplRe = /\{(\d+)\}/g;
4951     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4952     var tagTokenRe = /^(#)?([\w-\*]+)/;
4953     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4954
4955     function child(p, index){
4956         var i = 0;
4957         var n = p.firstChild;
4958         while(n){
4959             if(n.nodeType == 1){
4960                if(++i == index){
4961                    return n;
4962                }
4963             }
4964             n = n.nextSibling;
4965         }
4966         return null;
4967     };
4968
4969     function next(n){
4970         while((n = n.nextSibling) && n.nodeType != 1);
4971         return n;
4972     };
4973
4974     function prev(n){
4975         while((n = n.previousSibling) && n.nodeType != 1);
4976         return n;
4977     };
4978
4979     function children(d){
4980         var n = d.firstChild, ni = -1;
4981             while(n){
4982                 var nx = n.nextSibling;
4983                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4984                     d.removeChild(n);
4985                 }else{
4986                     n.nodeIndex = ++ni;
4987                 }
4988                 n = nx;
4989             }
4990             return this;
4991         };
4992
4993     function byClassName(c, a, v){
4994         if(!v){
4995             return c;
4996         }
4997         var r = [], ri = -1, cn;
4998         for(var i = 0, ci; ci = c[i]; i++){
4999             if((' '+ci.className+' ').indexOf(v) != -1){
5000                 r[++ri] = ci;
5001             }
5002         }
5003         return r;
5004     };
5005
5006     function attrValue(n, attr){
5007         if(!n.tagName && typeof n.length != "undefined"){
5008             n = n[0];
5009         }
5010         if(!n){
5011             return null;
5012         }
5013         if(attr == "for"){
5014             return n.htmlFor;
5015         }
5016         if(attr == "class" || attr == "className"){
5017             return n.className;
5018         }
5019         return n.getAttribute(attr) || n[attr];
5020
5021     };
5022
5023     function getNodes(ns, mode, tagName){
5024         var result = [], ri = -1, cs;
5025         if(!ns){
5026             return result;
5027         }
5028         tagName = tagName || "*";
5029         if(typeof ns.getElementsByTagName != "undefined"){
5030             ns = [ns];
5031         }
5032         if(!mode){
5033             for(var i = 0, ni; ni = ns[i]; i++){
5034                 cs = ni.getElementsByTagName(tagName);
5035                 for(var j = 0, ci; ci = cs[j]; j++){
5036                     result[++ri] = ci;
5037                 }
5038             }
5039         }else if(mode == "/" || mode == ">"){
5040             var utag = tagName.toUpperCase();
5041             for(var i = 0, ni, cn; ni = ns[i]; i++){
5042                 cn = ni.children || ni.childNodes;
5043                 for(var j = 0, cj; cj = cn[j]; j++){
5044                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5045                         result[++ri] = cj;
5046                     }
5047                 }
5048             }
5049         }else if(mode == "+"){
5050             var utag = tagName.toUpperCase();
5051             for(var i = 0, n; n = ns[i]; i++){
5052                 while((n = n.nextSibling) && n.nodeType != 1);
5053                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5054                     result[++ri] = n;
5055                 }
5056             }
5057         }else if(mode == "~"){
5058             for(var i = 0, n; n = ns[i]; i++){
5059                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5060                 if(n){
5061                     result[++ri] = n;
5062                 }
5063             }
5064         }
5065         return result;
5066     };
5067
5068     function concat(a, b){
5069         if(b.slice){
5070             return a.concat(b);
5071         }
5072         for(var i = 0, l = b.length; i < l; i++){
5073             a[a.length] = b[i];
5074         }
5075         return a;
5076     }
5077
5078     function byTag(cs, tagName){
5079         if(cs.tagName || cs == document){
5080             cs = [cs];
5081         }
5082         if(!tagName){
5083             return cs;
5084         }
5085         var r = [], ri = -1;
5086         tagName = tagName.toLowerCase();
5087         for(var i = 0, ci; ci = cs[i]; i++){
5088             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5089                 r[++ri] = ci;
5090             }
5091         }
5092         return r;
5093     };
5094
5095     function byId(cs, attr, id){
5096         if(cs.tagName || cs == document){
5097             cs = [cs];
5098         }
5099         if(!id){
5100             return cs;
5101         }
5102         var r = [], ri = -1;
5103         for(var i = 0,ci; ci = cs[i]; i++){
5104             if(ci && ci.id == id){
5105                 r[++ri] = ci;
5106                 return r;
5107             }
5108         }
5109         return r;
5110     };
5111
5112     function byAttribute(cs, attr, value, op, custom){
5113         var r = [], ri = -1, st = custom=="{";
5114         var f = Roo.DomQuery.operators[op];
5115         for(var i = 0, ci; ci = cs[i]; i++){
5116             var a;
5117             if(st){
5118                 a = Roo.DomQuery.getStyle(ci, attr);
5119             }
5120             else if(attr == "class" || attr == "className"){
5121                 a = ci.className;
5122             }else if(attr == "for"){
5123                 a = ci.htmlFor;
5124             }else if(attr == "href"){
5125                 a = ci.getAttribute("href", 2);
5126             }else{
5127                 a = ci.getAttribute(attr);
5128             }
5129             if((f && f(a, value)) || (!f && a)){
5130                 r[++ri] = ci;
5131             }
5132         }
5133         return r;
5134     };
5135
5136     function byPseudo(cs, name, value){
5137         return Roo.DomQuery.pseudos[name](cs, value);
5138     };
5139
5140     // This is for IE MSXML which does not support expandos.
5141     // IE runs the same speed using setAttribute, however FF slows way down
5142     // and Safari completely fails so they need to continue to use expandos.
5143     var isIE = window.ActiveXObject ? true : false;
5144
5145     // this eval is stop the compressor from
5146     // renaming the variable to something shorter
5147     
5148     /** eval:var:batch */
5149     var batch = 30803; 
5150
5151     var key = 30803;
5152
5153     function nodupIEXml(cs){
5154         var d = ++key;
5155         cs[0].setAttribute("_nodup", d);
5156         var r = [cs[0]];
5157         for(var i = 1, len = cs.length; i < len; i++){
5158             var c = cs[i];
5159             if(!c.getAttribute("_nodup") != d){
5160                 c.setAttribute("_nodup", d);
5161                 r[r.length] = c;
5162             }
5163         }
5164         for(var i = 0, len = cs.length; i < len; i++){
5165             cs[i].removeAttribute("_nodup");
5166         }
5167         return r;
5168     }
5169
5170     function nodup(cs){
5171         if(!cs){
5172             return [];
5173         }
5174         var len = cs.length, c, i, r = cs, cj, ri = -1;
5175         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5176             return cs;
5177         }
5178         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5179             return nodupIEXml(cs);
5180         }
5181         var d = ++key;
5182         cs[0]._nodup = d;
5183         for(i = 1; c = cs[i]; i++){
5184             if(c._nodup != d){
5185                 c._nodup = d;
5186             }else{
5187                 r = [];
5188                 for(var j = 0; j < i; j++){
5189                     r[++ri] = cs[j];
5190                 }
5191                 for(j = i+1; cj = cs[j]; j++){
5192                     if(cj._nodup != d){
5193                         cj._nodup = d;
5194                         r[++ri] = cj;
5195                     }
5196                 }
5197                 return r;
5198             }
5199         }
5200         return r;
5201     }
5202
5203     function quickDiffIEXml(c1, c2){
5204         var d = ++key;
5205         for(var i = 0, len = c1.length; i < len; i++){
5206             c1[i].setAttribute("_qdiff", d);
5207         }
5208         var r = [];
5209         for(var i = 0, len = c2.length; i < len; i++){
5210             if(c2[i].getAttribute("_qdiff") != d){
5211                 r[r.length] = c2[i];
5212             }
5213         }
5214         for(var i = 0, len = c1.length; i < len; i++){
5215            c1[i].removeAttribute("_qdiff");
5216         }
5217         return r;
5218     }
5219
5220     function quickDiff(c1, c2){
5221         var len1 = c1.length;
5222         if(!len1){
5223             return c2;
5224         }
5225         if(isIE && c1[0].selectSingleNode){
5226             return quickDiffIEXml(c1, c2);
5227         }
5228         var d = ++key;
5229         for(var i = 0; i < len1; i++){
5230             c1[i]._qdiff = d;
5231         }
5232         var r = [];
5233         for(var i = 0, len = c2.length; i < len; i++){
5234             if(c2[i]._qdiff != d){
5235                 r[r.length] = c2[i];
5236             }
5237         }
5238         return r;
5239     }
5240
5241     function quickId(ns, mode, root, id){
5242         if(ns == root){
5243            var d = root.ownerDocument || root;
5244            return d.getElementById(id);
5245         }
5246         ns = getNodes(ns, mode, "*");
5247         return byId(ns, null, id);
5248     }
5249
5250     return {
5251         getStyle : function(el, name){
5252             return Roo.fly(el).getStyle(name);
5253         },
5254         /**
5255          * Compiles a selector/xpath query into a reusable function. The returned function
5256          * takes one parameter "root" (optional), which is the context node from where the query should start.
5257          * @param {String} selector The selector/xpath query
5258          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5259          * @return {Function}
5260          */
5261         compile : function(path, type){
5262             type = type || "select";
5263             
5264             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5265             var q = path, mode, lq;
5266             var tk = Roo.DomQuery.matchers;
5267             var tklen = tk.length;
5268             var mm;
5269
5270             // accept leading mode switch
5271             var lmode = q.match(modeRe);
5272             if(lmode && lmode[1]){
5273                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5274                 q = q.replace(lmode[1], "");
5275             }
5276             // strip leading slashes
5277             while(path.substr(0, 1)=="/"){
5278                 path = path.substr(1);
5279             }
5280
5281             while(q && lq != q){
5282                 lq = q;
5283                 var tm = q.match(tagTokenRe);
5284                 if(type == "select"){
5285                     if(tm){
5286                         if(tm[1] == "#"){
5287                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5288                         }else{
5289                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5290                         }
5291                         q = q.replace(tm[0], "");
5292                     }else if(q.substr(0, 1) != '@'){
5293                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5294                     }
5295                 }else{
5296                     if(tm){
5297                         if(tm[1] == "#"){
5298                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5299                         }else{
5300                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5301                         }
5302                         q = q.replace(tm[0], "");
5303                     }
5304                 }
5305                 while(!(mm = q.match(modeRe))){
5306                     var matched = false;
5307                     for(var j = 0; j < tklen; j++){
5308                         var t = tk[j];
5309                         var m = q.match(t.re);
5310                         if(m){
5311                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5312                                                     return m[i];
5313                                                 });
5314                             q = q.replace(m[0], "");
5315                             matched = true;
5316                             break;
5317                         }
5318                     }
5319                     // prevent infinite loop on bad selector
5320                     if(!matched){
5321                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5322                     }
5323                 }
5324                 if(mm[1]){
5325                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5326                     q = q.replace(mm[1], "");
5327                 }
5328             }
5329             fn[fn.length] = "return nodup(n);\n}";
5330             
5331              /** 
5332               * list of variables that need from compression as they are used by eval.
5333              *  eval:var:batch 
5334              *  eval:var:nodup
5335              *  eval:var:byTag
5336              *  eval:var:ById
5337              *  eval:var:getNodes
5338              *  eval:var:quickId
5339              *  eval:var:mode
5340              *  eval:var:root
5341              *  eval:var:n
5342              *  eval:var:byClassName
5343              *  eval:var:byPseudo
5344              *  eval:var:byAttribute
5345              *  eval:var:attrValue
5346              * 
5347              **/ 
5348             eval(fn.join(""));
5349             return f;
5350         },
5351
5352         /**
5353          * Selects a group of elements.
5354          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5355          * @param {Node} root (optional) The start of the query (defaults to document).
5356          * @return {Array}
5357          */
5358         select : function(path, root, type){
5359             if(!root || root == document){
5360                 root = document;
5361             }
5362             if(typeof root == "string"){
5363                 root = document.getElementById(root);
5364             }
5365             var paths = path.split(",");
5366             var results = [];
5367             for(var i = 0, len = paths.length; i < len; i++){
5368                 var p = paths[i].replace(trimRe, "");
5369                 if(!cache[p]){
5370                     cache[p] = Roo.DomQuery.compile(p);
5371                     if(!cache[p]){
5372                         throw p + " is not a valid selector";
5373                     }
5374                 }
5375                 var result = cache[p](root);
5376                 if(result && result != document){
5377                     results = results.concat(result);
5378                 }
5379             }
5380             if(paths.length > 1){
5381                 return nodup(results);
5382             }
5383             return results;
5384         },
5385
5386         /**
5387          * Selects a single element.
5388          * @param {String} selector The selector/xpath query
5389          * @param {Node} root (optional) The start of the query (defaults to document).
5390          * @return {Element}
5391          */
5392         selectNode : function(path, root){
5393             return Roo.DomQuery.select(path, root)[0];
5394         },
5395
5396         /**
5397          * Selects the value of a node, optionally replacing null with the defaultValue.
5398          * @param {String} selector The selector/xpath query
5399          * @param {Node} root (optional) The start of the query (defaults to document).
5400          * @param {String} defaultValue
5401          */
5402         selectValue : function(path, root, defaultValue){
5403             path = path.replace(trimRe, "");
5404             if(!valueCache[path]){
5405                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5406             }
5407             var n = valueCache[path](root);
5408             n = n[0] ? n[0] : n;
5409             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5410             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5411         },
5412
5413         /**
5414          * Selects the value of a node, parsing integers and floats.
5415          * @param {String} selector The selector/xpath query
5416          * @param {Node} root (optional) The start of the query (defaults to document).
5417          * @param {Number} defaultValue
5418          * @return {Number}
5419          */
5420         selectNumber : function(path, root, defaultValue){
5421             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5422             return parseFloat(v);
5423         },
5424
5425         /**
5426          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5427          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5428          * @param {String} selector The simple selector to test
5429          * @return {Boolean}
5430          */
5431         is : function(el, ss){
5432             if(typeof el == "string"){
5433                 el = document.getElementById(el);
5434             }
5435             var isArray = (el instanceof Array);
5436             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5437             return isArray ? (result.length == el.length) : (result.length > 0);
5438         },
5439
5440         /**
5441          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5442          * @param {Array} el An array of elements to filter
5443          * @param {String} selector The simple selector to test
5444          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5445          * the selector instead of the ones that match
5446          * @return {Array}
5447          */
5448         filter : function(els, ss, nonMatches){
5449             ss = ss.replace(trimRe, "");
5450             if(!simpleCache[ss]){
5451                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5452             }
5453             var result = simpleCache[ss](els);
5454             return nonMatches ? quickDiff(result, els) : result;
5455         },
5456
5457         /**
5458          * Collection of matching regular expressions and code snippets.
5459          */
5460         matchers : [{
5461                 re: /^\.([\w-]+)/,
5462                 select: 'n = byClassName(n, null, " {1} ");'
5463             }, {
5464                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5465                 select: 'n = byPseudo(n, "{1}", "{2}");'
5466             },{
5467                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5468                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5469             }, {
5470                 re: /^#([\w-]+)/,
5471                 select: 'n = byId(n, null, "{1}");'
5472             },{
5473                 re: /^@([\w-]+)/,
5474                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5475             }
5476         ],
5477
5478         /**
5479          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5480          * 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;.
5481          */
5482         operators : {
5483             "=" : function(a, v){
5484                 return a == v;
5485             },
5486             "!=" : function(a, v){
5487                 return a != v;
5488             },
5489             "^=" : function(a, v){
5490                 return a && a.substr(0, v.length) == v;
5491             },
5492             "$=" : function(a, v){
5493                 return a && a.substr(a.length-v.length) == v;
5494             },
5495             "*=" : function(a, v){
5496                 return a && a.indexOf(v) !== -1;
5497             },
5498             "%=" : function(a, v){
5499                 return (a % v) == 0;
5500             },
5501             "|=" : function(a, v){
5502                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5503             },
5504             "~=" : function(a, v){
5505                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5506             }
5507         },
5508
5509         /**
5510          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5511          * and the argument (if any) supplied in the selector.
5512          */
5513         pseudos : {
5514             "first-child" : function(c){
5515                 var r = [], ri = -1, n;
5516                 for(var i = 0, ci; ci = n = c[i]; i++){
5517                     while((n = n.previousSibling) && n.nodeType != 1);
5518                     if(!n){
5519                         r[++ri] = ci;
5520                     }
5521                 }
5522                 return r;
5523             },
5524
5525             "last-child" : function(c){
5526                 var r = [], ri = -1, n;
5527                 for(var i = 0, ci; ci = n = c[i]; i++){
5528                     while((n = n.nextSibling) && n.nodeType != 1);
5529                     if(!n){
5530                         r[++ri] = ci;
5531                     }
5532                 }
5533                 return r;
5534             },
5535
5536             "nth-child" : function(c, a) {
5537                 var r = [], ri = -1;
5538                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5539                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5540                 for(var i = 0, n; n = c[i]; i++){
5541                     var pn = n.parentNode;
5542                     if (batch != pn._batch) {
5543                         var j = 0;
5544                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5545                             if(cn.nodeType == 1){
5546                                cn.nodeIndex = ++j;
5547                             }
5548                         }
5549                         pn._batch = batch;
5550                     }
5551                     if (f == 1) {
5552                         if (l == 0 || n.nodeIndex == l){
5553                             r[++ri] = n;
5554                         }
5555                     } else if ((n.nodeIndex + l) % f == 0){
5556                         r[++ri] = n;
5557                     }
5558                 }
5559
5560                 return r;
5561             },
5562
5563             "only-child" : function(c){
5564                 var r = [], ri = -1;;
5565                 for(var i = 0, ci; ci = c[i]; i++){
5566                     if(!prev(ci) && !next(ci)){
5567                         r[++ri] = ci;
5568                     }
5569                 }
5570                 return r;
5571             },
5572
5573             "empty" : function(c){
5574                 var r = [], ri = -1;
5575                 for(var i = 0, ci; ci = c[i]; i++){
5576                     var cns = ci.childNodes, j = 0, cn, empty = true;
5577                     while(cn = cns[j]){
5578                         ++j;
5579                         if(cn.nodeType == 1 || cn.nodeType == 3){
5580                             empty = false;
5581                             break;
5582                         }
5583                     }
5584                     if(empty){
5585                         r[++ri] = ci;
5586                     }
5587                 }
5588                 return r;
5589             },
5590
5591             "contains" : function(c, v){
5592                 var r = [], ri = -1;
5593                 for(var i = 0, ci; ci = c[i]; i++){
5594                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5595                         r[++ri] = ci;
5596                     }
5597                 }
5598                 return r;
5599             },
5600
5601             "nodeValue" : function(c, v){
5602                 var r = [], ri = -1;
5603                 for(var i = 0, ci; ci = c[i]; i++){
5604                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5605                         r[++ri] = ci;
5606                     }
5607                 }
5608                 return r;
5609             },
5610
5611             "checked" : function(c){
5612                 var r = [], ri = -1;
5613                 for(var i = 0, ci; ci = c[i]; i++){
5614                     if(ci.checked == true){
5615                         r[++ri] = ci;
5616                     }
5617                 }
5618                 return r;
5619             },
5620
5621             "not" : function(c, ss){
5622                 return Roo.DomQuery.filter(c, ss, true);
5623             },
5624
5625             "odd" : function(c){
5626                 return this["nth-child"](c, "odd");
5627             },
5628
5629             "even" : function(c){
5630                 return this["nth-child"](c, "even");
5631             },
5632
5633             "nth" : function(c, a){
5634                 return c[a-1] || [];
5635             },
5636
5637             "first" : function(c){
5638                 return c[0] || [];
5639             },
5640
5641             "last" : function(c){
5642                 return c[c.length-1] || [];
5643             },
5644
5645             "has" : function(c, ss){
5646                 var s = Roo.DomQuery.select;
5647                 var r = [], ri = -1;
5648                 for(var i = 0, ci; ci = c[i]; i++){
5649                     if(s(ss, ci).length > 0){
5650                         r[++ri] = ci;
5651                     }
5652                 }
5653                 return r;
5654             },
5655
5656             "next" : function(c, ss){
5657                 var is = Roo.DomQuery.is;
5658                 var r = [], ri = -1;
5659                 for(var i = 0, ci; ci = c[i]; i++){
5660                     var n = next(ci);
5661                     if(n && is(n, ss)){
5662                         r[++ri] = ci;
5663                     }
5664                 }
5665                 return r;
5666             },
5667
5668             "prev" : function(c, ss){
5669                 var is = Roo.DomQuery.is;
5670                 var r = [], ri = -1;
5671                 for(var i = 0, ci; ci = c[i]; i++){
5672                     var n = prev(ci);
5673                     if(n && is(n, ss)){
5674                         r[++ri] = ci;
5675                     }
5676                 }
5677                 return r;
5678             }
5679         }
5680     };
5681 }();
5682
5683 /**
5684  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5685  * @param {String} path The selector/xpath query
5686  * @param {Node} root (optional) The start of the query (defaults to document).
5687  * @return {Array}
5688  * @member Roo
5689  * @method query
5690  */
5691 Roo.query = Roo.DomQuery.select;
5692 /*
5693  * Based on:
5694  * Ext JS Library 1.1.1
5695  * Copyright(c) 2006-2007, Ext JS, LLC.
5696  *
5697  * Originally Released Under LGPL - original licence link has changed is not relivant.
5698  *
5699  * Fork - LGPL
5700  * <script type="text/javascript">
5701  */
5702
5703 /**
5704  * @class Roo.util.Observable
5705  * Base class that provides a common interface for publishing events. Subclasses are expected to
5706  * to have a property "events" with all the events defined.<br>
5707  * For example:
5708  * <pre><code>
5709  Employee = function(name){
5710     this.name = name;
5711     this.addEvents({
5712         "fired" : true,
5713         "quit" : true
5714     });
5715  }
5716  Roo.extend(Employee, Roo.util.Observable);
5717 </code></pre>
5718  * @param {Object} config properties to use (incuding events / listeners)
5719  */
5720
5721 Roo.util.Observable = function(cfg){
5722     
5723     cfg = cfg|| {};
5724     this.addEvents(cfg.events || {});
5725     if (cfg.events) {
5726         delete cfg.events; // make sure
5727     }
5728      
5729     Roo.apply(this, cfg);
5730     
5731     if(this.listeners){
5732         this.on(this.listeners);
5733         delete this.listeners;
5734     }
5735 };
5736 Roo.util.Observable.prototype = {
5737     /** 
5738  * @cfg {Object} listeners  list of events and functions to call for this object, 
5739  * For example :
5740  * <pre><code>
5741     listeners :  { 
5742        'click' : function(e) {
5743            ..... 
5744         } ,
5745         .... 
5746     } 
5747   </code></pre>
5748  */
5749     
5750     
5751     /**
5752      * Fires the specified event with the passed parameters (minus the event name).
5753      * @param {String} eventName
5754      * @param {Object...} args Variable number of parameters are passed to handlers
5755      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5756      */
5757     fireEvent : function(){
5758         var ce = this.events[arguments[0].toLowerCase()];
5759         if(typeof ce == "object"){
5760             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5761         }else{
5762             return true;
5763         }
5764     },
5765
5766     // private
5767     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5768
5769     /**
5770      * Appends an event handler to this component
5771      * @param {String}   eventName The type of event to listen for
5772      * @param {Function} handler The method the event invokes
5773      * @param {Object}   scope (optional) The scope in which to execute the handler
5774      * function. The handler function's "this" context.
5775      * @param {Object}   options (optional) An object containing handler configuration
5776      * properties. This may contain any of the following properties:<ul>
5777      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5778      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5779      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5780      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5781      * by the specified number of milliseconds. If the event fires again within that time, the original
5782      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5783      * </ul><br>
5784      * <p>
5785      * <b>Combining Options</b><br>
5786      * Using the options argument, it is possible to combine different types of listeners:<br>
5787      * <br>
5788      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5789                 <pre><code>
5790                 el.on('click', this.onClick, this, {
5791                         single: true,
5792                 delay: 100,
5793                 forumId: 4
5794                 });
5795                 </code></pre>
5796      * <p>
5797      * <b>Attaching multiple handlers in 1 call</b><br>
5798      * The method also allows for a single argument to be passed which is a config object containing properties
5799      * which specify multiple handlers.
5800      * <pre><code>
5801                 el.on({
5802                         'click': {
5803                         fn: this.onClick,
5804                         scope: this,
5805                         delay: 100
5806                 }, 
5807                 'mouseover': {
5808                         fn: this.onMouseOver,
5809                         scope: this
5810                 },
5811                 'mouseout': {
5812                         fn: this.onMouseOut,
5813                         scope: this
5814                 }
5815                 });
5816                 </code></pre>
5817      * <p>
5818      * Or a shorthand syntax which passes the same scope object to all handlers:
5819         <pre><code>
5820                 el.on({
5821                         'click': this.onClick,
5822                 'mouseover': this.onMouseOver,
5823                 'mouseout': this.onMouseOut,
5824                 scope: this
5825                 });
5826                 </code></pre>
5827      */
5828     addListener : function(eventName, fn, scope, o){
5829         if(typeof eventName == "object"){
5830             o = eventName;
5831             for(var e in o){
5832                 if(this.filterOptRe.test(e)){
5833                     continue;
5834                 }
5835                 if(typeof o[e] == "function"){
5836                     // shared options
5837                     this.addListener(e, o[e], o.scope,  o);
5838                 }else{
5839                     // individual options
5840                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5841                 }
5842             }
5843             return;
5844         }
5845         o = (!o || typeof o == "boolean") ? {} : o;
5846         eventName = eventName.toLowerCase();
5847         var ce = this.events[eventName] || true;
5848         if(typeof ce == "boolean"){
5849             ce = new Roo.util.Event(this, eventName);
5850             this.events[eventName] = ce;
5851         }
5852         ce.addListener(fn, scope, o);
5853     },
5854
5855     /**
5856      * Removes a listener
5857      * @param {String}   eventName     The type of event to listen for
5858      * @param {Function} handler        The handler to remove
5859      * @param {Object}   scope  (optional) The scope (this object) for the handler
5860      */
5861     removeListener : function(eventName, fn, scope){
5862         var ce = this.events[eventName.toLowerCase()];
5863         if(typeof ce == "object"){
5864             ce.removeListener(fn, scope);
5865         }
5866     },
5867
5868     /**
5869      * Removes all listeners for this object
5870      */
5871     purgeListeners : function(){
5872         for(var evt in this.events){
5873             if(typeof this.events[evt] == "object"){
5874                  this.events[evt].clearListeners();
5875             }
5876         }
5877     },
5878
5879     relayEvents : function(o, events){
5880         var createHandler = function(ename){
5881             return function(){
5882                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5883             };
5884         };
5885         for(var i = 0, len = events.length; i < len; i++){
5886             var ename = events[i];
5887             if(!this.events[ename]){ this.events[ename] = true; };
5888             o.on(ename, createHandler(ename), this);
5889         }
5890     },
5891
5892     /**
5893      * Used to define events on this Observable
5894      * @param {Object} object The object with the events defined
5895      */
5896     addEvents : function(o){
5897         if(!this.events){
5898             this.events = {};
5899         }
5900         Roo.applyIf(this.events, o);
5901     },
5902
5903     /**
5904      * Checks to see if this object has any listeners for a specified event
5905      * @param {String} eventName The name of the event to check for
5906      * @return {Boolean} True if the event is being listened for, else false
5907      */
5908     hasListener : function(eventName){
5909         var e = this.events[eventName];
5910         return typeof e == "object" && e.listeners.length > 0;
5911     }
5912 };
5913 /**
5914  * Appends an event handler to this element (shorthand for addListener)
5915  * @param {String}   eventName     The type of event to listen for
5916  * @param {Function} handler        The method the event invokes
5917  * @param {Object}   scope (optional) The scope in which to execute the handler
5918  * function. The handler function's "this" context.
5919  * @param {Object}   options  (optional)
5920  * @method
5921  */
5922 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5923 /**
5924  * Removes a listener (shorthand for removeListener)
5925  * @param {String}   eventName     The type of event to listen for
5926  * @param {Function} handler        The handler to remove
5927  * @param {Object}   scope  (optional) The scope (this object) for the handler
5928  * @method
5929  */
5930 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5931
5932 /**
5933  * Starts capture on the specified Observable. All events will be passed
5934  * to the supplied function with the event name + standard signature of the event
5935  * <b>before</b> the event is fired. If the supplied function returns false,
5936  * the event will not fire.
5937  * @param {Observable} o The Observable to capture
5938  * @param {Function} fn The function to call
5939  * @param {Object} scope (optional) The scope (this object) for the fn
5940  * @static
5941  */
5942 Roo.util.Observable.capture = function(o, fn, scope){
5943     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5944 };
5945
5946 /**
5947  * Removes <b>all</b> added captures from the Observable.
5948  * @param {Observable} o The Observable to release
5949  * @static
5950  */
5951 Roo.util.Observable.releaseCapture = function(o){
5952     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5953 };
5954
5955 (function(){
5956
5957     var createBuffered = function(h, o, scope){
5958         var task = new Roo.util.DelayedTask();
5959         return function(){
5960             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5961         };
5962     };
5963
5964     var createSingle = function(h, e, fn, scope){
5965         return function(){
5966             e.removeListener(fn, scope);
5967             return h.apply(scope, arguments);
5968         };
5969     };
5970
5971     var createDelayed = function(h, o, scope){
5972         return function(){
5973             var args = Array.prototype.slice.call(arguments, 0);
5974             setTimeout(function(){
5975                 h.apply(scope, args);
5976             }, o.delay || 10);
5977         };
5978     };
5979
5980     Roo.util.Event = function(obj, name){
5981         this.name = name;
5982         this.obj = obj;
5983         this.listeners = [];
5984     };
5985
5986     Roo.util.Event.prototype = {
5987         addListener : function(fn, scope, options){
5988             var o = options || {};
5989             scope = scope || this.obj;
5990             if(!this.isListening(fn, scope)){
5991                 var l = {fn: fn, scope: scope, options: o};
5992                 var h = fn;
5993                 if(o.delay){
5994                     h = createDelayed(h, o, scope);
5995                 }
5996                 if(o.single){
5997                     h = createSingle(h, this, fn, scope);
5998                 }
5999                 if(o.buffer){
6000                     h = createBuffered(h, o, scope);
6001                 }
6002                 l.fireFn = h;
6003                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6004                     this.listeners.push(l);
6005                 }else{
6006                     this.listeners = this.listeners.slice(0);
6007                     this.listeners.push(l);
6008                 }
6009             }
6010         },
6011
6012         findListener : function(fn, scope){
6013             scope = scope || this.obj;
6014             var ls = this.listeners;
6015             for(var i = 0, len = ls.length; i < len; i++){
6016                 var l = ls[i];
6017                 if(l.fn == fn && l.scope == scope){
6018                     return i;
6019                 }
6020             }
6021             return -1;
6022         },
6023
6024         isListening : function(fn, scope){
6025             return this.findListener(fn, scope) != -1;
6026         },
6027
6028         removeListener : function(fn, scope){
6029             var index;
6030             if((index = this.findListener(fn, scope)) != -1){
6031                 if(!this.firing){
6032                     this.listeners.splice(index, 1);
6033                 }else{
6034                     this.listeners = this.listeners.slice(0);
6035                     this.listeners.splice(index, 1);
6036                 }
6037                 return true;
6038             }
6039             return false;
6040         },
6041
6042         clearListeners : function(){
6043             this.listeners = [];
6044         },
6045
6046         fire : function(){
6047             var ls = this.listeners, scope, len = ls.length;
6048             if(len > 0){
6049                 this.firing = true;
6050                 var args = Array.prototype.slice.call(arguments, 0);
6051                 for(var i = 0; i < len; i++){
6052                     var l = ls[i];
6053                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6054                         this.firing = false;
6055                         return false;
6056                     }
6057                 }
6058                 this.firing = false;
6059             }
6060             return true;
6061         }
6062     };
6063 })();/*
6064  * RooJS Library 
6065  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6066  *
6067  * Licence LGPL 
6068  *
6069  */
6070  
6071 /**
6072  * @class Roo.Document
6073  * @extends Roo.util.Observable
6074  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6075  * 
6076  * @param {Object} config the methods and properties of the 'base' class for the application.
6077  * 
6078  *  Generic Page handler - implement this to start your app..
6079  * 
6080  * eg.
6081  *  MyProject = new Roo.Document({
6082         events : {
6083             'load' : true // your events..
6084         },
6085         listeners : {
6086             'ready' : function() {
6087                 // fired on Roo.onReady()
6088             }
6089         }
6090  * 
6091  */
6092 Roo.Document = function(cfg) {
6093      
6094     this.addEvents({ 
6095         'ready' : true
6096     });
6097     Roo.util.Observable.call(this,cfg);
6098     
6099     var _this = this;
6100     
6101     Roo.onReady(function() {
6102         _this.fireEvent('ready');
6103     },null,false);
6104     
6105     
6106 }
6107
6108 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6109  * Based on:
6110  * Ext JS Library 1.1.1
6111  * Copyright(c) 2006-2007, Ext JS, LLC.
6112  *
6113  * Originally Released Under LGPL - original licence link has changed is not relivant.
6114  *
6115  * Fork - LGPL
6116  * <script type="text/javascript">
6117  */
6118
6119 /**
6120  * @class Roo.EventManager
6121  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6122  * several useful events directly.
6123  * See {@link Roo.EventObject} for more details on normalized event objects.
6124  * @singleton
6125  */
6126 Roo.EventManager = function(){
6127     var docReadyEvent, docReadyProcId, docReadyState = false;
6128     var resizeEvent, resizeTask, textEvent, textSize;
6129     var E = Roo.lib.Event;
6130     var D = Roo.lib.Dom;
6131
6132     
6133     
6134
6135     var fireDocReady = function(){
6136         if(!docReadyState){
6137             docReadyState = true;
6138             Roo.isReady = true;
6139             if(docReadyProcId){
6140                 clearInterval(docReadyProcId);
6141             }
6142             if(Roo.isGecko || Roo.isOpera) {
6143                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6144             }
6145             if(Roo.isIE){
6146                 var defer = document.getElementById("ie-deferred-loader");
6147                 if(defer){
6148                     defer.onreadystatechange = null;
6149                     defer.parentNode.removeChild(defer);
6150                 }
6151             }
6152             if(docReadyEvent){
6153                 docReadyEvent.fire();
6154                 docReadyEvent.clearListeners();
6155             }
6156         }
6157     };
6158     
6159     var initDocReady = function(){
6160         docReadyEvent = new Roo.util.Event();
6161         if(Roo.isGecko || Roo.isOpera) {
6162             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6163         }else if(Roo.isIE){
6164             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6165             var defer = document.getElementById("ie-deferred-loader");
6166             defer.onreadystatechange = function(){
6167                 if(this.readyState == "complete"){
6168                     fireDocReady();
6169                 }
6170             };
6171         }else if(Roo.isSafari){ 
6172             docReadyProcId = setInterval(function(){
6173                 var rs = document.readyState;
6174                 if(rs == "complete") {
6175                     fireDocReady();     
6176                  }
6177             }, 10);
6178         }
6179         // no matter what, make sure it fires on load
6180         E.on(window, "load", fireDocReady);
6181     };
6182
6183     var createBuffered = function(h, o){
6184         var task = new Roo.util.DelayedTask(h);
6185         return function(e){
6186             // create new event object impl so new events don't wipe out properties
6187             e = new Roo.EventObjectImpl(e);
6188             task.delay(o.buffer, h, null, [e]);
6189         };
6190     };
6191
6192     var createSingle = function(h, el, ename, fn){
6193         return function(e){
6194             Roo.EventManager.removeListener(el, ename, fn);
6195             h(e);
6196         };
6197     };
6198
6199     var createDelayed = function(h, o){
6200         return function(e){
6201             // create new event object impl so new events don't wipe out properties
6202             e = new Roo.EventObjectImpl(e);
6203             setTimeout(function(){
6204                 h(e);
6205             }, o.delay || 10);
6206         };
6207     };
6208     var transitionEndVal = false;
6209     
6210     var transitionEnd = function()
6211     {
6212         if (transitionEndVal) {
6213             return transitionEndVal;
6214         }
6215         var el = document.createElement('div');
6216
6217         var transEndEventNames = {
6218             WebkitTransition : 'webkitTransitionEnd',
6219             MozTransition    : 'transitionend',
6220             OTransition      : 'oTransitionEnd otransitionend',
6221             transition       : 'transitionend'
6222         };
6223     
6224         for (var name in transEndEventNames) {
6225             if (el.style[name] !== undefined) {
6226                 transitionEndVal = transEndEventNames[name];
6227                 return  transitionEndVal ;
6228             }
6229         }
6230     }
6231     
6232
6233     var listen = function(element, ename, opt, fn, scope){
6234         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6235         fn = fn || o.fn; scope = scope || o.scope;
6236         var el = Roo.getDom(element);
6237         
6238         
6239         if(!el){
6240             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6241         }
6242         
6243         if (ename == 'transitionend') {
6244             ename = transitionEnd();
6245         }
6246         var h = function(e){
6247             e = Roo.EventObject.setEvent(e);
6248             var t;
6249             if(o.delegate){
6250                 t = e.getTarget(o.delegate, el);
6251                 if(!t){
6252                     return;
6253                 }
6254             }else{
6255                 t = e.target;
6256             }
6257             if(o.stopEvent === true){
6258                 e.stopEvent();
6259             }
6260             if(o.preventDefault === true){
6261                e.preventDefault();
6262             }
6263             if(o.stopPropagation === true){
6264                 e.stopPropagation();
6265             }
6266
6267             if(o.normalized === false){
6268                 e = e.browserEvent;
6269             }
6270
6271             fn.call(scope || el, e, t, o);
6272         };
6273         if(o.delay){
6274             h = createDelayed(h, o);
6275         }
6276         if(o.single){
6277             h = createSingle(h, el, ename, fn);
6278         }
6279         if(o.buffer){
6280             h = createBuffered(h, o);
6281         }
6282         
6283         fn._handlers = fn._handlers || [];
6284         
6285         
6286         fn._handlers.push([Roo.id(el), ename, h]);
6287         
6288         
6289          
6290         E.on(el, ename, h);
6291         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6292             el.addEventListener("DOMMouseScroll", h, false);
6293             E.on(window, 'unload', function(){
6294                 el.removeEventListener("DOMMouseScroll", h, false);
6295             });
6296         }
6297         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6298             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6299         }
6300         return h;
6301     };
6302
6303     var stopListening = function(el, ename, fn){
6304         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6305         if(hds){
6306             for(var i = 0, len = hds.length; i < len; i++){
6307                 var h = hds[i];
6308                 if(h[0] == id && h[1] == ename){
6309                     hd = h[2];
6310                     hds.splice(i, 1);
6311                     break;
6312                 }
6313             }
6314         }
6315         E.un(el, ename, hd);
6316         el = Roo.getDom(el);
6317         if(ename == "mousewheel" && el.addEventListener){
6318             el.removeEventListener("DOMMouseScroll", hd, false);
6319         }
6320         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6321             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6322         }
6323     };
6324
6325     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6326     
6327     var pub = {
6328         
6329         
6330         /** 
6331          * Fix for doc tools
6332          * @scope Roo.EventManager
6333          */
6334         
6335         
6336         /** 
6337          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6338          * object with a Roo.EventObject
6339          * @param {Function} fn        The method the event invokes
6340          * @param {Object}   scope    An object that becomes the scope of the handler
6341          * @param {boolean}  override If true, the obj passed in becomes
6342          *                             the execution scope of the listener
6343          * @return {Function} The wrapped function
6344          * @deprecated
6345          */
6346         wrap : function(fn, scope, override){
6347             return function(e){
6348                 Roo.EventObject.setEvent(e);
6349                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6350             };
6351         },
6352         
6353         /**
6354      * Appends an event handler to an element (shorthand for addListener)
6355      * @param {String/HTMLElement}   element        The html element or id to assign the
6356      * @param {String}   eventName The type of event to listen for
6357      * @param {Function} handler The method the event invokes
6358      * @param {Object}   scope (optional) The scope in which to execute the handler
6359      * function. The handler function's "this" context.
6360      * @param {Object}   options (optional) An object containing handler configuration
6361      * properties. This may contain any of the following properties:<ul>
6362      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6363      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6364      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6365      * <li>preventDefault {Boolean} True to prevent the default action</li>
6366      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6367      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6368      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6369      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6370      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6371      * by the specified number of milliseconds. If the event fires again within that time, the original
6372      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6373      * </ul><br>
6374      * <p>
6375      * <b>Combining Options</b><br>
6376      * Using the options argument, it is possible to combine different types of listeners:<br>
6377      * <br>
6378      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6379      * Code:<pre><code>
6380 el.on('click', this.onClick, this, {
6381     single: true,
6382     delay: 100,
6383     stopEvent : true,
6384     forumId: 4
6385 });</code></pre>
6386      * <p>
6387      * <b>Attaching multiple handlers in 1 call</b><br>
6388       * The method also allows for a single argument to be passed which is a config object containing properties
6389      * which specify multiple handlers.
6390      * <p>
6391      * Code:<pre><code>
6392 el.on({
6393     'click' : {
6394         fn: this.onClick
6395         scope: this,
6396         delay: 100
6397     },
6398     'mouseover' : {
6399         fn: this.onMouseOver
6400         scope: this
6401     },
6402     'mouseout' : {
6403         fn: this.onMouseOut
6404         scope: this
6405     }
6406 });</code></pre>
6407      * <p>
6408      * Or a shorthand syntax:<br>
6409      * Code:<pre><code>
6410 el.on({
6411     'click' : this.onClick,
6412     'mouseover' : this.onMouseOver,
6413     'mouseout' : this.onMouseOut
6414     scope: this
6415 });</code></pre>
6416      */
6417         addListener : function(element, eventName, fn, scope, options){
6418             if(typeof eventName == "object"){
6419                 var o = eventName;
6420                 for(var e in o){
6421                     if(propRe.test(e)){
6422                         continue;
6423                     }
6424                     if(typeof o[e] == "function"){
6425                         // shared options
6426                         listen(element, e, o, o[e], o.scope);
6427                     }else{
6428                         // individual options
6429                         listen(element, e, o[e]);
6430                     }
6431                 }
6432                 return;
6433             }
6434             return listen(element, eventName, options, fn, scope);
6435         },
6436         
6437         /**
6438          * Removes an event handler
6439          *
6440          * @param {String/HTMLElement}   element        The id or html element to remove the 
6441          *                             event from
6442          * @param {String}   eventName     The type of event
6443          * @param {Function} fn
6444          * @return {Boolean} True if a listener was actually removed
6445          */
6446         removeListener : function(element, eventName, fn){
6447             return stopListening(element, eventName, fn);
6448         },
6449         
6450         /**
6451          * Fires when the document is ready (before onload and before images are loaded). Can be 
6452          * accessed shorthanded Roo.onReady().
6453          * @param {Function} fn        The method the event invokes
6454          * @param {Object}   scope    An  object that becomes the scope of the handler
6455          * @param {boolean}  options
6456          */
6457         onDocumentReady : function(fn, scope, options){
6458             if(docReadyState){ // if it already fired
6459                 docReadyEvent.addListener(fn, scope, options);
6460                 docReadyEvent.fire();
6461                 docReadyEvent.clearListeners();
6462                 return;
6463             }
6464             if(!docReadyEvent){
6465                 initDocReady();
6466             }
6467             docReadyEvent.addListener(fn, scope, options);
6468         },
6469         
6470         /**
6471          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6472          * @param {Function} fn        The method the event invokes
6473          * @param {Object}   scope    An object that becomes the scope of the handler
6474          * @param {boolean}  options
6475          */
6476         onWindowResize : function(fn, scope, options){
6477             if(!resizeEvent){
6478                 resizeEvent = new Roo.util.Event();
6479                 resizeTask = new Roo.util.DelayedTask(function(){
6480                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6481                 });
6482                 E.on(window, "resize", function(){
6483                     if(Roo.isIE){
6484                         resizeTask.delay(50);
6485                     }else{
6486                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6487                     }
6488                 });
6489             }
6490             resizeEvent.addListener(fn, scope, options);
6491         },
6492
6493         /**
6494          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6495          * @param {Function} fn        The method the event invokes
6496          * @param {Object}   scope    An object that becomes the scope of the handler
6497          * @param {boolean}  options
6498          */
6499         onTextResize : function(fn, scope, options){
6500             if(!textEvent){
6501                 textEvent = new Roo.util.Event();
6502                 var textEl = new Roo.Element(document.createElement('div'));
6503                 textEl.dom.className = 'x-text-resize';
6504                 textEl.dom.innerHTML = 'X';
6505                 textEl.appendTo(document.body);
6506                 textSize = textEl.dom.offsetHeight;
6507                 setInterval(function(){
6508                     if(textEl.dom.offsetHeight != textSize){
6509                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6510                     }
6511                 }, this.textResizeInterval);
6512             }
6513             textEvent.addListener(fn, scope, options);
6514         },
6515
6516         /**
6517          * Removes the passed window resize listener.
6518          * @param {Function} fn        The method the event invokes
6519          * @param {Object}   scope    The scope of handler
6520          */
6521         removeResizeListener : function(fn, scope){
6522             if(resizeEvent){
6523                 resizeEvent.removeListener(fn, scope);
6524             }
6525         },
6526
6527         // private
6528         fireResize : function(){
6529             if(resizeEvent){
6530                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6531             }   
6532         },
6533         /**
6534          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6535          */
6536         ieDeferSrc : false,
6537         /**
6538          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6539          */
6540         textResizeInterval : 50
6541     };
6542     
6543     /**
6544      * Fix for doc tools
6545      * @scopeAlias pub=Roo.EventManager
6546      */
6547     
6548      /**
6549      * Appends an event handler to an element (shorthand for addListener)
6550      * @param {String/HTMLElement}   element        The html element or id to assign the
6551      * @param {String}   eventName The type of event to listen for
6552      * @param {Function} handler The method the event invokes
6553      * @param {Object}   scope (optional) The scope in which to execute the handler
6554      * function. The handler function's "this" context.
6555      * @param {Object}   options (optional) An object containing handler configuration
6556      * properties. This may contain any of the following properties:<ul>
6557      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6558      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6559      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6560      * <li>preventDefault {Boolean} True to prevent the default action</li>
6561      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6562      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6563      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6564      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6565      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6566      * by the specified number of milliseconds. If the event fires again within that time, the original
6567      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6568      * </ul><br>
6569      * <p>
6570      * <b>Combining Options</b><br>
6571      * Using the options argument, it is possible to combine different types of listeners:<br>
6572      * <br>
6573      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6574      * Code:<pre><code>
6575 el.on('click', this.onClick, this, {
6576     single: true,
6577     delay: 100,
6578     stopEvent : true,
6579     forumId: 4
6580 });</code></pre>
6581      * <p>
6582      * <b>Attaching multiple handlers in 1 call</b><br>
6583       * The method also allows for a single argument to be passed which is a config object containing properties
6584      * which specify multiple handlers.
6585      * <p>
6586      * Code:<pre><code>
6587 el.on({
6588     'click' : {
6589         fn: this.onClick
6590         scope: this,
6591         delay: 100
6592     },
6593     'mouseover' : {
6594         fn: this.onMouseOver
6595         scope: this
6596     },
6597     'mouseout' : {
6598         fn: this.onMouseOut
6599         scope: this
6600     }
6601 });</code></pre>
6602      * <p>
6603      * Or a shorthand syntax:<br>
6604      * Code:<pre><code>
6605 el.on({
6606     'click' : this.onClick,
6607     'mouseover' : this.onMouseOver,
6608     'mouseout' : this.onMouseOut
6609     scope: this
6610 });</code></pre>
6611      */
6612     pub.on = pub.addListener;
6613     pub.un = pub.removeListener;
6614
6615     pub.stoppedMouseDownEvent = new Roo.util.Event();
6616     return pub;
6617 }();
6618 /**
6619   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6620   * @param {Function} fn        The method the event invokes
6621   * @param {Object}   scope    An  object that becomes the scope of the handler
6622   * @param {boolean}  override If true, the obj passed in becomes
6623   *                             the execution scope of the listener
6624   * @member Roo
6625   * @method onReady
6626  */
6627 Roo.onReady = Roo.EventManager.onDocumentReady;
6628
6629 Roo.onReady(function(){
6630     var bd = Roo.get(document.body);
6631     if(!bd){ return; }
6632
6633     var cls = [
6634             Roo.isIE ? "roo-ie"
6635             : Roo.isIE11 ? "roo-ie11"
6636             : Roo.isEdge ? "roo-edge"
6637             : Roo.isGecko ? "roo-gecko"
6638             : Roo.isOpera ? "roo-opera"
6639             : Roo.isSafari ? "roo-safari" : ""];
6640
6641     if(Roo.isMac){
6642         cls.push("roo-mac");
6643     }
6644     if(Roo.isLinux){
6645         cls.push("roo-linux");
6646     }
6647     if(Roo.isIOS){
6648         cls.push("roo-ios");
6649     }
6650     if(Roo.isTouch){
6651         cls.push("roo-touch");
6652     }
6653     if(Roo.isBorderBox){
6654         cls.push('roo-border-box');
6655     }
6656     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6657         var p = bd.dom.parentNode;
6658         if(p){
6659             p.className += ' roo-strict';
6660         }
6661     }
6662     bd.addClass(cls.join(' '));
6663 });
6664
6665 /**
6666  * @class Roo.EventObject
6667  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6668  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6669  * Example:
6670  * <pre><code>
6671  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6672     e.preventDefault();
6673     var target = e.getTarget();
6674     ...
6675  }
6676  var myDiv = Roo.get("myDiv");
6677  myDiv.on("click", handleClick);
6678  //or
6679  Roo.EventManager.on("myDiv", 'click', handleClick);
6680  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6681  </code></pre>
6682  * @singleton
6683  */
6684 Roo.EventObject = function(){
6685     
6686     var E = Roo.lib.Event;
6687     
6688     // safari keypress events for special keys return bad keycodes
6689     var safariKeys = {
6690         63234 : 37, // left
6691         63235 : 39, // right
6692         63232 : 38, // up
6693         63233 : 40, // down
6694         63276 : 33, // page up
6695         63277 : 34, // page down
6696         63272 : 46, // delete
6697         63273 : 36, // home
6698         63275 : 35  // end
6699     };
6700
6701     // normalize button clicks
6702     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6703                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6704
6705     Roo.EventObjectImpl = function(e){
6706         if(e){
6707             this.setEvent(e.browserEvent || e);
6708         }
6709     };
6710     Roo.EventObjectImpl.prototype = {
6711         /**
6712          * Used to fix doc tools.
6713          * @scope Roo.EventObject.prototype
6714          */
6715             
6716
6717         
6718         
6719         /** The normal browser event */
6720         browserEvent : null,
6721         /** The button pressed in a mouse event */
6722         button : -1,
6723         /** True if the shift key was down during the event */
6724         shiftKey : false,
6725         /** True if the control key was down during the event */
6726         ctrlKey : false,
6727         /** True if the alt key was down during the event */
6728         altKey : false,
6729
6730         /** Key constant 
6731         * @type Number */
6732         BACKSPACE : 8,
6733         /** Key constant 
6734         * @type Number */
6735         TAB : 9,
6736         /** Key constant 
6737         * @type Number */
6738         RETURN : 13,
6739         /** Key constant 
6740         * @type Number */
6741         ENTER : 13,
6742         /** Key constant 
6743         * @type Number */
6744         SHIFT : 16,
6745         /** Key constant 
6746         * @type Number */
6747         CONTROL : 17,
6748         /** Key constant 
6749         * @type Number */
6750         ESC : 27,
6751         /** Key constant 
6752         * @type Number */
6753         SPACE : 32,
6754         /** Key constant 
6755         * @type Number */
6756         PAGEUP : 33,
6757         /** Key constant 
6758         * @type Number */
6759         PAGEDOWN : 34,
6760         /** Key constant 
6761         * @type Number */
6762         END : 35,
6763         /** Key constant 
6764         * @type Number */
6765         HOME : 36,
6766         /** Key constant 
6767         * @type Number */
6768         LEFT : 37,
6769         /** Key constant 
6770         * @type Number */
6771         UP : 38,
6772         /** Key constant 
6773         * @type Number */
6774         RIGHT : 39,
6775         /** Key constant 
6776         * @type Number */
6777         DOWN : 40,
6778         /** Key constant 
6779         * @type Number */
6780         DELETE : 46,
6781         /** Key constant 
6782         * @type Number */
6783         F5 : 116,
6784
6785            /** @private */
6786         setEvent : function(e){
6787             if(e == this || (e && e.browserEvent)){ // already wrapped
6788                 return e;
6789             }
6790             this.browserEvent = e;
6791             if(e){
6792                 // normalize buttons
6793                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6794                 if(e.type == 'click' && this.button == -1){
6795                     this.button = 0;
6796                 }
6797                 this.type = e.type;
6798                 this.shiftKey = e.shiftKey;
6799                 // mac metaKey behaves like ctrlKey
6800                 this.ctrlKey = e.ctrlKey || e.metaKey;
6801                 this.altKey = e.altKey;
6802                 // in getKey these will be normalized for the mac
6803                 this.keyCode = e.keyCode;
6804                 // keyup warnings on firefox.
6805                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6806                 // cache the target for the delayed and or buffered events
6807                 this.target = E.getTarget(e);
6808                 // same for XY
6809                 this.xy = E.getXY(e);
6810             }else{
6811                 this.button = -1;
6812                 this.shiftKey = false;
6813                 this.ctrlKey = false;
6814                 this.altKey = false;
6815                 this.keyCode = 0;
6816                 this.charCode =0;
6817                 this.target = null;
6818                 this.xy = [0, 0];
6819             }
6820             return this;
6821         },
6822
6823         /**
6824          * Stop the event (preventDefault and stopPropagation)
6825          */
6826         stopEvent : function(){
6827             if(this.browserEvent){
6828                 if(this.browserEvent.type == 'mousedown'){
6829                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6830                 }
6831                 E.stopEvent(this.browserEvent);
6832             }
6833         },
6834
6835         /**
6836          * Prevents the browsers default handling of the event.
6837          */
6838         preventDefault : function(){
6839             if(this.browserEvent){
6840                 E.preventDefault(this.browserEvent);
6841             }
6842         },
6843
6844         /** @private */
6845         isNavKeyPress : function(){
6846             var k = this.keyCode;
6847             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6848             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6849         },
6850
6851         isSpecialKey : function(){
6852             var k = this.keyCode;
6853             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6854             (k == 16) || (k == 17) ||
6855             (k >= 18 && k <= 20) ||
6856             (k >= 33 && k <= 35) ||
6857             (k >= 36 && k <= 39) ||
6858             (k >= 44 && k <= 45);
6859         },
6860         /**
6861          * Cancels bubbling of the event.
6862          */
6863         stopPropagation : function(){
6864             if(this.browserEvent){
6865                 if(this.type == 'mousedown'){
6866                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6867                 }
6868                 E.stopPropagation(this.browserEvent);
6869             }
6870         },
6871
6872         /**
6873          * Gets the key code for the event.
6874          * @return {Number}
6875          */
6876         getCharCode : function(){
6877             return this.charCode || this.keyCode;
6878         },
6879
6880         /**
6881          * Returns a normalized keyCode for the event.
6882          * @return {Number} The key code
6883          */
6884         getKey : function(){
6885             var k = this.keyCode || this.charCode;
6886             return Roo.isSafari ? (safariKeys[k] || k) : k;
6887         },
6888
6889         /**
6890          * Gets the x coordinate of the event.
6891          * @return {Number}
6892          */
6893         getPageX : function(){
6894             return this.xy[0];
6895         },
6896
6897         /**
6898          * Gets the y coordinate of the event.
6899          * @return {Number}
6900          */
6901         getPageY : function(){
6902             return this.xy[1];
6903         },
6904
6905         /**
6906          * Gets the time of the event.
6907          * @return {Number}
6908          */
6909         getTime : function(){
6910             if(this.browserEvent){
6911                 return E.getTime(this.browserEvent);
6912             }
6913             return null;
6914         },
6915
6916         /**
6917          * Gets the page coordinates of the event.
6918          * @return {Array} The xy values like [x, y]
6919          */
6920         getXY : function(){
6921             return this.xy;
6922         },
6923
6924         /**
6925          * Gets the target for the event.
6926          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6927          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6928                 search as a number or element (defaults to 10 || document.body)
6929          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6930          * @return {HTMLelement}
6931          */
6932         getTarget : function(selector, maxDepth, returnEl){
6933             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6934         },
6935         /**
6936          * Gets the related target.
6937          * @return {HTMLElement}
6938          */
6939         getRelatedTarget : function(){
6940             if(this.browserEvent){
6941                 return E.getRelatedTarget(this.browserEvent);
6942             }
6943             return null;
6944         },
6945
6946         /**
6947          * Normalizes mouse wheel delta across browsers
6948          * @return {Number} The delta
6949          */
6950         getWheelDelta : function(){
6951             var e = this.browserEvent;
6952             var delta = 0;
6953             if(e.wheelDelta){ /* IE/Opera. */
6954                 delta = e.wheelDelta/120;
6955             }else if(e.detail){ /* Mozilla case. */
6956                 delta = -e.detail/3;
6957             }
6958             return delta;
6959         },
6960
6961         /**
6962          * Returns true if the control, meta, shift or alt key was pressed during this event.
6963          * @return {Boolean}
6964          */
6965         hasModifier : function(){
6966             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6967         },
6968
6969         /**
6970          * Returns true if the target of this event equals el or is a child of el
6971          * @param {String/HTMLElement/Element} el
6972          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6973          * @return {Boolean}
6974          */
6975         within : function(el, related){
6976             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6977             return t && Roo.fly(el).contains(t);
6978         },
6979
6980         getPoint : function(){
6981             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6982         }
6983     };
6984
6985     return new Roo.EventObjectImpl();
6986 }();
6987             
6988     /*
6989  * Based on:
6990  * Ext JS Library 1.1.1
6991  * Copyright(c) 2006-2007, Ext JS, LLC.
6992  *
6993  * Originally Released Under LGPL - original licence link has changed is not relivant.
6994  *
6995  * Fork - LGPL
6996  * <script type="text/javascript">
6997  */
6998
6999  
7000 // was in Composite Element!??!?!
7001  
7002 (function(){
7003     var D = Roo.lib.Dom;
7004     var E = Roo.lib.Event;
7005     var A = Roo.lib.Anim;
7006
7007     // local style camelizing for speed
7008     var propCache = {};
7009     var camelRe = /(-[a-z])/gi;
7010     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7011     var view = document.defaultView;
7012
7013 /**
7014  * @class Roo.Element
7015  * Represents an Element in the DOM.<br><br>
7016  * Usage:<br>
7017 <pre><code>
7018 var el = Roo.get("my-div");
7019
7020 // or with getEl
7021 var el = getEl("my-div");
7022
7023 // or with a DOM element
7024 var el = Roo.get(myDivElement);
7025 </code></pre>
7026  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7027  * each call instead of constructing a new one.<br><br>
7028  * <b>Animations</b><br />
7029  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7030  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7031 <pre>
7032 Option    Default   Description
7033 --------- --------  ---------------------------------------------
7034 duration  .35       The duration of the animation in seconds
7035 easing    easeOut   The YUI easing method
7036 callback  none      A function to execute when the anim completes
7037 scope     this      The scope (this) of the callback function
7038 </pre>
7039 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7040 * manipulate the animation. Here's an example:
7041 <pre><code>
7042 var el = Roo.get("my-div");
7043
7044 // no animation
7045 el.setWidth(100);
7046
7047 // default animation
7048 el.setWidth(100, true);
7049
7050 // animation with some options set
7051 el.setWidth(100, {
7052     duration: 1,
7053     callback: this.foo,
7054     scope: this
7055 });
7056
7057 // using the "anim" property to get the Anim object
7058 var opt = {
7059     duration: 1,
7060     callback: this.foo,
7061     scope: this
7062 };
7063 el.setWidth(100, opt);
7064 ...
7065 if(opt.anim.isAnimated()){
7066     opt.anim.stop();
7067 }
7068 </code></pre>
7069 * <b> Composite (Collections of) Elements</b><br />
7070  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7071  * @constructor Create a new Element directly.
7072  * @param {String/HTMLElement} element
7073  * @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).
7074  */
7075     Roo.Element = function(element, forceNew){
7076         var dom = typeof element == "string" ?
7077                 document.getElementById(element) : element;
7078         if(!dom){ // invalid id/element
7079             return null;
7080         }
7081         var id = dom.id;
7082         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7083             return Roo.Element.cache[id];
7084         }
7085
7086         /**
7087          * The DOM element
7088          * @type HTMLElement
7089          */
7090         this.dom = dom;
7091
7092         /**
7093          * The DOM element ID
7094          * @type String
7095          */
7096         this.id = id || Roo.id(dom);
7097     };
7098
7099     var El = Roo.Element;
7100
7101     El.prototype = {
7102         /**
7103          * The element's default display mode  (defaults to "")
7104          * @type String
7105          */
7106         originalDisplay : "",
7107
7108         visibilityMode : 1,
7109         /**
7110          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7111          * @type String
7112          */
7113         defaultUnit : "px",
7114         
7115         /**
7116          * Sets the element's visibility mode. When setVisible() is called it
7117          * will use this to determine whether to set the visibility or the display property.
7118          * @param visMode Element.VISIBILITY or Element.DISPLAY
7119          * @return {Roo.Element} this
7120          */
7121         setVisibilityMode : function(visMode){
7122             this.visibilityMode = visMode;
7123             return this;
7124         },
7125         /**
7126          * Convenience method for setVisibilityMode(Element.DISPLAY)
7127          * @param {String} display (optional) What to set display to when visible
7128          * @return {Roo.Element} this
7129          */
7130         enableDisplayMode : function(display){
7131             this.setVisibilityMode(El.DISPLAY);
7132             if(typeof display != "undefined") { this.originalDisplay = display; }
7133             return this;
7134         },
7135
7136         /**
7137          * 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)
7138          * @param {String} selector The simple selector to test
7139          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7140                 search as a number or element (defaults to 10 || document.body)
7141          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7142          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7143          */
7144         findParent : function(simpleSelector, maxDepth, returnEl){
7145             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7146             maxDepth = maxDepth || 50;
7147             if(typeof maxDepth != "number"){
7148                 stopEl = Roo.getDom(maxDepth);
7149                 maxDepth = 10;
7150             }
7151             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7152                 if(dq.is(p, simpleSelector)){
7153                     return returnEl ? Roo.get(p) : p;
7154                 }
7155                 depth++;
7156                 p = p.parentNode;
7157             }
7158             return null;
7159         },
7160
7161
7162         /**
7163          * Looks 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         findParentNode : function(simpleSelector, maxDepth, returnEl){
7171             var p = Roo.fly(this.dom.parentNode, '_internal');
7172             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7173         },
7174         
7175         /**
7176          * Looks at  the scrollable parent element
7177          */
7178         findScrollableParent : function()
7179         {
7180             var overflowRegex = /(auto|scroll)/;
7181             
7182             if(this.getStyle('position') === 'fixed'){
7183                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7184             }
7185             
7186             var excludeStaticParent = this.getStyle('position') === "absolute";
7187             
7188             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7189                 
7190                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7191                     continue;
7192                 }
7193                 
7194                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7195                     return parent;
7196                 }
7197                 
7198                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7199                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7200                 }
7201             }
7202             
7203             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7204         },
7205
7206         /**
7207          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7208          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7209          * @param {String} selector The simple selector to test
7210          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7211                 search as a number or element (defaults to 10 || document.body)
7212          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7213          */
7214         up : function(simpleSelector, maxDepth){
7215             return this.findParentNode(simpleSelector, maxDepth, true);
7216         },
7217
7218
7219
7220         /**
7221          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7222          * @param {String} selector The simple selector to test
7223          * @return {Boolean} True if this element matches the selector, else false
7224          */
7225         is : function(simpleSelector){
7226             return Roo.DomQuery.is(this.dom, simpleSelector);
7227         },
7228
7229         /**
7230          * Perform animation on this element.
7231          * @param {Object} args The YUI animation control args
7232          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7233          * @param {Function} onComplete (optional) Function to call when animation completes
7234          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7235          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7236          * @return {Roo.Element} this
7237          */
7238         animate : function(args, duration, onComplete, easing, animType){
7239             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7240             return this;
7241         },
7242
7243         /*
7244          * @private Internal animation call
7245          */
7246         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7247             animType = animType || 'run';
7248             opt = opt || {};
7249             var anim = Roo.lib.Anim[animType](
7250                 this.dom, args,
7251                 (opt.duration || defaultDur) || .35,
7252                 (opt.easing || defaultEase) || 'easeOut',
7253                 function(){
7254                     Roo.callback(cb, this);
7255                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7256                 },
7257                 this
7258             );
7259             opt.anim = anim;
7260             return anim;
7261         },
7262
7263         // private legacy anim prep
7264         preanim : function(a, i){
7265             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7266         },
7267
7268         /**
7269          * Removes worthless text nodes
7270          * @param {Boolean} forceReclean (optional) By default the element
7271          * keeps track if it has been cleaned already so
7272          * you can call this over and over. However, if you update the element and
7273          * need to force a reclean, you can pass true.
7274          */
7275         clean : function(forceReclean){
7276             if(this.isCleaned && forceReclean !== true){
7277                 return this;
7278             }
7279             var ns = /\S/;
7280             var d = this.dom, n = d.firstChild, ni = -1;
7281             while(n){
7282                 var nx = n.nextSibling;
7283                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7284                     d.removeChild(n);
7285                 }else{
7286                     n.nodeIndex = ++ni;
7287                 }
7288                 n = nx;
7289             }
7290             this.isCleaned = true;
7291             return this;
7292         },
7293
7294         // private
7295         calcOffsetsTo : function(el){
7296             el = Roo.get(el);
7297             var d = el.dom;
7298             var restorePos = false;
7299             if(el.getStyle('position') == 'static'){
7300                 el.position('relative');
7301                 restorePos = true;
7302             }
7303             var x = 0, y =0;
7304             var op = this.dom;
7305             while(op && op != d && op.tagName != 'HTML'){
7306                 x+= op.offsetLeft;
7307                 y+= op.offsetTop;
7308                 op = op.offsetParent;
7309             }
7310             if(restorePos){
7311                 el.position('static');
7312             }
7313             return [x, y];
7314         },
7315
7316         /**
7317          * Scrolls this element into view within the passed container.
7318          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7319          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7320          * @return {Roo.Element} this
7321          */
7322         scrollIntoView : function(container, hscroll){
7323             var c = Roo.getDom(container) || document.body;
7324             var el = this.dom;
7325
7326             var o = this.calcOffsetsTo(c),
7327                 l = o[0],
7328                 t = o[1],
7329                 b = t+el.offsetHeight,
7330                 r = l+el.offsetWidth;
7331
7332             var ch = c.clientHeight;
7333             var ct = parseInt(c.scrollTop, 10);
7334             var cl = parseInt(c.scrollLeft, 10);
7335             var cb = ct + ch;
7336             var cr = cl + c.clientWidth;
7337
7338             if(t < ct){
7339                 c.scrollTop = t;
7340             }else if(b > cb){
7341                 c.scrollTop = b-ch;
7342             }
7343
7344             if(hscroll !== false){
7345                 if(l < cl){
7346                     c.scrollLeft = l;
7347                 }else if(r > cr){
7348                     c.scrollLeft = r-c.clientWidth;
7349                 }
7350             }
7351             return this;
7352         },
7353
7354         // private
7355         scrollChildIntoView : function(child, hscroll){
7356             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7357         },
7358
7359         /**
7360          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7361          * the new height may not be available immediately.
7362          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7363          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7364          * @param {Function} onComplete (optional) Function to call when animation completes
7365          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7366          * @return {Roo.Element} this
7367          */
7368         autoHeight : function(animate, duration, onComplete, easing){
7369             var oldHeight = this.getHeight();
7370             this.clip();
7371             this.setHeight(1); // force clipping
7372             setTimeout(function(){
7373                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7374                 if(!animate){
7375                     this.setHeight(height);
7376                     this.unclip();
7377                     if(typeof onComplete == "function"){
7378                         onComplete();
7379                     }
7380                 }else{
7381                     this.setHeight(oldHeight); // restore original height
7382                     this.setHeight(height, animate, duration, function(){
7383                         this.unclip();
7384                         if(typeof onComplete == "function") { onComplete(); }
7385                     }.createDelegate(this), easing);
7386                 }
7387             }.createDelegate(this), 0);
7388             return this;
7389         },
7390
7391         /**
7392          * Returns true if this element is an ancestor of the passed element
7393          * @param {HTMLElement/String} el The element to check
7394          * @return {Boolean} True if this element is an ancestor of el, else false
7395          */
7396         contains : function(el){
7397             if(!el){return false;}
7398             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7399         },
7400
7401         /**
7402          * Checks whether the element is currently visible using both visibility and display properties.
7403          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7404          * @return {Boolean} True if the element is currently visible, else false
7405          */
7406         isVisible : function(deep) {
7407             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7408             if(deep !== true || !vis){
7409                 return vis;
7410             }
7411             var p = this.dom.parentNode;
7412             while(p && p.tagName.toLowerCase() != "body"){
7413                 if(!Roo.fly(p, '_isVisible').isVisible()){
7414                     return false;
7415                 }
7416                 p = p.parentNode;
7417             }
7418             return true;
7419         },
7420
7421         /**
7422          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7423          * @param {String} selector The CSS selector
7424          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7425          * @return {CompositeElement/CompositeElementLite} The composite element
7426          */
7427         select : function(selector, unique){
7428             return El.select(selector, unique, this.dom);
7429         },
7430
7431         /**
7432          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7433          * @param {String} selector The CSS selector
7434          * @return {Array} An array of the matched nodes
7435          */
7436         query : function(selector, unique){
7437             return Roo.DomQuery.select(selector, this.dom);
7438         },
7439
7440         /**
7441          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7442          * @param {String} selector The CSS selector
7443          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7444          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7445          */
7446         child : function(selector, returnDom){
7447             var n = Roo.DomQuery.selectNode(selector, this.dom);
7448             return returnDom ? n : Roo.get(n);
7449         },
7450
7451         /**
7452          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7453          * @param {String} selector The CSS selector
7454          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7455          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7456          */
7457         down : function(selector, returnDom){
7458             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7459             return returnDom ? n : Roo.get(n);
7460         },
7461
7462         /**
7463          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7464          * @param {String} group The group the DD object is member of
7465          * @param {Object} config The DD config object
7466          * @param {Object} overrides An object containing methods to override/implement on the DD object
7467          * @return {Roo.dd.DD} The DD object
7468          */
7469         initDD : function(group, config, overrides){
7470             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7471             return Roo.apply(dd, overrides);
7472         },
7473
7474         /**
7475          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7476          * @param {String} group The group the DDProxy object is member of
7477          * @param {Object} config The DDProxy config object
7478          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7479          * @return {Roo.dd.DDProxy} The DDProxy object
7480          */
7481         initDDProxy : function(group, config, overrides){
7482             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7483             return Roo.apply(dd, overrides);
7484         },
7485
7486         /**
7487          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7488          * @param {String} group The group the DDTarget object is member of
7489          * @param {Object} config The DDTarget config object
7490          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7491          * @return {Roo.dd.DDTarget} The DDTarget object
7492          */
7493         initDDTarget : function(group, config, overrides){
7494             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7495             return Roo.apply(dd, overrides);
7496         },
7497
7498         /**
7499          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7500          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7501          * @param {Boolean} visible Whether the element is visible
7502          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7503          * @return {Roo.Element} this
7504          */
7505          setVisible : function(visible, animate){
7506             if(!animate || !A){
7507                 if(this.visibilityMode == El.DISPLAY){
7508                     this.setDisplayed(visible);
7509                 }else{
7510                     this.fixDisplay();
7511                     this.dom.style.visibility = visible ? "visible" : "hidden";
7512                 }
7513             }else{
7514                 // closure for composites
7515                 var dom = this.dom;
7516                 var visMode = this.visibilityMode;
7517                 if(visible){
7518                     this.setOpacity(.01);
7519                     this.setVisible(true);
7520                 }
7521                 this.anim({opacity: { to: (visible?1:0) }},
7522                       this.preanim(arguments, 1),
7523                       null, .35, 'easeIn', function(){
7524                          if(!visible){
7525                              if(visMode == El.DISPLAY){
7526                                  dom.style.display = "none";
7527                              }else{
7528                                  dom.style.visibility = "hidden";
7529                              }
7530                              Roo.get(dom).setOpacity(1);
7531                          }
7532                      });
7533             }
7534             return this;
7535         },
7536
7537         /**
7538          * Returns true if display is not "none"
7539          * @return {Boolean}
7540          */
7541         isDisplayed : function() {
7542             return this.getStyle("display") != "none";
7543         },
7544
7545         /**
7546          * Toggles the element's visibility or display, depending on visibility mode.
7547          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7548          * @return {Roo.Element} this
7549          */
7550         toggle : function(animate){
7551             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7552             return this;
7553         },
7554
7555         /**
7556          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7557          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7558          * @return {Roo.Element} this
7559          */
7560         setDisplayed : function(value) {
7561             if(typeof value == "boolean"){
7562                value = value ? this.originalDisplay : "none";
7563             }
7564             this.setStyle("display", value);
7565             return this;
7566         },
7567
7568         /**
7569          * Tries to focus the element. Any exceptions are caught and ignored.
7570          * @return {Roo.Element} this
7571          */
7572         focus : function() {
7573             try{
7574                 this.dom.focus();
7575             }catch(e){}
7576             return this;
7577         },
7578
7579         /**
7580          * Tries to blur the element. Any exceptions are caught and ignored.
7581          * @return {Roo.Element} this
7582          */
7583         blur : function() {
7584             try{
7585                 this.dom.blur();
7586             }catch(e){}
7587             return this;
7588         },
7589
7590         /**
7591          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7592          * @param {String/Array} className The CSS class to add, or an array of classes
7593          * @return {Roo.Element} this
7594          */
7595         addClass : function(className){
7596             if(className instanceof Array){
7597                 for(var i = 0, len = className.length; i < len; i++) {
7598                     this.addClass(className[i]);
7599                 }
7600             }else{
7601                 if(className && !this.hasClass(className)){
7602                     this.dom.className = this.dom.className + " " + className;
7603                 }
7604             }
7605             return this;
7606         },
7607
7608         /**
7609          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7610          * @param {String/Array} className The CSS class to add, or an array of classes
7611          * @return {Roo.Element} this
7612          */
7613         radioClass : function(className){
7614             var siblings = this.dom.parentNode.childNodes;
7615             for(var i = 0; i < siblings.length; i++) {
7616                 var s = siblings[i];
7617                 if(s.nodeType == 1){
7618                     Roo.get(s).removeClass(className);
7619                 }
7620             }
7621             this.addClass(className);
7622             return this;
7623         },
7624
7625         /**
7626          * Removes one or more CSS classes from the element.
7627          * @param {String/Array} className The CSS class to remove, or an array of classes
7628          * @return {Roo.Element} this
7629          */
7630         removeClass : function(className){
7631             if(!className || !this.dom.className){
7632                 return this;
7633             }
7634             if(className instanceof Array){
7635                 for(var i = 0, len = className.length; i < len; i++) {
7636                     this.removeClass(className[i]);
7637                 }
7638             }else{
7639                 if(this.hasClass(className)){
7640                     var re = this.classReCache[className];
7641                     if (!re) {
7642                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7643                        this.classReCache[className] = re;
7644                     }
7645                     this.dom.className =
7646                         this.dom.className.replace(re, " ");
7647                 }
7648             }
7649             return this;
7650         },
7651
7652         // private
7653         classReCache: {},
7654
7655         /**
7656          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7657          * @param {String} className The CSS class to toggle
7658          * @return {Roo.Element} this
7659          */
7660         toggleClass : function(className){
7661             if(this.hasClass(className)){
7662                 this.removeClass(className);
7663             }else{
7664                 this.addClass(className);
7665             }
7666             return this;
7667         },
7668
7669         /**
7670          * Checks if the specified CSS class exists on this element's DOM node.
7671          * @param {String} className The CSS class to check for
7672          * @return {Boolean} True if the class exists, else false
7673          */
7674         hasClass : function(className){
7675             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7676         },
7677
7678         /**
7679          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7680          * @param {String} oldClassName The CSS class to replace
7681          * @param {String} newClassName The replacement CSS class
7682          * @return {Roo.Element} this
7683          */
7684         replaceClass : function(oldClassName, newClassName){
7685             this.removeClass(oldClassName);
7686             this.addClass(newClassName);
7687             return this;
7688         },
7689
7690         /**
7691          * Returns an object with properties matching the styles requested.
7692          * For example, el.getStyles('color', 'font-size', 'width') might return
7693          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7694          * @param {String} style1 A style name
7695          * @param {String} style2 A style name
7696          * @param {String} etc.
7697          * @return {Object} The style object
7698          */
7699         getStyles : function(){
7700             var a = arguments, len = a.length, r = {};
7701             for(var i = 0; i < len; i++){
7702                 r[a[i]] = this.getStyle(a[i]);
7703             }
7704             return r;
7705         },
7706
7707         /**
7708          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7709          * @param {String} property The style property whose value is returned.
7710          * @return {String} The current value of the style property for this element.
7711          */
7712         getStyle : function(){
7713             return view && view.getComputedStyle ?
7714                 function(prop){
7715                     var el = this.dom, v, cs, camel;
7716                     if(prop == 'float'){
7717                         prop = "cssFloat";
7718                     }
7719                     if(el.style && (v = el.style[prop])){
7720                         return v;
7721                     }
7722                     if(cs = view.getComputedStyle(el, "")){
7723                         if(!(camel = propCache[prop])){
7724                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7725                         }
7726                         return cs[camel];
7727                     }
7728                     return null;
7729                 } :
7730                 function(prop){
7731                     var el = this.dom, v, cs, camel;
7732                     if(prop == 'opacity'){
7733                         if(typeof el.style.filter == 'string'){
7734                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7735                             if(m){
7736                                 var fv = parseFloat(m[1]);
7737                                 if(!isNaN(fv)){
7738                                     return fv ? fv / 100 : 0;
7739                                 }
7740                             }
7741                         }
7742                         return 1;
7743                     }else if(prop == 'float'){
7744                         prop = "styleFloat";
7745                     }
7746                     if(!(camel = propCache[prop])){
7747                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7748                     }
7749                     if(v = el.style[camel]){
7750                         return v;
7751                     }
7752                     if(cs = el.currentStyle){
7753                         return cs[camel];
7754                     }
7755                     return null;
7756                 };
7757         }(),
7758
7759         /**
7760          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7761          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7762          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7763          * @return {Roo.Element} this
7764          */
7765         setStyle : function(prop, value){
7766             if(typeof prop == "string"){
7767                 
7768                 if (prop == 'float') {
7769                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7770                     return this;
7771                 }
7772                 
7773                 var camel;
7774                 if(!(camel = propCache[prop])){
7775                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7776                 }
7777                 
7778                 if(camel == 'opacity') {
7779                     this.setOpacity(value);
7780                 }else{
7781                     this.dom.style[camel] = value;
7782                 }
7783             }else{
7784                 for(var style in prop){
7785                     if(typeof prop[style] != "function"){
7786                        this.setStyle(style, prop[style]);
7787                     }
7788                 }
7789             }
7790             return this;
7791         },
7792
7793         /**
7794          * More flexible version of {@link #setStyle} for setting style properties.
7795          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7796          * a function which returns such a specification.
7797          * @return {Roo.Element} this
7798          */
7799         applyStyles : function(style){
7800             Roo.DomHelper.applyStyles(this.dom, style);
7801             return this;
7802         },
7803
7804         /**
7805           * 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).
7806           * @return {Number} The X position of the element
7807           */
7808         getX : function(){
7809             return D.getX(this.dom);
7810         },
7811
7812         /**
7813           * 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).
7814           * @return {Number} The Y position of the element
7815           */
7816         getY : function(){
7817             return D.getY(this.dom);
7818         },
7819
7820         /**
7821           * 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).
7822           * @return {Array} The XY position of the element
7823           */
7824         getXY : function(){
7825             return D.getXY(this.dom);
7826         },
7827
7828         /**
7829          * 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).
7830          * @param {Number} The X position of the element
7831          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7832          * @return {Roo.Element} this
7833          */
7834         setX : function(x, animate){
7835             if(!animate || !A){
7836                 D.setX(this.dom, x);
7837             }else{
7838                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7839             }
7840             return this;
7841         },
7842
7843         /**
7844          * 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).
7845          * @param {Number} The Y position of the element
7846          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7847          * @return {Roo.Element} this
7848          */
7849         setY : function(y, animate){
7850             if(!animate || !A){
7851                 D.setY(this.dom, y);
7852             }else{
7853                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7854             }
7855             return this;
7856         },
7857
7858         /**
7859          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7860          * @param {String} left The left CSS property value
7861          * @return {Roo.Element} this
7862          */
7863         setLeft : function(left){
7864             this.setStyle("left", this.addUnits(left));
7865             return this;
7866         },
7867
7868         /**
7869          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7870          * @param {String} top The top CSS property value
7871          * @return {Roo.Element} this
7872          */
7873         setTop : function(top){
7874             this.setStyle("top", this.addUnits(top));
7875             return this;
7876         },
7877
7878         /**
7879          * Sets the element's CSS right style.
7880          * @param {String} right The right CSS property value
7881          * @return {Roo.Element} this
7882          */
7883         setRight : function(right){
7884             this.setStyle("right", this.addUnits(right));
7885             return this;
7886         },
7887
7888         /**
7889          * Sets the element's CSS bottom style.
7890          * @param {String} bottom The bottom CSS property value
7891          * @return {Roo.Element} this
7892          */
7893         setBottom : function(bottom){
7894             this.setStyle("bottom", this.addUnits(bottom));
7895             return this;
7896         },
7897
7898         /**
7899          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7900          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7901          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7902          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7903          * @return {Roo.Element} this
7904          */
7905         setXY : function(pos, animate){
7906             if(!animate || !A){
7907                 D.setXY(this.dom, pos);
7908             }else{
7909                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7910             }
7911             return this;
7912         },
7913
7914         /**
7915          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7916          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7917          * @param {Number} x X value for new position (coordinates are page-based)
7918          * @param {Number} y Y value for new position (coordinates are page-based)
7919          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7920          * @return {Roo.Element} this
7921          */
7922         setLocation : function(x, y, animate){
7923             this.setXY([x, y], this.preanim(arguments, 2));
7924             return this;
7925         },
7926
7927         /**
7928          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7929          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7930          * @param {Number} x X value for new position (coordinates are page-based)
7931          * @param {Number} y Y value for new position (coordinates are page-based)
7932          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7933          * @return {Roo.Element} this
7934          */
7935         moveTo : function(x, y, animate){
7936             this.setXY([x, y], this.preanim(arguments, 2));
7937             return this;
7938         },
7939
7940         /**
7941          * Returns the region of the given element.
7942          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7943          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7944          */
7945         getRegion : function(){
7946             return D.getRegion(this.dom);
7947         },
7948
7949         /**
7950          * Returns the offset height of the element
7951          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7952          * @return {Number} The element's height
7953          */
7954         getHeight : function(contentHeight){
7955             var h = this.dom.offsetHeight || 0;
7956             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7957         },
7958
7959         /**
7960          * Returns the offset width of the element
7961          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7962          * @return {Number} The element's width
7963          */
7964         getWidth : function(contentWidth){
7965             var w = this.dom.offsetWidth || 0;
7966             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7967         },
7968
7969         /**
7970          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7971          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7972          * if a height has not been set using CSS.
7973          * @return {Number}
7974          */
7975         getComputedHeight : function(){
7976             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7977             if(!h){
7978                 h = parseInt(this.getStyle('height'), 10) || 0;
7979                 if(!this.isBorderBox()){
7980                     h += this.getFrameWidth('tb');
7981                 }
7982             }
7983             return h;
7984         },
7985
7986         /**
7987          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7988          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7989          * if a width has not been set using CSS.
7990          * @return {Number}
7991          */
7992         getComputedWidth : function(){
7993             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7994             if(!w){
7995                 w = parseInt(this.getStyle('width'), 10) || 0;
7996                 if(!this.isBorderBox()){
7997                     w += this.getFrameWidth('lr');
7998                 }
7999             }
8000             return w;
8001         },
8002
8003         /**
8004          * Returns the size of the element.
8005          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8006          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8007          */
8008         getSize : function(contentSize){
8009             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8010         },
8011
8012         /**
8013          * Returns the width and height of the viewport.
8014          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8015          */
8016         getViewSize : function(){
8017             var d = this.dom, doc = document, aw = 0, ah = 0;
8018             if(d == doc || d == doc.body){
8019                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8020             }else{
8021                 return {
8022                     width : d.clientWidth,
8023                     height: d.clientHeight
8024                 };
8025             }
8026         },
8027
8028         /**
8029          * Returns the value of the "value" attribute
8030          * @param {Boolean} asNumber true to parse the value as a number
8031          * @return {String/Number}
8032          */
8033         getValue : function(asNumber){
8034             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8035         },
8036
8037         // private
8038         adjustWidth : function(width){
8039             if(typeof width == "number"){
8040                 if(this.autoBoxAdjust && !this.isBorderBox()){
8041                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8042                 }
8043                 if(width < 0){
8044                     width = 0;
8045                 }
8046             }
8047             return width;
8048         },
8049
8050         // private
8051         adjustHeight : function(height){
8052             if(typeof height == "number"){
8053                if(this.autoBoxAdjust && !this.isBorderBox()){
8054                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8055                }
8056                if(height < 0){
8057                    height = 0;
8058                }
8059             }
8060             return height;
8061         },
8062
8063         /**
8064          * Set the width of the element
8065          * @param {Number} width The new width
8066          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8067          * @return {Roo.Element} this
8068          */
8069         setWidth : function(width, animate){
8070             width = this.adjustWidth(width);
8071             if(!animate || !A){
8072                 this.dom.style.width = this.addUnits(width);
8073             }else{
8074                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8075             }
8076             return this;
8077         },
8078
8079         /**
8080          * Set the height of the element
8081          * @param {Number} height The new height
8082          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8083          * @return {Roo.Element} this
8084          */
8085          setHeight : function(height, animate){
8086             height = this.adjustHeight(height);
8087             if(!animate || !A){
8088                 this.dom.style.height = this.addUnits(height);
8089             }else{
8090                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8091             }
8092             return this;
8093         },
8094
8095         /**
8096          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8097          * @param {Number} width The new width
8098          * @param {Number} height The new height
8099          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8100          * @return {Roo.Element} this
8101          */
8102          setSize : function(width, height, animate){
8103             if(typeof width == "object"){ // in case of object from getSize()
8104                 height = width.height; width = width.width;
8105             }
8106             width = this.adjustWidth(width); height = this.adjustHeight(height);
8107             if(!animate || !A){
8108                 this.dom.style.width = this.addUnits(width);
8109                 this.dom.style.height = this.addUnits(height);
8110             }else{
8111                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8112             }
8113             return this;
8114         },
8115
8116         /**
8117          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8118          * @param {Number} x X value for new position (coordinates are page-based)
8119          * @param {Number} y Y value for new position (coordinates are page-based)
8120          * @param {Number} width The new width
8121          * @param {Number} height The new height
8122          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8123          * @return {Roo.Element} this
8124          */
8125         setBounds : function(x, y, width, height, animate){
8126             if(!animate || !A){
8127                 this.setSize(width, height);
8128                 this.setLocation(x, y);
8129             }else{
8130                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8131                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8132                               this.preanim(arguments, 4), 'motion');
8133             }
8134             return this;
8135         },
8136
8137         /**
8138          * 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.
8139          * @param {Roo.lib.Region} region The region to fill
8140          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8141          * @return {Roo.Element} this
8142          */
8143         setRegion : function(region, animate){
8144             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8145             return this;
8146         },
8147
8148         /**
8149          * Appends an event handler
8150          *
8151          * @param {String}   eventName     The type of event to append
8152          * @param {Function} fn        The method the event invokes
8153          * @param {Object} scope       (optional) The scope (this object) of the fn
8154          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8155          */
8156         addListener : function(eventName, fn, scope, options){
8157             if (this.dom) {
8158                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8159             }
8160         },
8161
8162         /**
8163          * Removes an event handler from this element
8164          * @param {String} eventName the type of event to remove
8165          * @param {Function} fn the method the event invokes
8166          * @return {Roo.Element} this
8167          */
8168         removeListener : function(eventName, fn){
8169             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8170             return this;
8171         },
8172
8173         /**
8174          * Removes all previous added listeners from this element
8175          * @return {Roo.Element} this
8176          */
8177         removeAllListeners : function(){
8178             E.purgeElement(this.dom);
8179             return this;
8180         },
8181
8182         relayEvent : function(eventName, observable){
8183             this.on(eventName, function(e){
8184                 observable.fireEvent(eventName, e);
8185             });
8186         },
8187
8188         /**
8189          * Set the opacity of the element
8190          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8191          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8192          * @return {Roo.Element} this
8193          */
8194          setOpacity : function(opacity, animate){
8195             if(!animate || !A){
8196                 var s = this.dom.style;
8197                 if(Roo.isIE){
8198                     s.zoom = 1;
8199                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8200                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8201                 }else{
8202                     s.opacity = opacity;
8203                 }
8204             }else{
8205                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8206             }
8207             return this;
8208         },
8209
8210         /**
8211          * Gets the left X coordinate
8212          * @param {Boolean} local True to get the local css position instead of page coordinate
8213          * @return {Number}
8214          */
8215         getLeft : function(local){
8216             if(!local){
8217                 return this.getX();
8218             }else{
8219                 return parseInt(this.getStyle("left"), 10) || 0;
8220             }
8221         },
8222
8223         /**
8224          * Gets the right X coordinate of the element (element X position + element width)
8225          * @param {Boolean} local True to get the local css position instead of page coordinate
8226          * @return {Number}
8227          */
8228         getRight : function(local){
8229             if(!local){
8230                 return this.getX() + this.getWidth();
8231             }else{
8232                 return (this.getLeft(true) + this.getWidth()) || 0;
8233             }
8234         },
8235
8236         /**
8237          * Gets the top Y coordinate
8238          * @param {Boolean} local True to get the local css position instead of page coordinate
8239          * @return {Number}
8240          */
8241         getTop : function(local) {
8242             if(!local){
8243                 return this.getY();
8244             }else{
8245                 return parseInt(this.getStyle("top"), 10) || 0;
8246             }
8247         },
8248
8249         /**
8250          * Gets the bottom Y coordinate of the element (element Y position + element height)
8251          * @param {Boolean} local True to get the local css position instead of page coordinate
8252          * @return {Number}
8253          */
8254         getBottom : function(local){
8255             if(!local){
8256                 return this.getY() + this.getHeight();
8257             }else{
8258                 return (this.getTop(true) + this.getHeight()) || 0;
8259             }
8260         },
8261
8262         /**
8263         * Initializes positioning on this element. If a desired position is not passed, it will make the
8264         * the element positioned relative IF it is not already positioned.
8265         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8266         * @param {Number} zIndex (optional) The zIndex to apply
8267         * @param {Number} x (optional) Set the page X position
8268         * @param {Number} y (optional) Set the page Y position
8269         */
8270         position : function(pos, zIndex, x, y){
8271             if(!pos){
8272                if(this.getStyle('position') == 'static'){
8273                    this.setStyle('position', 'relative');
8274                }
8275             }else{
8276                 this.setStyle("position", pos);
8277             }
8278             if(zIndex){
8279                 this.setStyle("z-index", zIndex);
8280             }
8281             if(x !== undefined && y !== undefined){
8282                 this.setXY([x, y]);
8283             }else if(x !== undefined){
8284                 this.setX(x);
8285             }else if(y !== undefined){
8286                 this.setY(y);
8287             }
8288         },
8289
8290         /**
8291         * Clear positioning back to the default when the document was loaded
8292         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8293         * @return {Roo.Element} this
8294          */
8295         clearPositioning : function(value){
8296             value = value ||'';
8297             this.setStyle({
8298                 "left": value,
8299                 "right": value,
8300                 "top": value,
8301                 "bottom": value,
8302                 "z-index": "",
8303                 "position" : "static"
8304             });
8305             return this;
8306         },
8307
8308         /**
8309         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8310         * snapshot before performing an update and then restoring the element.
8311         * @return {Object}
8312         */
8313         getPositioning : function(){
8314             var l = this.getStyle("left");
8315             var t = this.getStyle("top");
8316             return {
8317                 "position" : this.getStyle("position"),
8318                 "left" : l,
8319                 "right" : l ? "" : this.getStyle("right"),
8320                 "top" : t,
8321                 "bottom" : t ? "" : this.getStyle("bottom"),
8322                 "z-index" : this.getStyle("z-index")
8323             };
8324         },
8325
8326         /**
8327          * Gets the width of the border(s) for the specified side(s)
8328          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8329          * passing lr would get the border (l)eft width + the border (r)ight width.
8330          * @return {Number} The width of the sides passed added together
8331          */
8332         getBorderWidth : function(side){
8333             return this.addStyles(side, El.borders);
8334         },
8335
8336         /**
8337          * Gets the width of the padding(s) for the specified side(s)
8338          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8339          * passing lr would get the padding (l)eft + the padding (r)ight.
8340          * @return {Number} The padding of the sides passed added together
8341          */
8342         getPadding : function(side){
8343             return this.addStyles(side, El.paddings);
8344         },
8345
8346         /**
8347         * Set positioning with an object returned by getPositioning().
8348         * @param {Object} posCfg
8349         * @return {Roo.Element} this
8350          */
8351         setPositioning : function(pc){
8352             this.applyStyles(pc);
8353             if(pc.right == "auto"){
8354                 this.dom.style.right = "";
8355             }
8356             if(pc.bottom == "auto"){
8357                 this.dom.style.bottom = "";
8358             }
8359             return this;
8360         },
8361
8362         // private
8363         fixDisplay : function(){
8364             if(this.getStyle("display") == "none"){
8365                 this.setStyle("visibility", "hidden");
8366                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8367                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8368                     this.setStyle("display", "block");
8369                 }
8370             }
8371         },
8372
8373         /**
8374          * Quick set left and top adding default units
8375          * @param {String} left The left CSS property value
8376          * @param {String} top The top CSS property value
8377          * @return {Roo.Element} this
8378          */
8379          setLeftTop : function(left, top){
8380             this.dom.style.left = this.addUnits(left);
8381             this.dom.style.top = this.addUnits(top);
8382             return this;
8383         },
8384
8385         /**
8386          * Move this element relative to its current position.
8387          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8388          * @param {Number} distance How far to move the element in pixels
8389          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8390          * @return {Roo.Element} this
8391          */
8392          move : function(direction, distance, animate){
8393             var xy = this.getXY();
8394             direction = direction.toLowerCase();
8395             switch(direction){
8396                 case "l":
8397                 case "left":
8398                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8399                     break;
8400                case "r":
8401                case "right":
8402                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8403                     break;
8404                case "t":
8405                case "top":
8406                case "up":
8407                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8408                     break;
8409                case "b":
8410                case "bottom":
8411                case "down":
8412                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8413                     break;
8414             }
8415             return this;
8416         },
8417
8418         /**
8419          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8420          * @return {Roo.Element} this
8421          */
8422         clip : function(){
8423             if(!this.isClipped){
8424                this.isClipped = true;
8425                this.originalClip = {
8426                    "o": this.getStyle("overflow"),
8427                    "x": this.getStyle("overflow-x"),
8428                    "y": this.getStyle("overflow-y")
8429                };
8430                this.setStyle("overflow", "hidden");
8431                this.setStyle("overflow-x", "hidden");
8432                this.setStyle("overflow-y", "hidden");
8433             }
8434             return this;
8435         },
8436
8437         /**
8438          *  Return clipping (overflow) to original clipping before clip() was called
8439          * @return {Roo.Element} this
8440          */
8441         unclip : function(){
8442             if(this.isClipped){
8443                 this.isClipped = false;
8444                 var o = this.originalClip;
8445                 if(o.o){this.setStyle("overflow", o.o);}
8446                 if(o.x){this.setStyle("overflow-x", o.x);}
8447                 if(o.y){this.setStyle("overflow-y", o.y);}
8448             }
8449             return this;
8450         },
8451
8452
8453         /**
8454          * Gets the x,y coordinates specified by the anchor position on the element.
8455          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8456          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8457          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8458          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8459          * @return {Array} [x, y] An array containing the element's x and y coordinates
8460          */
8461         getAnchorXY : function(anchor, local, s){
8462             //Passing a different size is useful for pre-calculating anchors,
8463             //especially for anchored animations that change the el size.
8464
8465             var w, h, vp = false;
8466             if(!s){
8467                 var d = this.dom;
8468                 if(d == document.body || d == document){
8469                     vp = true;
8470                     w = D.getViewWidth(); h = D.getViewHeight();
8471                 }else{
8472                     w = this.getWidth(); h = this.getHeight();
8473                 }
8474             }else{
8475                 w = s.width;  h = s.height;
8476             }
8477             var x = 0, y = 0, r = Math.round;
8478             switch((anchor || "tl").toLowerCase()){
8479                 case "c":
8480                     x = r(w*.5);
8481                     y = r(h*.5);
8482                 break;
8483                 case "t":
8484                     x = r(w*.5);
8485                     y = 0;
8486                 break;
8487                 case "l":
8488                     x = 0;
8489                     y = r(h*.5);
8490                 break;
8491                 case "r":
8492                     x = w;
8493                     y = r(h*.5);
8494                 break;
8495                 case "b":
8496                     x = r(w*.5);
8497                     y = h;
8498                 break;
8499                 case "tl":
8500                     x = 0;
8501                     y = 0;
8502                 break;
8503                 case "bl":
8504                     x = 0;
8505                     y = h;
8506                 break;
8507                 case "br":
8508                     x = w;
8509                     y = h;
8510                 break;
8511                 case "tr":
8512                     x = w;
8513                     y = 0;
8514                 break;
8515             }
8516             if(local === true){
8517                 return [x, y];
8518             }
8519             if(vp){
8520                 var sc = this.getScroll();
8521                 return [x + sc.left, y + sc.top];
8522             }
8523             //Add the element's offset xy
8524             var o = this.getXY();
8525             return [x+o[0], y+o[1]];
8526         },
8527
8528         /**
8529          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8530          * supported position values.
8531          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8532          * @param {String} position The position to align to.
8533          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8534          * @return {Array} [x, y]
8535          */
8536         getAlignToXY : function(el, p, o){
8537             el = Roo.get(el);
8538             var d = this.dom;
8539             if(!el.dom){
8540                 throw "Element.alignTo with an element that doesn't exist";
8541             }
8542             var c = false; //constrain to viewport
8543             var p1 = "", p2 = "";
8544             o = o || [0,0];
8545
8546             if(!p){
8547                 p = "tl-bl";
8548             }else if(p == "?"){
8549                 p = "tl-bl?";
8550             }else if(p.indexOf("-") == -1){
8551                 p = "tl-" + p;
8552             }
8553             p = p.toLowerCase();
8554             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8555             if(!m){
8556                throw "Element.alignTo with an invalid alignment " + p;
8557             }
8558             p1 = m[1]; p2 = m[2]; c = !!m[3];
8559
8560             //Subtract the aligned el's internal xy from the target's offset xy
8561             //plus custom offset to get the aligned el's new offset xy
8562             var a1 = this.getAnchorXY(p1, true);
8563             var a2 = el.getAnchorXY(p2, false);
8564             var x = a2[0] - a1[0] + o[0];
8565             var y = a2[1] - a1[1] + o[1];
8566             if(c){
8567                 //constrain the aligned el to viewport if necessary
8568                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8569                 // 5px of margin for ie
8570                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8571
8572                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8573                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8574                 //otherwise swap the aligned el to the opposite border of the target.
8575                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8576                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8577                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8578                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8579
8580                var doc = document;
8581                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8582                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8583
8584                if((x+w) > dw + scrollX){
8585                     x = swapX ? r.left-w : dw+scrollX-w;
8586                 }
8587                if(x < scrollX){
8588                    x = swapX ? r.right : scrollX;
8589                }
8590                if((y+h) > dh + scrollY){
8591                     y = swapY ? r.top-h : dh+scrollY-h;
8592                 }
8593                if (y < scrollY){
8594                    y = swapY ? r.bottom : scrollY;
8595                }
8596             }
8597             return [x,y];
8598         },
8599
8600         // private
8601         getConstrainToXY : function(){
8602             var os = {top:0, left:0, bottom:0, right: 0};
8603
8604             return function(el, local, offsets, proposedXY){
8605                 el = Roo.get(el);
8606                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8607
8608                 var vw, vh, vx = 0, vy = 0;
8609                 if(el.dom == document.body || el.dom == document){
8610                     vw = Roo.lib.Dom.getViewWidth();
8611                     vh = Roo.lib.Dom.getViewHeight();
8612                 }else{
8613                     vw = el.dom.clientWidth;
8614                     vh = el.dom.clientHeight;
8615                     if(!local){
8616                         var vxy = el.getXY();
8617                         vx = vxy[0];
8618                         vy = vxy[1];
8619                     }
8620                 }
8621
8622                 var s = el.getScroll();
8623
8624                 vx += offsets.left + s.left;
8625                 vy += offsets.top + s.top;
8626
8627                 vw -= offsets.right;
8628                 vh -= offsets.bottom;
8629
8630                 var vr = vx+vw;
8631                 var vb = vy+vh;
8632
8633                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8634                 var x = xy[0], y = xy[1];
8635                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8636
8637                 // only move it if it needs it
8638                 var moved = false;
8639
8640                 // first validate right/bottom
8641                 if((x + w) > vr){
8642                     x = vr - w;
8643                     moved = true;
8644                 }
8645                 if((y + h) > vb){
8646                     y = vb - h;
8647                     moved = true;
8648                 }
8649                 // then make sure top/left isn't negative
8650                 if(x < vx){
8651                     x = vx;
8652                     moved = true;
8653                 }
8654                 if(y < vy){
8655                     y = vy;
8656                     moved = true;
8657                 }
8658                 return moved ? [x, y] : false;
8659             };
8660         }(),
8661
8662         // private
8663         adjustForConstraints : function(xy, parent, offsets){
8664             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8665         },
8666
8667         /**
8668          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8669          * document it aligns it to the viewport.
8670          * The position parameter is optional, and can be specified in any one of the following formats:
8671          * <ul>
8672          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8673          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8674          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8675          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8676          *   <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
8677          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8678          * </ul>
8679          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8680          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8681          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8682          * that specified in order to enforce the viewport constraints.
8683          * Following are all of the supported anchor positions:
8684     <pre>
8685     Value  Description
8686     -----  -----------------------------
8687     tl     The top left corner (default)
8688     t      The center of the top edge
8689     tr     The top right corner
8690     l      The center of the left edge
8691     c      In the center of the element
8692     r      The center of the right edge
8693     bl     The bottom left corner
8694     b      The center of the bottom edge
8695     br     The bottom right corner
8696     </pre>
8697     Example Usage:
8698     <pre><code>
8699     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8700     el.alignTo("other-el");
8701
8702     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8703     el.alignTo("other-el", "tr?");
8704
8705     // align the bottom right corner of el with the center left edge of other-el
8706     el.alignTo("other-el", "br-l?");
8707
8708     // align the center of el with the bottom left corner of other-el and
8709     // adjust the x position by -6 pixels (and the y position by 0)
8710     el.alignTo("other-el", "c-bl", [-6, 0]);
8711     </code></pre>
8712          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8713          * @param {String} position The position to align to.
8714          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8715          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8716          * @return {Roo.Element} this
8717          */
8718         alignTo : function(element, position, offsets, animate){
8719             var xy = this.getAlignToXY(element, position, offsets);
8720             this.setXY(xy, this.preanim(arguments, 3));
8721             return this;
8722         },
8723
8724         /**
8725          * Anchors an element to another element and realigns it when the window is resized.
8726          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8727          * @param {String} position The position to align to.
8728          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8729          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8730          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8731          * is a number, it is used as the buffer delay (defaults to 50ms).
8732          * @param {Function} callback The function to call after the animation finishes
8733          * @return {Roo.Element} this
8734          */
8735         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8736             var action = function(){
8737                 this.alignTo(el, alignment, offsets, animate);
8738                 Roo.callback(callback, this);
8739             };
8740             Roo.EventManager.onWindowResize(action, this);
8741             var tm = typeof monitorScroll;
8742             if(tm != 'undefined'){
8743                 Roo.EventManager.on(window, 'scroll', action, this,
8744                     {buffer: tm == 'number' ? monitorScroll : 50});
8745             }
8746             action.call(this); // align immediately
8747             return this;
8748         },
8749         /**
8750          * Clears any opacity settings from this element. Required in some cases for IE.
8751          * @return {Roo.Element} this
8752          */
8753         clearOpacity : function(){
8754             if (window.ActiveXObject) {
8755                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8756                     this.dom.style.filter = "";
8757                 }
8758             } else {
8759                 this.dom.style.opacity = "";
8760                 this.dom.style["-moz-opacity"] = "";
8761                 this.dom.style["-khtml-opacity"] = "";
8762             }
8763             return this;
8764         },
8765
8766         /**
8767          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8768          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8769          * @return {Roo.Element} this
8770          */
8771         hide : function(animate){
8772             this.setVisible(false, this.preanim(arguments, 0));
8773             return this;
8774         },
8775
8776         /**
8777         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8778         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8779          * @return {Roo.Element} this
8780          */
8781         show : function(animate){
8782             this.setVisible(true, this.preanim(arguments, 0));
8783             return this;
8784         },
8785
8786         /**
8787          * @private Test if size has a unit, otherwise appends the default
8788          */
8789         addUnits : function(size){
8790             return Roo.Element.addUnits(size, this.defaultUnit);
8791         },
8792
8793         /**
8794          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8795          * @return {Roo.Element} this
8796          */
8797         beginMeasure : function(){
8798             var el = this.dom;
8799             if(el.offsetWidth || el.offsetHeight){
8800                 return this; // offsets work already
8801             }
8802             var changed = [];
8803             var p = this.dom, b = document.body; // start with this element
8804             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8805                 var pe = Roo.get(p);
8806                 if(pe.getStyle('display') == 'none'){
8807                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8808                     p.style.visibility = "hidden";
8809                     p.style.display = "block";
8810                 }
8811                 p = p.parentNode;
8812             }
8813             this._measureChanged = changed;
8814             return this;
8815
8816         },
8817
8818         /**
8819          * Restores displays to before beginMeasure was called
8820          * @return {Roo.Element} this
8821          */
8822         endMeasure : function(){
8823             var changed = this._measureChanged;
8824             if(changed){
8825                 for(var i = 0, len = changed.length; i < len; i++) {
8826                     var r = changed[i];
8827                     r.el.style.visibility = r.visibility;
8828                     r.el.style.display = "none";
8829                 }
8830                 this._measureChanged = null;
8831             }
8832             return this;
8833         },
8834
8835         /**
8836         * Update the innerHTML of this element, optionally searching for and processing scripts
8837         * @param {String} html The new HTML
8838         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8839         * @param {Function} callback For async script loading you can be noticed when the update completes
8840         * @return {Roo.Element} this
8841          */
8842         update : function(html, loadScripts, callback){
8843             if(typeof html == "undefined"){
8844                 html = "";
8845             }
8846             if(loadScripts !== true){
8847                 this.dom.innerHTML = html;
8848                 if(typeof callback == "function"){
8849                     callback();
8850                 }
8851                 return this;
8852             }
8853             var id = Roo.id();
8854             var dom = this.dom;
8855
8856             html += '<span id="' + id + '"></span>';
8857
8858             E.onAvailable(id, function(){
8859                 var hd = document.getElementsByTagName("head")[0];
8860                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8861                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8862                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8863
8864                 var match;
8865                 while(match = re.exec(html)){
8866                     var attrs = match[1];
8867                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8868                     if(srcMatch && srcMatch[2]){
8869                        var s = document.createElement("script");
8870                        s.src = srcMatch[2];
8871                        var typeMatch = attrs.match(typeRe);
8872                        if(typeMatch && typeMatch[2]){
8873                            s.type = typeMatch[2];
8874                        }
8875                        hd.appendChild(s);
8876                     }else if(match[2] && match[2].length > 0){
8877                         if(window.execScript) {
8878                            window.execScript(match[2]);
8879                         } else {
8880                             /**
8881                              * eval:var:id
8882                              * eval:var:dom
8883                              * eval:var:html
8884                              * 
8885                              */
8886                            window.eval(match[2]);
8887                         }
8888                     }
8889                 }
8890                 var el = document.getElementById(id);
8891                 if(el){el.parentNode.removeChild(el);}
8892                 if(typeof callback == "function"){
8893                     callback();
8894                 }
8895             });
8896             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8897             return this;
8898         },
8899
8900         /**
8901          * Direct access to the UpdateManager update() method (takes the same parameters).
8902          * @param {String/Function} url The url for this request or a function to call to get the url
8903          * @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}
8904          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8905          * @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.
8906          * @return {Roo.Element} this
8907          */
8908         load : function(){
8909             var um = this.getUpdateManager();
8910             um.update.apply(um, arguments);
8911             return this;
8912         },
8913
8914         /**
8915         * Gets this element's UpdateManager
8916         * @return {Roo.UpdateManager} The UpdateManager
8917         */
8918         getUpdateManager : function(){
8919             if(!this.updateManager){
8920                 this.updateManager = new Roo.UpdateManager(this);
8921             }
8922             return this.updateManager;
8923         },
8924
8925         /**
8926          * Disables text selection for this element (normalized across browsers)
8927          * @return {Roo.Element} this
8928          */
8929         unselectable : function(){
8930             this.dom.unselectable = "on";
8931             this.swallowEvent("selectstart", true);
8932             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8933             this.addClass("x-unselectable");
8934             return this;
8935         },
8936
8937         /**
8938         * Calculates the x, y to center this element on the screen
8939         * @return {Array} The x, y values [x, y]
8940         */
8941         getCenterXY : function(){
8942             return this.getAlignToXY(document, 'c-c');
8943         },
8944
8945         /**
8946         * Centers the Element in either the viewport, or another Element.
8947         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8948         */
8949         center : function(centerIn){
8950             this.alignTo(centerIn || document, 'c-c');
8951             return this;
8952         },
8953
8954         /**
8955          * Tests various css rules/browsers to determine if this element uses a border box
8956          * @return {Boolean}
8957          */
8958         isBorderBox : function(){
8959             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8960         },
8961
8962         /**
8963          * Return a box {x, y, width, height} that can be used to set another elements
8964          * size/location to match this element.
8965          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8966          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8967          * @return {Object} box An object in the format {x, y, width, height}
8968          */
8969         getBox : function(contentBox, local){
8970             var xy;
8971             if(!local){
8972                 xy = this.getXY();
8973             }else{
8974                 var left = parseInt(this.getStyle("left"), 10) || 0;
8975                 var top = parseInt(this.getStyle("top"), 10) || 0;
8976                 xy = [left, top];
8977             }
8978             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8979             if(!contentBox){
8980                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8981             }else{
8982                 var l = this.getBorderWidth("l")+this.getPadding("l");
8983                 var r = this.getBorderWidth("r")+this.getPadding("r");
8984                 var t = this.getBorderWidth("t")+this.getPadding("t");
8985                 var b = this.getBorderWidth("b")+this.getPadding("b");
8986                 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)};
8987             }
8988             bx.right = bx.x + bx.width;
8989             bx.bottom = bx.y + bx.height;
8990             return bx;
8991         },
8992
8993         /**
8994          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8995          for more information about the sides.
8996          * @param {String} sides
8997          * @return {Number}
8998          */
8999         getFrameWidth : function(sides, onlyContentBox){
9000             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9001         },
9002
9003         /**
9004          * 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.
9005          * @param {Object} box The box to fill {x, y, width, height}
9006          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9007          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9008          * @return {Roo.Element} this
9009          */
9010         setBox : function(box, adjust, animate){
9011             var w = box.width, h = box.height;
9012             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9013                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9014                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9015             }
9016             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9017             return this;
9018         },
9019
9020         /**
9021          * Forces the browser to repaint this element
9022          * @return {Roo.Element} this
9023          */
9024          repaint : function(){
9025             var dom = this.dom;
9026             this.addClass("x-repaint");
9027             setTimeout(function(){
9028                 Roo.get(dom).removeClass("x-repaint");
9029             }, 1);
9030             return this;
9031         },
9032
9033         /**
9034          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9035          * then it returns the calculated width of the sides (see getPadding)
9036          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9037          * @return {Object/Number}
9038          */
9039         getMargins : function(side){
9040             if(!side){
9041                 return {
9042                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9043                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9044                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9045                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9046                 };
9047             }else{
9048                 return this.addStyles(side, El.margins);
9049              }
9050         },
9051
9052         // private
9053         addStyles : function(sides, styles){
9054             var val = 0, v, w;
9055             for(var i = 0, len = sides.length; i < len; i++){
9056                 v = this.getStyle(styles[sides.charAt(i)]);
9057                 if(v){
9058                      w = parseInt(v, 10);
9059                      if(w){ val += w; }
9060                 }
9061             }
9062             return val;
9063         },
9064
9065         /**
9066          * Creates a proxy element of this element
9067          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9068          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9069          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9070          * @return {Roo.Element} The new proxy element
9071          */
9072         createProxy : function(config, renderTo, matchBox){
9073             if(renderTo){
9074                 renderTo = Roo.getDom(renderTo);
9075             }else{
9076                 renderTo = document.body;
9077             }
9078             config = typeof config == "object" ?
9079                 config : {tag : "div", cls: config};
9080             var proxy = Roo.DomHelper.append(renderTo, config, true);
9081             if(matchBox){
9082                proxy.setBox(this.getBox());
9083             }
9084             return proxy;
9085         },
9086
9087         /**
9088          * Puts a mask over this element to disable user interaction. Requires core.css.
9089          * This method can only be applied to elements which accept child nodes.
9090          * @param {String} msg (optional) A message to display in the mask
9091          * @param {String} msgCls (optional) A css class to apply to the msg element
9092          * @return {Element} The mask  element
9093          */
9094         mask : function(msg, msgCls)
9095         {
9096             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9097                 this.setStyle("position", "relative");
9098             }
9099             if(!this._mask){
9100                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9101             }
9102             this.addClass("x-masked");
9103             this._mask.setDisplayed(true);
9104             
9105             // we wander
9106             var z = 0;
9107             var dom = this.dom;
9108             while (dom && dom.style) {
9109                 if (!isNaN(parseInt(dom.style.zIndex))) {
9110                     z = Math.max(z, parseInt(dom.style.zIndex));
9111                 }
9112                 dom = dom.parentNode;
9113             }
9114             // if we are masking the body - then it hides everything..
9115             if (this.dom == document.body) {
9116                 z = 1000000;
9117                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9118                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9119             }
9120            
9121             if(typeof msg == 'string'){
9122                 if(!this._maskMsg){
9123                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9124                 }
9125                 var mm = this._maskMsg;
9126                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9127                 if (mm.dom.firstChild) { // weird IE issue?
9128                     mm.dom.firstChild.innerHTML = msg;
9129                 }
9130                 mm.setDisplayed(true);
9131                 mm.center(this);
9132                 mm.setStyle('z-index', z + 102);
9133             }
9134             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9135                 this._mask.setHeight(this.getHeight());
9136             }
9137             this._mask.setStyle('z-index', z + 100);
9138             
9139             return this._mask;
9140         },
9141
9142         /**
9143          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9144          * it is cached for reuse.
9145          */
9146         unmask : function(removeEl){
9147             if(this._mask){
9148                 if(removeEl === true){
9149                     this._mask.remove();
9150                     delete this._mask;
9151                     if(this._maskMsg){
9152                         this._maskMsg.remove();
9153                         delete this._maskMsg;
9154                     }
9155                 }else{
9156                     this._mask.setDisplayed(false);
9157                     if(this._maskMsg){
9158                         this._maskMsg.setDisplayed(false);
9159                     }
9160                 }
9161             }
9162             this.removeClass("x-masked");
9163         },
9164
9165         /**
9166          * Returns true if this element is masked
9167          * @return {Boolean}
9168          */
9169         isMasked : function(){
9170             return this._mask && this._mask.isVisible();
9171         },
9172
9173         /**
9174          * Creates an iframe shim for this element to keep selects and other windowed objects from
9175          * showing through.
9176          * @return {Roo.Element} The new shim element
9177          */
9178         createShim : function(){
9179             var el = document.createElement('iframe');
9180             el.frameBorder = 'no';
9181             el.className = 'roo-shim';
9182             if(Roo.isIE && Roo.isSecure){
9183                 el.src = Roo.SSL_SECURE_URL;
9184             }
9185             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9186             shim.autoBoxAdjust = false;
9187             return shim;
9188         },
9189
9190         /**
9191          * Removes this element from the DOM and deletes it from the cache
9192          */
9193         remove : function(){
9194             if(this.dom.parentNode){
9195                 this.dom.parentNode.removeChild(this.dom);
9196             }
9197             delete El.cache[this.dom.id];
9198         },
9199
9200         /**
9201          * Sets up event handlers to add and remove a css class when the mouse is over this element
9202          * @param {String} className
9203          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9204          * mouseout events for children elements
9205          * @return {Roo.Element} this
9206          */
9207         addClassOnOver : function(className, preventFlicker){
9208             this.on("mouseover", function(){
9209                 Roo.fly(this, '_internal').addClass(className);
9210             }, this.dom);
9211             var removeFn = function(e){
9212                 if(preventFlicker !== true || !e.within(this, true)){
9213                     Roo.fly(this, '_internal').removeClass(className);
9214                 }
9215             };
9216             this.on("mouseout", removeFn, this.dom);
9217             return this;
9218         },
9219
9220         /**
9221          * Sets up event handlers to add and remove a css class when this element has the focus
9222          * @param {String} className
9223          * @return {Roo.Element} this
9224          */
9225         addClassOnFocus : function(className){
9226             this.on("focus", function(){
9227                 Roo.fly(this, '_internal').addClass(className);
9228             }, this.dom);
9229             this.on("blur", function(){
9230                 Roo.fly(this, '_internal').removeClass(className);
9231             }, this.dom);
9232             return this;
9233         },
9234         /**
9235          * 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)
9236          * @param {String} className
9237          * @return {Roo.Element} this
9238          */
9239         addClassOnClick : function(className){
9240             var dom = this.dom;
9241             this.on("mousedown", function(){
9242                 Roo.fly(dom, '_internal').addClass(className);
9243                 var d = Roo.get(document);
9244                 var fn = function(){
9245                     Roo.fly(dom, '_internal').removeClass(className);
9246                     d.removeListener("mouseup", fn);
9247                 };
9248                 d.on("mouseup", fn);
9249             });
9250             return this;
9251         },
9252
9253         /**
9254          * Stops the specified event from bubbling and optionally prevents the default action
9255          * @param {String} eventName
9256          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9257          * @return {Roo.Element} this
9258          */
9259         swallowEvent : function(eventName, preventDefault){
9260             var fn = function(e){
9261                 e.stopPropagation();
9262                 if(preventDefault){
9263                     e.preventDefault();
9264                 }
9265             };
9266             if(eventName instanceof Array){
9267                 for(var i = 0, len = eventName.length; i < len; i++){
9268                      this.on(eventName[i], fn);
9269                 }
9270                 return this;
9271             }
9272             this.on(eventName, fn);
9273             return this;
9274         },
9275
9276         /**
9277          * @private
9278          */
9279       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9280
9281         /**
9282          * Sizes this element to its parent element's dimensions performing
9283          * neccessary box adjustments.
9284          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9285          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9286          * @return {Roo.Element} this
9287          */
9288         fitToParent : function(monitorResize, targetParent) {
9289           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9290           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9291           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9292             return;
9293           }
9294           var p = Roo.get(targetParent || this.dom.parentNode);
9295           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9296           if (monitorResize === true) {
9297             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9298             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9299           }
9300           return this;
9301         },
9302
9303         /**
9304          * Gets the next sibling, skipping text nodes
9305          * @return {HTMLElement} The next sibling or null
9306          */
9307         getNextSibling : function(){
9308             var n = this.dom.nextSibling;
9309             while(n && n.nodeType != 1){
9310                 n = n.nextSibling;
9311             }
9312             return n;
9313         },
9314
9315         /**
9316          * Gets the previous sibling, skipping text nodes
9317          * @return {HTMLElement} The previous sibling or null
9318          */
9319         getPrevSibling : function(){
9320             var n = this.dom.previousSibling;
9321             while(n && n.nodeType != 1){
9322                 n = n.previousSibling;
9323             }
9324             return n;
9325         },
9326
9327
9328         /**
9329          * Appends the passed element(s) to this element
9330          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9331          * @return {Roo.Element} this
9332          */
9333         appendChild: function(el){
9334             el = Roo.get(el);
9335             el.appendTo(this);
9336             return this;
9337         },
9338
9339         /**
9340          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9341          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9342          * automatically generated with the specified attributes.
9343          * @param {HTMLElement} insertBefore (optional) a child element of this element
9344          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9345          * @return {Roo.Element} The new child element
9346          */
9347         createChild: function(config, insertBefore, returnDom){
9348             config = config || {tag:'div'};
9349             if(insertBefore){
9350                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9351             }
9352             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9353         },
9354
9355         /**
9356          * Appends this element to the passed element
9357          * @param {String/HTMLElement/Element} el The new parent element
9358          * @return {Roo.Element} this
9359          */
9360         appendTo: function(el){
9361             el = Roo.getDom(el);
9362             el.appendChild(this.dom);
9363             return this;
9364         },
9365
9366         /**
9367          * Inserts this element before the passed element in the DOM
9368          * @param {String/HTMLElement/Element} el The element to insert before
9369          * @return {Roo.Element} this
9370          */
9371         insertBefore: function(el){
9372             el = Roo.getDom(el);
9373             el.parentNode.insertBefore(this.dom, el);
9374             return this;
9375         },
9376
9377         /**
9378          * Inserts this element after the passed element in the DOM
9379          * @param {String/HTMLElement/Element} el The element to insert after
9380          * @return {Roo.Element} this
9381          */
9382         insertAfter: function(el){
9383             el = Roo.getDom(el);
9384             el.parentNode.insertBefore(this.dom, el.nextSibling);
9385             return this;
9386         },
9387
9388         /**
9389          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9390          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9391          * @return {Roo.Element} The new child
9392          */
9393         insertFirst: function(el, returnDom){
9394             el = el || {};
9395             if(typeof el == 'object' && !el.nodeType){ // dh config
9396                 return this.createChild(el, this.dom.firstChild, returnDom);
9397             }else{
9398                 el = Roo.getDom(el);
9399                 this.dom.insertBefore(el, this.dom.firstChild);
9400                 return !returnDom ? Roo.get(el) : el;
9401             }
9402         },
9403
9404         /**
9405          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9406          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9407          * @param {String} where (optional) 'before' or 'after' defaults to before
9408          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9409          * @return {Roo.Element} the inserted Element
9410          */
9411         insertSibling: function(el, where, returnDom){
9412             where = where ? where.toLowerCase() : 'before';
9413             el = el || {};
9414             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9415
9416             if(typeof el == 'object' && !el.nodeType){ // dh config
9417                 if(where == 'after' && !this.dom.nextSibling){
9418                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9419                 }else{
9420                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9421                 }
9422
9423             }else{
9424                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9425                             where == 'before' ? this.dom : this.dom.nextSibling);
9426                 if(!returnDom){
9427                     rt = Roo.get(rt);
9428                 }
9429             }
9430             return rt;
9431         },
9432
9433         /**
9434          * Creates and wraps this element with another element
9435          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9436          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9437          * @return {HTMLElement/Element} The newly created wrapper element
9438          */
9439         wrap: function(config, returnDom){
9440             if(!config){
9441                 config = {tag: "div"};
9442             }
9443             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9444             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9445             return newEl;
9446         },
9447
9448         /**
9449          * Replaces the passed element with this element
9450          * @param {String/HTMLElement/Element} el The element to replace
9451          * @return {Roo.Element} this
9452          */
9453         replace: function(el){
9454             el = Roo.get(el);
9455             this.insertBefore(el);
9456             el.remove();
9457             return this;
9458         },
9459
9460         /**
9461          * Inserts an html fragment into this element
9462          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9463          * @param {String} html The HTML fragment
9464          * @param {Boolean} returnEl True to return an Roo.Element
9465          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9466          */
9467         insertHtml : function(where, html, returnEl){
9468             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9469             return returnEl ? Roo.get(el) : el;
9470         },
9471
9472         /**
9473          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9474          * @param {Object} o The object with the attributes
9475          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9476          * @return {Roo.Element} this
9477          */
9478         set : function(o, useSet){
9479             var el = this.dom;
9480             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9481             for(var attr in o){
9482                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9483                 if(attr=="cls"){
9484                     el.className = o["cls"];
9485                 }else{
9486                     if(useSet) {
9487                         el.setAttribute(attr, o[attr]);
9488                     } else {
9489                         el[attr] = o[attr];
9490                     }
9491                 }
9492             }
9493             if(o.style){
9494                 Roo.DomHelper.applyStyles(el, o.style);
9495             }
9496             return this;
9497         },
9498
9499         /**
9500          * Convenience method for constructing a KeyMap
9501          * @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:
9502          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9503          * @param {Function} fn The function to call
9504          * @param {Object} scope (optional) The scope of the function
9505          * @return {Roo.KeyMap} The KeyMap created
9506          */
9507         addKeyListener : function(key, fn, scope){
9508             var config;
9509             if(typeof key != "object" || key instanceof Array){
9510                 config = {
9511                     key: key,
9512                     fn: fn,
9513                     scope: scope
9514                 };
9515             }else{
9516                 config = {
9517                     key : key.key,
9518                     shift : key.shift,
9519                     ctrl : key.ctrl,
9520                     alt : key.alt,
9521                     fn: fn,
9522                     scope: scope
9523                 };
9524             }
9525             return new Roo.KeyMap(this, config);
9526         },
9527
9528         /**
9529          * Creates a KeyMap for this element
9530          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9531          * @return {Roo.KeyMap} The KeyMap created
9532          */
9533         addKeyMap : function(config){
9534             return new Roo.KeyMap(this, config);
9535         },
9536
9537         /**
9538          * Returns true if this element is scrollable.
9539          * @return {Boolean}
9540          */
9541          isScrollable : function(){
9542             var dom = this.dom;
9543             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9544         },
9545
9546         /**
9547          * 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().
9548          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9549          * @param {Number} value The new scroll value
9550          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9551          * @return {Element} this
9552          */
9553
9554         scrollTo : function(side, value, animate){
9555             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9556             if(!animate || !A){
9557                 this.dom[prop] = value;
9558             }else{
9559                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9560                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9561             }
9562             return this;
9563         },
9564
9565         /**
9566          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9567          * within this element's scrollable range.
9568          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9569          * @param {Number} distance How far to scroll the element in pixels
9570          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9571          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9572          * was scrolled as far as it could go.
9573          */
9574          scroll : function(direction, distance, animate){
9575              if(!this.isScrollable()){
9576                  return;
9577              }
9578              var el = this.dom;
9579              var l = el.scrollLeft, t = el.scrollTop;
9580              var w = el.scrollWidth, h = el.scrollHeight;
9581              var cw = el.clientWidth, ch = el.clientHeight;
9582              direction = direction.toLowerCase();
9583              var scrolled = false;
9584              var a = this.preanim(arguments, 2);
9585              switch(direction){
9586                  case "l":
9587                  case "left":
9588                      if(w - l > cw){
9589                          var v = Math.min(l + distance, w-cw);
9590                          this.scrollTo("left", v, a);
9591                          scrolled = true;
9592                      }
9593                      break;
9594                 case "r":
9595                 case "right":
9596                      if(l > 0){
9597                          var v = Math.max(l - distance, 0);
9598                          this.scrollTo("left", v, a);
9599                          scrolled = true;
9600                      }
9601                      break;
9602                 case "t":
9603                 case "top":
9604                 case "up":
9605                      if(t > 0){
9606                          var v = Math.max(t - distance, 0);
9607                          this.scrollTo("top", v, a);
9608                          scrolled = true;
9609                      }
9610                      break;
9611                 case "b":
9612                 case "bottom":
9613                 case "down":
9614                      if(h - t > ch){
9615                          var v = Math.min(t + distance, h-ch);
9616                          this.scrollTo("top", v, a);
9617                          scrolled = true;
9618                      }
9619                      break;
9620              }
9621              return scrolled;
9622         },
9623
9624         /**
9625          * Translates the passed page coordinates into left/top css values for this element
9626          * @param {Number/Array} x The page x or an array containing [x, y]
9627          * @param {Number} y The page y
9628          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9629          */
9630         translatePoints : function(x, y){
9631             if(typeof x == 'object' || x instanceof Array){
9632                 y = x[1]; x = x[0];
9633             }
9634             var p = this.getStyle('position');
9635             var o = this.getXY();
9636
9637             var l = parseInt(this.getStyle('left'), 10);
9638             var t = parseInt(this.getStyle('top'), 10);
9639
9640             if(isNaN(l)){
9641                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9642             }
9643             if(isNaN(t)){
9644                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9645             }
9646
9647             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9648         },
9649
9650         /**
9651          * Returns the current scroll position of the element.
9652          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9653          */
9654         getScroll : function(){
9655             var d = this.dom, doc = document;
9656             if(d == doc || d == doc.body){
9657                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9658                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9659                 return {left: l, top: t};
9660             }else{
9661                 return {left: d.scrollLeft, top: d.scrollTop};
9662             }
9663         },
9664
9665         /**
9666          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9667          * are convert to standard 6 digit hex color.
9668          * @param {String} attr The css attribute
9669          * @param {String} defaultValue The default value to use when a valid color isn't found
9670          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9671          * YUI color anims.
9672          */
9673         getColor : function(attr, defaultValue, prefix){
9674             var v = this.getStyle(attr);
9675             if(!v || v == "transparent" || v == "inherit") {
9676                 return defaultValue;
9677             }
9678             var color = typeof prefix == "undefined" ? "#" : prefix;
9679             if(v.substr(0, 4) == "rgb("){
9680                 var rvs = v.slice(4, v.length -1).split(",");
9681                 for(var i = 0; i < 3; i++){
9682                     var h = parseInt(rvs[i]).toString(16);
9683                     if(h < 16){
9684                         h = "0" + h;
9685                     }
9686                     color += h;
9687                 }
9688             } else {
9689                 if(v.substr(0, 1) == "#"){
9690                     if(v.length == 4) {
9691                         for(var i = 1; i < 4; i++){
9692                             var c = v.charAt(i);
9693                             color +=  c + c;
9694                         }
9695                     }else if(v.length == 7){
9696                         color += v.substr(1);
9697                     }
9698                 }
9699             }
9700             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9701         },
9702
9703         /**
9704          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9705          * gradient background, rounded corners and a 4-way shadow.
9706          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9707          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9708          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9709          * @return {Roo.Element} this
9710          */
9711         boxWrap : function(cls){
9712             cls = cls || 'x-box';
9713             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9714             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9715             return el;
9716         },
9717
9718         /**
9719          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9720          * @param {String} namespace The namespace in which to look for the attribute
9721          * @param {String} name The attribute name
9722          * @return {String} The attribute value
9723          */
9724         getAttributeNS : Roo.isIE ? function(ns, name){
9725             var d = this.dom;
9726             var type = typeof d[ns+":"+name];
9727             if(type != 'undefined' && type != 'unknown'){
9728                 return d[ns+":"+name];
9729             }
9730             return d[name];
9731         } : function(ns, name){
9732             var d = this.dom;
9733             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9734         },
9735         
9736         
9737         /**
9738          * Sets or Returns the value the dom attribute value
9739          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9740          * @param {String} value (optional) The value to set the attribute to
9741          * @return {String} The attribute value
9742          */
9743         attr : function(name){
9744             if (arguments.length > 1) {
9745                 this.dom.setAttribute(name, arguments[1]);
9746                 return arguments[1];
9747             }
9748             if (typeof(name) == 'object') {
9749                 for(var i in name) {
9750                     this.attr(i, name[i]);
9751                 }
9752                 return name;
9753             }
9754             
9755             
9756             if (!this.dom.hasAttribute(name)) {
9757                 return undefined;
9758             }
9759             return this.dom.getAttribute(name);
9760         }
9761         
9762         
9763         
9764     };
9765
9766     var ep = El.prototype;
9767
9768     /**
9769      * Appends an event handler (Shorthand for addListener)
9770      * @param {String}   eventName     The type of event to append
9771      * @param {Function} fn        The method the event invokes
9772      * @param {Object} scope       (optional) The scope (this object) of the fn
9773      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9774      * @method
9775      */
9776     ep.on = ep.addListener;
9777         // backwards compat
9778     ep.mon = ep.addListener;
9779
9780     /**
9781      * Removes an event handler from this element (shorthand for removeListener)
9782      * @param {String} eventName the type of event to remove
9783      * @param {Function} fn the method the event invokes
9784      * @return {Roo.Element} this
9785      * @method
9786      */
9787     ep.un = ep.removeListener;
9788
9789     /**
9790      * true to automatically adjust width and height settings for box-model issues (default to true)
9791      */
9792     ep.autoBoxAdjust = true;
9793
9794     // private
9795     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9796
9797     // private
9798     El.addUnits = function(v, defaultUnit){
9799         if(v === "" || v == "auto"){
9800             return v;
9801         }
9802         if(v === undefined){
9803             return '';
9804         }
9805         if(typeof v == "number" || !El.unitPattern.test(v)){
9806             return v + (defaultUnit || 'px');
9807         }
9808         return v;
9809     };
9810
9811     // special markup used throughout Roo when box wrapping elements
9812     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>';
9813     /**
9814      * Visibility mode constant - Use visibility to hide element
9815      * @static
9816      * @type Number
9817      */
9818     El.VISIBILITY = 1;
9819     /**
9820      * Visibility mode constant - Use display to hide element
9821      * @static
9822      * @type Number
9823      */
9824     El.DISPLAY = 2;
9825
9826     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9827     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9828     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9829
9830
9831
9832     /**
9833      * @private
9834      */
9835     El.cache = {};
9836
9837     var docEl;
9838
9839     /**
9840      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9841      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9842      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9843      * @return {Element} The Element object
9844      * @static
9845      */
9846     El.get = function(el){
9847         var ex, elm, id;
9848         if(!el){ return null; }
9849         if(typeof el == "string"){ // element id
9850             if(!(elm = document.getElementById(el))){
9851                 return null;
9852             }
9853             if(ex = El.cache[el]){
9854                 ex.dom = elm;
9855             }else{
9856                 ex = El.cache[el] = new El(elm);
9857             }
9858             return ex;
9859         }else if(el.tagName){ // dom element
9860             if(!(id = el.id)){
9861                 id = Roo.id(el);
9862             }
9863             if(ex = El.cache[id]){
9864                 ex.dom = el;
9865             }else{
9866                 ex = El.cache[id] = new El(el);
9867             }
9868             return ex;
9869         }else if(el instanceof El){
9870             if(el != docEl){
9871                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9872                                                               // catch case where it hasn't been appended
9873                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9874             }
9875             return el;
9876         }else if(el.isComposite){
9877             return el;
9878         }else if(el instanceof Array){
9879             return El.select(el);
9880         }else if(el == document){
9881             // create a bogus element object representing the document object
9882             if(!docEl){
9883                 var f = function(){};
9884                 f.prototype = El.prototype;
9885                 docEl = new f();
9886                 docEl.dom = document;
9887             }
9888             return docEl;
9889         }
9890         return null;
9891     };
9892
9893     // private
9894     El.uncache = function(el){
9895         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9896             if(a[i]){
9897                 delete El.cache[a[i].id || a[i]];
9898             }
9899         }
9900     };
9901
9902     // private
9903     // Garbage collection - uncache elements/purge listeners on orphaned elements
9904     // so we don't hold a reference and cause the browser to retain them
9905     El.garbageCollect = function(){
9906         if(!Roo.enableGarbageCollector){
9907             clearInterval(El.collectorThread);
9908             return;
9909         }
9910         for(var eid in El.cache){
9911             var el = El.cache[eid], d = el.dom;
9912             // -------------------------------------------------------
9913             // Determining what is garbage:
9914             // -------------------------------------------------------
9915             // !d
9916             // dom node is null, definitely garbage
9917             // -------------------------------------------------------
9918             // !d.parentNode
9919             // no parentNode == direct orphan, definitely garbage
9920             // -------------------------------------------------------
9921             // !d.offsetParent && !document.getElementById(eid)
9922             // display none elements have no offsetParent so we will
9923             // also try to look it up by it's id. However, check
9924             // offsetParent first so we don't do unneeded lookups.
9925             // This enables collection of elements that are not orphans
9926             // directly, but somewhere up the line they have an orphan
9927             // parent.
9928             // -------------------------------------------------------
9929             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9930                 delete El.cache[eid];
9931                 if(d && Roo.enableListenerCollection){
9932                     E.purgeElement(d);
9933                 }
9934             }
9935         }
9936     }
9937     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9938
9939
9940     // dom is optional
9941     El.Flyweight = function(dom){
9942         this.dom = dom;
9943     };
9944     El.Flyweight.prototype = El.prototype;
9945
9946     El._flyweights = {};
9947     /**
9948      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9949      * the dom node can be overwritten by other code.
9950      * @param {String/HTMLElement} el The dom node or id
9951      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9952      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9953      * @static
9954      * @return {Element} The shared Element object
9955      */
9956     El.fly = function(el, named){
9957         named = named || '_global';
9958         el = Roo.getDom(el);
9959         if(!el){
9960             return null;
9961         }
9962         if(!El._flyweights[named]){
9963             El._flyweights[named] = new El.Flyweight();
9964         }
9965         El._flyweights[named].dom = el;
9966         return El._flyweights[named];
9967     };
9968
9969     /**
9970      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9971      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9972      * Shorthand of {@link Roo.Element#get}
9973      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9974      * @return {Element} The Element object
9975      * @member Roo
9976      * @method get
9977      */
9978     Roo.get = El.get;
9979     /**
9980      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9981      * the dom node can be overwritten by other code.
9982      * Shorthand of {@link Roo.Element#fly}
9983      * @param {String/HTMLElement} el The dom node or id
9984      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9985      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9986      * @static
9987      * @return {Element} The shared Element object
9988      * @member Roo
9989      * @method fly
9990      */
9991     Roo.fly = El.fly;
9992
9993     // speedy lookup for elements never to box adjust
9994     var noBoxAdjust = Roo.isStrict ? {
9995         select:1
9996     } : {
9997         input:1, select:1, textarea:1
9998     };
9999     if(Roo.isIE || Roo.isGecko){
10000         noBoxAdjust['button'] = 1;
10001     }
10002
10003
10004     Roo.EventManager.on(window, 'unload', function(){
10005         delete El.cache;
10006         delete El._flyweights;
10007     });
10008 })();
10009
10010
10011
10012
10013 if(Roo.DomQuery){
10014     Roo.Element.selectorFunction = Roo.DomQuery.select;
10015 }
10016
10017 Roo.Element.select = function(selector, unique, root){
10018     var els;
10019     if(typeof selector == "string"){
10020         els = Roo.Element.selectorFunction(selector, root);
10021     }else if(selector.length !== undefined){
10022         els = selector;
10023     }else{
10024         throw "Invalid selector";
10025     }
10026     if(unique === true){
10027         return new Roo.CompositeElement(els);
10028     }else{
10029         return new Roo.CompositeElementLite(els);
10030     }
10031 };
10032 /**
10033  * Selects elements based on the passed CSS selector to enable working on them as 1.
10034  * @param {String/Array} selector The CSS selector or an array of elements
10035  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10036  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10037  * @return {CompositeElementLite/CompositeElement}
10038  * @member Roo
10039  * @method select
10040  */
10041 Roo.select = Roo.Element.select;
10042
10043
10044
10045
10046
10047
10048
10049
10050
10051
10052
10053
10054
10055
10056 /*
10057  * Based on:
10058  * Ext JS Library 1.1.1
10059  * Copyright(c) 2006-2007, Ext JS, LLC.
10060  *
10061  * Originally Released Under LGPL - original licence link has changed is not relivant.
10062  *
10063  * Fork - LGPL
10064  * <script type="text/javascript">
10065  */
10066
10067
10068
10069 //Notifies Element that fx methods are available
10070 Roo.enableFx = true;
10071
10072 /**
10073  * @class Roo.Fx
10074  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10075  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10076  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10077  * Element effects to work.</p><br/>
10078  *
10079  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10080  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10081  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10082  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10083  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10084  * expected results and should be done with care.</p><br/>
10085  *
10086  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10087  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10088 <pre>
10089 Value  Description
10090 -----  -----------------------------
10091 tl     The top left corner
10092 t      The center of the top edge
10093 tr     The top right corner
10094 l      The center of the left edge
10095 r      The center of the right edge
10096 bl     The bottom left corner
10097 b      The center of the bottom edge
10098 br     The bottom right corner
10099 </pre>
10100  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10101  * below are common options that can be passed to any Fx method.</b>
10102  * @cfg {Function} callback A function called when the effect is finished
10103  * @cfg {Object} scope The scope of the effect function
10104  * @cfg {String} easing A valid Easing value for the effect
10105  * @cfg {String} afterCls A css class to apply after the effect
10106  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10107  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10108  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10109  * effects that end with the element being visually hidden, ignored otherwise)
10110  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10111  * a function which returns such a specification that will be applied to the Element after the effect finishes
10112  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10113  * @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
10114  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10115  */
10116 Roo.Fx = {
10117         /**
10118          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10119          * origin for the slide effect.  This function automatically handles wrapping the element with
10120          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10121          * Usage:
10122          *<pre><code>
10123 // default: slide the element in from the top
10124 el.slideIn();
10125
10126 // custom: slide the element in from the right with a 2-second duration
10127 el.slideIn('r', { duration: 2 });
10128
10129 // common config options shown with default values
10130 el.slideIn('t', {
10131     easing: 'easeOut',
10132     duration: .5
10133 });
10134 </code></pre>
10135          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10136          * @param {Object} options (optional) Object literal with any of the Fx config options
10137          * @return {Roo.Element} The Element
10138          */
10139     slideIn : function(anchor, o){
10140         var el = this.getFxEl();
10141         o = o || {};
10142
10143         el.queueFx(o, function(){
10144
10145             anchor = anchor || "t";
10146
10147             // fix display to visibility
10148             this.fixDisplay();
10149
10150             // restore values after effect
10151             var r = this.getFxRestore();
10152             var b = this.getBox();
10153             // fixed size for slide
10154             this.setSize(b);
10155
10156             // wrap if needed
10157             var wrap = this.fxWrap(r.pos, o, "hidden");
10158
10159             var st = this.dom.style;
10160             st.visibility = "visible";
10161             st.position = "absolute";
10162
10163             // clear out temp styles after slide and unwrap
10164             var after = function(){
10165                 el.fxUnwrap(wrap, r.pos, o);
10166                 st.width = r.width;
10167                 st.height = r.height;
10168                 el.afterFx(o);
10169             };
10170             // time to calc the positions
10171             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10172
10173             switch(anchor.toLowerCase()){
10174                 case "t":
10175                     wrap.setSize(b.width, 0);
10176                     st.left = st.bottom = "0";
10177                     a = {height: bh};
10178                 break;
10179                 case "l":
10180                     wrap.setSize(0, b.height);
10181                     st.right = st.top = "0";
10182                     a = {width: bw};
10183                 break;
10184                 case "r":
10185                     wrap.setSize(0, b.height);
10186                     wrap.setX(b.right);
10187                     st.left = st.top = "0";
10188                     a = {width: bw, points: pt};
10189                 break;
10190                 case "b":
10191                     wrap.setSize(b.width, 0);
10192                     wrap.setY(b.bottom);
10193                     st.left = st.top = "0";
10194                     a = {height: bh, points: pt};
10195                 break;
10196                 case "tl":
10197                     wrap.setSize(0, 0);
10198                     st.right = st.bottom = "0";
10199                     a = {width: bw, height: bh};
10200                 break;
10201                 case "bl":
10202                     wrap.setSize(0, 0);
10203                     wrap.setY(b.y+b.height);
10204                     st.right = st.top = "0";
10205                     a = {width: bw, height: bh, points: pt};
10206                 break;
10207                 case "br":
10208                     wrap.setSize(0, 0);
10209                     wrap.setXY([b.right, b.bottom]);
10210                     st.left = st.top = "0";
10211                     a = {width: bw, height: bh, points: pt};
10212                 break;
10213                 case "tr":
10214                     wrap.setSize(0, 0);
10215                     wrap.setX(b.x+b.width);
10216                     st.left = st.bottom = "0";
10217                     a = {width: bw, height: bh, points: pt};
10218                 break;
10219             }
10220             this.dom.style.visibility = "visible";
10221             wrap.show();
10222
10223             arguments.callee.anim = wrap.fxanim(a,
10224                 o,
10225                 'motion',
10226                 .5,
10227                 'easeOut', after);
10228         });
10229         return this;
10230     },
10231     
10232         /**
10233          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10234          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10235          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10236          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10237          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10238          * Usage:
10239          *<pre><code>
10240 // default: slide the element out to the top
10241 el.slideOut();
10242
10243 // custom: slide the element out to the right with a 2-second duration
10244 el.slideOut('r', { duration: 2 });
10245
10246 // common config options shown with default values
10247 el.slideOut('t', {
10248     easing: 'easeOut',
10249     duration: .5,
10250     remove: false,
10251     useDisplay: false
10252 });
10253 </code></pre>
10254          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10255          * @param {Object} options (optional) Object literal with any of the Fx config options
10256          * @return {Roo.Element} The Element
10257          */
10258     slideOut : function(anchor, o){
10259         var el = this.getFxEl();
10260         o = o || {};
10261
10262         el.queueFx(o, function(){
10263
10264             anchor = anchor || "t";
10265
10266             // restore values after effect
10267             var r = this.getFxRestore();
10268             
10269             var b = this.getBox();
10270             // fixed size for slide
10271             this.setSize(b);
10272
10273             // wrap if needed
10274             var wrap = this.fxWrap(r.pos, o, "visible");
10275
10276             var st = this.dom.style;
10277             st.visibility = "visible";
10278             st.position = "absolute";
10279
10280             wrap.setSize(b);
10281
10282             var after = function(){
10283                 if(o.useDisplay){
10284                     el.setDisplayed(false);
10285                 }else{
10286                     el.hide();
10287                 }
10288
10289                 el.fxUnwrap(wrap, r.pos, o);
10290
10291                 st.width = r.width;
10292                 st.height = r.height;
10293
10294                 el.afterFx(o);
10295             };
10296
10297             var a, zero = {to: 0};
10298             switch(anchor.toLowerCase()){
10299                 case "t":
10300                     st.left = st.bottom = "0";
10301                     a = {height: zero};
10302                 break;
10303                 case "l":
10304                     st.right = st.top = "0";
10305                     a = {width: zero};
10306                 break;
10307                 case "r":
10308                     st.left = st.top = "0";
10309                     a = {width: zero, points: {to:[b.right, b.y]}};
10310                 break;
10311                 case "b":
10312                     st.left = st.top = "0";
10313                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10314                 break;
10315                 case "tl":
10316                     st.right = st.bottom = "0";
10317                     a = {width: zero, height: zero};
10318                 break;
10319                 case "bl":
10320                     st.right = st.top = "0";
10321                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10322                 break;
10323                 case "br":
10324                     st.left = st.top = "0";
10325                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10326                 break;
10327                 case "tr":
10328                     st.left = st.bottom = "0";
10329                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10330                 break;
10331             }
10332
10333             arguments.callee.anim = wrap.fxanim(a,
10334                 o,
10335                 'motion',
10336                 .5,
10337                 "easeOut", after);
10338         });
10339         return this;
10340     },
10341
10342         /**
10343          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10344          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10345          * The element must be removed from the DOM using the 'remove' config option if desired.
10346          * Usage:
10347          *<pre><code>
10348 // default
10349 el.puff();
10350
10351 // common config options shown with default values
10352 el.puff({
10353     easing: 'easeOut',
10354     duration: .5,
10355     remove: false,
10356     useDisplay: false
10357 });
10358 </code></pre>
10359          * @param {Object} options (optional) Object literal with any of the Fx config options
10360          * @return {Roo.Element} The Element
10361          */
10362     puff : function(o){
10363         var el = this.getFxEl();
10364         o = o || {};
10365
10366         el.queueFx(o, function(){
10367             this.clearOpacity();
10368             this.show();
10369
10370             // restore values after effect
10371             var r = this.getFxRestore();
10372             var st = this.dom.style;
10373
10374             var after = function(){
10375                 if(o.useDisplay){
10376                     el.setDisplayed(false);
10377                 }else{
10378                     el.hide();
10379                 }
10380
10381                 el.clearOpacity();
10382
10383                 el.setPositioning(r.pos);
10384                 st.width = r.width;
10385                 st.height = r.height;
10386                 st.fontSize = '';
10387                 el.afterFx(o);
10388             };
10389
10390             var width = this.getWidth();
10391             var height = this.getHeight();
10392
10393             arguments.callee.anim = this.fxanim({
10394                     width : {to: this.adjustWidth(width * 2)},
10395                     height : {to: this.adjustHeight(height * 2)},
10396                     points : {by: [-(width * .5), -(height * .5)]},
10397                     opacity : {to: 0},
10398                     fontSize: {to:200, unit: "%"}
10399                 },
10400                 o,
10401                 'motion',
10402                 .5,
10403                 "easeOut", after);
10404         });
10405         return this;
10406     },
10407
10408         /**
10409          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10410          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10411          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10412          * Usage:
10413          *<pre><code>
10414 // default
10415 el.switchOff();
10416
10417 // all config options shown with default values
10418 el.switchOff({
10419     easing: 'easeIn',
10420     duration: .3,
10421     remove: false,
10422     useDisplay: false
10423 });
10424 </code></pre>
10425          * @param {Object} options (optional) Object literal with any of the Fx config options
10426          * @return {Roo.Element} The Element
10427          */
10428     switchOff : function(o){
10429         var el = this.getFxEl();
10430         o = o || {};
10431
10432         el.queueFx(o, function(){
10433             this.clearOpacity();
10434             this.clip();
10435
10436             // restore values after effect
10437             var r = this.getFxRestore();
10438             var st = this.dom.style;
10439
10440             var after = function(){
10441                 if(o.useDisplay){
10442                     el.setDisplayed(false);
10443                 }else{
10444                     el.hide();
10445                 }
10446
10447                 el.clearOpacity();
10448                 el.setPositioning(r.pos);
10449                 st.width = r.width;
10450                 st.height = r.height;
10451
10452                 el.afterFx(o);
10453             };
10454
10455             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10456                 this.clearOpacity();
10457                 (function(){
10458                     this.fxanim({
10459                         height:{to:1},
10460                         points:{by:[0, this.getHeight() * .5]}
10461                     }, o, 'motion', 0.3, 'easeIn', after);
10462                 }).defer(100, this);
10463             });
10464         });
10465         return this;
10466     },
10467
10468     /**
10469      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10470      * changed using the "attr" config option) and then fading back to the original color. If no original
10471      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10472      * Usage:
10473 <pre><code>
10474 // default: highlight background to yellow
10475 el.highlight();
10476
10477 // custom: highlight foreground text to blue for 2 seconds
10478 el.highlight("0000ff", { attr: 'color', duration: 2 });
10479
10480 // common config options shown with default values
10481 el.highlight("ffff9c", {
10482     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10483     endColor: (current color) or "ffffff",
10484     easing: 'easeIn',
10485     duration: 1
10486 });
10487 </code></pre>
10488      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10489      * @param {Object} options (optional) Object literal with any of the Fx config options
10490      * @return {Roo.Element} The Element
10491      */ 
10492     highlight : function(color, o){
10493         var el = this.getFxEl();
10494         o = o || {};
10495
10496         el.queueFx(o, function(){
10497             color = color || "ffff9c";
10498             attr = o.attr || "backgroundColor";
10499
10500             this.clearOpacity();
10501             this.show();
10502
10503             var origColor = this.getColor(attr);
10504             var restoreColor = this.dom.style[attr];
10505             endColor = (o.endColor || origColor) || "ffffff";
10506
10507             var after = function(){
10508                 el.dom.style[attr] = restoreColor;
10509                 el.afterFx(o);
10510             };
10511
10512             var a = {};
10513             a[attr] = {from: color, to: endColor};
10514             arguments.callee.anim = this.fxanim(a,
10515                 o,
10516                 'color',
10517                 1,
10518                 'easeIn', after);
10519         });
10520         return this;
10521     },
10522
10523    /**
10524     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10525     * Usage:
10526 <pre><code>
10527 // default: a single light blue ripple
10528 el.frame();
10529
10530 // custom: 3 red ripples lasting 3 seconds total
10531 el.frame("ff0000", 3, { duration: 3 });
10532
10533 // common config options shown with default values
10534 el.frame("C3DAF9", 1, {
10535     duration: 1 //duration of entire animation (not each individual ripple)
10536     // Note: Easing is not configurable and will be ignored if included
10537 });
10538 </code></pre>
10539     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10540     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10541     * @param {Object} options (optional) Object literal with any of the Fx config options
10542     * @return {Roo.Element} The Element
10543     */
10544     frame : function(color, count, o){
10545         var el = this.getFxEl();
10546         o = o || {};
10547
10548         el.queueFx(o, function(){
10549             color = color || "#C3DAF9";
10550             if(color.length == 6){
10551                 color = "#" + color;
10552             }
10553             count = count || 1;
10554             duration = o.duration || 1;
10555             this.show();
10556
10557             var b = this.getBox();
10558             var animFn = function(){
10559                 var proxy = this.createProxy({
10560
10561                      style:{
10562                         visbility:"hidden",
10563                         position:"absolute",
10564                         "z-index":"35000", // yee haw
10565                         border:"0px solid " + color
10566                      }
10567                   });
10568                 var scale = Roo.isBorderBox ? 2 : 1;
10569                 proxy.animate({
10570                     top:{from:b.y, to:b.y - 20},
10571                     left:{from:b.x, to:b.x - 20},
10572                     borderWidth:{from:0, to:10},
10573                     opacity:{from:1, to:0},
10574                     height:{from:b.height, to:(b.height + (20*scale))},
10575                     width:{from:b.width, to:(b.width + (20*scale))}
10576                 }, duration, function(){
10577                     proxy.remove();
10578                 });
10579                 if(--count > 0){
10580                      animFn.defer((duration/2)*1000, this);
10581                 }else{
10582                     el.afterFx(o);
10583                 }
10584             };
10585             animFn.call(this);
10586         });
10587         return this;
10588     },
10589
10590    /**
10591     * Creates a pause before any subsequent queued effects begin.  If there are
10592     * no effects queued after the pause it will have no effect.
10593     * Usage:
10594 <pre><code>
10595 el.pause(1);
10596 </code></pre>
10597     * @param {Number} seconds The length of time to pause (in seconds)
10598     * @return {Roo.Element} The Element
10599     */
10600     pause : function(seconds){
10601         var el = this.getFxEl();
10602         var o = {};
10603
10604         el.queueFx(o, function(){
10605             setTimeout(function(){
10606                 el.afterFx(o);
10607             }, seconds * 1000);
10608         });
10609         return this;
10610     },
10611
10612    /**
10613     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10614     * using the "endOpacity" config option.
10615     * Usage:
10616 <pre><code>
10617 // default: fade in from opacity 0 to 100%
10618 el.fadeIn();
10619
10620 // custom: fade in from opacity 0 to 75% over 2 seconds
10621 el.fadeIn({ endOpacity: .75, duration: 2});
10622
10623 // common config options shown with default values
10624 el.fadeIn({
10625     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10626     easing: 'easeOut',
10627     duration: .5
10628 });
10629 </code></pre>
10630     * @param {Object} options (optional) Object literal with any of the Fx config options
10631     * @return {Roo.Element} The Element
10632     */
10633     fadeIn : function(o){
10634         var el = this.getFxEl();
10635         o = o || {};
10636         el.queueFx(o, function(){
10637             this.setOpacity(0);
10638             this.fixDisplay();
10639             this.dom.style.visibility = 'visible';
10640             var to = o.endOpacity || 1;
10641             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10642                 o, null, .5, "easeOut", function(){
10643                 if(to == 1){
10644                     this.clearOpacity();
10645                 }
10646                 el.afterFx(o);
10647             });
10648         });
10649         return this;
10650     },
10651
10652    /**
10653     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10654     * using the "endOpacity" config option.
10655     * Usage:
10656 <pre><code>
10657 // default: fade out from the element's current opacity to 0
10658 el.fadeOut();
10659
10660 // custom: fade out from the element's current opacity to 25% over 2 seconds
10661 el.fadeOut({ endOpacity: .25, duration: 2});
10662
10663 // common config options shown with default values
10664 el.fadeOut({
10665     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10666     easing: 'easeOut',
10667     duration: .5
10668     remove: false,
10669     useDisplay: false
10670 });
10671 </code></pre>
10672     * @param {Object} options (optional) Object literal with any of the Fx config options
10673     * @return {Roo.Element} The Element
10674     */
10675     fadeOut : function(o){
10676         var el = this.getFxEl();
10677         o = o || {};
10678         el.queueFx(o, function(){
10679             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10680                 o, null, .5, "easeOut", function(){
10681                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10682                      this.dom.style.display = "none";
10683                 }else{
10684                      this.dom.style.visibility = "hidden";
10685                 }
10686                 this.clearOpacity();
10687                 el.afterFx(o);
10688             });
10689         });
10690         return this;
10691     },
10692
10693    /**
10694     * Animates the transition of an element's dimensions from a starting height/width
10695     * to an ending height/width.
10696     * Usage:
10697 <pre><code>
10698 // change height and width to 100x100 pixels
10699 el.scale(100, 100);
10700
10701 // common config options shown with default values.  The height and width will default to
10702 // the element's existing values if passed as null.
10703 el.scale(
10704     [element's width],
10705     [element's height], {
10706     easing: 'easeOut',
10707     duration: .35
10708 });
10709 </code></pre>
10710     * @param {Number} width  The new width (pass undefined to keep the original width)
10711     * @param {Number} height  The new height (pass undefined to keep the original height)
10712     * @param {Object} options (optional) Object literal with any of the Fx config options
10713     * @return {Roo.Element} The Element
10714     */
10715     scale : function(w, h, o){
10716         this.shift(Roo.apply({}, o, {
10717             width: w,
10718             height: h
10719         }));
10720         return this;
10721     },
10722
10723    /**
10724     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10725     * Any of these properties not specified in the config object will not be changed.  This effect 
10726     * requires that at least one new dimension, position or opacity setting must be passed in on
10727     * the config object in order for the function to have any effect.
10728     * Usage:
10729 <pre><code>
10730 // slide the element horizontally to x position 200 while changing the height and opacity
10731 el.shift({ x: 200, height: 50, opacity: .8 });
10732
10733 // common config options shown with default values.
10734 el.shift({
10735     width: [element's width],
10736     height: [element's height],
10737     x: [element's x position],
10738     y: [element's y position],
10739     opacity: [element's opacity],
10740     easing: 'easeOut',
10741     duration: .35
10742 });
10743 </code></pre>
10744     * @param {Object} options  Object literal with any of the Fx config options
10745     * @return {Roo.Element} The Element
10746     */
10747     shift : function(o){
10748         var el = this.getFxEl();
10749         o = o || {};
10750         el.queueFx(o, function(){
10751             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10752             if(w !== undefined){
10753                 a.width = {to: this.adjustWidth(w)};
10754             }
10755             if(h !== undefined){
10756                 a.height = {to: this.adjustHeight(h)};
10757             }
10758             if(x !== undefined || y !== undefined){
10759                 a.points = {to: [
10760                     x !== undefined ? x : this.getX(),
10761                     y !== undefined ? y : this.getY()
10762                 ]};
10763             }
10764             if(op !== undefined){
10765                 a.opacity = {to: op};
10766             }
10767             if(o.xy !== undefined){
10768                 a.points = {to: o.xy};
10769             }
10770             arguments.callee.anim = this.fxanim(a,
10771                 o, 'motion', .35, "easeOut", function(){
10772                 el.afterFx(o);
10773             });
10774         });
10775         return this;
10776     },
10777
10778         /**
10779          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10780          * ending point of the effect.
10781          * Usage:
10782          *<pre><code>
10783 // default: slide the element downward while fading out
10784 el.ghost();
10785
10786 // custom: slide the element out to the right with a 2-second duration
10787 el.ghost('r', { duration: 2 });
10788
10789 // common config options shown with default values
10790 el.ghost('b', {
10791     easing: 'easeOut',
10792     duration: .5
10793     remove: false,
10794     useDisplay: false
10795 });
10796 </code></pre>
10797          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10798          * @param {Object} options (optional) Object literal with any of the Fx config options
10799          * @return {Roo.Element} The Element
10800          */
10801     ghost : function(anchor, o){
10802         var el = this.getFxEl();
10803         o = o || {};
10804
10805         el.queueFx(o, function(){
10806             anchor = anchor || "b";
10807
10808             // restore values after effect
10809             var r = this.getFxRestore();
10810             var w = this.getWidth(),
10811                 h = this.getHeight();
10812
10813             var st = this.dom.style;
10814
10815             var after = function(){
10816                 if(o.useDisplay){
10817                     el.setDisplayed(false);
10818                 }else{
10819                     el.hide();
10820                 }
10821
10822                 el.clearOpacity();
10823                 el.setPositioning(r.pos);
10824                 st.width = r.width;
10825                 st.height = r.height;
10826
10827                 el.afterFx(o);
10828             };
10829
10830             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10831             switch(anchor.toLowerCase()){
10832                 case "t":
10833                     pt.by = [0, -h];
10834                 break;
10835                 case "l":
10836                     pt.by = [-w, 0];
10837                 break;
10838                 case "r":
10839                     pt.by = [w, 0];
10840                 break;
10841                 case "b":
10842                     pt.by = [0, h];
10843                 break;
10844                 case "tl":
10845                     pt.by = [-w, -h];
10846                 break;
10847                 case "bl":
10848                     pt.by = [-w, h];
10849                 break;
10850                 case "br":
10851                     pt.by = [w, h];
10852                 break;
10853                 case "tr":
10854                     pt.by = [w, -h];
10855                 break;
10856             }
10857
10858             arguments.callee.anim = this.fxanim(a,
10859                 o,
10860                 'motion',
10861                 .5,
10862                 "easeOut", after);
10863         });
10864         return this;
10865     },
10866
10867         /**
10868          * Ensures that all effects queued after syncFx is called on the element are
10869          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10870          * @return {Roo.Element} The Element
10871          */
10872     syncFx : function(){
10873         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10874             block : false,
10875             concurrent : true,
10876             stopFx : false
10877         });
10878         return this;
10879     },
10880
10881         /**
10882          * Ensures that all effects queued after sequenceFx is called on the element are
10883          * run in sequence.  This is the opposite of {@link #syncFx}.
10884          * @return {Roo.Element} The Element
10885          */
10886     sequenceFx : function(){
10887         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10888             block : false,
10889             concurrent : false,
10890             stopFx : false
10891         });
10892         return this;
10893     },
10894
10895         /* @private */
10896     nextFx : function(){
10897         var ef = this.fxQueue[0];
10898         if(ef){
10899             ef.call(this);
10900         }
10901     },
10902
10903         /**
10904          * Returns true if the element has any effects actively running or queued, else returns false.
10905          * @return {Boolean} True if element has active effects, else false
10906          */
10907     hasActiveFx : function(){
10908         return this.fxQueue && this.fxQueue[0];
10909     },
10910
10911         /**
10912          * Stops any running effects and clears the element's internal effects queue if it contains
10913          * any additional effects that haven't started yet.
10914          * @return {Roo.Element} The Element
10915          */
10916     stopFx : function(){
10917         if(this.hasActiveFx()){
10918             var cur = this.fxQueue[0];
10919             if(cur && cur.anim && cur.anim.isAnimated()){
10920                 this.fxQueue = [cur]; // clear out others
10921                 cur.anim.stop(true);
10922             }
10923         }
10924         return this;
10925     },
10926
10927         /* @private */
10928     beforeFx : function(o){
10929         if(this.hasActiveFx() && !o.concurrent){
10930            if(o.stopFx){
10931                this.stopFx();
10932                return true;
10933            }
10934            return false;
10935         }
10936         return true;
10937     },
10938
10939         /**
10940          * Returns true if the element is currently blocking so that no other effect can be queued
10941          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10942          * used to ensure that an effect initiated by a user action runs to completion prior to the
10943          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10944          * @return {Boolean} True if blocking, else false
10945          */
10946     hasFxBlock : function(){
10947         var q = this.fxQueue;
10948         return q && q[0] && q[0].block;
10949     },
10950
10951         /* @private */
10952     queueFx : function(o, fn){
10953         if(!this.fxQueue){
10954             this.fxQueue = [];
10955         }
10956         if(!this.hasFxBlock()){
10957             Roo.applyIf(o, this.fxDefaults);
10958             if(!o.concurrent){
10959                 var run = this.beforeFx(o);
10960                 fn.block = o.block;
10961                 this.fxQueue.push(fn);
10962                 if(run){
10963                     this.nextFx();
10964                 }
10965             }else{
10966                 fn.call(this);
10967             }
10968         }
10969         return this;
10970     },
10971
10972         /* @private */
10973     fxWrap : function(pos, o, vis){
10974         var wrap;
10975         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10976             var wrapXY;
10977             if(o.fixPosition){
10978                 wrapXY = this.getXY();
10979             }
10980             var div = document.createElement("div");
10981             div.style.visibility = vis;
10982             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10983             wrap.setPositioning(pos);
10984             if(wrap.getStyle("position") == "static"){
10985                 wrap.position("relative");
10986             }
10987             this.clearPositioning('auto');
10988             wrap.clip();
10989             wrap.dom.appendChild(this.dom);
10990             if(wrapXY){
10991                 wrap.setXY(wrapXY);
10992             }
10993         }
10994         return wrap;
10995     },
10996
10997         /* @private */
10998     fxUnwrap : function(wrap, pos, o){
10999         this.clearPositioning();
11000         this.setPositioning(pos);
11001         if(!o.wrap){
11002             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11003             wrap.remove();
11004         }
11005     },
11006
11007         /* @private */
11008     getFxRestore : function(){
11009         var st = this.dom.style;
11010         return {pos: this.getPositioning(), width: st.width, height : st.height};
11011     },
11012
11013         /* @private */
11014     afterFx : function(o){
11015         if(o.afterStyle){
11016             this.applyStyles(o.afterStyle);
11017         }
11018         if(o.afterCls){
11019             this.addClass(o.afterCls);
11020         }
11021         if(o.remove === true){
11022             this.remove();
11023         }
11024         Roo.callback(o.callback, o.scope, [this]);
11025         if(!o.concurrent){
11026             this.fxQueue.shift();
11027             this.nextFx();
11028         }
11029     },
11030
11031         /* @private */
11032     getFxEl : function(){ // support for composite element fx
11033         return Roo.get(this.dom);
11034     },
11035
11036         /* @private */
11037     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11038         animType = animType || 'run';
11039         opt = opt || {};
11040         var anim = Roo.lib.Anim[animType](
11041             this.dom, args,
11042             (opt.duration || defaultDur) || .35,
11043             (opt.easing || defaultEase) || 'easeOut',
11044             function(){
11045                 Roo.callback(cb, this);
11046             },
11047             this
11048         );
11049         opt.anim = anim;
11050         return anim;
11051     }
11052 };
11053
11054 // backwords compat
11055 Roo.Fx.resize = Roo.Fx.scale;
11056
11057 //When included, Roo.Fx is automatically applied to Element so that all basic
11058 //effects are available directly via the Element API
11059 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11060  * Based on:
11061  * Ext JS Library 1.1.1
11062  * Copyright(c) 2006-2007, Ext JS, LLC.
11063  *
11064  * Originally Released Under LGPL - original licence link has changed is not relivant.
11065  *
11066  * Fork - LGPL
11067  * <script type="text/javascript">
11068  */
11069
11070
11071 /**
11072  * @class Roo.CompositeElement
11073  * Standard composite class. Creates a Roo.Element for every element in the collection.
11074  * <br><br>
11075  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11076  * actions will be performed on all the elements in this collection.</b>
11077  * <br><br>
11078  * All methods return <i>this</i> and can be chained.
11079  <pre><code>
11080  var els = Roo.select("#some-el div.some-class", true);
11081  // or select directly from an existing element
11082  var el = Roo.get('some-el');
11083  el.select('div.some-class', true);
11084
11085  els.setWidth(100); // all elements become 100 width
11086  els.hide(true); // all elements fade out and hide
11087  // or
11088  els.setWidth(100).hide(true);
11089  </code></pre>
11090  */
11091 Roo.CompositeElement = function(els){
11092     this.elements = [];
11093     this.addElements(els);
11094 };
11095 Roo.CompositeElement.prototype = {
11096     isComposite: true,
11097     addElements : function(els){
11098         if(!els) {
11099             return this;
11100         }
11101         if(typeof els == "string"){
11102             els = Roo.Element.selectorFunction(els);
11103         }
11104         var yels = this.elements;
11105         var index = yels.length-1;
11106         for(var i = 0, len = els.length; i < len; i++) {
11107                 yels[++index] = Roo.get(els[i]);
11108         }
11109         return this;
11110     },
11111
11112     /**
11113     * Clears this composite and adds the elements returned by the passed selector.
11114     * @param {String/Array} els A string CSS selector, an array of elements or an element
11115     * @return {CompositeElement} this
11116     */
11117     fill : function(els){
11118         this.elements = [];
11119         this.add(els);
11120         return this;
11121     },
11122
11123     /**
11124     * Filters this composite to only elements that match the passed selector.
11125     * @param {String} selector A string CSS selector
11126     * @param {Boolean} inverse return inverse filter (not matches)
11127     * @return {CompositeElement} this
11128     */
11129     filter : function(selector, inverse){
11130         var els = [];
11131         inverse = inverse || false;
11132         this.each(function(el){
11133             var match = inverse ? !el.is(selector) : el.is(selector);
11134             if(match){
11135                 els[els.length] = el.dom;
11136             }
11137         });
11138         this.fill(els);
11139         return this;
11140     },
11141
11142     invoke : function(fn, args){
11143         var els = this.elements;
11144         for(var i = 0, len = els.length; i < len; i++) {
11145                 Roo.Element.prototype[fn].apply(els[i], args);
11146         }
11147         return this;
11148     },
11149     /**
11150     * Adds elements to this composite.
11151     * @param {String/Array} els A string CSS selector, an array of elements or an element
11152     * @return {CompositeElement} this
11153     */
11154     add : function(els){
11155         if(typeof els == "string"){
11156             this.addElements(Roo.Element.selectorFunction(els));
11157         }else if(els.length !== undefined){
11158             this.addElements(els);
11159         }else{
11160             this.addElements([els]);
11161         }
11162         return this;
11163     },
11164     /**
11165     * Calls the passed function passing (el, this, index) for each element in this composite.
11166     * @param {Function} fn The function to call
11167     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11168     * @return {CompositeElement} this
11169     */
11170     each : function(fn, scope){
11171         var els = this.elements;
11172         for(var i = 0, len = els.length; i < len; i++){
11173             if(fn.call(scope || els[i], els[i], this, i) === false) {
11174                 break;
11175             }
11176         }
11177         return this;
11178     },
11179
11180     /**
11181      * Returns the Element object at the specified index
11182      * @param {Number} index
11183      * @return {Roo.Element}
11184      */
11185     item : function(index){
11186         return this.elements[index] || null;
11187     },
11188
11189     /**
11190      * Returns the first Element
11191      * @return {Roo.Element}
11192      */
11193     first : function(){
11194         return this.item(0);
11195     },
11196
11197     /**
11198      * Returns the last Element
11199      * @return {Roo.Element}
11200      */
11201     last : function(){
11202         return this.item(this.elements.length-1);
11203     },
11204
11205     /**
11206      * Returns the number of elements in this composite
11207      * @return Number
11208      */
11209     getCount : function(){
11210         return this.elements.length;
11211     },
11212
11213     /**
11214      * Returns true if this composite contains the passed element
11215      * @return Boolean
11216      */
11217     contains : function(el){
11218         return this.indexOf(el) !== -1;
11219     },
11220
11221     /**
11222      * Returns true if this composite contains the passed element
11223      * @return Boolean
11224      */
11225     indexOf : function(el){
11226         return this.elements.indexOf(Roo.get(el));
11227     },
11228
11229
11230     /**
11231     * Removes the specified element(s).
11232     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11233     * or an array of any of those.
11234     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11235     * @return {CompositeElement} this
11236     */
11237     removeElement : function(el, removeDom){
11238         if(el instanceof Array){
11239             for(var i = 0, len = el.length; i < len; i++){
11240                 this.removeElement(el[i]);
11241             }
11242             return this;
11243         }
11244         var index = typeof el == 'number' ? el : this.indexOf(el);
11245         if(index !== -1){
11246             if(removeDom){
11247                 var d = this.elements[index];
11248                 if(d.dom){
11249                     d.remove();
11250                 }else{
11251                     d.parentNode.removeChild(d);
11252                 }
11253             }
11254             this.elements.splice(index, 1);
11255         }
11256         return this;
11257     },
11258
11259     /**
11260     * Replaces the specified element with the passed element.
11261     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11262     * to replace.
11263     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11264     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11265     * @return {CompositeElement} this
11266     */
11267     replaceElement : function(el, replacement, domReplace){
11268         var index = typeof el == 'number' ? el : this.indexOf(el);
11269         if(index !== -1){
11270             if(domReplace){
11271                 this.elements[index].replaceWith(replacement);
11272             }else{
11273                 this.elements.splice(index, 1, Roo.get(replacement))
11274             }
11275         }
11276         return this;
11277     },
11278
11279     /**
11280      * Removes all elements.
11281      */
11282     clear : function(){
11283         this.elements = [];
11284     }
11285 };
11286 (function(){
11287     Roo.CompositeElement.createCall = function(proto, fnName){
11288         if(!proto[fnName]){
11289             proto[fnName] = function(){
11290                 return this.invoke(fnName, arguments);
11291             };
11292         }
11293     };
11294     for(var fnName in Roo.Element.prototype){
11295         if(typeof Roo.Element.prototype[fnName] == "function"){
11296             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11297         }
11298     };
11299 })();
11300 /*
11301  * Based on:
11302  * Ext JS Library 1.1.1
11303  * Copyright(c) 2006-2007, Ext JS, LLC.
11304  *
11305  * Originally Released Under LGPL - original licence link has changed is not relivant.
11306  *
11307  * Fork - LGPL
11308  * <script type="text/javascript">
11309  */
11310
11311 /**
11312  * @class Roo.CompositeElementLite
11313  * @extends Roo.CompositeElement
11314  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11315  <pre><code>
11316  var els = Roo.select("#some-el div.some-class");
11317  // or select directly from an existing element
11318  var el = Roo.get('some-el');
11319  el.select('div.some-class');
11320
11321  els.setWidth(100); // all elements become 100 width
11322  els.hide(true); // all elements fade out and hide
11323  // or
11324  els.setWidth(100).hide(true);
11325  </code></pre><br><br>
11326  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11327  * actions will be performed on all the elements in this collection.</b>
11328  */
11329 Roo.CompositeElementLite = function(els){
11330     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11331     this.el = new Roo.Element.Flyweight();
11332 };
11333 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11334     addElements : function(els){
11335         if(els){
11336             if(els instanceof Array){
11337                 this.elements = this.elements.concat(els);
11338             }else{
11339                 var yels = this.elements;
11340                 var index = yels.length-1;
11341                 for(var i = 0, len = els.length; i < len; i++) {
11342                     yels[++index] = els[i];
11343                 }
11344             }
11345         }
11346         return this;
11347     },
11348     invoke : function(fn, args){
11349         var els = this.elements;
11350         var el = this.el;
11351         for(var i = 0, len = els.length; i < len; i++) {
11352             el.dom = els[i];
11353                 Roo.Element.prototype[fn].apply(el, args);
11354         }
11355         return this;
11356     },
11357     /**
11358      * Returns a flyweight Element of the dom element object at the specified index
11359      * @param {Number} index
11360      * @return {Roo.Element}
11361      */
11362     item : function(index){
11363         if(!this.elements[index]){
11364             return null;
11365         }
11366         this.el.dom = this.elements[index];
11367         return this.el;
11368     },
11369
11370     // fixes scope with flyweight
11371     addListener : function(eventName, handler, scope, opt){
11372         var els = this.elements;
11373         for(var i = 0, len = els.length; i < len; i++) {
11374             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11375         }
11376         return this;
11377     },
11378
11379     /**
11380     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11381     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11382     * a reference to the dom node, use el.dom.</b>
11383     * @param {Function} fn The function to call
11384     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11385     * @return {CompositeElement} this
11386     */
11387     each : function(fn, scope){
11388         var els = this.elements;
11389         var el = this.el;
11390         for(var i = 0, len = els.length; i < len; i++){
11391             el.dom = els[i];
11392                 if(fn.call(scope || el, el, this, i) === false){
11393                 break;
11394             }
11395         }
11396         return this;
11397     },
11398
11399     indexOf : function(el){
11400         return this.elements.indexOf(Roo.getDom(el));
11401     },
11402
11403     replaceElement : function(el, replacement, domReplace){
11404         var index = typeof el == 'number' ? el : this.indexOf(el);
11405         if(index !== -1){
11406             replacement = Roo.getDom(replacement);
11407             if(domReplace){
11408                 var d = this.elements[index];
11409                 d.parentNode.insertBefore(replacement, d);
11410                 d.parentNode.removeChild(d);
11411             }
11412             this.elements.splice(index, 1, replacement);
11413         }
11414         return this;
11415     }
11416 });
11417 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11418
11419 /*
11420  * Based on:
11421  * Ext JS Library 1.1.1
11422  * Copyright(c) 2006-2007, Ext JS, LLC.
11423  *
11424  * Originally Released Under LGPL - original licence link has changed is not relivant.
11425  *
11426  * Fork - LGPL
11427  * <script type="text/javascript">
11428  */
11429
11430  
11431
11432 /**
11433  * @class Roo.data.Connection
11434  * @extends Roo.util.Observable
11435  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11436  * either to a configured URL, or to a URL specified at request time.<br><br>
11437  * <p>
11438  * Requests made by this class are asynchronous, and will return immediately. No data from
11439  * the server will be available to the statement immediately following the {@link #request} call.
11440  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11441  * <p>
11442  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11443  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11444  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11445  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11446  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11447  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11448  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11449  * standard DOM methods.
11450  * @constructor
11451  * @param {Object} config a configuration object.
11452  */
11453 Roo.data.Connection = function(config){
11454     Roo.apply(this, config);
11455     this.addEvents({
11456         /**
11457          * @event beforerequest
11458          * Fires before a network request is made to retrieve a data object.
11459          * @param {Connection} conn This Connection object.
11460          * @param {Object} options The options config object passed to the {@link #request} method.
11461          */
11462         "beforerequest" : true,
11463         /**
11464          * @event requestcomplete
11465          * Fires if the request was successfully completed.
11466          * @param {Connection} conn This Connection object.
11467          * @param {Object} response The XHR object containing the response data.
11468          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11469          * @param {Object} options The options config object passed to the {@link #request} method.
11470          */
11471         "requestcomplete" : true,
11472         /**
11473          * @event requestexception
11474          * Fires if an error HTTP status was returned from the server.
11475          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11476          * @param {Connection} conn This Connection object.
11477          * @param {Object} response The XHR object containing the response data.
11478          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11479          * @param {Object} options The options config object passed to the {@link #request} method.
11480          */
11481         "requestexception" : true
11482     });
11483     Roo.data.Connection.superclass.constructor.call(this);
11484 };
11485
11486 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11487     /**
11488      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11489      */
11490     /**
11491      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11492      * extra parameters to each request made by this object. (defaults to undefined)
11493      */
11494     /**
11495      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11496      *  to each request made by this object. (defaults to undefined)
11497      */
11498     /**
11499      * @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)
11500      */
11501     /**
11502      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11503      */
11504     timeout : 30000,
11505     /**
11506      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11507      * @type Boolean
11508      */
11509     autoAbort:false,
11510
11511     /**
11512      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11513      * @type Boolean
11514      */
11515     disableCaching: true,
11516
11517     /**
11518      * Sends an HTTP request to a remote server.
11519      * @param {Object} options An object which may contain the following properties:<ul>
11520      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11521      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11522      * request, a url encoded string or a function to call to get either.</li>
11523      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11524      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11525      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11526      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11527      * <li>options {Object} The parameter to the request call.</li>
11528      * <li>success {Boolean} True if the request succeeded.</li>
11529      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11530      * </ul></li>
11531      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11532      * The callback is passed the following parameters:<ul>
11533      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11534      * <li>options {Object} The parameter to the request call.</li>
11535      * </ul></li>
11536      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11537      * The callback is passed the following parameters:<ul>
11538      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11539      * <li>options {Object} The parameter to the request call.</li>
11540      * </ul></li>
11541      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11542      * for the callback function. Defaults to the browser window.</li>
11543      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11544      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11545      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11546      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11547      * params for the post data. Any params will be appended to the URL.</li>
11548      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11549      * </ul>
11550      * @return {Number} transactionId
11551      */
11552     request : function(o){
11553         if(this.fireEvent("beforerequest", this, o) !== false){
11554             var p = o.params;
11555
11556             if(typeof p == "function"){
11557                 p = p.call(o.scope||window, o);
11558             }
11559             if(typeof p == "object"){
11560                 p = Roo.urlEncode(o.params);
11561             }
11562             if(this.extraParams){
11563                 var extras = Roo.urlEncode(this.extraParams);
11564                 p = p ? (p + '&' + extras) : extras;
11565             }
11566
11567             var url = o.url || this.url;
11568             if(typeof url == 'function'){
11569                 url = url.call(o.scope||window, o);
11570             }
11571
11572             if(o.form){
11573                 var form = Roo.getDom(o.form);
11574                 url = url || form.action;
11575
11576                 var enctype = form.getAttribute("enctype");
11577                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11578                     return this.doFormUpload(o, p, url);
11579                 }
11580                 var f = Roo.lib.Ajax.serializeForm(form);
11581                 p = p ? (p + '&' + f) : f;
11582             }
11583
11584             var hs = o.headers;
11585             if(this.defaultHeaders){
11586                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11587                 if(!o.headers){
11588                     o.headers = hs;
11589                 }
11590             }
11591
11592             var cb = {
11593                 success: this.handleResponse,
11594                 failure: this.handleFailure,
11595                 scope: this,
11596                 argument: {options: o},
11597                 timeout : o.timeout || this.timeout
11598             };
11599
11600             var method = o.method||this.method||(p ? "POST" : "GET");
11601
11602             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11603                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11604             }
11605
11606             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11607                 if(o.autoAbort){
11608                     this.abort();
11609                 }
11610             }else if(this.autoAbort !== false){
11611                 this.abort();
11612             }
11613
11614             if((method == 'GET' && p) || o.xmlData){
11615                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11616                 p = '';
11617             }
11618             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11619             return this.transId;
11620         }else{
11621             Roo.callback(o.callback, o.scope, [o, null, null]);
11622             return null;
11623         }
11624     },
11625
11626     /**
11627      * Determine whether this object has a request outstanding.
11628      * @param {Number} transactionId (Optional) defaults to the last transaction
11629      * @return {Boolean} True if there is an outstanding request.
11630      */
11631     isLoading : function(transId){
11632         if(transId){
11633             return Roo.lib.Ajax.isCallInProgress(transId);
11634         }else{
11635             return this.transId ? true : false;
11636         }
11637     },
11638
11639     /**
11640      * Aborts any outstanding request.
11641      * @param {Number} transactionId (Optional) defaults to the last transaction
11642      */
11643     abort : function(transId){
11644         if(transId || this.isLoading()){
11645             Roo.lib.Ajax.abort(transId || this.transId);
11646         }
11647     },
11648
11649     // private
11650     handleResponse : function(response){
11651         this.transId = false;
11652         var options = response.argument.options;
11653         response.argument = options ? options.argument : null;
11654         this.fireEvent("requestcomplete", this, response, options);
11655         Roo.callback(options.success, options.scope, [response, options]);
11656         Roo.callback(options.callback, options.scope, [options, true, response]);
11657     },
11658
11659     // private
11660     handleFailure : function(response, e){
11661         this.transId = false;
11662         var options = response.argument.options;
11663         response.argument = options ? options.argument : null;
11664         this.fireEvent("requestexception", this, response, options, e);
11665         Roo.callback(options.failure, options.scope, [response, options]);
11666         Roo.callback(options.callback, options.scope, [options, false, response]);
11667     },
11668
11669     // private
11670     doFormUpload : function(o, ps, url){
11671         var id = Roo.id();
11672         var frame = document.createElement('iframe');
11673         frame.id = id;
11674         frame.name = id;
11675         frame.className = 'x-hidden';
11676         if(Roo.isIE){
11677             frame.src = Roo.SSL_SECURE_URL;
11678         }
11679         document.body.appendChild(frame);
11680
11681         if(Roo.isIE){
11682            document.frames[id].name = id;
11683         }
11684
11685         var form = Roo.getDom(o.form);
11686         form.target = id;
11687         form.method = 'POST';
11688         form.enctype = form.encoding = 'multipart/form-data';
11689         if(url){
11690             form.action = url;
11691         }
11692
11693         var hiddens, hd;
11694         if(ps){ // add dynamic params
11695             hiddens = [];
11696             ps = Roo.urlDecode(ps, false);
11697             for(var k in ps){
11698                 if(ps.hasOwnProperty(k)){
11699                     hd = document.createElement('input');
11700                     hd.type = 'hidden';
11701                     hd.name = k;
11702                     hd.value = ps[k];
11703                     form.appendChild(hd);
11704                     hiddens.push(hd);
11705                 }
11706             }
11707         }
11708
11709         function cb(){
11710             var r = {  // bogus response object
11711                 responseText : '',
11712                 responseXML : null
11713             };
11714
11715             r.argument = o ? o.argument : null;
11716
11717             try { //
11718                 var doc;
11719                 if(Roo.isIE){
11720                     doc = frame.contentWindow.document;
11721                 }else {
11722                     doc = (frame.contentDocument || window.frames[id].document);
11723                 }
11724                 if(doc && doc.body){
11725                     r.responseText = doc.body.innerHTML;
11726                 }
11727                 if(doc && doc.XMLDocument){
11728                     r.responseXML = doc.XMLDocument;
11729                 }else {
11730                     r.responseXML = doc;
11731                 }
11732             }
11733             catch(e) {
11734                 // ignore
11735             }
11736
11737             Roo.EventManager.removeListener(frame, 'load', cb, this);
11738
11739             this.fireEvent("requestcomplete", this, r, o);
11740             Roo.callback(o.success, o.scope, [r, o]);
11741             Roo.callback(o.callback, o.scope, [o, true, r]);
11742
11743             setTimeout(function(){document.body.removeChild(frame);}, 100);
11744         }
11745
11746         Roo.EventManager.on(frame, 'load', cb, this);
11747         form.submit();
11748
11749         if(hiddens){ // remove dynamic params
11750             for(var i = 0, len = hiddens.length; i < len; i++){
11751                 form.removeChild(hiddens[i]);
11752             }
11753         }
11754     }
11755 });
11756 /*
11757  * Based on:
11758  * Ext JS Library 1.1.1
11759  * Copyright(c) 2006-2007, Ext JS, LLC.
11760  *
11761  * Originally Released Under LGPL - original licence link has changed is not relivant.
11762  *
11763  * Fork - LGPL
11764  * <script type="text/javascript">
11765  */
11766  
11767 /**
11768  * Global Ajax request class.
11769  * 
11770  * @class Roo.Ajax
11771  * @extends Roo.data.Connection
11772  * @static
11773  * 
11774  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11775  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11776  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11777  * @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)
11778  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11779  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11780  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11781  */
11782 Roo.Ajax = new Roo.data.Connection({
11783     // fix up the docs
11784     /**
11785      * @scope Roo.Ajax
11786      * @type {Boolear} 
11787      */
11788     autoAbort : false,
11789
11790     /**
11791      * Serialize the passed form into a url encoded string
11792      * @scope Roo.Ajax
11793      * @param {String/HTMLElement} form
11794      * @return {String}
11795      */
11796     serializeForm : function(form){
11797         return Roo.lib.Ajax.serializeForm(form);
11798     }
11799 });/*
11800  * Based on:
11801  * Ext JS Library 1.1.1
11802  * Copyright(c) 2006-2007, Ext JS, LLC.
11803  *
11804  * Originally Released Under LGPL - original licence link has changed is not relivant.
11805  *
11806  * Fork - LGPL
11807  * <script type="text/javascript">
11808  */
11809
11810  
11811 /**
11812  * @class Roo.UpdateManager
11813  * @extends Roo.util.Observable
11814  * Provides AJAX-style update for Element object.<br><br>
11815  * Usage:<br>
11816  * <pre><code>
11817  * // Get it from a Roo.Element object
11818  * var el = Roo.get("foo");
11819  * var mgr = el.getUpdateManager();
11820  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11821  * ...
11822  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11823  * <br>
11824  * // or directly (returns the same UpdateManager instance)
11825  * var mgr = new Roo.UpdateManager("myElementId");
11826  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11827  * mgr.on("update", myFcnNeedsToKnow);
11828  * <br>
11829    // short handed call directly from the element object
11830    Roo.get("foo").load({
11831         url: "bar.php",
11832         scripts:true,
11833         params: "for=bar",
11834         text: "Loading Foo..."
11835    });
11836  * </code></pre>
11837  * @constructor
11838  * Create new UpdateManager directly.
11839  * @param {String/HTMLElement/Roo.Element} el The element to update
11840  * @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).
11841  */
11842 Roo.UpdateManager = function(el, forceNew){
11843     el = Roo.get(el);
11844     if(!forceNew && el.updateManager){
11845         return el.updateManager;
11846     }
11847     /**
11848      * The Element object
11849      * @type Roo.Element
11850      */
11851     this.el = el;
11852     /**
11853      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11854      * @type String
11855      */
11856     this.defaultUrl = null;
11857
11858     this.addEvents({
11859         /**
11860          * @event beforeupdate
11861          * Fired before an update is made, return false from your handler and the update is cancelled.
11862          * @param {Roo.Element} el
11863          * @param {String/Object/Function} url
11864          * @param {String/Object} params
11865          */
11866         "beforeupdate": true,
11867         /**
11868          * @event update
11869          * Fired after successful update is made.
11870          * @param {Roo.Element} el
11871          * @param {Object} oResponseObject The response Object
11872          */
11873         "update": true,
11874         /**
11875          * @event failure
11876          * Fired on update failure.
11877          * @param {Roo.Element} el
11878          * @param {Object} oResponseObject The response Object
11879          */
11880         "failure": true
11881     });
11882     var d = Roo.UpdateManager.defaults;
11883     /**
11884      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11885      * @type String
11886      */
11887     this.sslBlankUrl = d.sslBlankUrl;
11888     /**
11889      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11890      * @type Boolean
11891      */
11892     this.disableCaching = d.disableCaching;
11893     /**
11894      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11895      * @type String
11896      */
11897     this.indicatorText = d.indicatorText;
11898     /**
11899      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11900      * @type String
11901      */
11902     this.showLoadIndicator = d.showLoadIndicator;
11903     /**
11904      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11905      * @type Number
11906      */
11907     this.timeout = d.timeout;
11908
11909     /**
11910      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11911      * @type Boolean
11912      */
11913     this.loadScripts = d.loadScripts;
11914
11915     /**
11916      * Transaction object of current executing transaction
11917      */
11918     this.transaction = null;
11919
11920     /**
11921      * @private
11922      */
11923     this.autoRefreshProcId = null;
11924     /**
11925      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11926      * @type Function
11927      */
11928     this.refreshDelegate = this.refresh.createDelegate(this);
11929     /**
11930      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11931      * @type Function
11932      */
11933     this.updateDelegate = this.update.createDelegate(this);
11934     /**
11935      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11936      * @type Function
11937      */
11938     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11939     /**
11940      * @private
11941      */
11942     this.successDelegate = this.processSuccess.createDelegate(this);
11943     /**
11944      * @private
11945      */
11946     this.failureDelegate = this.processFailure.createDelegate(this);
11947
11948     if(!this.renderer){
11949      /**
11950       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11951       */
11952     this.renderer = new Roo.UpdateManager.BasicRenderer();
11953     }
11954     
11955     Roo.UpdateManager.superclass.constructor.call(this);
11956 };
11957
11958 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11959     /**
11960      * Get the Element this UpdateManager is bound to
11961      * @return {Roo.Element} The element
11962      */
11963     getEl : function(){
11964         return this.el;
11965     },
11966     /**
11967      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11968      * @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:
11969 <pre><code>
11970 um.update({<br/>
11971     url: "your-url.php",<br/>
11972     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11973     callback: yourFunction,<br/>
11974     scope: yourObject, //(optional scope)  <br/>
11975     discardUrl: false, <br/>
11976     nocache: false,<br/>
11977     text: "Loading...",<br/>
11978     timeout: 30,<br/>
11979     scripts: false<br/>
11980 });
11981 </code></pre>
11982      * The only required property is url. The optional properties nocache, text and scripts
11983      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11984      * @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}
11985      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11986      * @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.
11987      */
11988     update : function(url, params, callback, discardUrl){
11989         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11990             var method = this.method,
11991                 cfg;
11992             if(typeof url == "object"){ // must be config object
11993                 cfg = url;
11994                 url = cfg.url;
11995                 params = params || cfg.params;
11996                 callback = callback || cfg.callback;
11997                 discardUrl = discardUrl || cfg.discardUrl;
11998                 if(callback && cfg.scope){
11999                     callback = callback.createDelegate(cfg.scope);
12000                 }
12001                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12002                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12003                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12004                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12005                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12006             }
12007             this.showLoading();
12008             if(!discardUrl){
12009                 this.defaultUrl = url;
12010             }
12011             if(typeof url == "function"){
12012                 url = url.call(this);
12013             }
12014
12015             method = method || (params ? "POST" : "GET");
12016             if(method == "GET"){
12017                 url = this.prepareUrl(url);
12018             }
12019
12020             var o = Roo.apply(cfg ||{}, {
12021                 url : url,
12022                 params: params,
12023                 success: this.successDelegate,
12024                 failure: this.failureDelegate,
12025                 callback: undefined,
12026                 timeout: (this.timeout*1000),
12027                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12028             });
12029             Roo.log("updated manager called with timeout of " + o.timeout);
12030             this.transaction = Roo.Ajax.request(o);
12031         }
12032     },
12033
12034     /**
12035      * 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.
12036      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12037      * @param {String/HTMLElement} form The form Id or form element
12038      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12039      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12040      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12041      */
12042     formUpdate : function(form, url, reset, callback){
12043         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12044             if(typeof url == "function"){
12045                 url = url.call(this);
12046             }
12047             form = Roo.getDom(form);
12048             this.transaction = Roo.Ajax.request({
12049                 form: form,
12050                 url:url,
12051                 success: this.successDelegate,
12052                 failure: this.failureDelegate,
12053                 timeout: (this.timeout*1000),
12054                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12055             });
12056             this.showLoading.defer(1, this);
12057         }
12058     },
12059
12060     /**
12061      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12062      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12063      */
12064     refresh : function(callback){
12065         if(this.defaultUrl == null){
12066             return;
12067         }
12068         this.update(this.defaultUrl, null, callback, true);
12069     },
12070
12071     /**
12072      * Set this element to auto refresh.
12073      * @param {Number} interval How often to update (in seconds).
12074      * @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)
12075      * @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}
12076      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12077      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12078      */
12079     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12080         if(refreshNow){
12081             this.update(url || this.defaultUrl, params, callback, true);
12082         }
12083         if(this.autoRefreshProcId){
12084             clearInterval(this.autoRefreshProcId);
12085         }
12086         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12087     },
12088
12089     /**
12090      * Stop auto refresh on this element.
12091      */
12092      stopAutoRefresh : function(){
12093         if(this.autoRefreshProcId){
12094             clearInterval(this.autoRefreshProcId);
12095             delete this.autoRefreshProcId;
12096         }
12097     },
12098
12099     isAutoRefreshing : function(){
12100        return this.autoRefreshProcId ? true : false;
12101     },
12102     /**
12103      * Called to update the element to "Loading" state. Override to perform custom action.
12104      */
12105     showLoading : function(){
12106         if(this.showLoadIndicator){
12107             this.el.update(this.indicatorText);
12108         }
12109     },
12110
12111     /**
12112      * Adds unique parameter to query string if disableCaching = true
12113      * @private
12114      */
12115     prepareUrl : function(url){
12116         if(this.disableCaching){
12117             var append = "_dc=" + (new Date().getTime());
12118             if(url.indexOf("?") !== -1){
12119                 url += "&" + append;
12120             }else{
12121                 url += "?" + append;
12122             }
12123         }
12124         return url;
12125     },
12126
12127     /**
12128      * @private
12129      */
12130     processSuccess : function(response){
12131         this.transaction = null;
12132         if(response.argument.form && response.argument.reset){
12133             try{ // put in try/catch since some older FF releases had problems with this
12134                 response.argument.form.reset();
12135             }catch(e){}
12136         }
12137         if(this.loadScripts){
12138             this.renderer.render(this.el, response, this,
12139                 this.updateComplete.createDelegate(this, [response]));
12140         }else{
12141             this.renderer.render(this.el, response, this);
12142             this.updateComplete(response);
12143         }
12144     },
12145
12146     updateComplete : function(response){
12147         this.fireEvent("update", this.el, response);
12148         if(typeof response.argument.callback == "function"){
12149             response.argument.callback(this.el, true, response);
12150         }
12151     },
12152
12153     /**
12154      * @private
12155      */
12156     processFailure : function(response){
12157         this.transaction = null;
12158         this.fireEvent("failure", this.el, response);
12159         if(typeof response.argument.callback == "function"){
12160             response.argument.callback(this.el, false, response);
12161         }
12162     },
12163
12164     /**
12165      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12166      * @param {Object} renderer The object implementing the render() method
12167      */
12168     setRenderer : function(renderer){
12169         this.renderer = renderer;
12170     },
12171
12172     getRenderer : function(){
12173        return this.renderer;
12174     },
12175
12176     /**
12177      * Set the defaultUrl used for updates
12178      * @param {String/Function} defaultUrl The url or a function to call to get the url
12179      */
12180     setDefaultUrl : function(defaultUrl){
12181         this.defaultUrl = defaultUrl;
12182     },
12183
12184     /**
12185      * Aborts the executing transaction
12186      */
12187     abort : function(){
12188         if(this.transaction){
12189             Roo.Ajax.abort(this.transaction);
12190         }
12191     },
12192
12193     /**
12194      * Returns true if an update is in progress
12195      * @return {Boolean}
12196      */
12197     isUpdating : function(){
12198         if(this.transaction){
12199             return Roo.Ajax.isLoading(this.transaction);
12200         }
12201         return false;
12202     }
12203 });
12204
12205 /**
12206  * @class Roo.UpdateManager.defaults
12207  * @static (not really - but it helps the doc tool)
12208  * The defaults collection enables customizing the default properties of UpdateManager
12209  */
12210    Roo.UpdateManager.defaults = {
12211        /**
12212          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12213          * @type Number
12214          */
12215          timeout : 30,
12216
12217          /**
12218          * True to process scripts by default (Defaults to false).
12219          * @type Boolean
12220          */
12221         loadScripts : false,
12222
12223         /**
12224         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12225         * @type String
12226         */
12227         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12228         /**
12229          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12230          * @type Boolean
12231          */
12232         disableCaching : false,
12233         /**
12234          * Whether to show indicatorText when loading (Defaults to true).
12235          * @type Boolean
12236          */
12237         showLoadIndicator : true,
12238         /**
12239          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12240          * @type String
12241          */
12242         indicatorText : '<div class="loading-indicator">Loading...</div>'
12243    };
12244
12245 /**
12246  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12247  *Usage:
12248  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12249  * @param {String/HTMLElement/Roo.Element} el The element to update
12250  * @param {String} url The url
12251  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12252  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12253  * @static
12254  * @deprecated
12255  * @member Roo.UpdateManager
12256  */
12257 Roo.UpdateManager.updateElement = function(el, url, params, options){
12258     var um = Roo.get(el, true).getUpdateManager();
12259     Roo.apply(um, options);
12260     um.update(url, params, options ? options.callback : null);
12261 };
12262 // alias for backwards compat
12263 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12264 /**
12265  * @class Roo.UpdateManager.BasicRenderer
12266  * Default Content renderer. Updates the elements innerHTML with the responseText.
12267  */
12268 Roo.UpdateManager.BasicRenderer = function(){};
12269
12270 Roo.UpdateManager.BasicRenderer.prototype = {
12271     /**
12272      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12273      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12274      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12275      * @param {Roo.Element} el The element being rendered
12276      * @param {Object} response The YUI Connect response object
12277      * @param {UpdateManager} updateManager The calling update manager
12278      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12279      */
12280      render : function(el, response, updateManager, callback){
12281         el.update(response.responseText, updateManager.loadScripts, callback);
12282     }
12283 };
12284 /*
12285  * Based on:
12286  * Roo JS
12287  * (c)) Alan Knowles
12288  * Licence : LGPL
12289  */
12290
12291
12292 /**
12293  * @class Roo.DomTemplate
12294  * @extends Roo.Template
12295  * An effort at a dom based template engine..
12296  *
12297  * Similar to XTemplate, except it uses dom parsing to create the template..
12298  *
12299  * Supported features:
12300  *
12301  *  Tags:
12302
12303 <pre><code>
12304       {a_variable} - output encoded.
12305       {a_variable.format:("Y-m-d")} - call a method on the variable
12306       {a_variable:raw} - unencoded output
12307       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12308       {a_variable:this.method_on_template(...)} - call a method on the template object.
12309  
12310 </code></pre>
12311  *  The tpl tag:
12312 <pre><code>
12313         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12314         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12315         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12316         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12317   
12318 </code></pre>
12319  *      
12320  */
12321 Roo.DomTemplate = function()
12322 {
12323      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12324      if (this.html) {
12325         this.compile();
12326      }
12327 };
12328
12329
12330 Roo.extend(Roo.DomTemplate, Roo.Template, {
12331     /**
12332      * id counter for sub templates.
12333      */
12334     id : 0,
12335     /**
12336      * flag to indicate if dom parser is inside a pre,
12337      * it will strip whitespace if not.
12338      */
12339     inPre : false,
12340     
12341     /**
12342      * The various sub templates
12343      */
12344     tpls : false,
12345     
12346     
12347     
12348     /**
12349      *
12350      * basic tag replacing syntax
12351      * WORD:WORD()
12352      *
12353      * // you can fake an object call by doing this
12354      *  x.t:(test,tesT) 
12355      * 
12356      */
12357     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12358     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12359     
12360     iterChild : function (node, method) {
12361         
12362         var oldPre = this.inPre;
12363         if (node.tagName == 'PRE') {
12364             this.inPre = true;
12365         }
12366         for( var i = 0; i < node.childNodes.length; i++) {
12367             method.call(this, node.childNodes[i]);
12368         }
12369         this.inPre = oldPre;
12370     },
12371     
12372     
12373     
12374     /**
12375      * compile the template
12376      *
12377      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12378      *
12379      */
12380     compile: function()
12381     {
12382         var s = this.html;
12383         
12384         // covert the html into DOM...
12385         var doc = false;
12386         var div =false;
12387         try {
12388             doc = document.implementation.createHTMLDocument("");
12389             doc.documentElement.innerHTML =   this.html  ;
12390             div = doc.documentElement;
12391         } catch (e) {
12392             // old IE... - nasty -- it causes all sorts of issues.. with
12393             // images getting pulled from server..
12394             div = document.createElement('div');
12395             div.innerHTML = this.html;
12396         }
12397         //doc.documentElement.innerHTML = htmlBody
12398          
12399         
12400         
12401         this.tpls = [];
12402         var _t = this;
12403         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12404         
12405         var tpls = this.tpls;
12406         
12407         // create a top level template from the snippet..
12408         
12409         //Roo.log(div.innerHTML);
12410         
12411         var tpl = {
12412             uid : 'master',
12413             id : this.id++,
12414             attr : false,
12415             value : false,
12416             body : div.innerHTML,
12417             
12418             forCall : false,
12419             execCall : false,
12420             dom : div,
12421             isTop : true
12422             
12423         };
12424         tpls.unshift(tpl);
12425         
12426         
12427         // compile them...
12428         this.tpls = [];
12429         Roo.each(tpls, function(tp){
12430             this.compileTpl(tp);
12431             this.tpls[tp.id] = tp;
12432         }, this);
12433         
12434         this.master = tpls[0];
12435         return this;
12436         
12437         
12438     },
12439     
12440     compileNode : function(node, istop) {
12441         // test for
12442         //Roo.log(node);
12443         
12444         
12445         // skip anything not a tag..
12446         if (node.nodeType != 1) {
12447             if (node.nodeType == 3 && !this.inPre) {
12448                 // reduce white space..
12449                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12450                 
12451             }
12452             return;
12453         }
12454         
12455         var tpl = {
12456             uid : false,
12457             id : false,
12458             attr : false,
12459             value : false,
12460             body : '',
12461             
12462             forCall : false,
12463             execCall : false,
12464             dom : false,
12465             isTop : istop
12466             
12467             
12468         };
12469         
12470         
12471         switch(true) {
12472             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12473             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12474             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12475             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12476             // no default..
12477         }
12478         
12479         
12480         if (!tpl.attr) {
12481             // just itterate children..
12482             this.iterChild(node,this.compileNode);
12483             return;
12484         }
12485         tpl.uid = this.id++;
12486         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12487         node.removeAttribute('roo-'+ tpl.attr);
12488         if (tpl.attr != 'name') {
12489             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12490             node.parentNode.replaceChild(placeholder,  node);
12491         } else {
12492             
12493             var placeholder =  document.createElement('span');
12494             placeholder.className = 'roo-tpl-' + tpl.value;
12495             node.parentNode.replaceChild(placeholder,  node);
12496         }
12497         
12498         // parent now sees '{domtplXXXX}
12499         this.iterChild(node,this.compileNode);
12500         
12501         // we should now have node body...
12502         var div = document.createElement('div');
12503         div.appendChild(node);
12504         tpl.dom = node;
12505         // this has the unfortunate side effect of converting tagged attributes
12506         // eg. href="{...}" into %7C...%7D
12507         // this has been fixed by searching for those combo's although it's a bit hacky..
12508         
12509         
12510         tpl.body = div.innerHTML;
12511         
12512         
12513          
12514         tpl.id = tpl.uid;
12515         switch(tpl.attr) {
12516             case 'for' :
12517                 switch (tpl.value) {
12518                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12519                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12520                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12521                 }
12522                 break;
12523             
12524             case 'exec':
12525                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12526                 break;
12527             
12528             case 'if':     
12529                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12530                 break;
12531             
12532             case 'name':
12533                 tpl.id  = tpl.value; // replace non characters???
12534                 break;
12535             
12536         }
12537         
12538         
12539         this.tpls.push(tpl);
12540         
12541         
12542         
12543     },
12544     
12545     
12546     
12547     
12548     /**
12549      * Compile a segment of the template into a 'sub-template'
12550      *
12551      * 
12552      * 
12553      *
12554      */
12555     compileTpl : function(tpl)
12556     {
12557         var fm = Roo.util.Format;
12558         var useF = this.disableFormats !== true;
12559         
12560         var sep = Roo.isGecko ? "+\n" : ",\n";
12561         
12562         var undef = function(str) {
12563             Roo.debug && Roo.log("Property not found :"  + str);
12564             return '';
12565         };
12566           
12567         //Roo.log(tpl.body);
12568         
12569         
12570         
12571         var fn = function(m, lbrace, name, format, args)
12572         {
12573             //Roo.log("ARGS");
12574             //Roo.log(arguments);
12575             args = args ? args.replace(/\\'/g,"'") : args;
12576             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12577             if (typeof(format) == 'undefined') {
12578                 format =  'htmlEncode'; 
12579             }
12580             if (format == 'raw' ) {
12581                 format = false;
12582             }
12583             
12584             if(name.substr(0, 6) == 'domtpl'){
12585                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12586             }
12587             
12588             // build an array of options to determine if value is undefined..
12589             
12590             // basically get 'xxxx.yyyy' then do
12591             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12592             //    (function () { Roo.log("Property not found"); return ''; })() :
12593             //    ......
12594             
12595             var udef_ar = [];
12596             var lookfor = '';
12597             Roo.each(name.split('.'), function(st) {
12598                 lookfor += (lookfor.length ? '.': '') + st;
12599                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12600             });
12601             
12602             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12603             
12604             
12605             if(format && useF){
12606                 
12607                 args = args ? ',' + args : "";
12608                  
12609                 if(format.substr(0, 5) != "this."){
12610                     format = "fm." + format + '(';
12611                 }else{
12612                     format = 'this.call("'+ format.substr(5) + '", ';
12613                     args = ", values";
12614                 }
12615                 
12616                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12617             }
12618              
12619             if (args && args.length) {
12620                 // called with xxyx.yuu:(test,test)
12621                 // change to ()
12622                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12623             }
12624             // raw.. - :raw modifier..
12625             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12626             
12627         };
12628         var body;
12629         // branched to use + in gecko and [].join() in others
12630         if(Roo.isGecko){
12631             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12632                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12633                     "';};};";
12634         }else{
12635             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12636             body.push(tpl.body.replace(/(\r\n|\n)/g,
12637                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12638             body.push("'].join('');};};");
12639             body = body.join('');
12640         }
12641         
12642         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12643        
12644         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12645         eval(body);
12646         
12647         return this;
12648     },
12649      
12650     /**
12651      * same as applyTemplate, except it's done to one of the subTemplates
12652      * when using named templates, you can do:
12653      *
12654      * var str = pl.applySubTemplate('your-name', values);
12655      *
12656      * 
12657      * @param {Number} id of the template
12658      * @param {Object} values to apply to template
12659      * @param {Object} parent (normaly the instance of this object)
12660      */
12661     applySubTemplate : function(id, values, parent)
12662     {
12663         
12664         
12665         var t = this.tpls[id];
12666         
12667         
12668         try { 
12669             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12670                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12671                 return '';
12672             }
12673         } catch(e) {
12674             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12675             Roo.log(values);
12676           
12677             return '';
12678         }
12679         try { 
12680             
12681             if(t.execCall && t.execCall.call(this, values, parent)){
12682                 return '';
12683             }
12684         } catch(e) {
12685             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12686             Roo.log(values);
12687             return '';
12688         }
12689         
12690         try {
12691             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12692             parent = t.target ? values : parent;
12693             if(t.forCall && vs instanceof Array){
12694                 var buf = [];
12695                 for(var i = 0, len = vs.length; i < len; i++){
12696                     try {
12697                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12698                     } catch (e) {
12699                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12700                         Roo.log(e.body);
12701                         //Roo.log(t.compiled);
12702                         Roo.log(vs[i]);
12703                     }   
12704                 }
12705                 return buf.join('');
12706             }
12707         } catch (e) {
12708             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12709             Roo.log(values);
12710             return '';
12711         }
12712         try {
12713             return t.compiled.call(this, vs, parent);
12714         } catch (e) {
12715             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12716             Roo.log(e.body);
12717             //Roo.log(t.compiled);
12718             Roo.log(values);
12719             return '';
12720         }
12721     },
12722
12723    
12724
12725     applyTemplate : function(values){
12726         return this.master.compiled.call(this, values, {});
12727         //var s = this.subs;
12728     },
12729
12730     apply : function(){
12731         return this.applyTemplate.apply(this, arguments);
12732     }
12733
12734  });
12735
12736 Roo.DomTemplate.from = function(el){
12737     el = Roo.getDom(el);
12738     return new Roo.Domtemplate(el.value || el.innerHTML);
12739 };/*
12740  * Based on:
12741  * Ext JS Library 1.1.1
12742  * Copyright(c) 2006-2007, Ext JS, LLC.
12743  *
12744  * Originally Released Under LGPL - original licence link has changed is not relivant.
12745  *
12746  * Fork - LGPL
12747  * <script type="text/javascript">
12748  */
12749
12750 /**
12751  * @class Roo.util.DelayedTask
12752  * Provides a convenient method of performing setTimeout where a new
12753  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12754  * You can use this class to buffer
12755  * the keypress events for a certain number of milliseconds, and perform only if they stop
12756  * for that amount of time.
12757  * @constructor The parameters to this constructor serve as defaults and are not required.
12758  * @param {Function} fn (optional) The default function to timeout
12759  * @param {Object} scope (optional) The default scope of that timeout
12760  * @param {Array} args (optional) The default Array of arguments
12761  */
12762 Roo.util.DelayedTask = function(fn, scope, args){
12763     var id = null, d, t;
12764
12765     var call = function(){
12766         var now = new Date().getTime();
12767         if(now - t >= d){
12768             clearInterval(id);
12769             id = null;
12770             fn.apply(scope, args || []);
12771         }
12772     };
12773     /**
12774      * Cancels any pending timeout and queues a new one
12775      * @param {Number} delay The milliseconds to delay
12776      * @param {Function} newFn (optional) Overrides function passed to constructor
12777      * @param {Object} newScope (optional) Overrides scope passed to constructor
12778      * @param {Array} newArgs (optional) Overrides args passed to constructor
12779      */
12780     this.delay = function(delay, newFn, newScope, newArgs){
12781         if(id && delay != d){
12782             this.cancel();
12783         }
12784         d = delay;
12785         t = new Date().getTime();
12786         fn = newFn || fn;
12787         scope = newScope || scope;
12788         args = newArgs || args;
12789         if(!id){
12790             id = setInterval(call, d);
12791         }
12792     };
12793
12794     /**
12795      * Cancel the last queued timeout
12796      */
12797     this.cancel = function(){
12798         if(id){
12799             clearInterval(id);
12800             id = null;
12801         }
12802     };
12803 };/*
12804  * Based on:
12805  * Ext JS Library 1.1.1
12806  * Copyright(c) 2006-2007, Ext JS, LLC.
12807  *
12808  * Originally Released Under LGPL - original licence link has changed is not relivant.
12809  *
12810  * Fork - LGPL
12811  * <script type="text/javascript">
12812  */
12813  
12814  
12815 Roo.util.TaskRunner = function(interval){
12816     interval = interval || 10;
12817     var tasks = [], removeQueue = [];
12818     var id = 0;
12819     var running = false;
12820
12821     var stopThread = function(){
12822         running = false;
12823         clearInterval(id);
12824         id = 0;
12825     };
12826
12827     var startThread = function(){
12828         if(!running){
12829             running = true;
12830             id = setInterval(runTasks, interval);
12831         }
12832     };
12833
12834     var removeTask = function(task){
12835         removeQueue.push(task);
12836         if(task.onStop){
12837             task.onStop();
12838         }
12839     };
12840
12841     var runTasks = function(){
12842         if(removeQueue.length > 0){
12843             for(var i = 0, len = removeQueue.length; i < len; i++){
12844                 tasks.remove(removeQueue[i]);
12845             }
12846             removeQueue = [];
12847             if(tasks.length < 1){
12848                 stopThread();
12849                 return;
12850             }
12851         }
12852         var now = new Date().getTime();
12853         for(var i = 0, len = tasks.length; i < len; ++i){
12854             var t = tasks[i];
12855             var itime = now - t.taskRunTime;
12856             if(t.interval <= itime){
12857                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12858                 t.taskRunTime = now;
12859                 if(rt === false || t.taskRunCount === t.repeat){
12860                     removeTask(t);
12861                     return;
12862                 }
12863             }
12864             if(t.duration && t.duration <= (now - t.taskStartTime)){
12865                 removeTask(t);
12866             }
12867         }
12868     };
12869
12870     /**
12871      * Queues a new task.
12872      * @param {Object} task
12873      */
12874     this.start = function(task){
12875         tasks.push(task);
12876         task.taskStartTime = new Date().getTime();
12877         task.taskRunTime = 0;
12878         task.taskRunCount = 0;
12879         startThread();
12880         return task;
12881     };
12882
12883     this.stop = function(task){
12884         removeTask(task);
12885         return task;
12886     };
12887
12888     this.stopAll = function(){
12889         stopThread();
12890         for(var i = 0, len = tasks.length; i < len; i++){
12891             if(tasks[i].onStop){
12892                 tasks[i].onStop();
12893             }
12894         }
12895         tasks = [];
12896         removeQueue = [];
12897     };
12898 };
12899
12900 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12901  * Based on:
12902  * Ext JS Library 1.1.1
12903  * Copyright(c) 2006-2007, Ext JS, LLC.
12904  *
12905  * Originally Released Under LGPL - original licence link has changed is not relivant.
12906  *
12907  * Fork - LGPL
12908  * <script type="text/javascript">
12909  */
12910
12911  
12912 /**
12913  * @class Roo.util.MixedCollection
12914  * @extends Roo.util.Observable
12915  * A Collection class that maintains both numeric indexes and keys and exposes events.
12916  * @constructor
12917  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12918  * collection (defaults to false)
12919  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12920  * and return the key value for that item.  This is used when available to look up the key on items that
12921  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12922  * equivalent to providing an implementation for the {@link #getKey} method.
12923  */
12924 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12925     this.items = [];
12926     this.map = {};
12927     this.keys = [];
12928     this.length = 0;
12929     this.addEvents({
12930         /**
12931          * @event clear
12932          * Fires when the collection is cleared.
12933          */
12934         "clear" : true,
12935         /**
12936          * @event add
12937          * Fires when an item is added to the collection.
12938          * @param {Number} index The index at which the item was added.
12939          * @param {Object} o The item added.
12940          * @param {String} key The key associated with the added item.
12941          */
12942         "add" : true,
12943         /**
12944          * @event replace
12945          * Fires when an item is replaced in the collection.
12946          * @param {String} key he key associated with the new added.
12947          * @param {Object} old The item being replaced.
12948          * @param {Object} new The new item.
12949          */
12950         "replace" : true,
12951         /**
12952          * @event remove
12953          * Fires when an item is removed from the collection.
12954          * @param {Object} o The item being removed.
12955          * @param {String} key (optional) The key associated with the removed item.
12956          */
12957         "remove" : true,
12958         "sort" : true
12959     });
12960     this.allowFunctions = allowFunctions === true;
12961     if(keyFn){
12962         this.getKey = keyFn;
12963     }
12964     Roo.util.MixedCollection.superclass.constructor.call(this);
12965 };
12966
12967 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12968     allowFunctions : false,
12969     
12970 /**
12971  * Adds an item to the collection.
12972  * @param {String} key The key to associate with the item
12973  * @param {Object} o The item to add.
12974  * @return {Object} The item added.
12975  */
12976     add : function(key, o){
12977         if(arguments.length == 1){
12978             o = arguments[0];
12979             key = this.getKey(o);
12980         }
12981         if(typeof key == "undefined" || key === null){
12982             this.length++;
12983             this.items.push(o);
12984             this.keys.push(null);
12985         }else{
12986             var old = this.map[key];
12987             if(old){
12988                 return this.replace(key, o);
12989             }
12990             this.length++;
12991             this.items.push(o);
12992             this.map[key] = o;
12993             this.keys.push(key);
12994         }
12995         this.fireEvent("add", this.length-1, o, key);
12996         return o;
12997     },
12998        
12999 /**
13000   * MixedCollection has a generic way to fetch keys if you implement getKey.
13001 <pre><code>
13002 // normal way
13003 var mc = new Roo.util.MixedCollection();
13004 mc.add(someEl.dom.id, someEl);
13005 mc.add(otherEl.dom.id, otherEl);
13006 //and so on
13007
13008 // using getKey
13009 var mc = new Roo.util.MixedCollection();
13010 mc.getKey = function(el){
13011    return el.dom.id;
13012 };
13013 mc.add(someEl);
13014 mc.add(otherEl);
13015
13016 // or via the constructor
13017 var mc = new Roo.util.MixedCollection(false, function(el){
13018    return el.dom.id;
13019 });
13020 mc.add(someEl);
13021 mc.add(otherEl);
13022 </code></pre>
13023  * @param o {Object} The item for which to find the key.
13024  * @return {Object} The key for the passed item.
13025  */
13026     getKey : function(o){
13027          return o.id; 
13028     },
13029    
13030 /**
13031  * Replaces an item in the collection.
13032  * @param {String} key The key associated with the item to replace, or the item to replace.
13033  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13034  * @return {Object}  The new item.
13035  */
13036     replace : function(key, o){
13037         if(arguments.length == 1){
13038             o = arguments[0];
13039             key = this.getKey(o);
13040         }
13041         var old = this.item(key);
13042         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13043              return this.add(key, o);
13044         }
13045         var index = this.indexOfKey(key);
13046         this.items[index] = o;
13047         this.map[key] = o;
13048         this.fireEvent("replace", key, old, o);
13049         return o;
13050     },
13051    
13052 /**
13053  * Adds all elements of an Array or an Object to the collection.
13054  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13055  * an Array of values, each of which are added to the collection.
13056  */
13057     addAll : function(objs){
13058         if(arguments.length > 1 || objs instanceof Array){
13059             var args = arguments.length > 1 ? arguments : objs;
13060             for(var i = 0, len = args.length; i < len; i++){
13061                 this.add(args[i]);
13062             }
13063         }else{
13064             for(var key in objs){
13065                 if(this.allowFunctions || typeof objs[key] != "function"){
13066                     this.add(key, objs[key]);
13067                 }
13068             }
13069         }
13070     },
13071    
13072 /**
13073  * Executes the specified function once for every item in the collection, passing each
13074  * item as the first and only parameter. returning false from the function will stop the iteration.
13075  * @param {Function} fn The function to execute for each item.
13076  * @param {Object} scope (optional) The scope in which to execute the function.
13077  */
13078     each : function(fn, scope){
13079         var items = [].concat(this.items); // each safe for removal
13080         for(var i = 0, len = items.length; i < len; i++){
13081             if(fn.call(scope || items[i], items[i], i, len) === false){
13082                 break;
13083             }
13084         }
13085     },
13086    
13087 /**
13088  * Executes the specified function once for every key in the collection, passing each
13089  * key, and its associated item as the first two parameters.
13090  * @param {Function} fn The function to execute for each item.
13091  * @param {Object} scope (optional) The scope in which to execute the function.
13092  */
13093     eachKey : function(fn, scope){
13094         for(var i = 0, len = this.keys.length; i < len; i++){
13095             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13096         }
13097     },
13098    
13099 /**
13100  * Returns the first item in the collection which elicits a true return value from the
13101  * passed selection function.
13102  * @param {Function} fn The selection function to execute for each item.
13103  * @param {Object} scope (optional) The scope in which to execute the function.
13104  * @return {Object} The first item in the collection which returned true from the selection function.
13105  */
13106     find : function(fn, scope){
13107         for(var i = 0, len = this.items.length; i < len; i++){
13108             if(fn.call(scope || window, this.items[i], this.keys[i])){
13109                 return this.items[i];
13110             }
13111         }
13112         return null;
13113     },
13114    
13115 /**
13116  * Inserts an item at the specified index in the collection.
13117  * @param {Number} index The index to insert the item at.
13118  * @param {String} key The key to associate with the new item, or the item itself.
13119  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13120  * @return {Object} The item inserted.
13121  */
13122     insert : function(index, key, o){
13123         if(arguments.length == 2){
13124             o = arguments[1];
13125             key = this.getKey(o);
13126         }
13127         if(index >= this.length){
13128             return this.add(key, o);
13129         }
13130         this.length++;
13131         this.items.splice(index, 0, o);
13132         if(typeof key != "undefined" && key != null){
13133             this.map[key] = o;
13134         }
13135         this.keys.splice(index, 0, key);
13136         this.fireEvent("add", index, o, key);
13137         return o;
13138     },
13139    
13140 /**
13141  * Removed an item from the collection.
13142  * @param {Object} o The item to remove.
13143  * @return {Object} The item removed.
13144  */
13145     remove : function(o){
13146         return this.removeAt(this.indexOf(o));
13147     },
13148    
13149 /**
13150  * Remove an item from a specified index in the collection.
13151  * @param {Number} index The index within the collection of the item to remove.
13152  */
13153     removeAt : function(index){
13154         if(index < this.length && index >= 0){
13155             this.length--;
13156             var o = this.items[index];
13157             this.items.splice(index, 1);
13158             var key = this.keys[index];
13159             if(typeof key != "undefined"){
13160                 delete this.map[key];
13161             }
13162             this.keys.splice(index, 1);
13163             this.fireEvent("remove", o, key);
13164         }
13165     },
13166    
13167 /**
13168  * Removed an item associated with the passed key fom the collection.
13169  * @param {String} key The key of the item to remove.
13170  */
13171     removeKey : function(key){
13172         return this.removeAt(this.indexOfKey(key));
13173     },
13174    
13175 /**
13176  * Returns the number of items in the collection.
13177  * @return {Number} the number of items in the collection.
13178  */
13179     getCount : function(){
13180         return this.length; 
13181     },
13182    
13183 /**
13184  * Returns index within the collection of the passed Object.
13185  * @param {Object} o The item to find the index of.
13186  * @return {Number} index of the item.
13187  */
13188     indexOf : function(o){
13189         if(!this.items.indexOf){
13190             for(var i = 0, len = this.items.length; i < len; i++){
13191                 if(this.items[i] == o) {
13192                     return i;
13193                 }
13194             }
13195             return -1;
13196         }else{
13197             return this.items.indexOf(o);
13198         }
13199     },
13200    
13201 /**
13202  * Returns index within the collection of the passed key.
13203  * @param {String} key The key to find the index of.
13204  * @return {Number} index of the key.
13205  */
13206     indexOfKey : function(key){
13207         if(!this.keys.indexOf){
13208             for(var i = 0, len = this.keys.length; i < len; i++){
13209                 if(this.keys[i] == key) {
13210                     return i;
13211                 }
13212             }
13213             return -1;
13214         }else{
13215             return this.keys.indexOf(key);
13216         }
13217     },
13218    
13219 /**
13220  * Returns the item associated with the passed key OR index. Key has priority over index.
13221  * @param {String/Number} key The key or index of the item.
13222  * @return {Object} The item associated with the passed key.
13223  */
13224     item : function(key){
13225         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13226         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13227     },
13228     
13229 /**
13230  * Returns the item at the specified index.
13231  * @param {Number} index The index of the item.
13232  * @return {Object}
13233  */
13234     itemAt : function(index){
13235         return this.items[index];
13236     },
13237     
13238 /**
13239  * Returns the item associated with the passed key.
13240  * @param {String/Number} key The key of the item.
13241  * @return {Object} The item associated with the passed key.
13242  */
13243     key : function(key){
13244         return this.map[key];
13245     },
13246    
13247 /**
13248  * Returns true if the collection contains the passed Object as an item.
13249  * @param {Object} o  The Object to look for in the collection.
13250  * @return {Boolean} True if the collection contains the Object as an item.
13251  */
13252     contains : function(o){
13253         return this.indexOf(o) != -1;
13254     },
13255    
13256 /**
13257  * Returns true if the collection contains the passed Object as a key.
13258  * @param {String} key The key to look for in the collection.
13259  * @return {Boolean} True if the collection contains the Object as a key.
13260  */
13261     containsKey : function(key){
13262         return typeof this.map[key] != "undefined";
13263     },
13264    
13265 /**
13266  * Removes all items from the collection.
13267  */
13268     clear : function(){
13269         this.length = 0;
13270         this.items = [];
13271         this.keys = [];
13272         this.map = {};
13273         this.fireEvent("clear");
13274     },
13275    
13276 /**
13277  * Returns the first item in the collection.
13278  * @return {Object} the first item in the collection..
13279  */
13280     first : function(){
13281         return this.items[0]; 
13282     },
13283    
13284 /**
13285  * Returns the last item in the collection.
13286  * @return {Object} the last item in the collection..
13287  */
13288     last : function(){
13289         return this.items[this.length-1];   
13290     },
13291     
13292     _sort : function(property, dir, fn){
13293         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13294         fn = fn || function(a, b){
13295             return a-b;
13296         };
13297         var c = [], k = this.keys, items = this.items;
13298         for(var i = 0, len = items.length; i < len; i++){
13299             c[c.length] = {key: k[i], value: items[i], index: i};
13300         }
13301         c.sort(function(a, b){
13302             var v = fn(a[property], b[property]) * dsc;
13303             if(v == 0){
13304                 v = (a.index < b.index ? -1 : 1);
13305             }
13306             return v;
13307         });
13308         for(var i = 0, len = c.length; i < len; i++){
13309             items[i] = c[i].value;
13310             k[i] = c[i].key;
13311         }
13312         this.fireEvent("sort", this);
13313     },
13314     
13315     /**
13316      * Sorts this collection with the passed comparison function
13317      * @param {String} direction (optional) "ASC" or "DESC"
13318      * @param {Function} fn (optional) comparison function
13319      */
13320     sort : function(dir, fn){
13321         this._sort("value", dir, fn);
13322     },
13323     
13324     /**
13325      * Sorts this collection by keys
13326      * @param {String} direction (optional) "ASC" or "DESC"
13327      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13328      */
13329     keySort : function(dir, fn){
13330         this._sort("key", dir, fn || function(a, b){
13331             return String(a).toUpperCase()-String(b).toUpperCase();
13332         });
13333     },
13334     
13335     /**
13336      * Returns a range of items in this collection
13337      * @param {Number} startIndex (optional) defaults to 0
13338      * @param {Number} endIndex (optional) default to the last item
13339      * @return {Array} An array of items
13340      */
13341     getRange : function(start, end){
13342         var items = this.items;
13343         if(items.length < 1){
13344             return [];
13345         }
13346         start = start || 0;
13347         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13348         var r = [];
13349         if(start <= end){
13350             for(var i = start; i <= end; i++) {
13351                     r[r.length] = items[i];
13352             }
13353         }else{
13354             for(var i = start; i >= end; i--) {
13355                     r[r.length] = items[i];
13356             }
13357         }
13358         return r;
13359     },
13360         
13361     /**
13362      * Filter the <i>objects</i> in this collection by a specific property. 
13363      * Returns a new collection that has been filtered.
13364      * @param {String} property A property on your objects
13365      * @param {String/RegExp} value Either string that the property values 
13366      * should start with or a RegExp to test against the property
13367      * @return {MixedCollection} The new filtered collection
13368      */
13369     filter : function(property, value){
13370         if(!value.exec){ // not a regex
13371             value = String(value);
13372             if(value.length == 0){
13373                 return this.clone();
13374             }
13375             value = new RegExp("^" + Roo.escapeRe(value), "i");
13376         }
13377         return this.filterBy(function(o){
13378             return o && value.test(o[property]);
13379         });
13380         },
13381     
13382     /**
13383      * Filter by a function. * Returns a new collection that has been filtered.
13384      * The passed function will be called with each 
13385      * object in the collection. If the function returns true, the value is included 
13386      * otherwise it is filtered.
13387      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13388      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13389      * @return {MixedCollection} The new filtered collection
13390      */
13391     filterBy : function(fn, scope){
13392         var r = new Roo.util.MixedCollection();
13393         r.getKey = this.getKey;
13394         var k = this.keys, it = this.items;
13395         for(var i = 0, len = it.length; i < len; i++){
13396             if(fn.call(scope||this, it[i], k[i])){
13397                                 r.add(k[i], it[i]);
13398                         }
13399         }
13400         return r;
13401     },
13402     
13403     /**
13404      * Creates a duplicate of this collection
13405      * @return {MixedCollection}
13406      */
13407     clone : function(){
13408         var r = new Roo.util.MixedCollection();
13409         var k = this.keys, it = this.items;
13410         for(var i = 0, len = it.length; i < len; i++){
13411             r.add(k[i], it[i]);
13412         }
13413         r.getKey = this.getKey;
13414         return r;
13415     }
13416 });
13417 /**
13418  * Returns the item associated with the passed key or index.
13419  * @method
13420  * @param {String/Number} key The key or index of the item.
13421  * @return {Object} The item associated with the passed key.
13422  */
13423 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13424  * Based on:
13425  * Ext JS Library 1.1.1
13426  * Copyright(c) 2006-2007, Ext JS, LLC.
13427  *
13428  * Originally Released Under LGPL - original licence link has changed is not relivant.
13429  *
13430  * Fork - LGPL
13431  * <script type="text/javascript">
13432  */
13433 /**
13434  * @class Roo.util.JSON
13435  * Modified version of Douglas Crockford"s json.js that doesn"t
13436  * mess with the Object prototype 
13437  * http://www.json.org/js.html
13438  * @singleton
13439  */
13440 Roo.util.JSON = new (function(){
13441     var useHasOwn = {}.hasOwnProperty ? true : false;
13442     
13443     // crashes Safari in some instances
13444     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13445     
13446     var pad = function(n) {
13447         return n < 10 ? "0" + n : n;
13448     };
13449     
13450     var m = {
13451         "\b": '\\b',
13452         "\t": '\\t',
13453         "\n": '\\n',
13454         "\f": '\\f',
13455         "\r": '\\r',
13456         '"' : '\\"',
13457         "\\": '\\\\'
13458     };
13459
13460     var encodeString = function(s){
13461         if (/["\\\x00-\x1f]/.test(s)) {
13462             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13463                 var c = m[b];
13464                 if(c){
13465                     return c;
13466                 }
13467                 c = b.charCodeAt();
13468                 return "\\u00" +
13469                     Math.floor(c / 16).toString(16) +
13470                     (c % 16).toString(16);
13471             }) + '"';
13472         }
13473         return '"' + s + '"';
13474     };
13475     
13476     var encodeArray = function(o){
13477         var a = ["["], b, i, l = o.length, v;
13478             for (i = 0; i < l; i += 1) {
13479                 v = o[i];
13480                 switch (typeof v) {
13481                     case "undefined":
13482                     case "function":
13483                     case "unknown":
13484                         break;
13485                     default:
13486                         if (b) {
13487                             a.push(',');
13488                         }
13489                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13490                         b = true;
13491                 }
13492             }
13493             a.push("]");
13494             return a.join("");
13495     };
13496     
13497     var encodeDate = function(o){
13498         return '"' + o.getFullYear() + "-" +
13499                 pad(o.getMonth() + 1) + "-" +
13500                 pad(o.getDate()) + "T" +
13501                 pad(o.getHours()) + ":" +
13502                 pad(o.getMinutes()) + ":" +
13503                 pad(o.getSeconds()) + '"';
13504     };
13505     
13506     /**
13507      * Encodes an Object, Array or other value
13508      * @param {Mixed} o The variable to encode
13509      * @return {String} The JSON string
13510      */
13511     this.encode = function(o)
13512     {
13513         // should this be extended to fully wrap stringify..
13514         
13515         if(typeof o == "undefined" || o === null){
13516             return "null";
13517         }else if(o instanceof Array){
13518             return encodeArray(o);
13519         }else if(o instanceof Date){
13520             return encodeDate(o);
13521         }else if(typeof o == "string"){
13522             return encodeString(o);
13523         }else if(typeof o == "number"){
13524             return isFinite(o) ? String(o) : "null";
13525         }else if(typeof o == "boolean"){
13526             return String(o);
13527         }else {
13528             var a = ["{"], b, i, v;
13529             for (i in o) {
13530                 if(!useHasOwn || o.hasOwnProperty(i)) {
13531                     v = o[i];
13532                     switch (typeof v) {
13533                     case "undefined":
13534                     case "function":
13535                     case "unknown":
13536                         break;
13537                     default:
13538                         if(b){
13539                             a.push(',');
13540                         }
13541                         a.push(this.encode(i), ":",
13542                                 v === null ? "null" : this.encode(v));
13543                         b = true;
13544                     }
13545                 }
13546             }
13547             a.push("}");
13548             return a.join("");
13549         }
13550     };
13551     
13552     /**
13553      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13554      * @param {String} json The JSON string
13555      * @return {Object} The resulting object
13556      */
13557     this.decode = function(json){
13558         
13559         return  /** eval:var:json */ eval("(" + json + ')');
13560     };
13561 })();
13562 /** 
13563  * Shorthand for {@link Roo.util.JSON#encode}
13564  * @member Roo encode 
13565  * @method */
13566 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13567 /** 
13568  * Shorthand for {@link Roo.util.JSON#decode}
13569  * @member Roo decode 
13570  * @method */
13571 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13572 /*
13573  * Based on:
13574  * Ext JS Library 1.1.1
13575  * Copyright(c) 2006-2007, Ext JS, LLC.
13576  *
13577  * Originally Released Under LGPL - original licence link has changed is not relivant.
13578  *
13579  * Fork - LGPL
13580  * <script type="text/javascript">
13581  */
13582  
13583 /**
13584  * @class Roo.util.Format
13585  * Reusable data formatting functions
13586  * @singleton
13587  */
13588 Roo.util.Format = function(){
13589     var trimRe = /^\s+|\s+$/g;
13590     return {
13591         /**
13592          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13593          * @param {String} value The string to truncate
13594          * @param {Number} length The maximum length to allow before truncating
13595          * @return {String} The converted text
13596          */
13597         ellipsis : function(value, len){
13598             if(value && value.length > len){
13599                 return value.substr(0, len-3)+"...";
13600             }
13601             return value;
13602         },
13603
13604         /**
13605          * Checks a reference and converts it to empty string if it is undefined
13606          * @param {Mixed} value Reference to check
13607          * @return {Mixed} Empty string if converted, otherwise the original value
13608          */
13609         undef : function(value){
13610             return typeof value != "undefined" ? value : "";
13611         },
13612
13613         /**
13614          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13615          * @param {String} value The string to encode
13616          * @return {String} The encoded text
13617          */
13618         htmlEncode : function(value){
13619             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13620         },
13621
13622         /**
13623          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13624          * @param {String} value The string to decode
13625          * @return {String} The decoded text
13626          */
13627         htmlDecode : function(value){
13628             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13629         },
13630
13631         /**
13632          * Trims any whitespace from either side of a string
13633          * @param {String} value The text to trim
13634          * @return {String} The trimmed text
13635          */
13636         trim : function(value){
13637             return String(value).replace(trimRe, "");
13638         },
13639
13640         /**
13641          * Returns a substring from within an original string
13642          * @param {String} value The original text
13643          * @param {Number} start The start index of the substring
13644          * @param {Number} length The length of the substring
13645          * @return {String} The substring
13646          */
13647         substr : function(value, start, length){
13648             return String(value).substr(start, length);
13649         },
13650
13651         /**
13652          * Converts a string to all lower case letters
13653          * @param {String} value The text to convert
13654          * @return {String} The converted text
13655          */
13656         lowercase : function(value){
13657             return String(value).toLowerCase();
13658         },
13659
13660         /**
13661          * Converts a string to all upper case letters
13662          * @param {String} value The text to convert
13663          * @return {String} The converted text
13664          */
13665         uppercase : function(value){
13666             return String(value).toUpperCase();
13667         },
13668
13669         /**
13670          * Converts the first character only of a string to upper case
13671          * @param {String} value The text to convert
13672          * @return {String} The converted text
13673          */
13674         capitalize : function(value){
13675             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13676         },
13677
13678         // private
13679         call : function(value, fn){
13680             if(arguments.length > 2){
13681                 var args = Array.prototype.slice.call(arguments, 2);
13682                 args.unshift(value);
13683                  
13684                 return /** eval:var:value */  eval(fn).apply(window, args);
13685             }else{
13686                 /** eval:var:value */
13687                 return /** eval:var:value */ eval(fn).call(window, value);
13688             }
13689         },
13690
13691        
13692         /**
13693          * safer version of Math.toFixed..??/
13694          * @param {Number/String} value The numeric value to format
13695          * @param {Number/String} value Decimal places 
13696          * @return {String} The formatted currency string
13697          */
13698         toFixed : function(v, n)
13699         {
13700             // why not use to fixed - precision is buggered???
13701             if (!n) {
13702                 return Math.round(v-0);
13703             }
13704             var fact = Math.pow(10,n+1);
13705             v = (Math.round((v-0)*fact))/fact;
13706             var z = (''+fact).substring(2);
13707             if (v == Math.floor(v)) {
13708                 return Math.floor(v) + '.' + z;
13709             }
13710             
13711             // now just padd decimals..
13712             var ps = String(v).split('.');
13713             var fd = (ps[1] + z);
13714             var r = fd.substring(0,n); 
13715             var rm = fd.substring(n); 
13716             if (rm < 5) {
13717                 return ps[0] + '.' + r;
13718             }
13719             r*=1; // turn it into a number;
13720             r++;
13721             if (String(r).length != n) {
13722                 ps[0]*=1;
13723                 ps[0]++;
13724                 r = String(r).substring(1); // chop the end off.
13725             }
13726             
13727             return ps[0] + '.' + r;
13728              
13729         },
13730         
13731         /**
13732          * Format a number as US currency
13733          * @param {Number/String} value The numeric value to format
13734          * @return {String} The formatted currency string
13735          */
13736         usMoney : function(v){
13737             return '$' + Roo.util.Format.number(v);
13738         },
13739         
13740         /**
13741          * Format a number
13742          * eventually this should probably emulate php's number_format
13743          * @param {Number/String} value The numeric value to format
13744          * @param {Number} decimals number of decimal places
13745          * @return {String} The formatted currency string
13746          */
13747         number : function(v,decimals)
13748         {
13749             // multiply and round.
13750             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13751             var mul = Math.pow(10, decimals);
13752             var zero = String(mul).substring(1);
13753             v = (Math.round((v-0)*mul))/mul;
13754             
13755             // if it's '0' number.. then
13756             
13757             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13758             v = String(v);
13759             var ps = v.split('.');
13760             var whole = ps[0];
13761             
13762             
13763             var r = /(\d+)(\d{3})/;
13764             // add comma's
13765             while (r.test(whole)) {
13766                 whole = whole.replace(r, '$1' + ',' + '$2');
13767             }
13768             
13769             
13770             var sub = ps[1] ?
13771                     // has decimals..
13772                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13773                     // does not have decimals
13774                     (decimals ? ('.' + zero) : '');
13775             
13776             
13777             return whole + sub ;
13778         },
13779         
13780         /**
13781          * Parse a value into a formatted date using the specified format pattern.
13782          * @param {Mixed} value The value to format
13783          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13784          * @return {String} The formatted date string
13785          */
13786         date : function(v, format){
13787             if(!v){
13788                 return "";
13789             }
13790             if(!(v instanceof Date)){
13791                 v = new Date(Date.parse(v));
13792             }
13793             return v.dateFormat(format || Roo.util.Format.defaults.date);
13794         },
13795
13796         /**
13797          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13798          * @param {String} format Any valid date format string
13799          * @return {Function} The date formatting function
13800          */
13801         dateRenderer : function(format){
13802             return function(v){
13803                 return Roo.util.Format.date(v, format);  
13804             };
13805         },
13806
13807         // private
13808         stripTagsRE : /<\/?[^>]+>/gi,
13809         
13810         /**
13811          * Strips all HTML tags
13812          * @param {Mixed} value The text from which to strip tags
13813          * @return {String} The stripped text
13814          */
13815         stripTags : function(v){
13816             return !v ? v : String(v).replace(this.stripTagsRE, "");
13817         }
13818     };
13819 }();
13820 Roo.util.Format.defaults = {
13821     date : 'd/M/Y'
13822 };/*
13823  * Based on:
13824  * Ext JS Library 1.1.1
13825  * Copyright(c) 2006-2007, Ext JS, LLC.
13826  *
13827  * Originally Released Under LGPL - original licence link has changed is not relivant.
13828  *
13829  * Fork - LGPL
13830  * <script type="text/javascript">
13831  */
13832
13833
13834  
13835
13836 /**
13837  * @class Roo.MasterTemplate
13838  * @extends Roo.Template
13839  * Provides a template that can have child templates. The syntax is:
13840 <pre><code>
13841 var t = new Roo.MasterTemplate(
13842         '&lt;select name="{name}"&gt;',
13843                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13844         '&lt;/select&gt;'
13845 );
13846 t.add('options', {value: 'foo', text: 'bar'});
13847 // or you can add multiple child elements in one shot
13848 t.addAll('options', [
13849     {value: 'foo', text: 'bar'},
13850     {value: 'foo2', text: 'bar2'},
13851     {value: 'foo3', text: 'bar3'}
13852 ]);
13853 // then append, applying the master template values
13854 t.append('my-form', {name: 'my-select'});
13855 </code></pre>
13856 * A name attribute for the child template is not required if you have only one child
13857 * template or you want to refer to them by index.
13858  */
13859 Roo.MasterTemplate = function(){
13860     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13861     this.originalHtml = this.html;
13862     var st = {};
13863     var m, re = this.subTemplateRe;
13864     re.lastIndex = 0;
13865     var subIndex = 0;
13866     while(m = re.exec(this.html)){
13867         var name = m[1], content = m[2];
13868         st[subIndex] = {
13869             name: name,
13870             index: subIndex,
13871             buffer: [],
13872             tpl : new Roo.Template(content)
13873         };
13874         if(name){
13875             st[name] = st[subIndex];
13876         }
13877         st[subIndex].tpl.compile();
13878         st[subIndex].tpl.call = this.call.createDelegate(this);
13879         subIndex++;
13880     }
13881     this.subCount = subIndex;
13882     this.subs = st;
13883 };
13884 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13885     /**
13886     * The regular expression used to match sub templates
13887     * @type RegExp
13888     * @property
13889     */
13890     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13891
13892     /**
13893      * Applies the passed values to a child template.
13894      * @param {String/Number} name (optional) The name or index of the child template
13895      * @param {Array/Object} values The values to be applied to the template
13896      * @return {MasterTemplate} this
13897      */
13898      add : function(name, values){
13899         if(arguments.length == 1){
13900             values = arguments[0];
13901             name = 0;
13902         }
13903         var s = this.subs[name];
13904         s.buffer[s.buffer.length] = s.tpl.apply(values);
13905         return this;
13906     },
13907
13908     /**
13909      * Applies all the passed values to a child template.
13910      * @param {String/Number} name (optional) The name or index of the child template
13911      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13912      * @param {Boolean} reset (optional) True to reset the template first
13913      * @return {MasterTemplate} this
13914      */
13915     fill : function(name, values, reset){
13916         var a = arguments;
13917         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13918             values = a[0];
13919             name = 0;
13920             reset = a[1];
13921         }
13922         if(reset){
13923             this.reset();
13924         }
13925         for(var i = 0, len = values.length; i < len; i++){
13926             this.add(name, values[i]);
13927         }
13928         return this;
13929     },
13930
13931     /**
13932      * Resets the template for reuse
13933      * @return {MasterTemplate} this
13934      */
13935      reset : function(){
13936         var s = this.subs;
13937         for(var i = 0; i < this.subCount; i++){
13938             s[i].buffer = [];
13939         }
13940         return this;
13941     },
13942
13943     applyTemplate : function(values){
13944         var s = this.subs;
13945         var replaceIndex = -1;
13946         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13947             return s[++replaceIndex].buffer.join("");
13948         });
13949         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13950     },
13951
13952     apply : function(){
13953         return this.applyTemplate.apply(this, arguments);
13954     },
13955
13956     compile : function(){return this;}
13957 });
13958
13959 /**
13960  * Alias for fill().
13961  * @method
13962  */
13963 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13964  /**
13965  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13966  * var tpl = Roo.MasterTemplate.from('element-id');
13967  * @param {String/HTMLElement} el
13968  * @param {Object} config
13969  * @static
13970  */
13971 Roo.MasterTemplate.from = function(el, config){
13972     el = Roo.getDom(el);
13973     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13974 };/*
13975  * Based on:
13976  * Ext JS Library 1.1.1
13977  * Copyright(c) 2006-2007, Ext JS, LLC.
13978  *
13979  * Originally Released Under LGPL - original licence link has changed is not relivant.
13980  *
13981  * Fork - LGPL
13982  * <script type="text/javascript">
13983  */
13984
13985  
13986 /**
13987  * @class Roo.util.CSS
13988  * Utility class for manipulating CSS rules
13989  * @singleton
13990  */
13991 Roo.util.CSS = function(){
13992         var rules = null;
13993         var doc = document;
13994
13995     var camelRe = /(-[a-z])/gi;
13996     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13997
13998    return {
13999    /**
14000     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14001     * tag and appended to the HEAD of the document.
14002     * @param {String|Object} cssText The text containing the css rules
14003     * @param {String} id An id to add to the stylesheet for later removal
14004     * @return {StyleSheet}
14005     */
14006     createStyleSheet : function(cssText, id){
14007         var ss;
14008         var head = doc.getElementsByTagName("head")[0];
14009         var nrules = doc.createElement("style");
14010         nrules.setAttribute("type", "text/css");
14011         if(id){
14012             nrules.setAttribute("id", id);
14013         }
14014         if (typeof(cssText) != 'string') {
14015             // support object maps..
14016             // not sure if this a good idea.. 
14017             // perhaps it should be merged with the general css handling
14018             // and handle js style props.
14019             var cssTextNew = [];
14020             for(var n in cssText) {
14021                 var citems = [];
14022                 for(var k in cssText[n]) {
14023                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14024                 }
14025                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14026                 
14027             }
14028             cssText = cssTextNew.join("\n");
14029             
14030         }
14031        
14032        
14033        if(Roo.isIE){
14034            head.appendChild(nrules);
14035            ss = nrules.styleSheet;
14036            ss.cssText = cssText;
14037        }else{
14038            try{
14039                 nrules.appendChild(doc.createTextNode(cssText));
14040            }catch(e){
14041                nrules.cssText = cssText; 
14042            }
14043            head.appendChild(nrules);
14044            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14045        }
14046        this.cacheStyleSheet(ss);
14047        return ss;
14048    },
14049
14050    /**
14051     * Removes a style or link tag by id
14052     * @param {String} id The id of the tag
14053     */
14054    removeStyleSheet : function(id){
14055        var existing = doc.getElementById(id);
14056        if(existing){
14057            existing.parentNode.removeChild(existing);
14058        }
14059    },
14060
14061    /**
14062     * Dynamically swaps an existing stylesheet reference for a new one
14063     * @param {String} id The id of an existing link tag to remove
14064     * @param {String} url The href of the new stylesheet to include
14065     */
14066    swapStyleSheet : function(id, url){
14067        this.removeStyleSheet(id);
14068        var ss = doc.createElement("link");
14069        ss.setAttribute("rel", "stylesheet");
14070        ss.setAttribute("type", "text/css");
14071        ss.setAttribute("id", id);
14072        ss.setAttribute("href", url);
14073        doc.getElementsByTagName("head")[0].appendChild(ss);
14074    },
14075    
14076    /**
14077     * Refresh the rule cache if you have dynamically added stylesheets
14078     * @return {Object} An object (hash) of rules indexed by selector
14079     */
14080    refreshCache : function(){
14081        return this.getRules(true);
14082    },
14083
14084    // private
14085    cacheStyleSheet : function(stylesheet){
14086        if(!rules){
14087            rules = {};
14088        }
14089        try{// try catch for cross domain access issue
14090            var ssRules = stylesheet.cssRules || stylesheet.rules;
14091            for(var j = ssRules.length-1; j >= 0; --j){
14092                rules[ssRules[j].selectorText] = ssRules[j];
14093            }
14094        }catch(e){}
14095    },
14096    
14097    /**
14098     * Gets all css rules for the document
14099     * @param {Boolean} refreshCache true to refresh the internal cache
14100     * @return {Object} An object (hash) of rules indexed by selector
14101     */
14102    getRules : function(refreshCache){
14103                 if(rules == null || refreshCache){
14104                         rules = {};
14105                         var ds = doc.styleSheets;
14106                         for(var i =0, len = ds.length; i < len; i++){
14107                             try{
14108                         this.cacheStyleSheet(ds[i]);
14109                     }catch(e){} 
14110                 }
14111                 }
14112                 return rules;
14113         },
14114         
14115         /**
14116     * Gets an an individual CSS rule by selector(s)
14117     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14118     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14119     * @return {CSSRule} The CSS rule or null if one is not found
14120     */
14121    getRule : function(selector, refreshCache){
14122                 var rs = this.getRules(refreshCache);
14123                 if(!(selector instanceof Array)){
14124                     return rs[selector];
14125                 }
14126                 for(var i = 0; i < selector.length; i++){
14127                         if(rs[selector[i]]){
14128                                 return rs[selector[i]];
14129                         }
14130                 }
14131                 return null;
14132         },
14133         
14134         
14135         /**
14136     * Updates a rule property
14137     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14138     * @param {String} property The css property
14139     * @param {String} value The new value for the property
14140     * @return {Boolean} true If a rule was found and updated
14141     */
14142    updateRule : function(selector, property, value){
14143                 if(!(selector instanceof Array)){
14144                         var rule = this.getRule(selector);
14145                         if(rule){
14146                                 rule.style[property.replace(camelRe, camelFn)] = value;
14147                                 return true;
14148                         }
14149                 }else{
14150                         for(var i = 0; i < selector.length; i++){
14151                                 if(this.updateRule(selector[i], property, value)){
14152                                         return true;
14153                                 }
14154                         }
14155                 }
14156                 return false;
14157         }
14158    };   
14159 }();/*
14160  * Based on:
14161  * Ext JS Library 1.1.1
14162  * Copyright(c) 2006-2007, Ext JS, LLC.
14163  *
14164  * Originally Released Under LGPL - original licence link has changed is not relivant.
14165  *
14166  * Fork - LGPL
14167  * <script type="text/javascript">
14168  */
14169
14170  
14171
14172 /**
14173  * @class Roo.util.ClickRepeater
14174  * @extends Roo.util.Observable
14175  * 
14176  * A wrapper class which can be applied to any element. Fires a "click" event while the
14177  * mouse is pressed. The interval between firings may be specified in the config but
14178  * defaults to 10 milliseconds.
14179  * 
14180  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14181  * 
14182  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14183  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14184  * Similar to an autorepeat key delay.
14185  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14186  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14187  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14188  *           "interval" and "delay" are ignored. "immediate" is honored.
14189  * @cfg {Boolean} preventDefault True to prevent the default click event
14190  * @cfg {Boolean} stopDefault True to stop the default click event
14191  * 
14192  * @history
14193  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14194  *     2007-02-02 jvs Renamed to ClickRepeater
14195  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14196  *
14197  *  @constructor
14198  * @param {String/HTMLElement/Element} el The element to listen on
14199  * @param {Object} config
14200  **/
14201 Roo.util.ClickRepeater = function(el, config)
14202 {
14203     this.el = Roo.get(el);
14204     this.el.unselectable();
14205
14206     Roo.apply(this, config);
14207
14208     this.addEvents({
14209     /**
14210      * @event mousedown
14211      * Fires when the mouse button is depressed.
14212      * @param {Roo.util.ClickRepeater} this
14213      */
14214         "mousedown" : true,
14215     /**
14216      * @event click
14217      * Fires on a specified interval during the time the element is pressed.
14218      * @param {Roo.util.ClickRepeater} this
14219      */
14220         "click" : true,
14221     /**
14222      * @event mouseup
14223      * Fires when the mouse key is released.
14224      * @param {Roo.util.ClickRepeater} this
14225      */
14226         "mouseup" : true
14227     });
14228
14229     this.el.on("mousedown", this.handleMouseDown, this);
14230     if(this.preventDefault || this.stopDefault){
14231         this.el.on("click", function(e){
14232             if(this.preventDefault){
14233                 e.preventDefault();
14234             }
14235             if(this.stopDefault){
14236                 e.stopEvent();
14237             }
14238         }, this);
14239     }
14240
14241     // allow inline handler
14242     if(this.handler){
14243         this.on("click", this.handler,  this.scope || this);
14244     }
14245
14246     Roo.util.ClickRepeater.superclass.constructor.call(this);
14247 };
14248
14249 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14250     interval : 20,
14251     delay: 250,
14252     preventDefault : true,
14253     stopDefault : false,
14254     timer : 0,
14255
14256     // private
14257     handleMouseDown : function(){
14258         clearTimeout(this.timer);
14259         this.el.blur();
14260         if(this.pressClass){
14261             this.el.addClass(this.pressClass);
14262         }
14263         this.mousedownTime = new Date();
14264
14265         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14266         this.el.on("mouseout", this.handleMouseOut, this);
14267
14268         this.fireEvent("mousedown", this);
14269         this.fireEvent("click", this);
14270         
14271         this.timer = this.click.defer(this.delay || this.interval, this);
14272     },
14273
14274     // private
14275     click : function(){
14276         this.fireEvent("click", this);
14277         this.timer = this.click.defer(this.getInterval(), this);
14278     },
14279
14280     // private
14281     getInterval: function(){
14282         if(!this.accelerate){
14283             return this.interval;
14284         }
14285         var pressTime = this.mousedownTime.getElapsed();
14286         if(pressTime < 500){
14287             return 400;
14288         }else if(pressTime < 1700){
14289             return 320;
14290         }else if(pressTime < 2600){
14291             return 250;
14292         }else if(pressTime < 3500){
14293             return 180;
14294         }else if(pressTime < 4400){
14295             return 140;
14296         }else if(pressTime < 5300){
14297             return 80;
14298         }else if(pressTime < 6200){
14299             return 50;
14300         }else{
14301             return 10;
14302         }
14303     },
14304
14305     // private
14306     handleMouseOut : function(){
14307         clearTimeout(this.timer);
14308         if(this.pressClass){
14309             this.el.removeClass(this.pressClass);
14310         }
14311         this.el.on("mouseover", this.handleMouseReturn, this);
14312     },
14313
14314     // private
14315     handleMouseReturn : function(){
14316         this.el.un("mouseover", this.handleMouseReturn);
14317         if(this.pressClass){
14318             this.el.addClass(this.pressClass);
14319         }
14320         this.click();
14321     },
14322
14323     // private
14324     handleMouseUp : function(){
14325         clearTimeout(this.timer);
14326         this.el.un("mouseover", this.handleMouseReturn);
14327         this.el.un("mouseout", this.handleMouseOut);
14328         Roo.get(document).un("mouseup", this.handleMouseUp);
14329         this.el.removeClass(this.pressClass);
14330         this.fireEvent("mouseup", this);
14331     }
14332 });/*
14333  * Based on:
14334  * Ext JS Library 1.1.1
14335  * Copyright(c) 2006-2007, Ext JS, LLC.
14336  *
14337  * Originally Released Under LGPL - original licence link has changed is not relivant.
14338  *
14339  * Fork - LGPL
14340  * <script type="text/javascript">
14341  */
14342
14343  
14344 /**
14345  * @class Roo.KeyNav
14346  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14347  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14348  * way to implement custom navigation schemes for any UI component.</p>
14349  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14350  * pageUp, pageDown, del, home, end.  Usage:</p>
14351  <pre><code>
14352 var nav = new Roo.KeyNav("my-element", {
14353     "left" : function(e){
14354         this.moveLeft(e.ctrlKey);
14355     },
14356     "right" : function(e){
14357         this.moveRight(e.ctrlKey);
14358     },
14359     "enter" : function(e){
14360         this.save();
14361     },
14362     scope : this
14363 });
14364 </code></pre>
14365  * @constructor
14366  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14367  * @param {Object} config The config
14368  */
14369 Roo.KeyNav = function(el, config){
14370     this.el = Roo.get(el);
14371     Roo.apply(this, config);
14372     if(!this.disabled){
14373         this.disabled = true;
14374         this.enable();
14375     }
14376 };
14377
14378 Roo.KeyNav.prototype = {
14379     /**
14380      * @cfg {Boolean} disabled
14381      * True to disable this KeyNav instance (defaults to false)
14382      */
14383     disabled : false,
14384     /**
14385      * @cfg {String} defaultEventAction
14386      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14387      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14388      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14389      */
14390     defaultEventAction: "stopEvent",
14391     /**
14392      * @cfg {Boolean} forceKeyDown
14393      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14394      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14395      * handle keydown instead of keypress.
14396      */
14397     forceKeyDown : false,
14398
14399     // private
14400     prepareEvent : function(e){
14401         var k = e.getKey();
14402         var h = this.keyToHandler[k];
14403         //if(h && this[h]){
14404         //    e.stopPropagation();
14405         //}
14406         if(Roo.isSafari && h && k >= 37 && k <= 40){
14407             e.stopEvent();
14408         }
14409     },
14410
14411     // private
14412     relay : function(e){
14413         var k = e.getKey();
14414         var h = this.keyToHandler[k];
14415         if(h && this[h]){
14416             if(this.doRelay(e, this[h], h) !== true){
14417                 e[this.defaultEventAction]();
14418             }
14419         }
14420     },
14421
14422     // private
14423     doRelay : function(e, h, hname){
14424         return h.call(this.scope || this, e);
14425     },
14426
14427     // possible handlers
14428     enter : false,
14429     left : false,
14430     right : false,
14431     up : false,
14432     down : false,
14433     tab : false,
14434     esc : false,
14435     pageUp : false,
14436     pageDown : false,
14437     del : false,
14438     home : false,
14439     end : false,
14440
14441     // quick lookup hash
14442     keyToHandler : {
14443         37 : "left",
14444         39 : "right",
14445         38 : "up",
14446         40 : "down",
14447         33 : "pageUp",
14448         34 : "pageDown",
14449         46 : "del",
14450         36 : "home",
14451         35 : "end",
14452         13 : "enter",
14453         27 : "esc",
14454         9  : "tab"
14455     },
14456
14457         /**
14458          * Enable this KeyNav
14459          */
14460         enable: function(){
14461                 if(this.disabled){
14462             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14463             // the EventObject will normalize Safari automatically
14464             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14465                 this.el.on("keydown", this.relay,  this);
14466             }else{
14467                 this.el.on("keydown", this.prepareEvent,  this);
14468                 this.el.on("keypress", this.relay,  this);
14469             }
14470                     this.disabled = false;
14471                 }
14472         },
14473
14474         /**
14475          * Disable this KeyNav
14476          */
14477         disable: function(){
14478                 if(!this.disabled){
14479                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14480                 this.el.un("keydown", this.relay);
14481             }else{
14482                 this.el.un("keydown", this.prepareEvent);
14483                 this.el.un("keypress", this.relay);
14484             }
14485                     this.disabled = true;
14486                 }
14487         }
14488 };/*
14489  * Based on:
14490  * Ext JS Library 1.1.1
14491  * Copyright(c) 2006-2007, Ext JS, LLC.
14492  *
14493  * Originally Released Under LGPL - original licence link has changed is not relivant.
14494  *
14495  * Fork - LGPL
14496  * <script type="text/javascript">
14497  */
14498
14499  
14500 /**
14501  * @class Roo.KeyMap
14502  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14503  * The constructor accepts the same config object as defined by {@link #addBinding}.
14504  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14505  * combination it will call the function with this signature (if the match is a multi-key
14506  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14507  * A KeyMap can also handle a string representation of keys.<br />
14508  * Usage:
14509  <pre><code>
14510 // map one key by key code
14511 var map = new Roo.KeyMap("my-element", {
14512     key: 13, // or Roo.EventObject.ENTER
14513     fn: myHandler,
14514     scope: myObject
14515 });
14516
14517 // map multiple keys to one action by string
14518 var map = new Roo.KeyMap("my-element", {
14519     key: "a\r\n\t",
14520     fn: myHandler,
14521     scope: myObject
14522 });
14523
14524 // map multiple keys to multiple actions by strings and array of codes
14525 var map = new Roo.KeyMap("my-element", [
14526     {
14527         key: [10,13],
14528         fn: function(){ alert("Return was pressed"); }
14529     }, {
14530         key: "abc",
14531         fn: function(){ alert('a, b or c was pressed'); }
14532     }, {
14533         key: "\t",
14534         ctrl:true,
14535         shift:true,
14536         fn: function(){ alert('Control + shift + tab was pressed.'); }
14537     }
14538 ]);
14539 </code></pre>
14540  * <b>Note: A KeyMap starts enabled</b>
14541  * @constructor
14542  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14543  * @param {Object} config The config (see {@link #addBinding})
14544  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14545  */
14546 Roo.KeyMap = function(el, config, eventName){
14547     this.el  = Roo.get(el);
14548     this.eventName = eventName || "keydown";
14549     this.bindings = [];
14550     if(config){
14551         this.addBinding(config);
14552     }
14553     this.enable();
14554 };
14555
14556 Roo.KeyMap.prototype = {
14557     /**
14558      * True to stop the event from bubbling and prevent the default browser action if the
14559      * key was handled by the KeyMap (defaults to false)
14560      * @type Boolean
14561      */
14562     stopEvent : false,
14563
14564     /**
14565      * Add a new binding to this KeyMap. The following config object properties are supported:
14566      * <pre>
14567 Property    Type             Description
14568 ----------  ---------------  ----------------------------------------------------------------------
14569 key         String/Array     A single keycode or an array of keycodes to handle
14570 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14571 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14572 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14573 fn          Function         The function to call when KeyMap finds the expected key combination
14574 scope       Object           The scope of the callback function
14575 </pre>
14576      *
14577      * Usage:
14578      * <pre><code>
14579 // Create a KeyMap
14580 var map = new Roo.KeyMap(document, {
14581     key: Roo.EventObject.ENTER,
14582     fn: handleKey,
14583     scope: this
14584 });
14585
14586 //Add a new binding to the existing KeyMap later
14587 map.addBinding({
14588     key: 'abc',
14589     shift: true,
14590     fn: handleKey,
14591     scope: this
14592 });
14593 </code></pre>
14594      * @param {Object/Array} config A single KeyMap config or an array of configs
14595      */
14596         addBinding : function(config){
14597         if(config instanceof Array){
14598             for(var i = 0, len = config.length; i < len; i++){
14599                 this.addBinding(config[i]);
14600             }
14601             return;
14602         }
14603         var keyCode = config.key,
14604             shift = config.shift, 
14605             ctrl = config.ctrl, 
14606             alt = config.alt,
14607             fn = config.fn,
14608             scope = config.scope;
14609         if(typeof keyCode == "string"){
14610             var ks = [];
14611             var keyString = keyCode.toUpperCase();
14612             for(var j = 0, len = keyString.length; j < len; j++){
14613                 ks.push(keyString.charCodeAt(j));
14614             }
14615             keyCode = ks;
14616         }
14617         var keyArray = keyCode instanceof Array;
14618         var handler = function(e){
14619             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14620                 var k = e.getKey();
14621                 if(keyArray){
14622                     for(var i = 0, len = keyCode.length; i < len; i++){
14623                         if(keyCode[i] == k){
14624                           if(this.stopEvent){
14625                               e.stopEvent();
14626                           }
14627                           fn.call(scope || window, k, e);
14628                           return;
14629                         }
14630                     }
14631                 }else{
14632                     if(k == keyCode){
14633                         if(this.stopEvent){
14634                            e.stopEvent();
14635                         }
14636                         fn.call(scope || window, k, e);
14637                     }
14638                 }
14639             }
14640         };
14641         this.bindings.push(handler);  
14642         },
14643
14644     /**
14645      * Shorthand for adding a single key listener
14646      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14647      * following options:
14648      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14649      * @param {Function} fn The function to call
14650      * @param {Object} scope (optional) The scope of the function
14651      */
14652     on : function(key, fn, scope){
14653         var keyCode, shift, ctrl, alt;
14654         if(typeof key == "object" && !(key instanceof Array)){
14655             keyCode = key.key;
14656             shift = key.shift;
14657             ctrl = key.ctrl;
14658             alt = key.alt;
14659         }else{
14660             keyCode = key;
14661         }
14662         this.addBinding({
14663             key: keyCode,
14664             shift: shift,
14665             ctrl: ctrl,
14666             alt: alt,
14667             fn: fn,
14668             scope: scope
14669         })
14670     },
14671
14672     // private
14673     handleKeyDown : function(e){
14674             if(this.enabled){ //just in case
14675             var b = this.bindings;
14676             for(var i = 0, len = b.length; i < len; i++){
14677                 b[i].call(this, e);
14678             }
14679             }
14680         },
14681         
14682         /**
14683          * Returns true if this KeyMap is enabled
14684          * @return {Boolean} 
14685          */
14686         isEnabled : function(){
14687             return this.enabled;  
14688         },
14689         
14690         /**
14691          * Enables this KeyMap
14692          */
14693         enable: function(){
14694                 if(!this.enabled){
14695                     this.el.on(this.eventName, this.handleKeyDown, this);
14696                     this.enabled = true;
14697                 }
14698         },
14699
14700         /**
14701          * Disable this KeyMap
14702          */
14703         disable: function(){
14704                 if(this.enabled){
14705                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14706                     this.enabled = false;
14707                 }
14708         }
14709 };/*
14710  * Based on:
14711  * Ext JS Library 1.1.1
14712  * Copyright(c) 2006-2007, Ext JS, LLC.
14713  *
14714  * Originally Released Under LGPL - original licence link has changed is not relivant.
14715  *
14716  * Fork - LGPL
14717  * <script type="text/javascript">
14718  */
14719
14720  
14721 /**
14722  * @class Roo.util.TextMetrics
14723  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14724  * wide, in pixels, a given block of text will be.
14725  * @singleton
14726  */
14727 Roo.util.TextMetrics = function(){
14728     var shared;
14729     return {
14730         /**
14731          * Measures the size of the specified text
14732          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14733          * that can affect the size of the rendered text
14734          * @param {String} text The text to measure
14735          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14736          * in order to accurately measure the text height
14737          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14738          */
14739         measure : function(el, text, fixedWidth){
14740             if(!shared){
14741                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14742             }
14743             shared.bind(el);
14744             shared.setFixedWidth(fixedWidth || 'auto');
14745             return shared.getSize(text);
14746         },
14747
14748         /**
14749          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14750          * the overhead of multiple calls to initialize the style properties on each measurement.
14751          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14752          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14753          * in order to accurately measure the text height
14754          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14755          */
14756         createInstance : function(el, fixedWidth){
14757             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14758         }
14759     };
14760 }();
14761
14762  
14763
14764 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14765     var ml = new Roo.Element(document.createElement('div'));
14766     document.body.appendChild(ml.dom);
14767     ml.position('absolute');
14768     ml.setLeftTop(-1000, -1000);
14769     ml.hide();
14770
14771     if(fixedWidth){
14772         ml.setWidth(fixedWidth);
14773     }
14774      
14775     var instance = {
14776         /**
14777          * Returns the size of the specified text based on the internal element's style and width properties
14778          * @memberOf Roo.util.TextMetrics.Instance#
14779          * @param {String} text The text to measure
14780          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14781          */
14782         getSize : function(text){
14783             ml.update(text);
14784             var s = ml.getSize();
14785             ml.update('');
14786             return s;
14787         },
14788
14789         /**
14790          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14791          * that can affect the size of the rendered text
14792          * @memberOf Roo.util.TextMetrics.Instance#
14793          * @param {String/HTMLElement} el The element, dom node or id
14794          */
14795         bind : function(el){
14796             ml.setStyle(
14797                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14798             );
14799         },
14800
14801         /**
14802          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14803          * to set a fixed width in order to accurately measure the text height.
14804          * @memberOf Roo.util.TextMetrics.Instance#
14805          * @param {Number} width The width to set on the element
14806          */
14807         setFixedWidth : function(width){
14808             ml.setWidth(width);
14809         },
14810
14811         /**
14812          * Returns the measured width of the specified text
14813          * @memberOf Roo.util.TextMetrics.Instance#
14814          * @param {String} text The text to measure
14815          * @return {Number} width The width in pixels
14816          */
14817         getWidth : function(text){
14818             ml.dom.style.width = 'auto';
14819             return this.getSize(text).width;
14820         },
14821
14822         /**
14823          * Returns the measured height of the specified text.  For multiline text, be sure to call
14824          * {@link #setFixedWidth} if necessary.
14825          * @memberOf Roo.util.TextMetrics.Instance#
14826          * @param {String} text The text to measure
14827          * @return {Number} height The height in pixels
14828          */
14829         getHeight : function(text){
14830             return this.getSize(text).height;
14831         }
14832     };
14833
14834     instance.bind(bindTo);
14835
14836     return instance;
14837 };
14838
14839 // backwards compat
14840 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14841  * Based on:
14842  * Ext JS Library 1.1.1
14843  * Copyright(c) 2006-2007, Ext JS, LLC.
14844  *
14845  * Originally Released Under LGPL - original licence link has changed is not relivant.
14846  *
14847  * Fork - LGPL
14848  * <script type="text/javascript">
14849  */
14850
14851 /**
14852  * @class Roo.state.Provider
14853  * Abstract base class for state provider implementations. This class provides methods
14854  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14855  * Provider interface.
14856  */
14857 Roo.state.Provider = function(){
14858     /**
14859      * @event statechange
14860      * Fires when a state change occurs.
14861      * @param {Provider} this This state provider
14862      * @param {String} key The state key which was changed
14863      * @param {String} value The encoded value for the state
14864      */
14865     this.addEvents({
14866         "statechange": true
14867     });
14868     this.state = {};
14869     Roo.state.Provider.superclass.constructor.call(this);
14870 };
14871 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14872     /**
14873      * Returns the current value for a key
14874      * @param {String} name The key name
14875      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14876      * @return {Mixed} The state data
14877      */
14878     get : function(name, defaultValue){
14879         return typeof this.state[name] == "undefined" ?
14880             defaultValue : this.state[name];
14881     },
14882     
14883     /**
14884      * Clears a value from the state
14885      * @param {String} name The key name
14886      */
14887     clear : function(name){
14888         delete this.state[name];
14889         this.fireEvent("statechange", this, name, null);
14890     },
14891     
14892     /**
14893      * Sets the value for a key
14894      * @param {String} name The key name
14895      * @param {Mixed} value The value to set
14896      */
14897     set : function(name, value){
14898         this.state[name] = value;
14899         this.fireEvent("statechange", this, name, value);
14900     },
14901     
14902     /**
14903      * Decodes a string previously encoded with {@link #encodeValue}.
14904      * @param {String} value The value to decode
14905      * @return {Mixed} The decoded value
14906      */
14907     decodeValue : function(cookie){
14908         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14909         var matches = re.exec(unescape(cookie));
14910         if(!matches || !matches[1]) {
14911             return; // non state cookie
14912         }
14913         var type = matches[1];
14914         var v = matches[2];
14915         switch(type){
14916             case "n":
14917                 return parseFloat(v);
14918             case "d":
14919                 return new Date(Date.parse(v));
14920             case "b":
14921                 return (v == "1");
14922             case "a":
14923                 var all = [];
14924                 var values = v.split("^");
14925                 for(var i = 0, len = values.length; i < len; i++){
14926                     all.push(this.decodeValue(values[i]));
14927                 }
14928                 return all;
14929            case "o":
14930                 var all = {};
14931                 var values = v.split("^");
14932                 for(var i = 0, len = values.length; i < len; i++){
14933                     var kv = values[i].split("=");
14934                     all[kv[0]] = this.decodeValue(kv[1]);
14935                 }
14936                 return all;
14937            default:
14938                 return v;
14939         }
14940     },
14941     
14942     /**
14943      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14944      * @param {Mixed} value The value to encode
14945      * @return {String} The encoded value
14946      */
14947     encodeValue : function(v){
14948         var enc;
14949         if(typeof v == "number"){
14950             enc = "n:" + v;
14951         }else if(typeof v == "boolean"){
14952             enc = "b:" + (v ? "1" : "0");
14953         }else if(v instanceof Date){
14954             enc = "d:" + v.toGMTString();
14955         }else if(v instanceof Array){
14956             var flat = "";
14957             for(var i = 0, len = v.length; i < len; i++){
14958                 flat += this.encodeValue(v[i]);
14959                 if(i != len-1) {
14960                     flat += "^";
14961                 }
14962             }
14963             enc = "a:" + flat;
14964         }else if(typeof v == "object"){
14965             var flat = "";
14966             for(var key in v){
14967                 if(typeof v[key] != "function"){
14968                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14969                 }
14970             }
14971             enc = "o:" + flat.substring(0, flat.length-1);
14972         }else{
14973             enc = "s:" + v;
14974         }
14975         return escape(enc);        
14976     }
14977 });
14978
14979 /*
14980  * Based on:
14981  * Ext JS Library 1.1.1
14982  * Copyright(c) 2006-2007, Ext JS, LLC.
14983  *
14984  * Originally Released Under LGPL - original licence link has changed is not relivant.
14985  *
14986  * Fork - LGPL
14987  * <script type="text/javascript">
14988  */
14989 /**
14990  * @class Roo.state.Manager
14991  * This is the global state manager. By default all components that are "state aware" check this class
14992  * for state information if you don't pass them a custom state provider. In order for this class
14993  * to be useful, it must be initialized with a provider when your application initializes.
14994  <pre><code>
14995 // in your initialization function
14996 init : function(){
14997    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14998    ...
14999    // supposed you have a {@link Roo.BorderLayout}
15000    var layout = new Roo.BorderLayout(...);
15001    layout.restoreState();
15002    // or a {Roo.BasicDialog}
15003    var dialog = new Roo.BasicDialog(...);
15004    dialog.restoreState();
15005  </code></pre>
15006  * @singleton
15007  */
15008 Roo.state.Manager = function(){
15009     var provider = new Roo.state.Provider();
15010     
15011     return {
15012         /**
15013          * Configures the default state provider for your application
15014          * @param {Provider} stateProvider The state provider to set
15015          */
15016         setProvider : function(stateProvider){
15017             provider = stateProvider;
15018         },
15019         
15020         /**
15021          * Returns the current value for a key
15022          * @param {String} name The key name
15023          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15024          * @return {Mixed} The state data
15025          */
15026         get : function(key, defaultValue){
15027             return provider.get(key, defaultValue);
15028         },
15029         
15030         /**
15031          * Sets the value for a key
15032          * @param {String} name The key name
15033          * @param {Mixed} value The state data
15034          */
15035          set : function(key, value){
15036             provider.set(key, value);
15037         },
15038         
15039         /**
15040          * Clears a value from the state
15041          * @param {String} name The key name
15042          */
15043         clear : function(key){
15044             provider.clear(key);
15045         },
15046         
15047         /**
15048          * Gets the currently configured state provider
15049          * @return {Provider} The state provider
15050          */
15051         getProvider : function(){
15052             return provider;
15053         }
15054     };
15055 }();
15056 /*
15057  * Based on:
15058  * Ext JS Library 1.1.1
15059  * Copyright(c) 2006-2007, Ext JS, LLC.
15060  *
15061  * Originally Released Under LGPL - original licence link has changed is not relivant.
15062  *
15063  * Fork - LGPL
15064  * <script type="text/javascript">
15065  */
15066 /**
15067  * @class Roo.state.CookieProvider
15068  * @extends Roo.state.Provider
15069  * The default Provider implementation which saves state via cookies.
15070  * <br />Usage:
15071  <pre><code>
15072    var cp = new Roo.state.CookieProvider({
15073        path: "/cgi-bin/",
15074        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15075        domain: "roojs.com"
15076    })
15077    Roo.state.Manager.setProvider(cp);
15078  </code></pre>
15079  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15080  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15081  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15082  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15083  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15084  * domain the page is running on including the 'www' like 'www.roojs.com')
15085  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15086  * @constructor
15087  * Create a new CookieProvider
15088  * @param {Object} config The configuration object
15089  */
15090 Roo.state.CookieProvider = function(config){
15091     Roo.state.CookieProvider.superclass.constructor.call(this);
15092     this.path = "/";
15093     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15094     this.domain = null;
15095     this.secure = false;
15096     Roo.apply(this, config);
15097     this.state = this.readCookies();
15098 };
15099
15100 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15101     // private
15102     set : function(name, value){
15103         if(typeof value == "undefined" || value === null){
15104             this.clear(name);
15105             return;
15106         }
15107         this.setCookie(name, value);
15108         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15109     },
15110
15111     // private
15112     clear : function(name){
15113         this.clearCookie(name);
15114         Roo.state.CookieProvider.superclass.clear.call(this, name);
15115     },
15116
15117     // private
15118     readCookies : function(){
15119         var cookies = {};
15120         var c = document.cookie + ";";
15121         var re = /\s?(.*?)=(.*?);/g;
15122         var matches;
15123         while((matches = re.exec(c)) != null){
15124             var name = matches[1];
15125             var value = matches[2];
15126             if(name && name.substring(0,3) == "ys-"){
15127                 cookies[name.substr(3)] = this.decodeValue(value);
15128             }
15129         }
15130         return cookies;
15131     },
15132
15133     // private
15134     setCookie : function(name, value){
15135         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15136            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15137            ((this.path == null) ? "" : ("; path=" + this.path)) +
15138            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15139            ((this.secure == true) ? "; secure" : "");
15140     },
15141
15142     // private
15143     clearCookie : function(name){
15144         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15145            ((this.path == null) ? "" : ("; path=" + this.path)) +
15146            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15147            ((this.secure == true) ? "; secure" : "");
15148     }
15149 });/*
15150  * Based on:
15151  * Ext JS Library 1.1.1
15152  * Copyright(c) 2006-2007, Ext JS, LLC.
15153  *
15154  * Originally Released Under LGPL - original licence link has changed is not relivant.
15155  *
15156  * Fork - LGPL
15157  * <script type="text/javascript">
15158  */
15159  
15160
15161 /**
15162  * @class Roo.ComponentMgr
15163  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15164  * @singleton
15165  */
15166 Roo.ComponentMgr = function(){
15167     var all = new Roo.util.MixedCollection();
15168
15169     return {
15170         /**
15171          * Registers a component.
15172          * @param {Roo.Component} c The component
15173          */
15174         register : function(c){
15175             all.add(c);
15176         },
15177
15178         /**
15179          * Unregisters a component.
15180          * @param {Roo.Component} c The component
15181          */
15182         unregister : function(c){
15183             all.remove(c);
15184         },
15185
15186         /**
15187          * Returns a component by id
15188          * @param {String} id The component id
15189          */
15190         get : function(id){
15191             return all.get(id);
15192         },
15193
15194         /**
15195          * Registers a function that will be called when a specified component is added to ComponentMgr
15196          * @param {String} id The component id
15197          * @param {Funtction} fn The callback function
15198          * @param {Object} scope The scope of the callback
15199          */
15200         onAvailable : function(id, fn, scope){
15201             all.on("add", function(index, o){
15202                 if(o.id == id){
15203                     fn.call(scope || o, o);
15204                     all.un("add", fn, scope);
15205                 }
15206             });
15207         }
15208     };
15209 }();/*
15210  * Based on:
15211  * Ext JS Library 1.1.1
15212  * Copyright(c) 2006-2007, Ext JS, LLC.
15213  *
15214  * Originally Released Under LGPL - original licence link has changed is not relivant.
15215  *
15216  * Fork - LGPL
15217  * <script type="text/javascript">
15218  */
15219  
15220 /**
15221  * @class Roo.Component
15222  * @extends Roo.util.Observable
15223  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15224  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15225  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15226  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15227  * All visual components (widgets) that require rendering into a layout should subclass Component.
15228  * @constructor
15229  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15230  * 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
15231  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15232  */
15233 Roo.Component = function(config){
15234     config = config || {};
15235     if(config.tagName || config.dom || typeof config == "string"){ // element object
15236         config = {el: config, id: config.id || config};
15237     }
15238     this.initialConfig = config;
15239
15240     Roo.apply(this, config);
15241     this.addEvents({
15242         /**
15243          * @event disable
15244          * Fires after the component is disabled.
15245              * @param {Roo.Component} this
15246              */
15247         disable : true,
15248         /**
15249          * @event enable
15250          * Fires after the component is enabled.
15251              * @param {Roo.Component} this
15252              */
15253         enable : true,
15254         /**
15255          * @event beforeshow
15256          * Fires before the component is shown.  Return false to stop the show.
15257              * @param {Roo.Component} this
15258              */
15259         beforeshow : true,
15260         /**
15261          * @event show
15262          * Fires after the component is shown.
15263              * @param {Roo.Component} this
15264              */
15265         show : true,
15266         /**
15267          * @event beforehide
15268          * Fires before the component is hidden. Return false to stop the hide.
15269              * @param {Roo.Component} this
15270              */
15271         beforehide : true,
15272         /**
15273          * @event hide
15274          * Fires after the component is hidden.
15275              * @param {Roo.Component} this
15276              */
15277         hide : true,
15278         /**
15279          * @event beforerender
15280          * Fires before the component is rendered. Return false to stop the render.
15281              * @param {Roo.Component} this
15282              */
15283         beforerender : true,
15284         /**
15285          * @event render
15286          * Fires after the component is rendered.
15287              * @param {Roo.Component} this
15288              */
15289         render : true,
15290         /**
15291          * @event beforedestroy
15292          * Fires before the component is destroyed. Return false to stop the destroy.
15293              * @param {Roo.Component} this
15294              */
15295         beforedestroy : true,
15296         /**
15297          * @event destroy
15298          * Fires after the component is destroyed.
15299              * @param {Roo.Component} this
15300              */
15301         destroy : true
15302     });
15303     if(!this.id){
15304         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15305     }
15306     Roo.ComponentMgr.register(this);
15307     Roo.Component.superclass.constructor.call(this);
15308     this.initComponent();
15309     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15310         this.render(this.renderTo);
15311         delete this.renderTo;
15312     }
15313 };
15314
15315 /** @private */
15316 Roo.Component.AUTO_ID = 1000;
15317
15318 Roo.extend(Roo.Component, Roo.util.Observable, {
15319     /**
15320      * @scope Roo.Component.prototype
15321      * @type {Boolean}
15322      * true if this component is hidden. Read-only.
15323      */
15324     hidden : false,
15325     /**
15326      * @type {Boolean}
15327      * true if this component is disabled. Read-only.
15328      */
15329     disabled : false,
15330     /**
15331      * @type {Boolean}
15332      * true if this component has been rendered. Read-only.
15333      */
15334     rendered : false,
15335     
15336     /** @cfg {String} disableClass
15337      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15338      */
15339     disabledClass : "x-item-disabled",
15340         /** @cfg {Boolean} allowDomMove
15341          * Whether the component can move the Dom node when rendering (defaults to true).
15342          */
15343     allowDomMove : true,
15344     /** @cfg {String} hideMode (display|visibility)
15345      * How this component should hidden. Supported values are
15346      * "visibility" (css visibility), "offsets" (negative offset position) and
15347      * "display" (css display) - defaults to "display".
15348      */
15349     hideMode: 'display',
15350
15351     /** @private */
15352     ctype : "Roo.Component",
15353
15354     /**
15355      * @cfg {String} actionMode 
15356      * which property holds the element that used for  hide() / show() / disable() / enable()
15357      * default is 'el' 
15358      */
15359     actionMode : "el",
15360
15361     /** @private */
15362     getActionEl : function(){
15363         return this[this.actionMode];
15364     },
15365
15366     initComponent : Roo.emptyFn,
15367     /**
15368      * If this is a lazy rendering component, render it to its container element.
15369      * @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.
15370      */
15371     render : function(container, position){
15372         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15373             if(!container && this.el){
15374                 this.el = Roo.get(this.el);
15375                 container = this.el.dom.parentNode;
15376                 this.allowDomMove = false;
15377             }
15378             this.container = Roo.get(container);
15379             this.rendered = true;
15380             if(position !== undefined){
15381                 if(typeof position == 'number'){
15382                     position = this.container.dom.childNodes[position];
15383                 }else{
15384                     position = Roo.getDom(position);
15385                 }
15386             }
15387             this.onRender(this.container, position || null);
15388             if(this.cls){
15389                 this.el.addClass(this.cls);
15390                 delete this.cls;
15391             }
15392             if(this.style){
15393                 this.el.applyStyles(this.style);
15394                 delete this.style;
15395             }
15396             this.fireEvent("render", this);
15397             this.afterRender(this.container);
15398             if(this.hidden){
15399                 this.hide();
15400             }
15401             if(this.disabled){
15402                 this.disable();
15403             }
15404         }
15405         return this;
15406     },
15407
15408     /** @private */
15409     // default function is not really useful
15410     onRender : function(ct, position){
15411         if(this.el){
15412             this.el = Roo.get(this.el);
15413             if(this.allowDomMove !== false){
15414                 ct.dom.insertBefore(this.el.dom, position);
15415             }
15416         }
15417     },
15418
15419     /** @private */
15420     getAutoCreate : function(){
15421         var cfg = typeof this.autoCreate == "object" ?
15422                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15423         if(this.id && !cfg.id){
15424             cfg.id = this.id;
15425         }
15426         return cfg;
15427     },
15428
15429     /** @private */
15430     afterRender : Roo.emptyFn,
15431
15432     /**
15433      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15434      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15435      */
15436     destroy : function(){
15437         if(this.fireEvent("beforedestroy", this) !== false){
15438             this.purgeListeners();
15439             this.beforeDestroy();
15440             if(this.rendered){
15441                 this.el.removeAllListeners();
15442                 this.el.remove();
15443                 if(this.actionMode == "container"){
15444                     this.container.remove();
15445                 }
15446             }
15447             this.onDestroy();
15448             Roo.ComponentMgr.unregister(this);
15449             this.fireEvent("destroy", this);
15450         }
15451     },
15452
15453         /** @private */
15454     beforeDestroy : function(){
15455
15456     },
15457
15458         /** @private */
15459         onDestroy : function(){
15460
15461     },
15462
15463     /**
15464      * Returns the underlying {@link Roo.Element}.
15465      * @return {Roo.Element} The element
15466      */
15467     getEl : function(){
15468         return this.el;
15469     },
15470
15471     /**
15472      * Returns the id of this component.
15473      * @return {String}
15474      */
15475     getId : function(){
15476         return this.id;
15477     },
15478
15479     /**
15480      * Try to focus this component.
15481      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15482      * @return {Roo.Component} this
15483      */
15484     focus : function(selectText){
15485         if(this.rendered){
15486             this.el.focus();
15487             if(selectText === true){
15488                 this.el.dom.select();
15489             }
15490         }
15491         return this;
15492     },
15493
15494     /** @private */
15495     blur : function(){
15496         if(this.rendered){
15497             this.el.blur();
15498         }
15499         return this;
15500     },
15501
15502     /**
15503      * Disable this component.
15504      * @return {Roo.Component} this
15505      */
15506     disable : function(){
15507         if(this.rendered){
15508             this.onDisable();
15509         }
15510         this.disabled = true;
15511         this.fireEvent("disable", this);
15512         return this;
15513     },
15514
15515         // private
15516     onDisable : function(){
15517         this.getActionEl().addClass(this.disabledClass);
15518         this.el.dom.disabled = true;
15519     },
15520
15521     /**
15522      * Enable this component.
15523      * @return {Roo.Component} this
15524      */
15525     enable : function(){
15526         if(this.rendered){
15527             this.onEnable();
15528         }
15529         this.disabled = false;
15530         this.fireEvent("enable", this);
15531         return this;
15532     },
15533
15534         // private
15535     onEnable : function(){
15536         this.getActionEl().removeClass(this.disabledClass);
15537         this.el.dom.disabled = false;
15538     },
15539
15540     /**
15541      * Convenience function for setting disabled/enabled by boolean.
15542      * @param {Boolean} disabled
15543      */
15544     setDisabled : function(disabled){
15545         this[disabled ? "disable" : "enable"]();
15546     },
15547
15548     /**
15549      * Show this component.
15550      * @return {Roo.Component} this
15551      */
15552     show: function(){
15553         if(this.fireEvent("beforeshow", this) !== false){
15554             this.hidden = false;
15555             if(this.rendered){
15556                 this.onShow();
15557             }
15558             this.fireEvent("show", this);
15559         }
15560         return this;
15561     },
15562
15563     // private
15564     onShow : function(){
15565         var ae = this.getActionEl();
15566         if(this.hideMode == 'visibility'){
15567             ae.dom.style.visibility = "visible";
15568         }else if(this.hideMode == 'offsets'){
15569             ae.removeClass('x-hidden');
15570         }else{
15571             ae.dom.style.display = "";
15572         }
15573     },
15574
15575     /**
15576      * Hide this component.
15577      * @return {Roo.Component} this
15578      */
15579     hide: function(){
15580         if(this.fireEvent("beforehide", this) !== false){
15581             this.hidden = true;
15582             if(this.rendered){
15583                 this.onHide();
15584             }
15585             this.fireEvent("hide", this);
15586         }
15587         return this;
15588     },
15589
15590     // private
15591     onHide : function(){
15592         var ae = this.getActionEl();
15593         if(this.hideMode == 'visibility'){
15594             ae.dom.style.visibility = "hidden";
15595         }else if(this.hideMode == 'offsets'){
15596             ae.addClass('x-hidden');
15597         }else{
15598             ae.dom.style.display = "none";
15599         }
15600     },
15601
15602     /**
15603      * Convenience function to hide or show this component by boolean.
15604      * @param {Boolean} visible True to show, false to hide
15605      * @return {Roo.Component} this
15606      */
15607     setVisible: function(visible){
15608         if(visible) {
15609             this.show();
15610         }else{
15611             this.hide();
15612         }
15613         return this;
15614     },
15615
15616     /**
15617      * Returns true if this component is visible.
15618      */
15619     isVisible : function(){
15620         return this.getActionEl().isVisible();
15621     },
15622
15623     cloneConfig : function(overrides){
15624         overrides = overrides || {};
15625         var id = overrides.id || Roo.id();
15626         var cfg = Roo.applyIf(overrides, this.initialConfig);
15627         cfg.id = id; // prevent dup id
15628         return new this.constructor(cfg);
15629     }
15630 });/*
15631  * Based on:
15632  * Ext JS Library 1.1.1
15633  * Copyright(c) 2006-2007, Ext JS, LLC.
15634  *
15635  * Originally Released Under LGPL - original licence link has changed is not relivant.
15636  *
15637  * Fork - LGPL
15638  * <script type="text/javascript">
15639  */
15640
15641 /**
15642  * @class Roo.BoxComponent
15643  * @extends Roo.Component
15644  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15645  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15646  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15647  * layout containers.
15648  * @constructor
15649  * @param {Roo.Element/String/Object} config The configuration options.
15650  */
15651 Roo.BoxComponent = function(config){
15652     Roo.Component.call(this, config);
15653     this.addEvents({
15654         /**
15655          * @event resize
15656          * Fires after the component is resized.
15657              * @param {Roo.Component} this
15658              * @param {Number} adjWidth The box-adjusted width that was set
15659              * @param {Number} adjHeight The box-adjusted height that was set
15660              * @param {Number} rawWidth The width that was originally specified
15661              * @param {Number} rawHeight The height that was originally specified
15662              */
15663         resize : true,
15664         /**
15665          * @event move
15666          * Fires after the component is moved.
15667              * @param {Roo.Component} this
15668              * @param {Number} x The new x position
15669              * @param {Number} y The new y position
15670              */
15671         move : true
15672     });
15673 };
15674
15675 Roo.extend(Roo.BoxComponent, Roo.Component, {
15676     // private, set in afterRender to signify that the component has been rendered
15677     boxReady : false,
15678     // private, used to defer height settings to subclasses
15679     deferHeight: false,
15680     /** @cfg {Number} width
15681      * width (optional) size of component
15682      */
15683      /** @cfg {Number} height
15684      * height (optional) size of component
15685      */
15686      
15687     /**
15688      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15689      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15690      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15691      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15692      * @return {Roo.BoxComponent} this
15693      */
15694     setSize : function(w, h){
15695         // support for standard size objects
15696         if(typeof w == 'object'){
15697             h = w.height;
15698             w = w.width;
15699         }
15700         // not rendered
15701         if(!this.boxReady){
15702             this.width = w;
15703             this.height = h;
15704             return this;
15705         }
15706
15707         // prevent recalcs when not needed
15708         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15709             return this;
15710         }
15711         this.lastSize = {width: w, height: h};
15712
15713         var adj = this.adjustSize(w, h);
15714         var aw = adj.width, ah = adj.height;
15715         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15716             var rz = this.getResizeEl();
15717             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15718                 rz.setSize(aw, ah);
15719             }else if(!this.deferHeight && ah !== undefined){
15720                 rz.setHeight(ah);
15721             }else if(aw !== undefined){
15722                 rz.setWidth(aw);
15723             }
15724             this.onResize(aw, ah, w, h);
15725             this.fireEvent('resize', this, aw, ah, w, h);
15726         }
15727         return this;
15728     },
15729
15730     /**
15731      * Gets the current size of the component's underlying element.
15732      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15733      */
15734     getSize : function(){
15735         return this.el.getSize();
15736     },
15737
15738     /**
15739      * Gets the current XY position of the component's underlying element.
15740      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15741      * @return {Array} The XY position of the element (e.g., [100, 200])
15742      */
15743     getPosition : function(local){
15744         if(local === true){
15745             return [this.el.getLeft(true), this.el.getTop(true)];
15746         }
15747         return this.xy || this.el.getXY();
15748     },
15749
15750     /**
15751      * Gets the current box measurements of the component's underlying element.
15752      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15753      * @returns {Object} box An object in the format {x, y, width, height}
15754      */
15755     getBox : function(local){
15756         var s = this.el.getSize();
15757         if(local){
15758             s.x = this.el.getLeft(true);
15759             s.y = this.el.getTop(true);
15760         }else{
15761             var xy = this.xy || this.el.getXY();
15762             s.x = xy[0];
15763             s.y = xy[1];
15764         }
15765         return s;
15766     },
15767
15768     /**
15769      * Sets the current box measurements of the component's underlying element.
15770      * @param {Object} box An object in the format {x, y, width, height}
15771      * @returns {Roo.BoxComponent} this
15772      */
15773     updateBox : function(box){
15774         this.setSize(box.width, box.height);
15775         this.setPagePosition(box.x, box.y);
15776         return this;
15777     },
15778
15779     // protected
15780     getResizeEl : function(){
15781         return this.resizeEl || this.el;
15782     },
15783
15784     // protected
15785     getPositionEl : function(){
15786         return this.positionEl || this.el;
15787     },
15788
15789     /**
15790      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15791      * This method fires the move event.
15792      * @param {Number} left The new left
15793      * @param {Number} top The new top
15794      * @returns {Roo.BoxComponent} this
15795      */
15796     setPosition : function(x, y){
15797         this.x = x;
15798         this.y = y;
15799         if(!this.boxReady){
15800             return this;
15801         }
15802         var adj = this.adjustPosition(x, y);
15803         var ax = adj.x, ay = adj.y;
15804
15805         var el = this.getPositionEl();
15806         if(ax !== undefined || ay !== undefined){
15807             if(ax !== undefined && ay !== undefined){
15808                 el.setLeftTop(ax, ay);
15809             }else if(ax !== undefined){
15810                 el.setLeft(ax);
15811             }else if(ay !== undefined){
15812                 el.setTop(ay);
15813             }
15814             this.onPosition(ax, ay);
15815             this.fireEvent('move', this, ax, ay);
15816         }
15817         return this;
15818     },
15819
15820     /**
15821      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15822      * This method fires the move event.
15823      * @param {Number} x The new x position
15824      * @param {Number} y The new y position
15825      * @returns {Roo.BoxComponent} this
15826      */
15827     setPagePosition : function(x, y){
15828         this.pageX = x;
15829         this.pageY = y;
15830         if(!this.boxReady){
15831             return;
15832         }
15833         if(x === undefined || y === undefined){ // cannot translate undefined points
15834             return;
15835         }
15836         var p = this.el.translatePoints(x, y);
15837         this.setPosition(p.left, p.top);
15838         return this;
15839     },
15840
15841     // private
15842     onRender : function(ct, position){
15843         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15844         if(this.resizeEl){
15845             this.resizeEl = Roo.get(this.resizeEl);
15846         }
15847         if(this.positionEl){
15848             this.positionEl = Roo.get(this.positionEl);
15849         }
15850     },
15851
15852     // private
15853     afterRender : function(){
15854         Roo.BoxComponent.superclass.afterRender.call(this);
15855         this.boxReady = true;
15856         this.setSize(this.width, this.height);
15857         if(this.x || this.y){
15858             this.setPosition(this.x, this.y);
15859         }
15860         if(this.pageX || this.pageY){
15861             this.setPagePosition(this.pageX, this.pageY);
15862         }
15863     },
15864
15865     /**
15866      * Force the component's size to recalculate based on the underlying element's current height and width.
15867      * @returns {Roo.BoxComponent} this
15868      */
15869     syncSize : function(){
15870         delete this.lastSize;
15871         this.setSize(this.el.getWidth(), this.el.getHeight());
15872         return this;
15873     },
15874
15875     /**
15876      * Called after the component is resized, this method is empty by default but can be implemented by any
15877      * subclass that needs to perform custom logic after a resize occurs.
15878      * @param {Number} adjWidth The box-adjusted width that was set
15879      * @param {Number} adjHeight The box-adjusted height that was set
15880      * @param {Number} rawWidth The width that was originally specified
15881      * @param {Number} rawHeight The height that was originally specified
15882      */
15883     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15884
15885     },
15886
15887     /**
15888      * Called after the component is moved, this method is empty by default but can be implemented by any
15889      * subclass that needs to perform custom logic after a move occurs.
15890      * @param {Number} x The new x position
15891      * @param {Number} y The new y position
15892      */
15893     onPosition : function(x, y){
15894
15895     },
15896
15897     // private
15898     adjustSize : function(w, h){
15899         if(this.autoWidth){
15900             w = 'auto';
15901         }
15902         if(this.autoHeight){
15903             h = 'auto';
15904         }
15905         return {width : w, height: h};
15906     },
15907
15908     // private
15909     adjustPosition : function(x, y){
15910         return {x : x, y: y};
15911     }
15912 });/*
15913  * Original code for Roojs - LGPL
15914  * <script type="text/javascript">
15915  */
15916  
15917 /**
15918  * @class Roo.XComponent
15919  * A delayed Element creator...
15920  * Or a way to group chunks of interface together.
15921  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15922  *  used in conjunction with XComponent.build() it will create an instance of each element,
15923  *  then call addxtype() to build the User interface.
15924  * 
15925  * Mypart.xyx = new Roo.XComponent({
15926
15927     parent : 'Mypart.xyz', // empty == document.element.!!
15928     order : '001',
15929     name : 'xxxx'
15930     region : 'xxxx'
15931     disabled : function() {} 
15932      
15933     tree : function() { // return an tree of xtype declared components
15934         var MODULE = this;
15935         return 
15936         {
15937             xtype : 'NestedLayoutPanel',
15938             // technicall
15939         }
15940      ]
15941  *})
15942  *
15943  *
15944  * It can be used to build a big heiracy, with parent etc.
15945  * or you can just use this to render a single compoent to a dom element
15946  * MYPART.render(Roo.Element | String(id) | dom_element )
15947  *
15948  *
15949  * Usage patterns.
15950  *
15951  * Classic Roo
15952  *
15953  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15954  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15955  *
15956  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15957  *
15958  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15959  * - if mulitple topModules exist, the last one is defined as the top module.
15960  *
15961  * Embeded Roo
15962  * 
15963  * When the top level or multiple modules are to embedded into a existing HTML page,
15964  * the parent element can container '#id' of the element where the module will be drawn.
15965  *
15966  * Bootstrap Roo
15967  *
15968  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15969  * it relies more on a include mechanism, where sub modules are included into an outer page.
15970  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15971  * 
15972  * Bootstrap Roo Included elements
15973  *
15974  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15975  * hence confusing the component builder as it thinks there are multiple top level elements. 
15976  *
15977  * 
15978  * 
15979  * @extends Roo.util.Observable
15980  * @constructor
15981  * @param cfg {Object} configuration of component
15982  * 
15983  */
15984 Roo.XComponent = function(cfg) {
15985     Roo.apply(this, cfg);
15986     this.addEvents({ 
15987         /**
15988              * @event built
15989              * Fires when this the componnt is built
15990              * @param {Roo.XComponent} c the component
15991              */
15992         'built' : true
15993         
15994     });
15995     this.region = this.region || 'center'; // default..
15996     Roo.XComponent.register(this);
15997     this.modules = false;
15998     this.el = false; // where the layout goes..
15999     
16000     
16001 }
16002 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16003     /**
16004      * @property el
16005      * The created element (with Roo.factory())
16006      * @type {Roo.Layout}
16007      */
16008     el  : false,
16009     
16010     /**
16011      * @property el
16012      * for BC  - use el in new code
16013      * @type {Roo.Layout}
16014      */
16015     panel : false,
16016     
16017     /**
16018      * @property layout
16019      * for BC  - use el in new code
16020      * @type {Roo.Layout}
16021      */
16022     layout : false,
16023     
16024      /**
16025      * @cfg {Function|boolean} disabled
16026      * If this module is disabled by some rule, return true from the funtion
16027      */
16028     disabled : false,
16029     
16030     /**
16031      * @cfg {String} parent 
16032      * Name of parent element which it get xtype added to..
16033      */
16034     parent: false,
16035     
16036     /**
16037      * @cfg {String} order
16038      * Used to set the order in which elements are created (usefull for multiple tabs)
16039      */
16040     
16041     order : false,
16042     /**
16043      * @cfg {String} name
16044      * String to display while loading.
16045      */
16046     name : false,
16047     /**
16048      * @cfg {String} region
16049      * Region to render component to (defaults to center)
16050      */
16051     region : 'center',
16052     
16053     /**
16054      * @cfg {Array} items
16055      * A single item array - the first element is the root of the tree..
16056      * It's done this way to stay compatible with the Xtype system...
16057      */
16058     items : false,
16059     
16060     /**
16061      * @property _tree
16062      * The method that retuns the tree of parts that make up this compoennt 
16063      * @type {function}
16064      */
16065     _tree  : false,
16066     
16067      /**
16068      * render
16069      * render element to dom or tree
16070      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16071      */
16072     
16073     render : function(el)
16074     {
16075         
16076         el = el || false;
16077         var hp = this.parent ? 1 : 0;
16078         Roo.debug &&  Roo.log(this);
16079         
16080         var tree = this._tree ? this._tree() : this.tree();
16081
16082         
16083         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16084             // if parent is a '#.....' string, then let's use that..
16085             var ename = this.parent.substr(1);
16086             this.parent = false;
16087             Roo.debug && Roo.log(ename);
16088             switch (ename) {
16089                 case 'bootstrap-body':
16090                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16091                         // this is the BorderLayout standard?
16092                        this.parent = { el : true };
16093                        break;
16094                     }
16095                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16096                         // need to insert stuff...
16097                         this.parent =  {
16098                              el : new Roo.bootstrap.layout.Border({
16099                                  el : document.body, 
16100                      
16101                                  center: {
16102                                     titlebar: false,
16103                                     autoScroll:false,
16104                                     closeOnTab: true,
16105                                     tabPosition: 'top',
16106                                       //resizeTabs: true,
16107                                     alwaysShowTabs: true,
16108                                     hideTabs: false
16109                                      //minTabWidth: 140
16110                                  }
16111                              })
16112                         
16113                          };
16114                          break;
16115                     }
16116                          
16117                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16118                         this.parent = { el :  new  Roo.bootstrap.Body() };
16119                         Roo.debug && Roo.log("setting el to doc body");
16120                          
16121                     } else {
16122                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16123                     }
16124                     break;
16125                 case 'bootstrap':
16126                     this.parent = { el : true};
16127                     // fall through
16128                 default:
16129                     el = Roo.get(ename);
16130                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16131                         this.parent = { el : true};
16132                     }
16133                     
16134                     break;
16135             }
16136                 
16137             
16138             if (!el && !this.parent) {
16139                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16140                 return;
16141             }
16142         }
16143         
16144         Roo.debug && Roo.log("EL:");
16145         Roo.debug && Roo.log(el);
16146         Roo.debug && Roo.log("this.parent.el:");
16147         Roo.debug && Roo.log(this.parent.el);
16148         
16149
16150         // altertive root elements ??? - we need a better way to indicate these.
16151         var is_alt = Roo.XComponent.is_alt ||
16152                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16153                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16154                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16155         
16156         
16157         
16158         if (!this.parent && is_alt) {
16159             //el = Roo.get(document.body);
16160             this.parent = { el : true };
16161         }
16162             
16163             
16164         
16165         if (!this.parent) {
16166             
16167             Roo.debug && Roo.log("no parent - creating one");
16168             
16169             el = el ? Roo.get(el) : false;      
16170             
16171             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16172                 
16173                 this.parent =  {
16174                     el : new Roo.bootstrap.layout.Border({
16175                         el: el || document.body,
16176                     
16177                         center: {
16178                             titlebar: false,
16179                             autoScroll:false,
16180                             closeOnTab: true,
16181                             tabPosition: 'top',
16182                              //resizeTabs: true,
16183                             alwaysShowTabs: false,
16184                             hideTabs: true,
16185                             minTabWidth: 140,
16186                             overflow: 'visible'
16187                          }
16188                      })
16189                 };
16190             } else {
16191             
16192                 // it's a top level one..
16193                 this.parent =  {
16194                     el : new Roo.BorderLayout(el || document.body, {
16195                         center: {
16196                             titlebar: false,
16197                             autoScroll:false,
16198                             closeOnTab: true,
16199                             tabPosition: 'top',
16200                              //resizeTabs: true,
16201                             alwaysShowTabs: el && hp? false :  true,
16202                             hideTabs: el || !hp ? true :  false,
16203                             minTabWidth: 140
16204                          }
16205                     })
16206                 };
16207             }
16208         }
16209         
16210         if (!this.parent.el) {
16211                 // probably an old style ctor, which has been disabled.
16212                 return;
16213
16214         }
16215                 // The 'tree' method is  '_tree now' 
16216             
16217         tree.region = tree.region || this.region;
16218         var is_body = false;
16219         if (this.parent.el === true) {
16220             // bootstrap... - body..
16221             if (el) {
16222                 tree.el = el;
16223             }
16224             this.parent.el = Roo.factory(tree);
16225             is_body = true;
16226         }
16227         
16228         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16229         this.fireEvent('built', this);
16230         
16231         this.panel = this.el;
16232         this.layout = this.panel.layout;
16233         this.parentLayout = this.parent.layout  || false;  
16234          
16235     }
16236     
16237 });
16238
16239 Roo.apply(Roo.XComponent, {
16240     /**
16241      * @property  hideProgress
16242      * true to disable the building progress bar.. usefull on single page renders.
16243      * @type Boolean
16244      */
16245     hideProgress : false,
16246     /**
16247      * @property  buildCompleted
16248      * True when the builder has completed building the interface.
16249      * @type Boolean
16250      */
16251     buildCompleted : false,
16252      
16253     /**
16254      * @property  topModule
16255      * the upper most module - uses document.element as it's constructor.
16256      * @type Object
16257      */
16258      
16259     topModule  : false,
16260       
16261     /**
16262      * @property  modules
16263      * array of modules to be created by registration system.
16264      * @type {Array} of Roo.XComponent
16265      */
16266     
16267     modules : [],
16268     /**
16269      * @property  elmodules
16270      * array of modules to be created by which use #ID 
16271      * @type {Array} of Roo.XComponent
16272      */
16273      
16274     elmodules : [],
16275
16276      /**
16277      * @property  is_alt
16278      * Is an alternative Root - normally used by bootstrap or other systems,
16279      *    where the top element in the tree can wrap 'body' 
16280      * @type {boolean}  (default false)
16281      */
16282      
16283     is_alt : false,
16284     /**
16285      * @property  build_from_html
16286      * Build elements from html - used by bootstrap HTML stuff 
16287      *    - this is cleared after build is completed
16288      * @type {boolean}    (default false)
16289      */
16290      
16291     build_from_html : false,
16292     /**
16293      * Register components to be built later.
16294      *
16295      * This solves the following issues
16296      * - Building is not done on page load, but after an authentication process has occured.
16297      * - Interface elements are registered on page load
16298      * - Parent Interface elements may not be loaded before child, so this handles that..
16299      * 
16300      *
16301      * example:
16302      * 
16303      * MyApp.register({
16304           order : '000001',
16305           module : 'Pman.Tab.projectMgr',
16306           region : 'center',
16307           parent : 'Pman.layout',
16308           disabled : false,  // or use a function..
16309         })
16310      
16311      * * @param {Object} details about module
16312      */
16313     register : function(obj) {
16314                 
16315         Roo.XComponent.event.fireEvent('register', obj);
16316         switch(typeof(obj.disabled) ) {
16317                 
16318             case 'undefined':
16319                 break;
16320             
16321             case 'function':
16322                 if ( obj.disabled() ) {
16323                         return;
16324                 }
16325                 break;
16326             
16327             default:
16328                 if (obj.disabled) {
16329                         return;
16330                 }
16331                 break;
16332         }
16333                 
16334         this.modules.push(obj);
16335          
16336     },
16337     /**
16338      * convert a string to an object..
16339      * eg. 'AAA.BBB' -> finds AAA.BBB
16340
16341      */
16342     
16343     toObject : function(str)
16344     {
16345         if (!str || typeof(str) == 'object') {
16346             return str;
16347         }
16348         if (str.substring(0,1) == '#') {
16349             return str;
16350         }
16351
16352         var ar = str.split('.');
16353         var rt, o;
16354         rt = ar.shift();
16355             /** eval:var:o */
16356         try {
16357             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16358         } catch (e) {
16359             throw "Module not found : " + str;
16360         }
16361         
16362         if (o === false) {
16363             throw "Module not found : " + str;
16364         }
16365         Roo.each(ar, function(e) {
16366             if (typeof(o[e]) == 'undefined') {
16367                 throw "Module not found : " + str;
16368             }
16369             o = o[e];
16370         });
16371         
16372         return o;
16373         
16374     },
16375     
16376     
16377     /**
16378      * move modules into their correct place in the tree..
16379      * 
16380      */
16381     preBuild : function ()
16382     {
16383         var _t = this;
16384         Roo.each(this.modules , function (obj)
16385         {
16386             Roo.XComponent.event.fireEvent('beforebuild', obj);
16387             
16388             var opar = obj.parent;
16389             try { 
16390                 obj.parent = this.toObject(opar);
16391             } catch(e) {
16392                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16393                 return;
16394             }
16395             
16396             if (!obj.parent) {
16397                 Roo.debug && Roo.log("GOT top level module");
16398                 Roo.debug && Roo.log(obj);
16399                 obj.modules = new Roo.util.MixedCollection(false, 
16400                     function(o) { return o.order + '' }
16401                 );
16402                 this.topModule = obj;
16403                 return;
16404             }
16405                         // parent is a string (usually a dom element name..)
16406             if (typeof(obj.parent) == 'string') {
16407                 this.elmodules.push(obj);
16408                 return;
16409             }
16410             if (obj.parent.constructor != Roo.XComponent) {
16411                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16412             }
16413             if (!obj.parent.modules) {
16414                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16415                     function(o) { return o.order + '' }
16416                 );
16417             }
16418             if (obj.parent.disabled) {
16419                 obj.disabled = true;
16420             }
16421             obj.parent.modules.add(obj);
16422         }, this);
16423     },
16424     
16425      /**
16426      * make a list of modules to build.
16427      * @return {Array} list of modules. 
16428      */ 
16429     
16430     buildOrder : function()
16431     {
16432         var _this = this;
16433         var cmp = function(a,b) {   
16434             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16435         };
16436         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16437             throw "No top level modules to build";
16438         }
16439         
16440         // make a flat list in order of modules to build.
16441         var mods = this.topModule ? [ this.topModule ] : [];
16442                 
16443         
16444         // elmodules (is a list of DOM based modules )
16445         Roo.each(this.elmodules, function(e) {
16446             mods.push(e);
16447             if (!this.topModule &&
16448                 typeof(e.parent) == 'string' &&
16449                 e.parent.substring(0,1) == '#' &&
16450                 Roo.get(e.parent.substr(1))
16451                ) {
16452                 
16453                 _this.topModule = e;
16454             }
16455             
16456         });
16457
16458         
16459         // add modules to their parents..
16460         var addMod = function(m) {
16461             Roo.debug && Roo.log("build Order: add: " + m.name);
16462                 
16463             mods.push(m);
16464             if (m.modules && !m.disabled) {
16465                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16466                 m.modules.keySort('ASC',  cmp );
16467                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16468     
16469                 m.modules.each(addMod);
16470             } else {
16471                 Roo.debug && Roo.log("build Order: no child modules");
16472             }
16473             // not sure if this is used any more..
16474             if (m.finalize) {
16475                 m.finalize.name = m.name + " (clean up) ";
16476                 mods.push(m.finalize);
16477             }
16478             
16479         }
16480         if (this.topModule && this.topModule.modules) { 
16481             this.topModule.modules.keySort('ASC',  cmp );
16482             this.topModule.modules.each(addMod);
16483         } 
16484         return mods;
16485     },
16486     
16487      /**
16488      * Build the registered modules.
16489      * @param {Object} parent element.
16490      * @param {Function} optional method to call after module has been added.
16491      * 
16492      */ 
16493    
16494     build : function(opts) 
16495     {
16496         
16497         if (typeof(opts) != 'undefined') {
16498             Roo.apply(this,opts);
16499         }
16500         
16501         this.preBuild();
16502         var mods = this.buildOrder();
16503       
16504         //this.allmods = mods;
16505         //Roo.debug && Roo.log(mods);
16506         //return;
16507         if (!mods.length) { // should not happen
16508             throw "NO modules!!!";
16509         }
16510         
16511         
16512         var msg = "Building Interface...";
16513         // flash it up as modal - so we store the mask!?
16514         if (!this.hideProgress && Roo.MessageBox) {
16515             Roo.MessageBox.show({ title: 'loading' });
16516             Roo.MessageBox.show({
16517                title: "Please wait...",
16518                msg: msg,
16519                width:450,
16520                progress:true,
16521                closable:false,
16522                modal: false
16523               
16524             });
16525         }
16526         var total = mods.length;
16527         
16528         var _this = this;
16529         var progressRun = function() {
16530             if (!mods.length) {
16531                 Roo.debug && Roo.log('hide?');
16532                 if (!this.hideProgress && Roo.MessageBox) {
16533                     Roo.MessageBox.hide();
16534                 }
16535                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16536                 
16537                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16538                 
16539                 // THE END...
16540                 return false;   
16541             }
16542             
16543             var m = mods.shift();
16544             
16545             
16546             Roo.debug && Roo.log(m);
16547             // not sure if this is supported any more.. - modules that are are just function
16548             if (typeof(m) == 'function') { 
16549                 m.call(this);
16550                 return progressRun.defer(10, _this);
16551             } 
16552             
16553             
16554             msg = "Building Interface " + (total  - mods.length) + 
16555                     " of " + total + 
16556                     (m.name ? (' - ' + m.name) : '');
16557                         Roo.debug && Roo.log(msg);
16558             if (!_this.hideProgress &&  Roo.MessageBox) { 
16559                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16560             }
16561             
16562          
16563             // is the module disabled?
16564             var disabled = (typeof(m.disabled) == 'function') ?
16565                 m.disabled.call(m.module.disabled) : m.disabled;    
16566             
16567             
16568             if (disabled) {
16569                 return progressRun(); // we do not update the display!
16570             }
16571             
16572             // now build 
16573             
16574                         
16575                         
16576             m.render();
16577             // it's 10 on top level, and 1 on others??? why...
16578             return progressRun.defer(10, _this);
16579              
16580         }
16581         progressRun.defer(1, _this);
16582      
16583         
16584         
16585     },
16586         
16587         
16588         /**
16589          * Event Object.
16590          *
16591          *
16592          */
16593         event: false, 
16594     /**
16595          * wrapper for event.on - aliased later..  
16596          * Typically use to register a event handler for register:
16597          *
16598          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16599          *
16600          */
16601     on : false
16602    
16603     
16604     
16605 });
16606
16607 Roo.XComponent.event = new Roo.util.Observable({
16608                 events : { 
16609                         /**
16610                          * @event register
16611                          * Fires when an Component is registered,
16612                          * set the disable property on the Component to stop registration.
16613                          * @param {Roo.XComponent} c the component being registerd.
16614                          * 
16615                          */
16616                         'register' : true,
16617             /**
16618                          * @event beforebuild
16619                          * Fires before each Component is built
16620                          * can be used to apply permissions.
16621                          * @param {Roo.XComponent} c the component being registerd.
16622                          * 
16623                          */
16624                         'beforebuild' : true,
16625                         /**
16626                          * @event buildcomplete
16627                          * Fires on the top level element when all elements have been built
16628                          * @param {Roo.XComponent} the top level component.
16629                          */
16630                         'buildcomplete' : true
16631                         
16632                 }
16633 });
16634
16635 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16636  //
16637  /**
16638  * marked - a markdown parser
16639  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16640  * https://github.com/chjj/marked
16641  */
16642
16643
16644 /**
16645  *
16646  * Roo.Markdown - is a very crude wrapper around marked..
16647  *
16648  * usage:
16649  * 
16650  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16651  * 
16652  * Note: move the sample code to the bottom of this
16653  * file before uncommenting it.
16654  *
16655  */
16656
16657 Roo.Markdown = {};
16658 Roo.Markdown.toHtml = function(text) {
16659     
16660     var c = new Roo.Markdown.marked.setOptions({
16661             renderer: new Roo.Markdown.marked.Renderer(),
16662             gfm: true,
16663             tables: true,
16664             breaks: false,
16665             pedantic: false,
16666             sanitize: false,
16667             smartLists: true,
16668             smartypants: false
16669           });
16670     // A FEW HACKS!!?
16671     
16672     text = text.replace(/\\\n/g,' ');
16673     return Roo.Markdown.marked(text);
16674 };
16675 //
16676 // converter
16677 //
16678 // Wraps all "globals" so that the only thing
16679 // exposed is makeHtml().
16680 //
16681 (function() {
16682     
16683     /**
16684      * Block-Level Grammar
16685      */
16686     
16687     var block = {
16688       newline: /^\n+/,
16689       code: /^( {4}[^\n]+\n*)+/,
16690       fences: noop,
16691       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16692       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16693       nptable: noop,
16694       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16695       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16696       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16697       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16698       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16699       table: noop,
16700       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16701       text: /^[^\n]+/
16702     };
16703     
16704     block.bullet = /(?:[*+-]|\d+\.)/;
16705     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16706     block.item = replace(block.item, 'gm')
16707       (/bull/g, block.bullet)
16708       ();
16709     
16710     block.list = replace(block.list)
16711       (/bull/g, block.bullet)
16712       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16713       ('def', '\\n+(?=' + block.def.source + ')')
16714       ();
16715     
16716     block.blockquote = replace(block.blockquote)
16717       ('def', block.def)
16718       ();
16719     
16720     block._tag = '(?!(?:'
16721       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16722       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16723       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16724     
16725     block.html = replace(block.html)
16726       ('comment', /<!--[\s\S]*?-->/)
16727       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16728       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16729       (/tag/g, block._tag)
16730       ();
16731     
16732     block.paragraph = replace(block.paragraph)
16733       ('hr', block.hr)
16734       ('heading', block.heading)
16735       ('lheading', block.lheading)
16736       ('blockquote', block.blockquote)
16737       ('tag', '<' + block._tag)
16738       ('def', block.def)
16739       ();
16740     
16741     /**
16742      * Normal Block Grammar
16743      */
16744     
16745     block.normal = merge({}, block);
16746     
16747     /**
16748      * GFM Block Grammar
16749      */
16750     
16751     block.gfm = merge({}, block.normal, {
16752       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16753       paragraph: /^/,
16754       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16755     });
16756     
16757     block.gfm.paragraph = replace(block.paragraph)
16758       ('(?!', '(?!'
16759         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16760         + block.list.source.replace('\\1', '\\3') + '|')
16761       ();
16762     
16763     /**
16764      * GFM + Tables Block Grammar
16765      */
16766     
16767     block.tables = merge({}, block.gfm, {
16768       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16769       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16770     });
16771     
16772     /**
16773      * Block Lexer
16774      */
16775     
16776     function Lexer(options) {
16777       this.tokens = [];
16778       this.tokens.links = {};
16779       this.options = options || marked.defaults;
16780       this.rules = block.normal;
16781     
16782       if (this.options.gfm) {
16783         if (this.options.tables) {
16784           this.rules = block.tables;
16785         } else {
16786           this.rules = block.gfm;
16787         }
16788       }
16789     }
16790     
16791     /**
16792      * Expose Block Rules
16793      */
16794     
16795     Lexer.rules = block;
16796     
16797     /**
16798      * Static Lex Method
16799      */
16800     
16801     Lexer.lex = function(src, options) {
16802       var lexer = new Lexer(options);
16803       return lexer.lex(src);
16804     };
16805     
16806     /**
16807      * Preprocessing
16808      */
16809     
16810     Lexer.prototype.lex = function(src) {
16811       src = src
16812         .replace(/\r\n|\r/g, '\n')
16813         .replace(/\t/g, '    ')
16814         .replace(/\u00a0/g, ' ')
16815         .replace(/\u2424/g, '\n');
16816     
16817       return this.token(src, true);
16818     };
16819     
16820     /**
16821      * Lexing
16822      */
16823     
16824     Lexer.prototype.token = function(src, top, bq) {
16825       var src = src.replace(/^ +$/gm, '')
16826         , next
16827         , loose
16828         , cap
16829         , bull
16830         , b
16831         , item
16832         , space
16833         , i
16834         , l;
16835     
16836       while (src) {
16837         // newline
16838         if (cap = this.rules.newline.exec(src)) {
16839           src = src.substring(cap[0].length);
16840           if (cap[0].length > 1) {
16841             this.tokens.push({
16842               type: 'space'
16843             });
16844           }
16845         }
16846     
16847         // code
16848         if (cap = this.rules.code.exec(src)) {
16849           src = src.substring(cap[0].length);
16850           cap = cap[0].replace(/^ {4}/gm, '');
16851           this.tokens.push({
16852             type: 'code',
16853             text: !this.options.pedantic
16854               ? cap.replace(/\n+$/, '')
16855               : cap
16856           });
16857           continue;
16858         }
16859     
16860         // fences (gfm)
16861         if (cap = this.rules.fences.exec(src)) {
16862           src = src.substring(cap[0].length);
16863           this.tokens.push({
16864             type: 'code',
16865             lang: cap[2],
16866             text: cap[3] || ''
16867           });
16868           continue;
16869         }
16870     
16871         // heading
16872         if (cap = this.rules.heading.exec(src)) {
16873           src = src.substring(cap[0].length);
16874           this.tokens.push({
16875             type: 'heading',
16876             depth: cap[1].length,
16877             text: cap[2]
16878           });
16879           continue;
16880         }
16881     
16882         // table no leading pipe (gfm)
16883         if (top && (cap = this.rules.nptable.exec(src))) {
16884           src = src.substring(cap[0].length);
16885     
16886           item = {
16887             type: 'table',
16888             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
16889             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
16890             cells: cap[3].replace(/\n$/, '').split('\n')
16891           };
16892     
16893           for (i = 0; i < item.align.length; i++) {
16894             if (/^ *-+: *$/.test(item.align[i])) {
16895               item.align[i] = 'right';
16896             } else if (/^ *:-+: *$/.test(item.align[i])) {
16897               item.align[i] = 'center';
16898             } else if (/^ *:-+ *$/.test(item.align[i])) {
16899               item.align[i] = 'left';
16900             } else {
16901               item.align[i] = null;
16902             }
16903           }
16904     
16905           for (i = 0; i < item.cells.length; i++) {
16906             item.cells[i] = item.cells[i].split(/ *\| */);
16907           }
16908     
16909           this.tokens.push(item);
16910     
16911           continue;
16912         }
16913     
16914         // lheading
16915         if (cap = this.rules.lheading.exec(src)) {
16916           src = src.substring(cap[0].length);
16917           this.tokens.push({
16918             type: 'heading',
16919             depth: cap[2] === '=' ? 1 : 2,
16920             text: cap[1]
16921           });
16922           continue;
16923         }
16924     
16925         // hr
16926         if (cap = this.rules.hr.exec(src)) {
16927           src = src.substring(cap[0].length);
16928           this.tokens.push({
16929             type: 'hr'
16930           });
16931           continue;
16932         }
16933     
16934         // blockquote
16935         if (cap = this.rules.blockquote.exec(src)) {
16936           src = src.substring(cap[0].length);
16937     
16938           this.tokens.push({
16939             type: 'blockquote_start'
16940           });
16941     
16942           cap = cap[0].replace(/^ *> ?/gm, '');
16943     
16944           // Pass `top` to keep the current
16945           // "toplevel" state. This is exactly
16946           // how markdown.pl works.
16947           this.token(cap, top, true);
16948     
16949           this.tokens.push({
16950             type: 'blockquote_end'
16951           });
16952     
16953           continue;
16954         }
16955     
16956         // list
16957         if (cap = this.rules.list.exec(src)) {
16958           src = src.substring(cap[0].length);
16959           bull = cap[2];
16960     
16961           this.tokens.push({
16962             type: 'list_start',
16963             ordered: bull.length > 1
16964           });
16965     
16966           // Get each top-level item.
16967           cap = cap[0].match(this.rules.item);
16968     
16969           next = false;
16970           l = cap.length;
16971           i = 0;
16972     
16973           for (; i < l; i++) {
16974             item = cap[i];
16975     
16976             // Remove the list item's bullet
16977             // so it is seen as the next token.
16978             space = item.length;
16979             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
16980     
16981             // Outdent whatever the
16982             // list item contains. Hacky.
16983             if (~item.indexOf('\n ')) {
16984               space -= item.length;
16985               item = !this.options.pedantic
16986                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
16987                 : item.replace(/^ {1,4}/gm, '');
16988             }
16989     
16990             // Determine whether the next list item belongs here.
16991             // Backpedal if it does not belong in this list.
16992             if (this.options.smartLists && i !== l - 1) {
16993               b = block.bullet.exec(cap[i + 1])[0];
16994               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
16995                 src = cap.slice(i + 1).join('\n') + src;
16996                 i = l - 1;
16997               }
16998             }
16999     
17000             // Determine whether item is loose or not.
17001             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17002             // for discount behavior.
17003             loose = next || /\n\n(?!\s*$)/.test(item);
17004             if (i !== l - 1) {
17005               next = item.charAt(item.length - 1) === '\n';
17006               if (!loose) { loose = next; }
17007             }
17008     
17009             this.tokens.push({
17010               type: loose
17011                 ? 'loose_item_start'
17012                 : 'list_item_start'
17013             });
17014     
17015             // Recurse.
17016             this.token(item, false, bq);
17017     
17018             this.tokens.push({
17019               type: 'list_item_end'
17020             });
17021           }
17022     
17023           this.tokens.push({
17024             type: 'list_end'
17025           });
17026     
17027           continue;
17028         }
17029     
17030         // html
17031         if (cap = this.rules.html.exec(src)) {
17032           src = src.substring(cap[0].length);
17033           this.tokens.push({
17034             type: this.options.sanitize
17035               ? 'paragraph'
17036               : 'html',
17037             pre: !this.options.sanitizer
17038               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17039             text: cap[0]
17040           });
17041           continue;
17042         }
17043     
17044         // def
17045         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17046           src = src.substring(cap[0].length);
17047           this.tokens.links[cap[1].toLowerCase()] = {
17048             href: cap[2],
17049             title: cap[3]
17050           };
17051           continue;
17052         }
17053     
17054         // table (gfm)
17055         if (top && (cap = this.rules.table.exec(src))) {
17056           src = src.substring(cap[0].length);
17057     
17058           item = {
17059             type: 'table',
17060             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17061             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17062             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17063           };
17064     
17065           for (i = 0; i < item.align.length; i++) {
17066             if (/^ *-+: *$/.test(item.align[i])) {
17067               item.align[i] = 'right';
17068             } else if (/^ *:-+: *$/.test(item.align[i])) {
17069               item.align[i] = 'center';
17070             } else if (/^ *:-+ *$/.test(item.align[i])) {
17071               item.align[i] = 'left';
17072             } else {
17073               item.align[i] = null;
17074             }
17075           }
17076     
17077           for (i = 0; i < item.cells.length; i++) {
17078             item.cells[i] = item.cells[i]
17079               .replace(/^ *\| *| *\| *$/g, '')
17080               .split(/ *\| */);
17081           }
17082     
17083           this.tokens.push(item);
17084     
17085           continue;
17086         }
17087     
17088         // top-level paragraph
17089         if (top && (cap = this.rules.paragraph.exec(src))) {
17090           src = src.substring(cap[0].length);
17091           this.tokens.push({
17092             type: 'paragraph',
17093             text: cap[1].charAt(cap[1].length - 1) === '\n'
17094               ? cap[1].slice(0, -1)
17095               : cap[1]
17096           });
17097           continue;
17098         }
17099     
17100         // text
17101         if (cap = this.rules.text.exec(src)) {
17102           // Top-level should never reach here.
17103           src = src.substring(cap[0].length);
17104           this.tokens.push({
17105             type: 'text',
17106             text: cap[0]
17107           });
17108           continue;
17109         }
17110     
17111         if (src) {
17112           throw new
17113             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17114         }
17115       }
17116     
17117       return this.tokens;
17118     };
17119     
17120     /**
17121      * Inline-Level Grammar
17122      */
17123     
17124     var inline = {
17125       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17126       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17127       url: noop,
17128       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17129       link: /^!?\[(inside)\]\(href\)/,
17130       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17131       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17132       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17133       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17134       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17135       br: /^ {2,}\n(?!\s*$)/,
17136       del: noop,
17137       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17138     };
17139     
17140     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17141     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17142     
17143     inline.link = replace(inline.link)
17144       ('inside', inline._inside)
17145       ('href', inline._href)
17146       ();
17147     
17148     inline.reflink = replace(inline.reflink)
17149       ('inside', inline._inside)
17150       ();
17151     
17152     /**
17153      * Normal Inline Grammar
17154      */
17155     
17156     inline.normal = merge({}, inline);
17157     
17158     /**
17159      * Pedantic Inline Grammar
17160      */
17161     
17162     inline.pedantic = merge({}, inline.normal, {
17163       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17164       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17165     });
17166     
17167     /**
17168      * GFM Inline Grammar
17169      */
17170     
17171     inline.gfm = merge({}, inline.normal, {
17172       escape: replace(inline.escape)('])', '~|])')(),
17173       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17174       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17175       text: replace(inline.text)
17176         (']|', '~]|')
17177         ('|', '|https?://|')
17178         ()
17179     });
17180     
17181     /**
17182      * GFM + Line Breaks Inline Grammar
17183      */
17184     
17185     inline.breaks = merge({}, inline.gfm, {
17186       br: replace(inline.br)('{2,}', '*')(),
17187       text: replace(inline.gfm.text)('{2,}', '*')()
17188     });
17189     
17190     /**
17191      * Inline Lexer & Compiler
17192      */
17193     
17194     function InlineLexer(links, options) {
17195       this.options = options || marked.defaults;
17196       this.links = links;
17197       this.rules = inline.normal;
17198       this.renderer = this.options.renderer || new Renderer;
17199       this.renderer.options = this.options;
17200     
17201       if (!this.links) {
17202         throw new
17203           Error('Tokens array requires a `links` property.');
17204       }
17205     
17206       if (this.options.gfm) {
17207         if (this.options.breaks) {
17208           this.rules = inline.breaks;
17209         } else {
17210           this.rules = inline.gfm;
17211         }
17212       } else if (this.options.pedantic) {
17213         this.rules = inline.pedantic;
17214       }
17215     }
17216     
17217     /**
17218      * Expose Inline Rules
17219      */
17220     
17221     InlineLexer.rules = inline;
17222     
17223     /**
17224      * Static Lexing/Compiling Method
17225      */
17226     
17227     InlineLexer.output = function(src, links, options) {
17228       var inline = new InlineLexer(links, options);
17229       return inline.output(src);
17230     };
17231     
17232     /**
17233      * Lexing/Compiling
17234      */
17235     
17236     InlineLexer.prototype.output = function(src) {
17237       var out = ''
17238         , link
17239         , text
17240         , href
17241         , cap;
17242     
17243       while (src) {
17244         // escape
17245         if (cap = this.rules.escape.exec(src)) {
17246           src = src.substring(cap[0].length);
17247           out += cap[1];
17248           continue;
17249         }
17250     
17251         // autolink
17252         if (cap = this.rules.autolink.exec(src)) {
17253           src = src.substring(cap[0].length);
17254           if (cap[2] === '@') {
17255             text = cap[1].charAt(6) === ':'
17256               ? this.mangle(cap[1].substring(7))
17257               : this.mangle(cap[1]);
17258             href = this.mangle('mailto:') + text;
17259           } else {
17260             text = escape(cap[1]);
17261             href = text;
17262           }
17263           out += this.renderer.link(href, null, text);
17264           continue;
17265         }
17266     
17267         // url (gfm)
17268         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17269           src = src.substring(cap[0].length);
17270           text = escape(cap[1]);
17271           href = text;
17272           out += this.renderer.link(href, null, text);
17273           continue;
17274         }
17275     
17276         // tag
17277         if (cap = this.rules.tag.exec(src)) {
17278           if (!this.inLink && /^<a /i.test(cap[0])) {
17279             this.inLink = true;
17280           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17281             this.inLink = false;
17282           }
17283           src = src.substring(cap[0].length);
17284           out += this.options.sanitize
17285             ? this.options.sanitizer
17286               ? this.options.sanitizer(cap[0])
17287               : escape(cap[0])
17288             : cap[0];
17289           continue;
17290         }
17291     
17292         // link
17293         if (cap = this.rules.link.exec(src)) {
17294           src = src.substring(cap[0].length);
17295           this.inLink = true;
17296           out += this.outputLink(cap, {
17297             href: cap[2],
17298             title: cap[3]
17299           });
17300           this.inLink = false;
17301           continue;
17302         }
17303     
17304         // reflink, nolink
17305         if ((cap = this.rules.reflink.exec(src))
17306             || (cap = this.rules.nolink.exec(src))) {
17307           src = src.substring(cap[0].length);
17308           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17309           link = this.links[link.toLowerCase()];
17310           if (!link || !link.href) {
17311             out += cap[0].charAt(0);
17312             src = cap[0].substring(1) + src;
17313             continue;
17314           }
17315           this.inLink = true;
17316           out += this.outputLink(cap, link);
17317           this.inLink = false;
17318           continue;
17319         }
17320     
17321         // strong
17322         if (cap = this.rules.strong.exec(src)) {
17323           src = src.substring(cap[0].length);
17324           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17325           continue;
17326         }
17327     
17328         // em
17329         if (cap = this.rules.em.exec(src)) {
17330           src = src.substring(cap[0].length);
17331           out += this.renderer.em(this.output(cap[2] || cap[1]));
17332           continue;
17333         }
17334     
17335         // code
17336         if (cap = this.rules.code.exec(src)) {
17337           src = src.substring(cap[0].length);
17338           out += this.renderer.codespan(escape(cap[2], true));
17339           continue;
17340         }
17341     
17342         // br
17343         if (cap = this.rules.br.exec(src)) {
17344           src = src.substring(cap[0].length);
17345           out += this.renderer.br();
17346           continue;
17347         }
17348     
17349         // del (gfm)
17350         if (cap = this.rules.del.exec(src)) {
17351           src = src.substring(cap[0].length);
17352           out += this.renderer.del(this.output(cap[1]));
17353           continue;
17354         }
17355     
17356         // text
17357         if (cap = this.rules.text.exec(src)) {
17358           src = src.substring(cap[0].length);
17359           out += this.renderer.text(escape(this.smartypants(cap[0])));
17360           continue;
17361         }
17362     
17363         if (src) {
17364           throw new
17365             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17366         }
17367       }
17368     
17369       return out;
17370     };
17371     
17372     /**
17373      * Compile Link
17374      */
17375     
17376     InlineLexer.prototype.outputLink = function(cap, link) {
17377       var href = escape(link.href)
17378         , title = link.title ? escape(link.title) : null;
17379     
17380       return cap[0].charAt(0) !== '!'
17381         ? this.renderer.link(href, title, this.output(cap[1]))
17382         : this.renderer.image(href, title, escape(cap[1]));
17383     };
17384     
17385     /**
17386      * Smartypants Transformations
17387      */
17388     
17389     InlineLexer.prototype.smartypants = function(text) {
17390       if (!this.options.smartypants)  { return text; }
17391       return text
17392         // em-dashes
17393         .replace(/---/g, '\u2014')
17394         // en-dashes
17395         .replace(/--/g, '\u2013')
17396         // opening singles
17397         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17398         // closing singles & apostrophes
17399         .replace(/'/g, '\u2019')
17400         // opening doubles
17401         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17402         // closing doubles
17403         .replace(/"/g, '\u201d')
17404         // ellipses
17405         .replace(/\.{3}/g, '\u2026');
17406     };
17407     
17408     /**
17409      * Mangle Links
17410      */
17411     
17412     InlineLexer.prototype.mangle = function(text) {
17413       if (!this.options.mangle) { return text; }
17414       var out = ''
17415         , l = text.length
17416         , i = 0
17417         , ch;
17418     
17419       for (; i < l; i++) {
17420         ch = text.charCodeAt(i);
17421         if (Math.random() > 0.5) {
17422           ch = 'x' + ch.toString(16);
17423         }
17424         out += '&#' + ch + ';';
17425       }
17426     
17427       return out;
17428     };
17429     
17430     /**
17431      * Renderer
17432      */
17433     
17434     function Renderer(options) {
17435       this.options = options || {};
17436     }
17437     
17438     Renderer.prototype.code = function(code, lang, escaped) {
17439       if (this.options.highlight) {
17440         var out = this.options.highlight(code, lang);
17441         if (out != null && out !== code) {
17442           escaped = true;
17443           code = out;
17444         }
17445       } else {
17446             // hack!!! - it's already escapeD?
17447             escaped = true;
17448       }
17449     
17450       if (!lang) {
17451         return '<pre><code>'
17452           + (escaped ? code : escape(code, true))
17453           + '\n</code></pre>';
17454       }
17455     
17456       return '<pre><code class="'
17457         + this.options.langPrefix
17458         + escape(lang, true)
17459         + '">'
17460         + (escaped ? code : escape(code, true))
17461         + '\n</code></pre>\n';
17462     };
17463     
17464     Renderer.prototype.blockquote = function(quote) {
17465       return '<blockquote>\n' + quote + '</blockquote>\n';
17466     };
17467     
17468     Renderer.prototype.html = function(html) {
17469       return html;
17470     };
17471     
17472     Renderer.prototype.heading = function(text, level, raw) {
17473       return '<h'
17474         + level
17475         + ' id="'
17476         + this.options.headerPrefix
17477         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17478         + '">'
17479         + text
17480         + '</h'
17481         + level
17482         + '>\n';
17483     };
17484     
17485     Renderer.prototype.hr = function() {
17486       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17487     };
17488     
17489     Renderer.prototype.list = function(body, ordered) {
17490       var type = ordered ? 'ol' : 'ul';
17491       return '<' + type + '>\n' + body + '</' + type + '>\n';
17492     };
17493     
17494     Renderer.prototype.listitem = function(text) {
17495       return '<li>' + text + '</li>\n';
17496     };
17497     
17498     Renderer.prototype.paragraph = function(text) {
17499       return '<p>' + text + '</p>\n';
17500     };
17501     
17502     Renderer.prototype.table = function(header, body) {
17503       return '<table class="table table-striped">\n'
17504         + '<thead>\n'
17505         + header
17506         + '</thead>\n'
17507         + '<tbody>\n'
17508         + body
17509         + '</tbody>\n'
17510         + '</table>\n';
17511     };
17512     
17513     Renderer.prototype.tablerow = function(content) {
17514       return '<tr>\n' + content + '</tr>\n';
17515     };
17516     
17517     Renderer.prototype.tablecell = function(content, flags) {
17518       var type = flags.header ? 'th' : 'td';
17519       var tag = flags.align
17520         ? '<' + type + ' style="text-align:' + flags.align + '">'
17521         : '<' + type + '>';
17522       return tag + content + '</' + type + '>\n';
17523     };
17524     
17525     // span level renderer
17526     Renderer.prototype.strong = function(text) {
17527       return '<strong>' + text + '</strong>';
17528     };
17529     
17530     Renderer.prototype.em = function(text) {
17531       return '<em>' + text + '</em>';
17532     };
17533     
17534     Renderer.prototype.codespan = function(text) {
17535       return '<code>' + text + '</code>';
17536     };
17537     
17538     Renderer.prototype.br = function() {
17539       return this.options.xhtml ? '<br/>' : '<br>';
17540     };
17541     
17542     Renderer.prototype.del = function(text) {
17543       return '<del>' + text + '</del>';
17544     };
17545     
17546     Renderer.prototype.link = function(href, title, text) {
17547       if (this.options.sanitize) {
17548         try {
17549           var prot = decodeURIComponent(unescape(href))
17550             .replace(/[^\w:]/g, '')
17551             .toLowerCase();
17552         } catch (e) {
17553           return '';
17554         }
17555         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17556           return '';
17557         }
17558       }
17559       var out = '<a href="' + href + '"';
17560       if (title) {
17561         out += ' title="' + title + '"';
17562       }
17563       out += '>' + text + '</a>';
17564       return out;
17565     };
17566     
17567     Renderer.prototype.image = function(href, title, text) {
17568       var out = '<img src="' + href + '" alt="' + text + '"';
17569       if (title) {
17570         out += ' title="' + title + '"';
17571       }
17572       out += this.options.xhtml ? '/>' : '>';
17573       return out;
17574     };
17575     
17576     Renderer.prototype.text = function(text) {
17577       return text;
17578     };
17579     
17580     /**
17581      * Parsing & Compiling
17582      */
17583     
17584     function Parser(options) {
17585       this.tokens = [];
17586       this.token = null;
17587       this.options = options || marked.defaults;
17588       this.options.renderer = this.options.renderer || new Renderer;
17589       this.renderer = this.options.renderer;
17590       this.renderer.options = this.options;
17591     }
17592     
17593     /**
17594      * Static Parse Method
17595      */
17596     
17597     Parser.parse = function(src, options, renderer) {
17598       var parser = new Parser(options, renderer);
17599       return parser.parse(src);
17600     };
17601     
17602     /**
17603      * Parse Loop
17604      */
17605     
17606     Parser.prototype.parse = function(src) {
17607       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17608       this.tokens = src.reverse();
17609     
17610       var out = '';
17611       while (this.next()) {
17612         out += this.tok();
17613       }
17614     
17615       return out;
17616     };
17617     
17618     /**
17619      * Next Token
17620      */
17621     
17622     Parser.prototype.next = function() {
17623       return this.token = this.tokens.pop();
17624     };
17625     
17626     /**
17627      * Preview Next Token
17628      */
17629     
17630     Parser.prototype.peek = function() {
17631       return this.tokens[this.tokens.length - 1] || 0;
17632     };
17633     
17634     /**
17635      * Parse Text Tokens
17636      */
17637     
17638     Parser.prototype.parseText = function() {
17639       var body = this.token.text;
17640     
17641       while (this.peek().type === 'text') {
17642         body += '\n' + this.next().text;
17643       }
17644     
17645       return this.inline.output(body);
17646     };
17647     
17648     /**
17649      * Parse Current Token
17650      */
17651     
17652     Parser.prototype.tok = function() {
17653       switch (this.token.type) {
17654         case 'space': {
17655           return '';
17656         }
17657         case 'hr': {
17658           return this.renderer.hr();
17659         }
17660         case 'heading': {
17661           return this.renderer.heading(
17662             this.inline.output(this.token.text),
17663             this.token.depth,
17664             this.token.text);
17665         }
17666         case 'code': {
17667           return this.renderer.code(this.token.text,
17668             this.token.lang,
17669             this.token.escaped);
17670         }
17671         case 'table': {
17672           var header = ''
17673             , body = ''
17674             , i
17675             , row
17676             , cell
17677             , flags
17678             , j;
17679     
17680           // header
17681           cell = '';
17682           for (i = 0; i < this.token.header.length; i++) {
17683             flags = { header: true, align: this.token.align[i] };
17684             cell += this.renderer.tablecell(
17685               this.inline.output(this.token.header[i]),
17686               { header: true, align: this.token.align[i] }
17687             );
17688           }
17689           header += this.renderer.tablerow(cell);
17690     
17691           for (i = 0; i < this.token.cells.length; i++) {
17692             row = this.token.cells[i];
17693     
17694             cell = '';
17695             for (j = 0; j < row.length; j++) {
17696               cell += this.renderer.tablecell(
17697                 this.inline.output(row[j]),
17698                 { header: false, align: this.token.align[j] }
17699               );
17700             }
17701     
17702             body += this.renderer.tablerow(cell);
17703           }
17704           return this.renderer.table(header, body);
17705         }
17706         case 'blockquote_start': {
17707           var body = '';
17708     
17709           while (this.next().type !== 'blockquote_end') {
17710             body += this.tok();
17711           }
17712     
17713           return this.renderer.blockquote(body);
17714         }
17715         case 'list_start': {
17716           var body = ''
17717             , ordered = this.token.ordered;
17718     
17719           while (this.next().type !== 'list_end') {
17720             body += this.tok();
17721           }
17722     
17723           return this.renderer.list(body, ordered);
17724         }
17725         case 'list_item_start': {
17726           var body = '';
17727     
17728           while (this.next().type !== 'list_item_end') {
17729             body += this.token.type === 'text'
17730               ? this.parseText()
17731               : this.tok();
17732           }
17733     
17734           return this.renderer.listitem(body);
17735         }
17736         case 'loose_item_start': {
17737           var body = '';
17738     
17739           while (this.next().type !== 'list_item_end') {
17740             body += this.tok();
17741           }
17742     
17743           return this.renderer.listitem(body);
17744         }
17745         case 'html': {
17746           var html = !this.token.pre && !this.options.pedantic
17747             ? this.inline.output(this.token.text)
17748             : this.token.text;
17749           return this.renderer.html(html);
17750         }
17751         case 'paragraph': {
17752           return this.renderer.paragraph(this.inline.output(this.token.text));
17753         }
17754         case 'text': {
17755           return this.renderer.paragraph(this.parseText());
17756         }
17757       }
17758     };
17759     
17760     /**
17761      * Helpers
17762      */
17763     
17764     function escape(html, encode) {
17765       return html
17766         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17767         .replace(/</g, '&lt;')
17768         .replace(/>/g, '&gt;')
17769         .replace(/"/g, '&quot;')
17770         .replace(/'/g, '&#39;');
17771     }
17772     
17773     function unescape(html) {
17774         // explicitly match decimal, hex, and named HTML entities 
17775       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17776         n = n.toLowerCase();
17777         if (n === 'colon') { return ':'; }
17778         if (n.charAt(0) === '#') {
17779           return n.charAt(1) === 'x'
17780             ? String.fromCharCode(parseInt(n.substring(2), 16))
17781             : String.fromCharCode(+n.substring(1));
17782         }
17783         return '';
17784       });
17785     }
17786     
17787     function replace(regex, opt) {
17788       regex = regex.source;
17789       opt = opt || '';
17790       return function self(name, val) {
17791         if (!name) { return new RegExp(regex, opt); }
17792         val = val.source || val;
17793         val = val.replace(/(^|[^\[])\^/g, '$1');
17794         regex = regex.replace(name, val);
17795         return self;
17796       };
17797     }
17798     
17799     function noop() {}
17800     noop.exec = noop;
17801     
17802     function merge(obj) {
17803       var i = 1
17804         , target
17805         , key;
17806     
17807       for (; i < arguments.length; i++) {
17808         target = arguments[i];
17809         for (key in target) {
17810           if (Object.prototype.hasOwnProperty.call(target, key)) {
17811             obj[key] = target[key];
17812           }
17813         }
17814       }
17815     
17816       return obj;
17817     }
17818     
17819     
17820     /**
17821      * Marked
17822      */
17823     
17824     function marked(src, opt, callback) {
17825       if (callback || typeof opt === 'function') {
17826         if (!callback) {
17827           callback = opt;
17828           opt = null;
17829         }
17830     
17831         opt = merge({}, marked.defaults, opt || {});
17832     
17833         var highlight = opt.highlight
17834           , tokens
17835           , pending
17836           , i = 0;
17837     
17838         try {
17839           tokens = Lexer.lex(src, opt)
17840         } catch (e) {
17841           return callback(e);
17842         }
17843     
17844         pending = tokens.length;
17845     
17846         var done = function(err) {
17847           if (err) {
17848             opt.highlight = highlight;
17849             return callback(err);
17850           }
17851     
17852           var out;
17853     
17854           try {
17855             out = Parser.parse(tokens, opt);
17856           } catch (e) {
17857             err = e;
17858           }
17859     
17860           opt.highlight = highlight;
17861     
17862           return err
17863             ? callback(err)
17864             : callback(null, out);
17865         };
17866     
17867         if (!highlight || highlight.length < 3) {
17868           return done();
17869         }
17870     
17871         delete opt.highlight;
17872     
17873         if (!pending) { return done(); }
17874     
17875         for (; i < tokens.length; i++) {
17876           (function(token) {
17877             if (token.type !== 'code') {
17878               return --pending || done();
17879             }
17880             return highlight(token.text, token.lang, function(err, code) {
17881               if (err) { return done(err); }
17882               if (code == null || code === token.text) {
17883                 return --pending || done();
17884               }
17885               token.text = code;
17886               token.escaped = true;
17887               --pending || done();
17888             });
17889           })(tokens[i]);
17890         }
17891     
17892         return;
17893       }
17894       try {
17895         if (opt) { opt = merge({}, marked.defaults, opt); }
17896         return Parser.parse(Lexer.lex(src, opt), opt);
17897       } catch (e) {
17898         e.message += '\nPlease report this to https://github.com/chjj/marked.';
17899         if ((opt || marked.defaults).silent) {
17900           return '<p>An error occured:</p><pre>'
17901             + escape(e.message + '', true)
17902             + '</pre>';
17903         }
17904         throw e;
17905       }
17906     }
17907     
17908     /**
17909      * Options
17910      */
17911     
17912     marked.options =
17913     marked.setOptions = function(opt) {
17914       merge(marked.defaults, opt);
17915       return marked;
17916     };
17917     
17918     marked.defaults = {
17919       gfm: true,
17920       tables: true,
17921       breaks: false,
17922       pedantic: false,
17923       sanitize: false,
17924       sanitizer: null,
17925       mangle: true,
17926       smartLists: false,
17927       silent: false,
17928       highlight: null,
17929       langPrefix: 'lang-',
17930       smartypants: false,
17931       headerPrefix: '',
17932       renderer: new Renderer,
17933       xhtml: false
17934     };
17935     
17936     /**
17937      * Expose
17938      */
17939     
17940     marked.Parser = Parser;
17941     marked.parser = Parser.parse;
17942     
17943     marked.Renderer = Renderer;
17944     
17945     marked.Lexer = Lexer;
17946     marked.lexer = Lexer.lex;
17947     
17948     marked.InlineLexer = InlineLexer;
17949     marked.inlineLexer = InlineLexer.output;
17950     
17951     marked.parse = marked;
17952     
17953     Roo.Markdown.marked = marked;
17954
17955 })();/*
17956  * Based on:
17957  * Ext JS Library 1.1.1
17958  * Copyright(c) 2006-2007, Ext JS, LLC.
17959  *
17960  * Originally Released Under LGPL - original licence link has changed is not relivant.
17961  *
17962  * Fork - LGPL
17963  * <script type="text/javascript">
17964  */
17965
17966
17967
17968 /*
17969  * These classes are derivatives of the similarly named classes in the YUI Library.
17970  * The original license:
17971  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
17972  * Code licensed under the BSD License:
17973  * http://developer.yahoo.net/yui/license.txt
17974  */
17975
17976 (function() {
17977
17978 var Event=Roo.EventManager;
17979 var Dom=Roo.lib.Dom;
17980
17981 /**
17982  * @class Roo.dd.DragDrop
17983  * @extends Roo.util.Observable
17984  * Defines the interface and base operation of items that that can be
17985  * dragged or can be drop targets.  It was designed to be extended, overriding
17986  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
17987  * Up to three html elements can be associated with a DragDrop instance:
17988  * <ul>
17989  * <li>linked element: the element that is passed into the constructor.
17990  * This is the element which defines the boundaries for interaction with
17991  * other DragDrop objects.</li>
17992  * <li>handle element(s): The drag operation only occurs if the element that
17993  * was clicked matches a handle element.  By default this is the linked
17994  * element, but there are times that you will want only a portion of the
17995  * linked element to initiate the drag operation, and the setHandleElId()
17996  * method provides a way to define this.</li>
17997  * <li>drag element: this represents the element that would be moved along
17998  * with the cursor during a drag operation.  By default, this is the linked
17999  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18000  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18001  * </li>
18002  * </ul>
18003  * This class should not be instantiated until the onload event to ensure that
18004  * the associated elements are available.
18005  * The following would define a DragDrop obj that would interact with any
18006  * other DragDrop obj in the "group1" group:
18007  * <pre>
18008  *  dd = new Roo.dd.DragDrop("div1", "group1");
18009  * </pre>
18010  * Since none of the event handlers have been implemented, nothing would
18011  * actually happen if you were to run the code above.  Normally you would
18012  * override this class or one of the default implementations, but you can
18013  * also override the methods you want on an instance of the class...
18014  * <pre>
18015  *  dd.onDragDrop = function(e, id) {
18016  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18017  *  }
18018  * </pre>
18019  * @constructor
18020  * @param {String} id of the element that is linked to this instance
18021  * @param {String} sGroup the group of related DragDrop objects
18022  * @param {object} config an object containing configurable attributes
18023  *                Valid properties for DragDrop:
18024  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18025  */
18026 Roo.dd.DragDrop = function(id, sGroup, config) {
18027     if (id) {
18028         this.init(id, sGroup, config);
18029     }
18030     
18031 };
18032
18033 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18034
18035     /**
18036      * The id of the element associated with this object.  This is what we
18037      * refer to as the "linked element" because the size and position of
18038      * this element is used to determine when the drag and drop objects have
18039      * interacted.
18040      * @property id
18041      * @type String
18042      */
18043     id: null,
18044
18045     /**
18046      * Configuration attributes passed into the constructor
18047      * @property config
18048      * @type object
18049      */
18050     config: null,
18051
18052     /**
18053      * The id of the element that will be dragged.  By default this is same
18054      * as the linked element , but could be changed to another element. Ex:
18055      * Roo.dd.DDProxy
18056      * @property dragElId
18057      * @type String
18058      * @private
18059      */
18060     dragElId: null,
18061
18062     /**
18063      * the id of the element that initiates the drag operation.  By default
18064      * this is the linked element, but could be changed to be a child of this
18065      * element.  This lets us do things like only starting the drag when the
18066      * header element within the linked html element is clicked.
18067      * @property handleElId
18068      * @type String
18069      * @private
18070      */
18071     handleElId: null,
18072
18073     /**
18074      * An associative array of HTML tags that will be ignored if clicked.
18075      * @property invalidHandleTypes
18076      * @type {string: string}
18077      */
18078     invalidHandleTypes: null,
18079
18080     /**
18081      * An associative array of ids for elements that will be ignored if clicked
18082      * @property invalidHandleIds
18083      * @type {string: string}
18084      */
18085     invalidHandleIds: null,
18086
18087     /**
18088      * An indexted array of css class names for elements that will be ignored
18089      * if clicked.
18090      * @property invalidHandleClasses
18091      * @type string[]
18092      */
18093     invalidHandleClasses: null,
18094
18095     /**
18096      * The linked element's absolute X position at the time the drag was
18097      * started
18098      * @property startPageX
18099      * @type int
18100      * @private
18101      */
18102     startPageX: 0,
18103
18104     /**
18105      * The linked element's absolute X position at the time the drag was
18106      * started
18107      * @property startPageY
18108      * @type int
18109      * @private
18110      */
18111     startPageY: 0,
18112
18113     /**
18114      * The group defines a logical collection of DragDrop objects that are
18115      * related.  Instances only get events when interacting with other
18116      * DragDrop object in the same group.  This lets us define multiple
18117      * groups using a single DragDrop subclass if we want.
18118      * @property groups
18119      * @type {string: string}
18120      */
18121     groups: null,
18122
18123     /**
18124      * Individual drag/drop instances can be locked.  This will prevent
18125      * onmousedown start drag.
18126      * @property locked
18127      * @type boolean
18128      * @private
18129      */
18130     locked: false,
18131
18132     /**
18133      * Lock this instance
18134      * @method lock
18135      */
18136     lock: function() { this.locked = true; },
18137
18138     /**
18139      * Unlock this instace
18140      * @method unlock
18141      */
18142     unlock: function() { this.locked = false; },
18143
18144     /**
18145      * By default, all insances can be a drop target.  This can be disabled by
18146      * setting isTarget to false.
18147      * @method isTarget
18148      * @type boolean
18149      */
18150     isTarget: true,
18151
18152     /**
18153      * The padding configured for this drag and drop object for calculating
18154      * the drop zone intersection with this object.
18155      * @method padding
18156      * @type int[]
18157      */
18158     padding: null,
18159
18160     /**
18161      * Cached reference to the linked element
18162      * @property _domRef
18163      * @private
18164      */
18165     _domRef: null,
18166
18167     /**
18168      * Internal typeof flag
18169      * @property __ygDragDrop
18170      * @private
18171      */
18172     __ygDragDrop: true,
18173
18174     /**
18175      * Set to true when horizontal contraints are applied
18176      * @property constrainX
18177      * @type boolean
18178      * @private
18179      */
18180     constrainX: false,
18181
18182     /**
18183      * Set to true when vertical contraints are applied
18184      * @property constrainY
18185      * @type boolean
18186      * @private
18187      */
18188     constrainY: false,
18189
18190     /**
18191      * The left constraint
18192      * @property minX
18193      * @type int
18194      * @private
18195      */
18196     minX: 0,
18197
18198     /**
18199      * The right constraint
18200      * @property maxX
18201      * @type int
18202      * @private
18203      */
18204     maxX: 0,
18205
18206     /**
18207      * The up constraint
18208      * @property minY
18209      * @type int
18210      * @type int
18211      * @private
18212      */
18213     minY: 0,
18214
18215     /**
18216      * The down constraint
18217      * @property maxY
18218      * @type int
18219      * @private
18220      */
18221     maxY: 0,
18222
18223     /**
18224      * Maintain offsets when we resetconstraints.  Set to true when you want
18225      * the position of the element relative to its parent to stay the same
18226      * when the page changes
18227      *
18228      * @property maintainOffset
18229      * @type boolean
18230      */
18231     maintainOffset: false,
18232
18233     /**
18234      * Array of pixel locations the element will snap to if we specified a
18235      * horizontal graduation/interval.  This array is generated automatically
18236      * when you define a tick interval.
18237      * @property xTicks
18238      * @type int[]
18239      */
18240     xTicks: null,
18241
18242     /**
18243      * Array of pixel locations the element will snap to if we specified a
18244      * vertical graduation/interval.  This array is generated automatically
18245      * when you define a tick interval.
18246      * @property yTicks
18247      * @type int[]
18248      */
18249     yTicks: null,
18250
18251     /**
18252      * By default the drag and drop instance will only respond to the primary
18253      * button click (left button for a right-handed mouse).  Set to true to
18254      * allow drag and drop to start with any mouse click that is propogated
18255      * by the browser
18256      * @property primaryButtonOnly
18257      * @type boolean
18258      */
18259     primaryButtonOnly: true,
18260
18261     /**
18262      * The availabe property is false until the linked dom element is accessible.
18263      * @property available
18264      * @type boolean
18265      */
18266     available: false,
18267
18268     /**
18269      * By default, drags can only be initiated if the mousedown occurs in the
18270      * region the linked element is.  This is done in part to work around a
18271      * bug in some browsers that mis-report the mousedown if the previous
18272      * mouseup happened outside of the window.  This property is set to true
18273      * if outer handles are defined.
18274      *
18275      * @property hasOuterHandles
18276      * @type boolean
18277      * @default false
18278      */
18279     hasOuterHandles: false,
18280
18281     /**
18282      * Code that executes immediately before the startDrag event
18283      * @method b4StartDrag
18284      * @private
18285      */
18286     b4StartDrag: function(x, y) { },
18287
18288     /**
18289      * Abstract method called after a drag/drop object is clicked
18290      * and the drag or mousedown time thresholds have beeen met.
18291      * @method startDrag
18292      * @param {int} X click location
18293      * @param {int} Y click location
18294      */
18295     startDrag: function(x, y) { /* override this */ },
18296
18297     /**
18298      * Code that executes immediately before the onDrag event
18299      * @method b4Drag
18300      * @private
18301      */
18302     b4Drag: function(e) { },
18303
18304     /**
18305      * Abstract method called during the onMouseMove event while dragging an
18306      * object.
18307      * @method onDrag
18308      * @param {Event} e the mousemove event
18309      */
18310     onDrag: function(e) { /* override this */ },
18311
18312     /**
18313      * Abstract method called when this element fist begins hovering over
18314      * another DragDrop obj
18315      * @method onDragEnter
18316      * @param {Event} e the mousemove event
18317      * @param {String|DragDrop[]} id In POINT mode, the element
18318      * id this is hovering over.  In INTERSECT mode, an array of one or more
18319      * dragdrop items being hovered over.
18320      */
18321     onDragEnter: function(e, id) { /* override this */ },
18322
18323     /**
18324      * Code that executes immediately before the onDragOver event
18325      * @method b4DragOver
18326      * @private
18327      */
18328     b4DragOver: function(e) { },
18329
18330     /**
18331      * Abstract method called when this element is hovering over another
18332      * DragDrop obj
18333      * @method onDragOver
18334      * @param {Event} e the mousemove event
18335      * @param {String|DragDrop[]} id In POINT mode, the element
18336      * id this is hovering over.  In INTERSECT mode, an array of dd items
18337      * being hovered over.
18338      */
18339     onDragOver: function(e, id) { /* override this */ },
18340
18341     /**
18342      * Code that executes immediately before the onDragOut event
18343      * @method b4DragOut
18344      * @private
18345      */
18346     b4DragOut: function(e) { },
18347
18348     /**
18349      * Abstract method called when we are no longer hovering over an element
18350      * @method onDragOut
18351      * @param {Event} e the mousemove event
18352      * @param {String|DragDrop[]} id In POINT mode, the element
18353      * id this was hovering over.  In INTERSECT mode, an array of dd items
18354      * that the mouse is no longer over.
18355      */
18356     onDragOut: function(e, id) { /* override this */ },
18357
18358     /**
18359      * Code that executes immediately before the onDragDrop event
18360      * @method b4DragDrop
18361      * @private
18362      */
18363     b4DragDrop: function(e) { },
18364
18365     /**
18366      * Abstract method called when this item is dropped on another DragDrop
18367      * obj
18368      * @method onDragDrop
18369      * @param {Event} e the mouseup event
18370      * @param {String|DragDrop[]} id In POINT mode, the element
18371      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18372      * was dropped on.
18373      */
18374     onDragDrop: function(e, id) { /* override this */ },
18375
18376     /**
18377      * Abstract method called when this item is dropped on an area with no
18378      * drop target
18379      * @method onInvalidDrop
18380      * @param {Event} e the mouseup event
18381      */
18382     onInvalidDrop: function(e) { /* override this */ },
18383
18384     /**
18385      * Code that executes immediately before the endDrag event
18386      * @method b4EndDrag
18387      * @private
18388      */
18389     b4EndDrag: function(e) { },
18390
18391     /**
18392      * Fired when we are done dragging the object
18393      * @method endDrag
18394      * @param {Event} e the mouseup event
18395      */
18396     endDrag: function(e) { /* override this */ },
18397
18398     /**
18399      * Code executed immediately before the onMouseDown event
18400      * @method b4MouseDown
18401      * @param {Event} e the mousedown event
18402      * @private
18403      */
18404     b4MouseDown: function(e) {  },
18405
18406     /**
18407      * Event handler that fires when a drag/drop obj gets a mousedown
18408      * @method onMouseDown
18409      * @param {Event} e the mousedown event
18410      */
18411     onMouseDown: function(e) { /* override this */ },
18412
18413     /**
18414      * Event handler that fires when a drag/drop obj gets a mouseup
18415      * @method onMouseUp
18416      * @param {Event} e the mouseup event
18417      */
18418     onMouseUp: function(e) { /* override this */ },
18419
18420     /**
18421      * Override the onAvailable method to do what is needed after the initial
18422      * position was determined.
18423      * @method onAvailable
18424      */
18425     onAvailable: function () {
18426     },
18427
18428     /*
18429      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18430      * @type Object
18431      */
18432     defaultPadding : {left:0, right:0, top:0, bottom:0},
18433
18434     /*
18435      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18436  *
18437  * Usage:
18438  <pre><code>
18439  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18440                 { dragElId: "existingProxyDiv" });
18441  dd.startDrag = function(){
18442      this.constrainTo("parent-id");
18443  };
18444  </code></pre>
18445  * Or you can initalize it using the {@link Roo.Element} object:
18446  <pre><code>
18447  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18448      startDrag : function(){
18449          this.constrainTo("parent-id");
18450      }
18451  });
18452  </code></pre>
18453      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18454      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18455      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18456      * an object containing the sides to pad. For example: {right:10, bottom:10}
18457      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18458      */
18459     constrainTo : function(constrainTo, pad, inContent){
18460         if(typeof pad == "number"){
18461             pad = {left: pad, right:pad, top:pad, bottom:pad};
18462         }
18463         pad = pad || this.defaultPadding;
18464         var b = Roo.get(this.getEl()).getBox();
18465         var ce = Roo.get(constrainTo);
18466         var s = ce.getScroll();
18467         var c, cd = ce.dom;
18468         if(cd == document.body){
18469             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18470         }else{
18471             xy = ce.getXY();
18472             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18473         }
18474
18475
18476         var topSpace = b.y - c.y;
18477         var leftSpace = b.x - c.x;
18478
18479         this.resetConstraints();
18480         this.setXConstraint(leftSpace - (pad.left||0), // left
18481                 c.width - leftSpace - b.width - (pad.right||0) //right
18482         );
18483         this.setYConstraint(topSpace - (pad.top||0), //top
18484                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18485         );
18486     },
18487
18488     /**
18489      * Returns a reference to the linked element
18490      * @method getEl
18491      * @return {HTMLElement} the html element
18492      */
18493     getEl: function() {
18494         if (!this._domRef) {
18495             this._domRef = Roo.getDom(this.id);
18496         }
18497
18498         return this._domRef;
18499     },
18500
18501     /**
18502      * Returns a reference to the actual element to drag.  By default this is
18503      * the same as the html element, but it can be assigned to another
18504      * element. An example of this can be found in Roo.dd.DDProxy
18505      * @method getDragEl
18506      * @return {HTMLElement} the html element
18507      */
18508     getDragEl: function() {
18509         return Roo.getDom(this.dragElId);
18510     },
18511
18512     /**
18513      * Sets up the DragDrop object.  Must be called in the constructor of any
18514      * Roo.dd.DragDrop subclass
18515      * @method init
18516      * @param id the id of the linked element
18517      * @param {String} sGroup the group of related items
18518      * @param {object} config configuration attributes
18519      */
18520     init: function(id, sGroup, config) {
18521         this.initTarget(id, sGroup, config);
18522         if (!Roo.isTouch) {
18523             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18524         }
18525         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18526         // Event.on(this.id, "selectstart", Event.preventDefault);
18527     },
18528
18529     /**
18530      * Initializes Targeting functionality only... the object does not
18531      * get a mousedown handler.
18532      * @method initTarget
18533      * @param id the id of the linked element
18534      * @param {String} sGroup the group of related items
18535      * @param {object} config configuration attributes
18536      */
18537     initTarget: function(id, sGroup, config) {
18538
18539         // configuration attributes
18540         this.config = config || {};
18541
18542         // create a local reference to the drag and drop manager
18543         this.DDM = Roo.dd.DDM;
18544         // initialize the groups array
18545         this.groups = {};
18546
18547         // assume that we have an element reference instead of an id if the
18548         // parameter is not a string
18549         if (typeof id !== "string") {
18550             id = Roo.id(id);
18551         }
18552
18553         // set the id
18554         this.id = id;
18555
18556         // add to an interaction group
18557         this.addToGroup((sGroup) ? sGroup : "default");
18558
18559         // We don't want to register this as the handle with the manager
18560         // so we just set the id rather than calling the setter.
18561         this.handleElId = id;
18562
18563         // the linked element is the element that gets dragged by default
18564         this.setDragElId(id);
18565
18566         // by default, clicked anchors will not start drag operations.
18567         this.invalidHandleTypes = { A: "A" };
18568         this.invalidHandleIds = {};
18569         this.invalidHandleClasses = [];
18570
18571         this.applyConfig();
18572
18573         this.handleOnAvailable();
18574     },
18575
18576     /**
18577      * Applies the configuration parameters that were passed into the constructor.
18578      * This is supposed to happen at each level through the inheritance chain.  So
18579      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18580      * DragDrop in order to get all of the parameters that are available in
18581      * each object.
18582      * @method applyConfig
18583      */
18584     applyConfig: function() {
18585
18586         // configurable properties:
18587         //    padding, isTarget, maintainOffset, primaryButtonOnly
18588         this.padding           = this.config.padding || [0, 0, 0, 0];
18589         this.isTarget          = (this.config.isTarget !== false);
18590         this.maintainOffset    = (this.config.maintainOffset);
18591         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18592
18593     },
18594
18595     /**
18596      * Executed when the linked element is available
18597      * @method handleOnAvailable
18598      * @private
18599      */
18600     handleOnAvailable: function() {
18601         this.available = true;
18602         this.resetConstraints();
18603         this.onAvailable();
18604     },
18605
18606      /**
18607      * Configures the padding for the target zone in px.  Effectively expands
18608      * (or reduces) the virtual object size for targeting calculations.
18609      * Supports css-style shorthand; if only one parameter is passed, all sides
18610      * will have that padding, and if only two are passed, the top and bottom
18611      * will have the first param, the left and right the second.
18612      * @method setPadding
18613      * @param {int} iTop    Top pad
18614      * @param {int} iRight  Right pad
18615      * @param {int} iBot    Bot pad
18616      * @param {int} iLeft   Left pad
18617      */
18618     setPadding: function(iTop, iRight, iBot, iLeft) {
18619         // this.padding = [iLeft, iRight, iTop, iBot];
18620         if (!iRight && 0 !== iRight) {
18621             this.padding = [iTop, iTop, iTop, iTop];
18622         } else if (!iBot && 0 !== iBot) {
18623             this.padding = [iTop, iRight, iTop, iRight];
18624         } else {
18625             this.padding = [iTop, iRight, iBot, iLeft];
18626         }
18627     },
18628
18629     /**
18630      * Stores the initial placement of the linked element.
18631      * @method setInitialPosition
18632      * @param {int} diffX   the X offset, default 0
18633      * @param {int} diffY   the Y offset, default 0
18634      */
18635     setInitPosition: function(diffX, diffY) {
18636         var el = this.getEl();
18637
18638         if (!this.DDM.verifyEl(el)) {
18639             return;
18640         }
18641
18642         var dx = diffX || 0;
18643         var dy = diffY || 0;
18644
18645         var p = Dom.getXY( el );
18646
18647         this.initPageX = p[0] - dx;
18648         this.initPageY = p[1] - dy;
18649
18650         this.lastPageX = p[0];
18651         this.lastPageY = p[1];
18652
18653
18654         this.setStartPosition(p);
18655     },
18656
18657     /**
18658      * Sets the start position of the element.  This is set when the obj
18659      * is initialized, the reset when a drag is started.
18660      * @method setStartPosition
18661      * @param pos current position (from previous lookup)
18662      * @private
18663      */
18664     setStartPosition: function(pos) {
18665         var p = pos || Dom.getXY( this.getEl() );
18666         this.deltaSetXY = null;
18667
18668         this.startPageX = p[0];
18669         this.startPageY = p[1];
18670     },
18671
18672     /**
18673      * Add this instance to a group of related drag/drop objects.  All
18674      * instances belong to at least one group, and can belong to as many
18675      * groups as needed.
18676      * @method addToGroup
18677      * @param sGroup {string} the name of the group
18678      */
18679     addToGroup: function(sGroup) {
18680         this.groups[sGroup] = true;
18681         this.DDM.regDragDrop(this, sGroup);
18682     },
18683
18684     /**
18685      * Remove's this instance from the supplied interaction group
18686      * @method removeFromGroup
18687      * @param {string}  sGroup  The group to drop
18688      */
18689     removeFromGroup: function(sGroup) {
18690         if (this.groups[sGroup]) {
18691             delete this.groups[sGroup];
18692         }
18693
18694         this.DDM.removeDDFromGroup(this, sGroup);
18695     },
18696
18697     /**
18698      * Allows you to specify that an element other than the linked element
18699      * will be moved with the cursor during a drag
18700      * @method setDragElId
18701      * @param id {string} the id of the element that will be used to initiate the drag
18702      */
18703     setDragElId: function(id) {
18704         this.dragElId = id;
18705     },
18706
18707     /**
18708      * Allows you to specify a child of the linked element that should be
18709      * used to initiate the drag operation.  An example of this would be if
18710      * you have a content div with text and links.  Clicking anywhere in the
18711      * content area would normally start the drag operation.  Use this method
18712      * to specify that an element inside of the content div is the element
18713      * that starts the drag operation.
18714      * @method setHandleElId
18715      * @param id {string} the id of the element that will be used to
18716      * initiate the drag.
18717      */
18718     setHandleElId: function(id) {
18719         if (typeof id !== "string") {
18720             id = Roo.id(id);
18721         }
18722         this.handleElId = id;
18723         this.DDM.regHandle(this.id, id);
18724     },
18725
18726     /**
18727      * Allows you to set an element outside of the linked element as a drag
18728      * handle
18729      * @method setOuterHandleElId
18730      * @param id the id of the element that will be used to initiate the drag
18731      */
18732     setOuterHandleElId: function(id) {
18733         if (typeof id !== "string") {
18734             id = Roo.id(id);
18735         }
18736         Event.on(id, "mousedown",
18737                 this.handleMouseDown, this);
18738         this.setHandleElId(id);
18739
18740         this.hasOuterHandles = true;
18741     },
18742
18743     /**
18744      * Remove all drag and drop hooks for this element
18745      * @method unreg
18746      */
18747     unreg: function() {
18748         Event.un(this.id, "mousedown",
18749                 this.handleMouseDown);
18750         Event.un(this.id, "touchstart",
18751                 this.handleMouseDown);
18752         this._domRef = null;
18753         this.DDM._remove(this);
18754     },
18755
18756     destroy : function(){
18757         this.unreg();
18758     },
18759
18760     /**
18761      * Returns true if this instance is locked, or the drag drop mgr is locked
18762      * (meaning that all drag/drop is disabled on the page.)
18763      * @method isLocked
18764      * @return {boolean} true if this obj or all drag/drop is locked, else
18765      * false
18766      */
18767     isLocked: function() {
18768         return (this.DDM.isLocked() || this.locked);
18769     },
18770
18771     /**
18772      * Fired when this object is clicked
18773      * @method handleMouseDown
18774      * @param {Event} e
18775      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18776      * @private
18777      */
18778     handleMouseDown: function(e, oDD){
18779      
18780         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18781             //Roo.log('not touch/ button !=0');
18782             return;
18783         }
18784         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18785             return; // double touch..
18786         }
18787         
18788
18789         if (this.isLocked()) {
18790             //Roo.log('locked');
18791             return;
18792         }
18793
18794         this.DDM.refreshCache(this.groups);
18795 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18796         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18797         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18798             //Roo.log('no outer handes or not over target');
18799                 // do nothing.
18800         } else {
18801 //            Roo.log('check validator');
18802             if (this.clickValidator(e)) {
18803 //                Roo.log('validate success');
18804                 // set the initial element position
18805                 this.setStartPosition();
18806
18807
18808                 this.b4MouseDown(e);
18809                 this.onMouseDown(e);
18810
18811                 this.DDM.handleMouseDown(e, this);
18812
18813                 this.DDM.stopEvent(e);
18814             } else {
18815
18816
18817             }
18818         }
18819     },
18820
18821     clickValidator: function(e) {
18822         var target = e.getTarget();
18823         return ( this.isValidHandleChild(target) &&
18824                     (this.id == this.handleElId ||
18825                         this.DDM.handleWasClicked(target, this.id)) );
18826     },
18827
18828     /**
18829      * Allows you to specify a tag name that should not start a drag operation
18830      * when clicked.  This is designed to facilitate embedding links within a
18831      * drag handle that do something other than start the drag.
18832      * @method addInvalidHandleType
18833      * @param {string} tagName the type of element to exclude
18834      */
18835     addInvalidHandleType: function(tagName) {
18836         var type = tagName.toUpperCase();
18837         this.invalidHandleTypes[type] = type;
18838     },
18839
18840     /**
18841      * Lets you to specify an element id for a child of a drag handle
18842      * that should not initiate a drag
18843      * @method addInvalidHandleId
18844      * @param {string} id the element id of the element you wish to ignore
18845      */
18846     addInvalidHandleId: function(id) {
18847         if (typeof id !== "string") {
18848             id = Roo.id(id);
18849         }
18850         this.invalidHandleIds[id] = id;
18851     },
18852
18853     /**
18854      * Lets you specify a css class of elements that will not initiate a drag
18855      * @method addInvalidHandleClass
18856      * @param {string} cssClass the class of the elements you wish to ignore
18857      */
18858     addInvalidHandleClass: function(cssClass) {
18859         this.invalidHandleClasses.push(cssClass);
18860     },
18861
18862     /**
18863      * Unsets an excluded tag name set by addInvalidHandleType
18864      * @method removeInvalidHandleType
18865      * @param {string} tagName the type of element to unexclude
18866      */
18867     removeInvalidHandleType: function(tagName) {
18868         var type = tagName.toUpperCase();
18869         // this.invalidHandleTypes[type] = null;
18870         delete this.invalidHandleTypes[type];
18871     },
18872
18873     /**
18874      * Unsets an invalid handle id
18875      * @method removeInvalidHandleId
18876      * @param {string} id the id of the element to re-enable
18877      */
18878     removeInvalidHandleId: function(id) {
18879         if (typeof id !== "string") {
18880             id = Roo.id(id);
18881         }
18882         delete this.invalidHandleIds[id];
18883     },
18884
18885     /**
18886      * Unsets an invalid css class
18887      * @method removeInvalidHandleClass
18888      * @param {string} cssClass the class of the element(s) you wish to
18889      * re-enable
18890      */
18891     removeInvalidHandleClass: function(cssClass) {
18892         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
18893             if (this.invalidHandleClasses[i] == cssClass) {
18894                 delete this.invalidHandleClasses[i];
18895             }
18896         }
18897     },
18898
18899     /**
18900      * Checks the tag exclusion list to see if this click should be ignored
18901      * @method isValidHandleChild
18902      * @param {HTMLElement} node the HTMLElement to evaluate
18903      * @return {boolean} true if this is a valid tag type, false if not
18904      */
18905     isValidHandleChild: function(node) {
18906
18907         var valid = true;
18908         // var n = (node.nodeName == "#text") ? node.parentNode : node;
18909         var nodeName;
18910         try {
18911             nodeName = node.nodeName.toUpperCase();
18912         } catch(e) {
18913             nodeName = node.nodeName;
18914         }
18915         valid = valid && !this.invalidHandleTypes[nodeName];
18916         valid = valid && !this.invalidHandleIds[node.id];
18917
18918         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
18919             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
18920         }
18921
18922
18923         return valid;
18924
18925     },
18926
18927     /**
18928      * Create the array of horizontal tick marks if an interval was specified
18929      * in setXConstraint().
18930      * @method setXTicks
18931      * @private
18932      */
18933     setXTicks: function(iStartX, iTickSize) {
18934         this.xTicks = [];
18935         this.xTickSize = iTickSize;
18936
18937         var tickMap = {};
18938
18939         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
18940             if (!tickMap[i]) {
18941                 this.xTicks[this.xTicks.length] = i;
18942                 tickMap[i] = true;
18943             }
18944         }
18945
18946         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
18947             if (!tickMap[i]) {
18948                 this.xTicks[this.xTicks.length] = i;
18949                 tickMap[i] = true;
18950             }
18951         }
18952
18953         this.xTicks.sort(this.DDM.numericSort) ;
18954     },
18955
18956     /**
18957      * Create the array of vertical tick marks if an interval was specified in
18958      * setYConstraint().
18959      * @method setYTicks
18960      * @private
18961      */
18962     setYTicks: function(iStartY, iTickSize) {
18963         this.yTicks = [];
18964         this.yTickSize = iTickSize;
18965
18966         var tickMap = {};
18967
18968         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
18969             if (!tickMap[i]) {
18970                 this.yTicks[this.yTicks.length] = i;
18971                 tickMap[i] = true;
18972             }
18973         }
18974
18975         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
18976             if (!tickMap[i]) {
18977                 this.yTicks[this.yTicks.length] = i;
18978                 tickMap[i] = true;
18979             }
18980         }
18981
18982         this.yTicks.sort(this.DDM.numericSort) ;
18983     },
18984
18985     /**
18986      * By default, the element can be dragged any place on the screen.  Use
18987      * this method to limit the horizontal travel of the element.  Pass in
18988      * 0,0 for the parameters if you want to lock the drag to the y axis.
18989      * @method setXConstraint
18990      * @param {int} iLeft the number of pixels the element can move to the left
18991      * @param {int} iRight the number of pixels the element can move to the
18992      * right
18993      * @param {int} iTickSize optional parameter for specifying that the
18994      * element
18995      * should move iTickSize pixels at a time.
18996      */
18997     setXConstraint: function(iLeft, iRight, iTickSize) {
18998         this.leftConstraint = iLeft;
18999         this.rightConstraint = iRight;
19000
19001         this.minX = this.initPageX - iLeft;
19002         this.maxX = this.initPageX + iRight;
19003         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19004
19005         this.constrainX = true;
19006     },
19007
19008     /**
19009      * Clears any constraints applied to this instance.  Also clears ticks
19010      * since they can't exist independent of a constraint at this time.
19011      * @method clearConstraints
19012      */
19013     clearConstraints: function() {
19014         this.constrainX = false;
19015         this.constrainY = false;
19016         this.clearTicks();
19017     },
19018
19019     /**
19020      * Clears any tick interval defined for this instance
19021      * @method clearTicks
19022      */
19023     clearTicks: function() {
19024         this.xTicks = null;
19025         this.yTicks = null;
19026         this.xTickSize = 0;
19027         this.yTickSize = 0;
19028     },
19029
19030     /**
19031      * By default, the element can be dragged any place on the screen.  Set
19032      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19033      * parameters if you want to lock the drag to the x axis.
19034      * @method setYConstraint
19035      * @param {int} iUp the number of pixels the element can move up
19036      * @param {int} iDown the number of pixels the element can move down
19037      * @param {int} iTickSize optional parameter for specifying that the
19038      * element should move iTickSize pixels at a time.
19039      */
19040     setYConstraint: function(iUp, iDown, iTickSize) {
19041         this.topConstraint = iUp;
19042         this.bottomConstraint = iDown;
19043
19044         this.minY = this.initPageY - iUp;
19045         this.maxY = this.initPageY + iDown;
19046         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19047
19048         this.constrainY = true;
19049
19050     },
19051
19052     /**
19053      * resetConstraints must be called if you manually reposition a dd element.
19054      * @method resetConstraints
19055      * @param {boolean} maintainOffset
19056      */
19057     resetConstraints: function() {
19058
19059
19060         // Maintain offsets if necessary
19061         if (this.initPageX || this.initPageX === 0) {
19062             // figure out how much this thing has moved
19063             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19064             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19065
19066             this.setInitPosition(dx, dy);
19067
19068         // This is the first time we have detected the element's position
19069         } else {
19070             this.setInitPosition();
19071         }
19072
19073         if (this.constrainX) {
19074             this.setXConstraint( this.leftConstraint,
19075                                  this.rightConstraint,
19076                                  this.xTickSize        );
19077         }
19078
19079         if (this.constrainY) {
19080             this.setYConstraint( this.topConstraint,
19081                                  this.bottomConstraint,
19082                                  this.yTickSize         );
19083         }
19084     },
19085
19086     /**
19087      * Normally the drag element is moved pixel by pixel, but we can specify
19088      * that it move a number of pixels at a time.  This method resolves the
19089      * location when we have it set up like this.
19090      * @method getTick
19091      * @param {int} val where we want to place the object
19092      * @param {int[]} tickArray sorted array of valid points
19093      * @return {int} the closest tick
19094      * @private
19095      */
19096     getTick: function(val, tickArray) {
19097
19098         if (!tickArray) {
19099             // If tick interval is not defined, it is effectively 1 pixel,
19100             // so we return the value passed to us.
19101             return val;
19102         } else if (tickArray[0] >= val) {
19103             // The value is lower than the first tick, so we return the first
19104             // tick.
19105             return tickArray[0];
19106         } else {
19107             for (var i=0, len=tickArray.length; i<len; ++i) {
19108                 var next = i + 1;
19109                 if (tickArray[next] && tickArray[next] >= val) {
19110                     var diff1 = val - tickArray[i];
19111                     var diff2 = tickArray[next] - val;
19112                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19113                 }
19114             }
19115
19116             // The value is larger than the last tick, so we return the last
19117             // tick.
19118             return tickArray[tickArray.length - 1];
19119         }
19120     },
19121
19122     /**
19123      * toString method
19124      * @method toString
19125      * @return {string} string representation of the dd obj
19126      */
19127     toString: function() {
19128         return ("DragDrop " + this.id);
19129     }
19130
19131 });
19132
19133 })();
19134 /*
19135  * Based on:
19136  * Ext JS Library 1.1.1
19137  * Copyright(c) 2006-2007, Ext JS, LLC.
19138  *
19139  * Originally Released Under LGPL - original licence link has changed is not relivant.
19140  *
19141  * Fork - LGPL
19142  * <script type="text/javascript">
19143  */
19144
19145
19146 /**
19147  * The drag and drop utility provides a framework for building drag and drop
19148  * applications.  In addition to enabling drag and drop for specific elements,
19149  * the drag and drop elements are tracked by the manager class, and the
19150  * interactions between the various elements are tracked during the drag and
19151  * the implementing code is notified about these important moments.
19152  */
19153
19154 // Only load the library once.  Rewriting the manager class would orphan
19155 // existing drag and drop instances.
19156 if (!Roo.dd.DragDropMgr) {
19157
19158 /**
19159  * @class Roo.dd.DragDropMgr
19160  * DragDropMgr is a singleton that tracks the element interaction for
19161  * all DragDrop items in the window.  Generally, you will not call
19162  * this class directly, but it does have helper methods that could
19163  * be useful in your DragDrop implementations.
19164  * @singleton
19165  */
19166 Roo.dd.DragDropMgr = function() {
19167
19168     var Event = Roo.EventManager;
19169
19170     return {
19171
19172         /**
19173          * Two dimensional Array of registered DragDrop objects.  The first
19174          * dimension is the DragDrop item group, the second the DragDrop
19175          * object.
19176          * @property ids
19177          * @type {string: string}
19178          * @private
19179          * @static
19180          */
19181         ids: {},
19182
19183         /**
19184          * Array of element ids defined as drag handles.  Used to determine
19185          * if the element that generated the mousedown event is actually the
19186          * handle and not the html element itself.
19187          * @property handleIds
19188          * @type {string: string}
19189          * @private
19190          * @static
19191          */
19192         handleIds: {},
19193
19194         /**
19195          * the DragDrop object that is currently being dragged
19196          * @property dragCurrent
19197          * @type DragDrop
19198          * @private
19199          * @static
19200          **/
19201         dragCurrent: null,
19202
19203         /**
19204          * the DragDrop object(s) that are being hovered over
19205          * @property dragOvers
19206          * @type Array
19207          * @private
19208          * @static
19209          */
19210         dragOvers: {},
19211
19212         /**
19213          * the X distance between the cursor and the object being dragged
19214          * @property deltaX
19215          * @type int
19216          * @private
19217          * @static
19218          */
19219         deltaX: 0,
19220
19221         /**
19222          * the Y distance between the cursor and the object being dragged
19223          * @property deltaY
19224          * @type int
19225          * @private
19226          * @static
19227          */
19228         deltaY: 0,
19229
19230         /**
19231          * Flag to determine if we should prevent the default behavior of the
19232          * events we define. By default this is true, but this can be set to
19233          * false if you need the default behavior (not recommended)
19234          * @property preventDefault
19235          * @type boolean
19236          * @static
19237          */
19238         preventDefault: true,
19239
19240         /**
19241          * Flag to determine if we should stop the propagation of the events
19242          * we generate. This is true by default but you may want to set it to
19243          * false if the html element contains other features that require the
19244          * mouse click.
19245          * @property stopPropagation
19246          * @type boolean
19247          * @static
19248          */
19249         stopPropagation: true,
19250
19251         /**
19252          * Internal flag that is set to true when drag and drop has been
19253          * intialized
19254          * @property initialized
19255          * @private
19256          * @static
19257          */
19258         initalized: false,
19259
19260         /**
19261          * All drag and drop can be disabled.
19262          * @property locked
19263          * @private
19264          * @static
19265          */
19266         locked: false,
19267
19268         /**
19269          * Called the first time an element is registered.
19270          * @method init
19271          * @private
19272          * @static
19273          */
19274         init: function() {
19275             this.initialized = true;
19276         },
19277
19278         /**
19279          * In point mode, drag and drop interaction is defined by the
19280          * location of the cursor during the drag/drop
19281          * @property POINT
19282          * @type int
19283          * @static
19284          */
19285         POINT: 0,
19286
19287         /**
19288          * In intersect mode, drag and drop interactio nis defined by the
19289          * overlap of two or more drag and drop objects.
19290          * @property INTERSECT
19291          * @type int
19292          * @static
19293          */
19294         INTERSECT: 1,
19295
19296         /**
19297          * The current drag and drop mode.  Default: POINT
19298          * @property mode
19299          * @type int
19300          * @static
19301          */
19302         mode: 0,
19303
19304         /**
19305          * Runs method on all drag and drop objects
19306          * @method _execOnAll
19307          * @private
19308          * @static
19309          */
19310         _execOnAll: function(sMethod, args) {
19311             for (var i in this.ids) {
19312                 for (var j in this.ids[i]) {
19313                     var oDD = this.ids[i][j];
19314                     if (! this.isTypeOfDD(oDD)) {
19315                         continue;
19316                     }
19317                     oDD[sMethod].apply(oDD, args);
19318                 }
19319             }
19320         },
19321
19322         /**
19323          * Drag and drop initialization.  Sets up the global event handlers
19324          * @method _onLoad
19325          * @private
19326          * @static
19327          */
19328         _onLoad: function() {
19329
19330             this.init();
19331
19332             if (!Roo.isTouch) {
19333                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19334                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19335             }
19336             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19337             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19338             
19339             Event.on(window,   "unload",    this._onUnload, this, true);
19340             Event.on(window,   "resize",    this._onResize, this, true);
19341             // Event.on(window,   "mouseout",    this._test);
19342
19343         },
19344
19345         /**
19346          * Reset constraints on all drag and drop objs
19347          * @method _onResize
19348          * @private
19349          * @static
19350          */
19351         _onResize: function(e) {
19352             this._execOnAll("resetConstraints", []);
19353         },
19354
19355         /**
19356          * Lock all drag and drop functionality
19357          * @method lock
19358          * @static
19359          */
19360         lock: function() { this.locked = true; },
19361
19362         /**
19363          * Unlock all drag and drop functionality
19364          * @method unlock
19365          * @static
19366          */
19367         unlock: function() { this.locked = false; },
19368
19369         /**
19370          * Is drag and drop locked?
19371          * @method isLocked
19372          * @return {boolean} True if drag and drop is locked, false otherwise.
19373          * @static
19374          */
19375         isLocked: function() { return this.locked; },
19376
19377         /**
19378          * Location cache that is set for all drag drop objects when a drag is
19379          * initiated, cleared when the drag is finished.
19380          * @property locationCache
19381          * @private
19382          * @static
19383          */
19384         locationCache: {},
19385
19386         /**
19387          * Set useCache to false if you want to force object the lookup of each
19388          * drag and drop linked element constantly during a drag.
19389          * @property useCache
19390          * @type boolean
19391          * @static
19392          */
19393         useCache: true,
19394
19395         /**
19396          * The number of pixels that the mouse needs to move after the
19397          * mousedown before the drag is initiated.  Default=3;
19398          * @property clickPixelThresh
19399          * @type int
19400          * @static
19401          */
19402         clickPixelThresh: 3,
19403
19404         /**
19405          * The number of milliseconds after the mousedown event to initiate the
19406          * drag if we don't get a mouseup event. Default=1000
19407          * @property clickTimeThresh
19408          * @type int
19409          * @static
19410          */
19411         clickTimeThresh: 350,
19412
19413         /**
19414          * Flag that indicates that either the drag pixel threshold or the
19415          * mousdown time threshold has been met
19416          * @property dragThreshMet
19417          * @type boolean
19418          * @private
19419          * @static
19420          */
19421         dragThreshMet: false,
19422
19423         /**
19424          * Timeout used for the click time threshold
19425          * @property clickTimeout
19426          * @type Object
19427          * @private
19428          * @static
19429          */
19430         clickTimeout: null,
19431
19432         /**
19433          * The X position of the mousedown event stored for later use when a
19434          * drag threshold is met.
19435          * @property startX
19436          * @type int
19437          * @private
19438          * @static
19439          */
19440         startX: 0,
19441
19442         /**
19443          * The Y position of the mousedown event stored for later use when a
19444          * drag threshold is met.
19445          * @property startY
19446          * @type int
19447          * @private
19448          * @static
19449          */
19450         startY: 0,
19451
19452         /**
19453          * Each DragDrop instance must be registered with the DragDropMgr.
19454          * This is executed in DragDrop.init()
19455          * @method regDragDrop
19456          * @param {DragDrop} oDD the DragDrop object to register
19457          * @param {String} sGroup the name of the group this element belongs to
19458          * @static
19459          */
19460         regDragDrop: function(oDD, sGroup) {
19461             if (!this.initialized) { this.init(); }
19462
19463             if (!this.ids[sGroup]) {
19464                 this.ids[sGroup] = {};
19465             }
19466             this.ids[sGroup][oDD.id] = oDD;
19467         },
19468
19469         /**
19470          * Removes the supplied dd instance from the supplied group. Executed
19471          * by DragDrop.removeFromGroup, so don't call this function directly.
19472          * @method removeDDFromGroup
19473          * @private
19474          * @static
19475          */
19476         removeDDFromGroup: function(oDD, sGroup) {
19477             if (!this.ids[sGroup]) {
19478                 this.ids[sGroup] = {};
19479             }
19480
19481             var obj = this.ids[sGroup];
19482             if (obj && obj[oDD.id]) {
19483                 delete obj[oDD.id];
19484             }
19485         },
19486
19487         /**
19488          * Unregisters a drag and drop item.  This is executed in
19489          * DragDrop.unreg, use that method instead of calling this directly.
19490          * @method _remove
19491          * @private
19492          * @static
19493          */
19494         _remove: function(oDD) {
19495             for (var g in oDD.groups) {
19496                 if (g && this.ids[g][oDD.id]) {
19497                     delete this.ids[g][oDD.id];
19498                 }
19499             }
19500             delete this.handleIds[oDD.id];
19501         },
19502
19503         /**
19504          * Each DragDrop handle element must be registered.  This is done
19505          * automatically when executing DragDrop.setHandleElId()
19506          * @method regHandle
19507          * @param {String} sDDId the DragDrop id this element is a handle for
19508          * @param {String} sHandleId the id of the element that is the drag
19509          * handle
19510          * @static
19511          */
19512         regHandle: function(sDDId, sHandleId) {
19513             if (!this.handleIds[sDDId]) {
19514                 this.handleIds[sDDId] = {};
19515             }
19516             this.handleIds[sDDId][sHandleId] = sHandleId;
19517         },
19518
19519         /**
19520          * Utility function to determine if a given element has been
19521          * registered as a drag drop item.
19522          * @method isDragDrop
19523          * @param {String} id the element id to check
19524          * @return {boolean} true if this element is a DragDrop item,
19525          * false otherwise
19526          * @static
19527          */
19528         isDragDrop: function(id) {
19529             return ( this.getDDById(id) ) ? true : false;
19530         },
19531
19532         /**
19533          * Returns the drag and drop instances that are in all groups the
19534          * passed in instance belongs to.
19535          * @method getRelated
19536          * @param {DragDrop} p_oDD the obj to get related data for
19537          * @param {boolean} bTargetsOnly if true, only return targetable objs
19538          * @return {DragDrop[]} the related instances
19539          * @static
19540          */
19541         getRelated: function(p_oDD, bTargetsOnly) {
19542             var oDDs = [];
19543             for (var i in p_oDD.groups) {
19544                 for (j in this.ids[i]) {
19545                     var dd = this.ids[i][j];
19546                     if (! this.isTypeOfDD(dd)) {
19547                         continue;
19548                     }
19549                     if (!bTargetsOnly || dd.isTarget) {
19550                         oDDs[oDDs.length] = dd;
19551                     }
19552                 }
19553             }
19554
19555             return oDDs;
19556         },
19557
19558         /**
19559          * Returns true if the specified dd target is a legal target for
19560          * the specifice drag obj
19561          * @method isLegalTarget
19562          * @param {DragDrop} the drag obj
19563          * @param {DragDrop} the target
19564          * @return {boolean} true if the target is a legal target for the
19565          * dd obj
19566          * @static
19567          */
19568         isLegalTarget: function (oDD, oTargetDD) {
19569             var targets = this.getRelated(oDD, true);
19570             for (var i=0, len=targets.length;i<len;++i) {
19571                 if (targets[i].id == oTargetDD.id) {
19572                     return true;
19573                 }
19574             }
19575
19576             return false;
19577         },
19578
19579         /**
19580          * My goal is to be able to transparently determine if an object is
19581          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19582          * returns "object", oDD.constructor.toString() always returns
19583          * "DragDrop" and not the name of the subclass.  So for now it just
19584          * evaluates a well-known variable in DragDrop.
19585          * @method isTypeOfDD
19586          * @param {Object} the object to evaluate
19587          * @return {boolean} true if typeof oDD = DragDrop
19588          * @static
19589          */
19590         isTypeOfDD: function (oDD) {
19591             return (oDD && oDD.__ygDragDrop);
19592         },
19593
19594         /**
19595          * Utility function to determine if a given element has been
19596          * registered as a drag drop handle for the given Drag Drop object.
19597          * @method isHandle
19598          * @param {String} id the element id to check
19599          * @return {boolean} true if this element is a DragDrop handle, false
19600          * otherwise
19601          * @static
19602          */
19603         isHandle: function(sDDId, sHandleId) {
19604             return ( this.handleIds[sDDId] &&
19605                             this.handleIds[sDDId][sHandleId] );
19606         },
19607
19608         /**
19609          * Returns the DragDrop instance for a given id
19610          * @method getDDById
19611          * @param {String} id the id of the DragDrop object
19612          * @return {DragDrop} the drag drop object, null if it is not found
19613          * @static
19614          */
19615         getDDById: function(id) {
19616             for (var i in this.ids) {
19617                 if (this.ids[i][id]) {
19618                     return this.ids[i][id];
19619                 }
19620             }
19621             return null;
19622         },
19623
19624         /**
19625          * Fired after a registered DragDrop object gets the mousedown event.
19626          * Sets up the events required to track the object being dragged
19627          * @method handleMouseDown
19628          * @param {Event} e the event
19629          * @param oDD the DragDrop object being dragged
19630          * @private
19631          * @static
19632          */
19633         handleMouseDown: function(e, oDD) {
19634             if(Roo.QuickTips){
19635                 Roo.QuickTips.disable();
19636             }
19637             this.currentTarget = e.getTarget();
19638
19639             this.dragCurrent = oDD;
19640
19641             var el = oDD.getEl();
19642
19643             // track start position
19644             this.startX = e.getPageX();
19645             this.startY = e.getPageY();
19646
19647             this.deltaX = this.startX - el.offsetLeft;
19648             this.deltaY = this.startY - el.offsetTop;
19649
19650             this.dragThreshMet = false;
19651
19652             this.clickTimeout = setTimeout(
19653                     function() {
19654                         var DDM = Roo.dd.DDM;
19655                         DDM.startDrag(DDM.startX, DDM.startY);
19656                     },
19657                     this.clickTimeThresh );
19658         },
19659
19660         /**
19661          * Fired when either the drag pixel threshol or the mousedown hold
19662          * time threshold has been met.
19663          * @method startDrag
19664          * @param x {int} the X position of the original mousedown
19665          * @param y {int} the Y position of the original mousedown
19666          * @static
19667          */
19668         startDrag: function(x, y) {
19669             clearTimeout(this.clickTimeout);
19670             if (this.dragCurrent) {
19671                 this.dragCurrent.b4StartDrag(x, y);
19672                 this.dragCurrent.startDrag(x, y);
19673             }
19674             this.dragThreshMet = true;
19675         },
19676
19677         /**
19678          * Internal function to handle the mouseup event.  Will be invoked
19679          * from the context of the document.
19680          * @method handleMouseUp
19681          * @param {Event} e the event
19682          * @private
19683          * @static
19684          */
19685         handleMouseUp: function(e) {
19686
19687             if(Roo.QuickTips){
19688                 Roo.QuickTips.enable();
19689             }
19690             if (! this.dragCurrent) {
19691                 return;
19692             }
19693
19694             clearTimeout(this.clickTimeout);
19695
19696             if (this.dragThreshMet) {
19697                 this.fireEvents(e, true);
19698             } else {
19699             }
19700
19701             this.stopDrag(e);
19702
19703             this.stopEvent(e);
19704         },
19705
19706         /**
19707          * Utility to stop event propagation and event default, if these
19708          * features are turned on.
19709          * @method stopEvent
19710          * @param {Event} e the event as returned by this.getEvent()
19711          * @static
19712          */
19713         stopEvent: function(e){
19714             if(this.stopPropagation) {
19715                 e.stopPropagation();
19716             }
19717
19718             if (this.preventDefault) {
19719                 e.preventDefault();
19720             }
19721         },
19722
19723         /**
19724          * Internal function to clean up event handlers after the drag
19725          * operation is complete
19726          * @method stopDrag
19727          * @param {Event} e the event
19728          * @private
19729          * @static
19730          */
19731         stopDrag: function(e) {
19732             // Fire the drag end event for the item that was dragged
19733             if (this.dragCurrent) {
19734                 if (this.dragThreshMet) {
19735                     this.dragCurrent.b4EndDrag(e);
19736                     this.dragCurrent.endDrag(e);
19737                 }
19738
19739                 this.dragCurrent.onMouseUp(e);
19740             }
19741
19742             this.dragCurrent = null;
19743             this.dragOvers = {};
19744         },
19745
19746         /**
19747          * Internal function to handle the mousemove event.  Will be invoked
19748          * from the context of the html element.
19749          *
19750          * @TODO figure out what we can do about mouse events lost when the
19751          * user drags objects beyond the window boundary.  Currently we can
19752          * detect this in internet explorer by verifying that the mouse is
19753          * down during the mousemove event.  Firefox doesn't give us the
19754          * button state on the mousemove event.
19755          * @method handleMouseMove
19756          * @param {Event} e the event
19757          * @private
19758          * @static
19759          */
19760         handleMouseMove: function(e) {
19761             if (! this.dragCurrent) {
19762                 return true;
19763             }
19764
19765             // var button = e.which || e.button;
19766
19767             // check for IE mouseup outside of page boundary
19768             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19769                 this.stopEvent(e);
19770                 return this.handleMouseUp(e);
19771             }
19772
19773             if (!this.dragThreshMet) {
19774                 var diffX = Math.abs(this.startX - e.getPageX());
19775                 var diffY = Math.abs(this.startY - e.getPageY());
19776                 if (diffX > this.clickPixelThresh ||
19777                             diffY > this.clickPixelThresh) {
19778                     this.startDrag(this.startX, this.startY);
19779                 }
19780             }
19781
19782             if (this.dragThreshMet) {
19783                 this.dragCurrent.b4Drag(e);
19784                 this.dragCurrent.onDrag(e);
19785                 if(!this.dragCurrent.moveOnly){
19786                     this.fireEvents(e, false);
19787                 }
19788             }
19789
19790             this.stopEvent(e);
19791
19792             return true;
19793         },
19794
19795         /**
19796          * Iterates over all of the DragDrop elements to find ones we are
19797          * hovering over or dropping on
19798          * @method fireEvents
19799          * @param {Event} e the event
19800          * @param {boolean} isDrop is this a drop op or a mouseover op?
19801          * @private
19802          * @static
19803          */
19804         fireEvents: function(e, isDrop) {
19805             var dc = this.dragCurrent;
19806
19807             // If the user did the mouse up outside of the window, we could
19808             // get here even though we have ended the drag.
19809             if (!dc || dc.isLocked()) {
19810                 return;
19811             }
19812
19813             var pt = e.getPoint();
19814
19815             // cache the previous dragOver array
19816             var oldOvers = [];
19817
19818             var outEvts   = [];
19819             var overEvts  = [];
19820             var dropEvts  = [];
19821             var enterEvts = [];
19822
19823             // Check to see if the object(s) we were hovering over is no longer
19824             // being hovered over so we can fire the onDragOut event
19825             for (var i in this.dragOvers) {
19826
19827                 var ddo = this.dragOvers[i];
19828
19829                 if (! this.isTypeOfDD(ddo)) {
19830                     continue;
19831                 }
19832
19833                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19834                     outEvts.push( ddo );
19835                 }
19836
19837                 oldOvers[i] = true;
19838                 delete this.dragOvers[i];
19839             }
19840
19841             for (var sGroup in dc.groups) {
19842
19843                 if ("string" != typeof sGroup) {
19844                     continue;
19845                 }
19846
19847                 for (i in this.ids[sGroup]) {
19848                     var oDD = this.ids[sGroup][i];
19849                     if (! this.isTypeOfDD(oDD)) {
19850                         continue;
19851                     }
19852
19853                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
19854                         if (this.isOverTarget(pt, oDD, this.mode)) {
19855                             // look for drop interactions
19856                             if (isDrop) {
19857                                 dropEvts.push( oDD );
19858                             // look for drag enter and drag over interactions
19859                             } else {
19860
19861                                 // initial drag over: dragEnter fires
19862                                 if (!oldOvers[oDD.id]) {
19863                                     enterEvts.push( oDD );
19864                                 // subsequent drag overs: dragOver fires
19865                                 } else {
19866                                     overEvts.push( oDD );
19867                                 }
19868
19869                                 this.dragOvers[oDD.id] = oDD;
19870                             }
19871                         }
19872                     }
19873                 }
19874             }
19875
19876             if (this.mode) {
19877                 if (outEvts.length) {
19878                     dc.b4DragOut(e, outEvts);
19879                     dc.onDragOut(e, outEvts);
19880                 }
19881
19882                 if (enterEvts.length) {
19883                     dc.onDragEnter(e, enterEvts);
19884                 }
19885
19886                 if (overEvts.length) {
19887                     dc.b4DragOver(e, overEvts);
19888                     dc.onDragOver(e, overEvts);
19889                 }
19890
19891                 if (dropEvts.length) {
19892                     dc.b4DragDrop(e, dropEvts);
19893                     dc.onDragDrop(e, dropEvts);
19894                 }
19895
19896             } else {
19897                 // fire dragout events
19898                 var len = 0;
19899                 for (i=0, len=outEvts.length; i<len; ++i) {
19900                     dc.b4DragOut(e, outEvts[i].id);
19901                     dc.onDragOut(e, outEvts[i].id);
19902                 }
19903
19904                 // fire enter events
19905                 for (i=0,len=enterEvts.length; i<len; ++i) {
19906                     // dc.b4DragEnter(e, oDD.id);
19907                     dc.onDragEnter(e, enterEvts[i].id);
19908                 }
19909
19910                 // fire over events
19911                 for (i=0,len=overEvts.length; i<len; ++i) {
19912                     dc.b4DragOver(e, overEvts[i].id);
19913                     dc.onDragOver(e, overEvts[i].id);
19914                 }
19915
19916                 // fire drop events
19917                 for (i=0, len=dropEvts.length; i<len; ++i) {
19918                     dc.b4DragDrop(e, dropEvts[i].id);
19919                     dc.onDragDrop(e, dropEvts[i].id);
19920                 }
19921
19922             }
19923
19924             // notify about a drop that did not find a target
19925             if (isDrop && !dropEvts.length) {
19926                 dc.onInvalidDrop(e);
19927             }
19928
19929         },
19930
19931         /**
19932          * Helper function for getting the best match from the list of drag
19933          * and drop objects returned by the drag and drop events when we are
19934          * in INTERSECT mode.  It returns either the first object that the
19935          * cursor is over, or the object that has the greatest overlap with
19936          * the dragged element.
19937          * @method getBestMatch
19938          * @param  {DragDrop[]} dds The array of drag and drop objects
19939          * targeted
19940          * @return {DragDrop}       The best single match
19941          * @static
19942          */
19943         getBestMatch: function(dds) {
19944             var winner = null;
19945             // Return null if the input is not what we expect
19946             //if (!dds || !dds.length || dds.length == 0) {
19947                // winner = null;
19948             // If there is only one item, it wins
19949             //} else if (dds.length == 1) {
19950
19951             var len = dds.length;
19952
19953             if (len == 1) {
19954                 winner = dds[0];
19955             } else {
19956                 // Loop through the targeted items
19957                 for (var i=0; i<len; ++i) {
19958                     var dd = dds[i];
19959                     // If the cursor is over the object, it wins.  If the
19960                     // cursor is over multiple matches, the first one we come
19961                     // to wins.
19962                     if (dd.cursorIsOver) {
19963                         winner = dd;
19964                         break;
19965                     // Otherwise the object with the most overlap wins
19966                     } else {
19967                         if (!winner ||
19968                             winner.overlap.getArea() < dd.overlap.getArea()) {
19969                             winner = dd;
19970                         }
19971                     }
19972                 }
19973             }
19974
19975             return winner;
19976         },
19977
19978         /**
19979          * Refreshes the cache of the top-left and bottom-right points of the
19980          * drag and drop objects in the specified group(s).  This is in the
19981          * format that is stored in the drag and drop instance, so typical
19982          * usage is:
19983          * <code>
19984          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
19985          * </code>
19986          * Alternatively:
19987          * <code>
19988          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
19989          * </code>
19990          * @TODO this really should be an indexed array.  Alternatively this
19991          * method could accept both.
19992          * @method refreshCache
19993          * @param {Object} groups an associative array of groups to refresh
19994          * @static
19995          */
19996         refreshCache: function(groups) {
19997             for (var sGroup in groups) {
19998                 if ("string" != typeof sGroup) {
19999                     continue;
20000                 }
20001                 for (var i in this.ids[sGroup]) {
20002                     var oDD = this.ids[sGroup][i];
20003
20004                     if (this.isTypeOfDD(oDD)) {
20005                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20006                         var loc = this.getLocation(oDD);
20007                         if (loc) {
20008                             this.locationCache[oDD.id] = loc;
20009                         } else {
20010                             delete this.locationCache[oDD.id];
20011                             // this will unregister the drag and drop object if
20012                             // the element is not in a usable state
20013                             // oDD.unreg();
20014                         }
20015                     }
20016                 }
20017             }
20018         },
20019
20020         /**
20021          * This checks to make sure an element exists and is in the DOM.  The
20022          * main purpose is to handle cases where innerHTML is used to remove
20023          * drag and drop objects from the DOM.  IE provides an 'unspecified
20024          * error' when trying to access the offsetParent of such an element
20025          * @method verifyEl
20026          * @param {HTMLElement} el the element to check
20027          * @return {boolean} true if the element looks usable
20028          * @static
20029          */
20030         verifyEl: function(el) {
20031             if (el) {
20032                 var parent;
20033                 if(Roo.isIE){
20034                     try{
20035                         parent = el.offsetParent;
20036                     }catch(e){}
20037                 }else{
20038                     parent = el.offsetParent;
20039                 }
20040                 if (parent) {
20041                     return true;
20042                 }
20043             }
20044
20045             return false;
20046         },
20047
20048         /**
20049          * Returns a Region object containing the drag and drop element's position
20050          * and size, including the padding configured for it
20051          * @method getLocation
20052          * @param {DragDrop} oDD the drag and drop object to get the
20053          *                       location for
20054          * @return {Roo.lib.Region} a Region object representing the total area
20055          *                             the element occupies, including any padding
20056          *                             the instance is configured for.
20057          * @static
20058          */
20059         getLocation: function(oDD) {
20060             if (! this.isTypeOfDD(oDD)) {
20061                 return null;
20062             }
20063
20064             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20065
20066             try {
20067                 pos= Roo.lib.Dom.getXY(el);
20068             } catch (e) { }
20069
20070             if (!pos) {
20071                 return null;
20072             }
20073
20074             x1 = pos[0];
20075             x2 = x1 + el.offsetWidth;
20076             y1 = pos[1];
20077             y2 = y1 + el.offsetHeight;
20078
20079             t = y1 - oDD.padding[0];
20080             r = x2 + oDD.padding[1];
20081             b = y2 + oDD.padding[2];
20082             l = x1 - oDD.padding[3];
20083
20084             return new Roo.lib.Region( t, r, b, l );
20085         },
20086
20087         /**
20088          * Checks the cursor location to see if it over the target
20089          * @method isOverTarget
20090          * @param {Roo.lib.Point} pt The point to evaluate
20091          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20092          * @return {boolean} true if the mouse is over the target
20093          * @private
20094          * @static
20095          */
20096         isOverTarget: function(pt, oTarget, intersect) {
20097             // use cache if available
20098             var loc = this.locationCache[oTarget.id];
20099             if (!loc || !this.useCache) {
20100                 loc = this.getLocation(oTarget);
20101                 this.locationCache[oTarget.id] = loc;
20102
20103             }
20104
20105             if (!loc) {
20106                 return false;
20107             }
20108
20109             oTarget.cursorIsOver = loc.contains( pt );
20110
20111             // DragDrop is using this as a sanity check for the initial mousedown
20112             // in this case we are done.  In POINT mode, if the drag obj has no
20113             // contraints, we are also done. Otherwise we need to evaluate the
20114             // location of the target as related to the actual location of the
20115             // dragged element.
20116             var dc = this.dragCurrent;
20117             if (!dc || !dc.getTargetCoord ||
20118                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20119                 return oTarget.cursorIsOver;
20120             }
20121
20122             oTarget.overlap = null;
20123
20124             // Get the current location of the drag element, this is the
20125             // location of the mouse event less the delta that represents
20126             // where the original mousedown happened on the element.  We
20127             // need to consider constraints and ticks as well.
20128             var pos = dc.getTargetCoord(pt.x, pt.y);
20129
20130             var el = dc.getDragEl();
20131             var curRegion = new Roo.lib.Region( pos.y,
20132                                                    pos.x + el.offsetWidth,
20133                                                    pos.y + el.offsetHeight,
20134                                                    pos.x );
20135
20136             var overlap = curRegion.intersect(loc);
20137
20138             if (overlap) {
20139                 oTarget.overlap = overlap;
20140                 return (intersect) ? true : oTarget.cursorIsOver;
20141             } else {
20142                 return false;
20143             }
20144         },
20145
20146         /**
20147          * unload event handler
20148          * @method _onUnload
20149          * @private
20150          * @static
20151          */
20152         _onUnload: function(e, me) {
20153             Roo.dd.DragDropMgr.unregAll();
20154         },
20155
20156         /**
20157          * Cleans up the drag and drop events and objects.
20158          * @method unregAll
20159          * @private
20160          * @static
20161          */
20162         unregAll: function() {
20163
20164             if (this.dragCurrent) {
20165                 this.stopDrag();
20166                 this.dragCurrent = null;
20167             }
20168
20169             this._execOnAll("unreg", []);
20170
20171             for (i in this.elementCache) {
20172                 delete this.elementCache[i];
20173             }
20174
20175             this.elementCache = {};
20176             this.ids = {};
20177         },
20178
20179         /**
20180          * A cache of DOM elements
20181          * @property elementCache
20182          * @private
20183          * @static
20184          */
20185         elementCache: {},
20186
20187         /**
20188          * Get the wrapper for the DOM element specified
20189          * @method getElWrapper
20190          * @param {String} id the id of the element to get
20191          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20192          * @private
20193          * @deprecated This wrapper isn't that useful
20194          * @static
20195          */
20196         getElWrapper: function(id) {
20197             var oWrapper = this.elementCache[id];
20198             if (!oWrapper || !oWrapper.el) {
20199                 oWrapper = this.elementCache[id] =
20200                     new this.ElementWrapper(Roo.getDom(id));
20201             }
20202             return oWrapper;
20203         },
20204
20205         /**
20206          * Returns the actual DOM element
20207          * @method getElement
20208          * @param {String} id the id of the elment to get
20209          * @return {Object} The element
20210          * @deprecated use Roo.getDom instead
20211          * @static
20212          */
20213         getElement: function(id) {
20214             return Roo.getDom(id);
20215         },
20216
20217         /**
20218          * Returns the style property for the DOM element (i.e.,
20219          * document.getElById(id).style)
20220          * @method getCss
20221          * @param {String} id the id of the elment to get
20222          * @return {Object} The style property of the element
20223          * @deprecated use Roo.getDom instead
20224          * @static
20225          */
20226         getCss: function(id) {
20227             var el = Roo.getDom(id);
20228             return (el) ? el.style : null;
20229         },
20230
20231         /**
20232          * Inner class for cached elements
20233          * @class DragDropMgr.ElementWrapper
20234          * @for DragDropMgr
20235          * @private
20236          * @deprecated
20237          */
20238         ElementWrapper: function(el) {
20239                 /**
20240                  * The element
20241                  * @property el
20242                  */
20243                 this.el = el || null;
20244                 /**
20245                  * The element id
20246                  * @property id
20247                  */
20248                 this.id = this.el && el.id;
20249                 /**
20250                  * A reference to the style property
20251                  * @property css
20252                  */
20253                 this.css = this.el && el.style;
20254             },
20255
20256         /**
20257          * Returns the X position of an html element
20258          * @method getPosX
20259          * @param el the element for which to get the position
20260          * @return {int} the X coordinate
20261          * @for DragDropMgr
20262          * @deprecated use Roo.lib.Dom.getX instead
20263          * @static
20264          */
20265         getPosX: function(el) {
20266             return Roo.lib.Dom.getX(el);
20267         },
20268
20269         /**
20270          * Returns the Y position of an html element
20271          * @method getPosY
20272          * @param el the element for which to get the position
20273          * @return {int} the Y coordinate
20274          * @deprecated use Roo.lib.Dom.getY instead
20275          * @static
20276          */
20277         getPosY: function(el) {
20278             return Roo.lib.Dom.getY(el);
20279         },
20280
20281         /**
20282          * Swap two nodes.  In IE, we use the native method, for others we
20283          * emulate the IE behavior
20284          * @method swapNode
20285          * @param n1 the first node to swap
20286          * @param n2 the other node to swap
20287          * @static
20288          */
20289         swapNode: function(n1, n2) {
20290             if (n1.swapNode) {
20291                 n1.swapNode(n2);
20292             } else {
20293                 var p = n2.parentNode;
20294                 var s = n2.nextSibling;
20295
20296                 if (s == n1) {
20297                     p.insertBefore(n1, n2);
20298                 } else if (n2 == n1.nextSibling) {
20299                     p.insertBefore(n2, n1);
20300                 } else {
20301                     n1.parentNode.replaceChild(n2, n1);
20302                     p.insertBefore(n1, s);
20303                 }
20304             }
20305         },
20306
20307         /**
20308          * Returns the current scroll position
20309          * @method getScroll
20310          * @private
20311          * @static
20312          */
20313         getScroll: function () {
20314             var t, l, dde=document.documentElement, db=document.body;
20315             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20316                 t = dde.scrollTop;
20317                 l = dde.scrollLeft;
20318             } else if (db) {
20319                 t = db.scrollTop;
20320                 l = db.scrollLeft;
20321             } else {
20322
20323             }
20324             return { top: t, left: l };
20325         },
20326
20327         /**
20328          * Returns the specified element style property
20329          * @method getStyle
20330          * @param {HTMLElement} el          the element
20331          * @param {string}      styleProp   the style property
20332          * @return {string} The value of the style property
20333          * @deprecated use Roo.lib.Dom.getStyle
20334          * @static
20335          */
20336         getStyle: function(el, styleProp) {
20337             return Roo.fly(el).getStyle(styleProp);
20338         },
20339
20340         /**
20341          * Gets the scrollTop
20342          * @method getScrollTop
20343          * @return {int} the document's scrollTop
20344          * @static
20345          */
20346         getScrollTop: function () { return this.getScroll().top; },
20347
20348         /**
20349          * Gets the scrollLeft
20350          * @method getScrollLeft
20351          * @return {int} the document's scrollTop
20352          * @static
20353          */
20354         getScrollLeft: function () { return this.getScroll().left; },
20355
20356         /**
20357          * Sets the x/y position of an element to the location of the
20358          * target element.
20359          * @method moveToEl
20360          * @param {HTMLElement} moveEl      The element to move
20361          * @param {HTMLElement} targetEl    The position reference element
20362          * @static
20363          */
20364         moveToEl: function (moveEl, targetEl) {
20365             var aCoord = Roo.lib.Dom.getXY(targetEl);
20366             Roo.lib.Dom.setXY(moveEl, aCoord);
20367         },
20368
20369         /**
20370          * Numeric array sort function
20371          * @method numericSort
20372          * @static
20373          */
20374         numericSort: function(a, b) { return (a - b); },
20375
20376         /**
20377          * Internal counter
20378          * @property _timeoutCount
20379          * @private
20380          * @static
20381          */
20382         _timeoutCount: 0,
20383
20384         /**
20385          * Trying to make the load order less important.  Without this we get
20386          * an error if this file is loaded before the Event Utility.
20387          * @method _addListeners
20388          * @private
20389          * @static
20390          */
20391         _addListeners: function() {
20392             var DDM = Roo.dd.DDM;
20393             if ( Roo.lib.Event && document ) {
20394                 DDM._onLoad();
20395             } else {
20396                 if (DDM._timeoutCount > 2000) {
20397                 } else {
20398                     setTimeout(DDM._addListeners, 10);
20399                     if (document && document.body) {
20400                         DDM._timeoutCount += 1;
20401                     }
20402                 }
20403             }
20404         },
20405
20406         /**
20407          * Recursively searches the immediate parent and all child nodes for
20408          * the handle element in order to determine wheter or not it was
20409          * clicked.
20410          * @method handleWasClicked
20411          * @param node the html element to inspect
20412          * @static
20413          */
20414         handleWasClicked: function(node, id) {
20415             if (this.isHandle(id, node.id)) {
20416                 return true;
20417             } else {
20418                 // check to see if this is a text node child of the one we want
20419                 var p = node.parentNode;
20420
20421                 while (p) {
20422                     if (this.isHandle(id, p.id)) {
20423                         return true;
20424                     } else {
20425                         p = p.parentNode;
20426                     }
20427                 }
20428             }
20429
20430             return false;
20431         }
20432
20433     };
20434
20435 }();
20436
20437 // shorter alias, save a few bytes
20438 Roo.dd.DDM = Roo.dd.DragDropMgr;
20439 Roo.dd.DDM._addListeners();
20440
20441 }/*
20442  * Based on:
20443  * Ext JS Library 1.1.1
20444  * Copyright(c) 2006-2007, Ext JS, LLC.
20445  *
20446  * Originally Released Under LGPL - original licence link has changed is not relivant.
20447  *
20448  * Fork - LGPL
20449  * <script type="text/javascript">
20450  */
20451
20452 /**
20453  * @class Roo.dd.DD
20454  * A DragDrop implementation where the linked element follows the
20455  * mouse cursor during a drag.
20456  * @extends Roo.dd.DragDrop
20457  * @constructor
20458  * @param {String} id the id of the linked element
20459  * @param {String} sGroup the group of related DragDrop items
20460  * @param {object} config an object containing configurable attributes
20461  *                Valid properties for DD:
20462  *                    scroll
20463  */
20464 Roo.dd.DD = function(id, sGroup, config) {
20465     if (id) {
20466         this.init(id, sGroup, config);
20467     }
20468 };
20469
20470 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20471
20472     /**
20473      * When set to true, the utility automatically tries to scroll the browser
20474      * window wehn a drag and drop element is dragged near the viewport boundary.
20475      * Defaults to true.
20476      * @property scroll
20477      * @type boolean
20478      */
20479     scroll: true,
20480
20481     /**
20482      * Sets the pointer offset to the distance between the linked element's top
20483      * left corner and the location the element was clicked
20484      * @method autoOffset
20485      * @param {int} iPageX the X coordinate of the click
20486      * @param {int} iPageY the Y coordinate of the click
20487      */
20488     autoOffset: function(iPageX, iPageY) {
20489         var x = iPageX - this.startPageX;
20490         var y = iPageY - this.startPageY;
20491         this.setDelta(x, y);
20492     },
20493
20494     /**
20495      * Sets the pointer offset.  You can call this directly to force the
20496      * offset to be in a particular location (e.g., pass in 0,0 to set it
20497      * to the center of the object)
20498      * @method setDelta
20499      * @param {int} iDeltaX the distance from the left
20500      * @param {int} iDeltaY the distance from the top
20501      */
20502     setDelta: function(iDeltaX, iDeltaY) {
20503         this.deltaX = iDeltaX;
20504         this.deltaY = iDeltaY;
20505     },
20506
20507     /**
20508      * Sets the drag element to the location of the mousedown or click event,
20509      * maintaining the cursor location relative to the location on the element
20510      * that was clicked.  Override this if you want to place the element in a
20511      * location other than where the cursor is.
20512      * @method setDragElPos
20513      * @param {int} iPageX the X coordinate of the mousedown or drag event
20514      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20515      */
20516     setDragElPos: function(iPageX, iPageY) {
20517         // the first time we do this, we are going to check to make sure
20518         // the element has css positioning
20519
20520         var el = this.getDragEl();
20521         this.alignElWithMouse(el, iPageX, iPageY);
20522     },
20523
20524     /**
20525      * Sets the element to the location of the mousedown or click event,
20526      * maintaining the cursor location relative to the location on the element
20527      * that was clicked.  Override this if you want to place the element in a
20528      * location other than where the cursor is.
20529      * @method alignElWithMouse
20530      * @param {HTMLElement} el the element to move
20531      * @param {int} iPageX the X coordinate of the mousedown or drag event
20532      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20533      */
20534     alignElWithMouse: function(el, iPageX, iPageY) {
20535         var oCoord = this.getTargetCoord(iPageX, iPageY);
20536         var fly = el.dom ? el : Roo.fly(el);
20537         if (!this.deltaSetXY) {
20538             var aCoord = [oCoord.x, oCoord.y];
20539             fly.setXY(aCoord);
20540             var newLeft = fly.getLeft(true);
20541             var newTop  = fly.getTop(true);
20542             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20543         } else {
20544             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20545         }
20546
20547         this.cachePosition(oCoord.x, oCoord.y);
20548         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20549         return oCoord;
20550     },
20551
20552     /**
20553      * Saves the most recent position so that we can reset the constraints and
20554      * tick marks on-demand.  We need to know this so that we can calculate the
20555      * number of pixels the element is offset from its original position.
20556      * @method cachePosition
20557      * @param iPageX the current x position (optional, this just makes it so we
20558      * don't have to look it up again)
20559      * @param iPageY the current y position (optional, this just makes it so we
20560      * don't have to look it up again)
20561      */
20562     cachePosition: function(iPageX, iPageY) {
20563         if (iPageX) {
20564             this.lastPageX = iPageX;
20565             this.lastPageY = iPageY;
20566         } else {
20567             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20568             this.lastPageX = aCoord[0];
20569             this.lastPageY = aCoord[1];
20570         }
20571     },
20572
20573     /**
20574      * Auto-scroll the window if the dragged object has been moved beyond the
20575      * visible window boundary.
20576      * @method autoScroll
20577      * @param {int} x the drag element's x position
20578      * @param {int} y the drag element's y position
20579      * @param {int} h the height of the drag element
20580      * @param {int} w the width of the drag element
20581      * @private
20582      */
20583     autoScroll: function(x, y, h, w) {
20584
20585         if (this.scroll) {
20586             // The client height
20587             var clientH = Roo.lib.Dom.getViewWidth();
20588
20589             // The client width
20590             var clientW = Roo.lib.Dom.getViewHeight();
20591
20592             // The amt scrolled down
20593             var st = this.DDM.getScrollTop();
20594
20595             // The amt scrolled right
20596             var sl = this.DDM.getScrollLeft();
20597
20598             // Location of the bottom of the element
20599             var bot = h + y;
20600
20601             // Location of the right of the element
20602             var right = w + x;
20603
20604             // The distance from the cursor to the bottom of the visible area,
20605             // adjusted so that we don't scroll if the cursor is beyond the
20606             // element drag constraints
20607             var toBot = (clientH + st - y - this.deltaY);
20608
20609             // The distance from the cursor to the right of the visible area
20610             var toRight = (clientW + sl - x - this.deltaX);
20611
20612
20613             // How close to the edge the cursor must be before we scroll
20614             // var thresh = (document.all) ? 100 : 40;
20615             var thresh = 40;
20616
20617             // How many pixels to scroll per autoscroll op.  This helps to reduce
20618             // clunky scrolling. IE is more sensitive about this ... it needs this
20619             // value to be higher.
20620             var scrAmt = (document.all) ? 80 : 30;
20621
20622             // Scroll down if we are near the bottom of the visible page and the
20623             // obj extends below the crease
20624             if ( bot > clientH && toBot < thresh ) {
20625                 window.scrollTo(sl, st + scrAmt);
20626             }
20627
20628             // Scroll up if the window is scrolled down and the top of the object
20629             // goes above the top border
20630             if ( y < st && st > 0 && y - st < thresh ) {
20631                 window.scrollTo(sl, st - scrAmt);
20632             }
20633
20634             // Scroll right if the obj is beyond the right border and the cursor is
20635             // near the border.
20636             if ( right > clientW && toRight < thresh ) {
20637                 window.scrollTo(sl + scrAmt, st);
20638             }
20639
20640             // Scroll left if the window has been scrolled to the right and the obj
20641             // extends past the left border
20642             if ( x < sl && sl > 0 && x - sl < thresh ) {
20643                 window.scrollTo(sl - scrAmt, st);
20644             }
20645         }
20646     },
20647
20648     /**
20649      * Finds the location the element should be placed if we want to move
20650      * it to where the mouse location less the click offset would place us.
20651      * @method getTargetCoord
20652      * @param {int} iPageX the X coordinate of the click
20653      * @param {int} iPageY the Y coordinate of the click
20654      * @return an object that contains the coordinates (Object.x and Object.y)
20655      * @private
20656      */
20657     getTargetCoord: function(iPageX, iPageY) {
20658
20659
20660         var x = iPageX - this.deltaX;
20661         var y = iPageY - this.deltaY;
20662
20663         if (this.constrainX) {
20664             if (x < this.minX) { x = this.minX; }
20665             if (x > this.maxX) { x = this.maxX; }
20666         }
20667
20668         if (this.constrainY) {
20669             if (y < this.minY) { y = this.minY; }
20670             if (y > this.maxY) { y = this.maxY; }
20671         }
20672
20673         x = this.getTick(x, this.xTicks);
20674         y = this.getTick(y, this.yTicks);
20675
20676
20677         return {x:x, y:y};
20678     },
20679
20680     /*
20681      * Sets up config options specific to this class. Overrides
20682      * Roo.dd.DragDrop, but all versions of this method through the
20683      * inheritance chain are called
20684      */
20685     applyConfig: function() {
20686         Roo.dd.DD.superclass.applyConfig.call(this);
20687         this.scroll = (this.config.scroll !== false);
20688     },
20689
20690     /*
20691      * Event that fires prior to the onMouseDown event.  Overrides
20692      * Roo.dd.DragDrop.
20693      */
20694     b4MouseDown: function(e) {
20695         // this.resetConstraints();
20696         this.autoOffset(e.getPageX(),
20697                             e.getPageY());
20698     },
20699
20700     /*
20701      * Event that fires prior to the onDrag event.  Overrides
20702      * Roo.dd.DragDrop.
20703      */
20704     b4Drag: function(e) {
20705         this.setDragElPos(e.getPageX(),
20706                             e.getPageY());
20707     },
20708
20709     toString: function() {
20710         return ("DD " + this.id);
20711     }
20712
20713     //////////////////////////////////////////////////////////////////////////
20714     // Debugging ygDragDrop events that can be overridden
20715     //////////////////////////////////////////////////////////////////////////
20716     /*
20717     startDrag: function(x, y) {
20718     },
20719
20720     onDrag: function(e) {
20721     },
20722
20723     onDragEnter: function(e, id) {
20724     },
20725
20726     onDragOver: function(e, id) {
20727     },
20728
20729     onDragOut: function(e, id) {
20730     },
20731
20732     onDragDrop: function(e, id) {
20733     },
20734
20735     endDrag: function(e) {
20736     }
20737
20738     */
20739
20740 });/*
20741  * Based on:
20742  * Ext JS Library 1.1.1
20743  * Copyright(c) 2006-2007, Ext JS, LLC.
20744  *
20745  * Originally Released Under LGPL - original licence link has changed is not relivant.
20746  *
20747  * Fork - LGPL
20748  * <script type="text/javascript">
20749  */
20750
20751 /**
20752  * @class Roo.dd.DDProxy
20753  * A DragDrop implementation that inserts an empty, bordered div into
20754  * the document that follows the cursor during drag operations.  At the time of
20755  * the click, the frame div is resized to the dimensions of the linked html
20756  * element, and moved to the exact location of the linked element.
20757  *
20758  * References to the "frame" element refer to the single proxy element that
20759  * was created to be dragged in place of all DDProxy elements on the
20760  * page.
20761  *
20762  * @extends Roo.dd.DD
20763  * @constructor
20764  * @param {String} id the id of the linked html element
20765  * @param {String} sGroup the group of related DragDrop objects
20766  * @param {object} config an object containing configurable attributes
20767  *                Valid properties for DDProxy in addition to those in DragDrop:
20768  *                   resizeFrame, centerFrame, dragElId
20769  */
20770 Roo.dd.DDProxy = function(id, sGroup, config) {
20771     if (id) {
20772         this.init(id, sGroup, config);
20773         this.initFrame();
20774     }
20775 };
20776
20777 /**
20778  * The default drag frame div id
20779  * @property Roo.dd.DDProxy.dragElId
20780  * @type String
20781  * @static
20782  */
20783 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20784
20785 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20786
20787     /**
20788      * By default we resize the drag frame to be the same size as the element
20789      * we want to drag (this is to get the frame effect).  We can turn it off
20790      * if we want a different behavior.
20791      * @property resizeFrame
20792      * @type boolean
20793      */
20794     resizeFrame: true,
20795
20796     /**
20797      * By default the frame is positioned exactly where the drag element is, so
20798      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20799      * you do not have constraints on the obj is to have the drag frame centered
20800      * around the cursor.  Set centerFrame to true for this effect.
20801      * @property centerFrame
20802      * @type boolean
20803      */
20804     centerFrame: false,
20805
20806     /**
20807      * Creates the proxy element if it does not yet exist
20808      * @method createFrame
20809      */
20810     createFrame: function() {
20811         var self = this;
20812         var body = document.body;
20813
20814         if (!body || !body.firstChild) {
20815             setTimeout( function() { self.createFrame(); }, 50 );
20816             return;
20817         }
20818
20819         var div = this.getDragEl();
20820
20821         if (!div) {
20822             div    = document.createElement("div");
20823             div.id = this.dragElId;
20824             var s  = div.style;
20825
20826             s.position   = "absolute";
20827             s.visibility = "hidden";
20828             s.cursor     = "move";
20829             s.border     = "2px solid #aaa";
20830             s.zIndex     = 999;
20831
20832             // appendChild can blow up IE if invoked prior to the window load event
20833             // while rendering a table.  It is possible there are other scenarios
20834             // that would cause this to happen as well.
20835             body.insertBefore(div, body.firstChild);
20836         }
20837     },
20838
20839     /**
20840      * Initialization for the drag frame element.  Must be called in the
20841      * constructor of all subclasses
20842      * @method initFrame
20843      */
20844     initFrame: function() {
20845         this.createFrame();
20846     },
20847
20848     applyConfig: function() {
20849         Roo.dd.DDProxy.superclass.applyConfig.call(this);
20850
20851         this.resizeFrame = (this.config.resizeFrame !== false);
20852         this.centerFrame = (this.config.centerFrame);
20853         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
20854     },
20855
20856     /**
20857      * Resizes the drag frame to the dimensions of the clicked object, positions
20858      * it over the object, and finally displays it
20859      * @method showFrame
20860      * @param {int} iPageX X click position
20861      * @param {int} iPageY Y click position
20862      * @private
20863      */
20864     showFrame: function(iPageX, iPageY) {
20865         var el = this.getEl();
20866         var dragEl = this.getDragEl();
20867         var s = dragEl.style;
20868
20869         this._resizeProxy();
20870
20871         if (this.centerFrame) {
20872             this.setDelta( Math.round(parseInt(s.width,  10)/2),
20873                            Math.round(parseInt(s.height, 10)/2) );
20874         }
20875
20876         this.setDragElPos(iPageX, iPageY);
20877
20878         Roo.fly(dragEl).show();
20879     },
20880
20881     /**
20882      * The proxy is automatically resized to the dimensions of the linked
20883      * element when a drag is initiated, unless resizeFrame is set to false
20884      * @method _resizeProxy
20885      * @private
20886      */
20887     _resizeProxy: function() {
20888         if (this.resizeFrame) {
20889             var el = this.getEl();
20890             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
20891         }
20892     },
20893
20894     // overrides Roo.dd.DragDrop
20895     b4MouseDown: function(e) {
20896         var x = e.getPageX();
20897         var y = e.getPageY();
20898         this.autoOffset(x, y);
20899         this.setDragElPos(x, y);
20900     },
20901
20902     // overrides Roo.dd.DragDrop
20903     b4StartDrag: function(x, y) {
20904         // show the drag frame
20905         this.showFrame(x, y);
20906     },
20907
20908     // overrides Roo.dd.DragDrop
20909     b4EndDrag: function(e) {
20910         Roo.fly(this.getDragEl()).hide();
20911     },
20912
20913     // overrides Roo.dd.DragDrop
20914     // By default we try to move the element to the last location of the frame.
20915     // This is so that the default behavior mirrors that of Roo.dd.DD.
20916     endDrag: function(e) {
20917
20918         var lel = this.getEl();
20919         var del = this.getDragEl();
20920
20921         // Show the drag frame briefly so we can get its position
20922         del.style.visibility = "";
20923
20924         this.beforeMove();
20925         // Hide the linked element before the move to get around a Safari
20926         // rendering bug.
20927         lel.style.visibility = "hidden";
20928         Roo.dd.DDM.moveToEl(lel, del);
20929         del.style.visibility = "hidden";
20930         lel.style.visibility = "";
20931
20932         this.afterDrag();
20933     },
20934
20935     beforeMove : function(){
20936
20937     },
20938
20939     afterDrag : function(){
20940
20941     },
20942
20943     toString: function() {
20944         return ("DDProxy " + this.id);
20945     }
20946
20947 });
20948 /*
20949  * Based on:
20950  * Ext JS Library 1.1.1
20951  * Copyright(c) 2006-2007, Ext JS, LLC.
20952  *
20953  * Originally Released Under LGPL - original licence link has changed is not relivant.
20954  *
20955  * Fork - LGPL
20956  * <script type="text/javascript">
20957  */
20958
20959  /**
20960  * @class Roo.dd.DDTarget
20961  * A DragDrop implementation that does not move, but can be a drop
20962  * target.  You would get the same result by simply omitting implementation
20963  * for the event callbacks, but this way we reduce the processing cost of the
20964  * event listener and the callbacks.
20965  * @extends Roo.dd.DragDrop
20966  * @constructor
20967  * @param {String} id the id of the element that is a drop target
20968  * @param {String} sGroup the group of related DragDrop objects
20969  * @param {object} config an object containing configurable attributes
20970  *                 Valid properties for DDTarget in addition to those in
20971  *                 DragDrop:
20972  *                    none
20973  */
20974 Roo.dd.DDTarget = function(id, sGroup, config) {
20975     if (id) {
20976         this.initTarget(id, sGroup, config);
20977     }
20978     if (config.listeners || config.events) { 
20979        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
20980             listeners : config.listeners || {}, 
20981             events : config.events || {} 
20982         });    
20983     }
20984 };
20985
20986 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
20987 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
20988     toString: function() {
20989         return ("DDTarget " + this.id);
20990     }
20991 });
20992 /*
20993  * Based on:
20994  * Ext JS Library 1.1.1
20995  * Copyright(c) 2006-2007, Ext JS, LLC.
20996  *
20997  * Originally Released Under LGPL - original licence link has changed is not relivant.
20998  *
20999  * Fork - LGPL
21000  * <script type="text/javascript">
21001  */
21002  
21003
21004 /**
21005  * @class Roo.dd.ScrollManager
21006  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21007  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21008  * @singleton
21009  */
21010 Roo.dd.ScrollManager = function(){
21011     var ddm = Roo.dd.DragDropMgr;
21012     var els = {};
21013     var dragEl = null;
21014     var proc = {};
21015     
21016     
21017     
21018     var onStop = function(e){
21019         dragEl = null;
21020         clearProc();
21021     };
21022     
21023     var triggerRefresh = function(){
21024         if(ddm.dragCurrent){
21025              ddm.refreshCache(ddm.dragCurrent.groups);
21026         }
21027     };
21028     
21029     var doScroll = function(){
21030         if(ddm.dragCurrent){
21031             var dds = Roo.dd.ScrollManager;
21032             if(!dds.animate){
21033                 if(proc.el.scroll(proc.dir, dds.increment)){
21034                     triggerRefresh();
21035                 }
21036             }else{
21037                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21038             }
21039         }
21040     };
21041     
21042     var clearProc = function(){
21043         if(proc.id){
21044             clearInterval(proc.id);
21045         }
21046         proc.id = 0;
21047         proc.el = null;
21048         proc.dir = "";
21049     };
21050     
21051     var startProc = function(el, dir){
21052          Roo.log('scroll startproc');
21053         clearProc();
21054         proc.el = el;
21055         proc.dir = dir;
21056         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21057     };
21058     
21059     var onFire = function(e, isDrop){
21060        
21061         if(isDrop || !ddm.dragCurrent){ return; }
21062         var dds = Roo.dd.ScrollManager;
21063         if(!dragEl || dragEl != ddm.dragCurrent){
21064             dragEl = ddm.dragCurrent;
21065             // refresh regions on drag start
21066             dds.refreshCache();
21067         }
21068         
21069         var xy = Roo.lib.Event.getXY(e);
21070         var pt = new Roo.lib.Point(xy[0], xy[1]);
21071         for(var id in els){
21072             var el = els[id], r = el._region;
21073             if(r && r.contains(pt) && el.isScrollable()){
21074                 if(r.bottom - pt.y <= dds.thresh){
21075                     if(proc.el != el){
21076                         startProc(el, "down");
21077                     }
21078                     return;
21079                 }else if(r.right - pt.x <= dds.thresh){
21080                     if(proc.el != el){
21081                         startProc(el, "left");
21082                     }
21083                     return;
21084                 }else if(pt.y - r.top <= dds.thresh){
21085                     if(proc.el != el){
21086                         startProc(el, "up");
21087                     }
21088                     return;
21089                 }else if(pt.x - r.left <= dds.thresh){
21090                     if(proc.el != el){
21091                         startProc(el, "right");
21092                     }
21093                     return;
21094                 }
21095             }
21096         }
21097         clearProc();
21098     };
21099     
21100     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21101     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21102     
21103     return {
21104         /**
21105          * Registers new overflow element(s) to auto scroll
21106          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21107          */
21108         register : function(el){
21109             if(el instanceof Array){
21110                 for(var i = 0, len = el.length; i < len; i++) {
21111                         this.register(el[i]);
21112                 }
21113             }else{
21114                 el = Roo.get(el);
21115                 els[el.id] = el;
21116             }
21117             Roo.dd.ScrollManager.els = els;
21118         },
21119         
21120         /**
21121          * Unregisters overflow element(s) so they are no longer scrolled
21122          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21123          */
21124         unregister : function(el){
21125             if(el instanceof Array){
21126                 for(var i = 0, len = el.length; i < len; i++) {
21127                         this.unregister(el[i]);
21128                 }
21129             }else{
21130                 el = Roo.get(el);
21131                 delete els[el.id];
21132             }
21133         },
21134         
21135         /**
21136          * The number of pixels from the edge of a container the pointer needs to be to 
21137          * trigger scrolling (defaults to 25)
21138          * @type Number
21139          */
21140         thresh : 25,
21141         
21142         /**
21143          * The number of pixels to scroll in each scroll increment (defaults to 50)
21144          * @type Number
21145          */
21146         increment : 100,
21147         
21148         /**
21149          * The frequency of scrolls in milliseconds (defaults to 500)
21150          * @type Number
21151          */
21152         frequency : 500,
21153         
21154         /**
21155          * True to animate the scroll (defaults to true)
21156          * @type Boolean
21157          */
21158         animate: true,
21159         
21160         /**
21161          * The animation duration in seconds - 
21162          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21163          * @type Number
21164          */
21165         animDuration: .4,
21166         
21167         /**
21168          * Manually trigger a cache refresh.
21169          */
21170         refreshCache : function(){
21171             for(var id in els){
21172                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21173                     els[id]._region = els[id].getRegion();
21174                 }
21175             }
21176         }
21177     };
21178 }();/*
21179  * Based on:
21180  * Ext JS Library 1.1.1
21181  * Copyright(c) 2006-2007, Ext JS, LLC.
21182  *
21183  * Originally Released Under LGPL - original licence link has changed is not relivant.
21184  *
21185  * Fork - LGPL
21186  * <script type="text/javascript">
21187  */
21188  
21189
21190 /**
21191  * @class Roo.dd.Registry
21192  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21193  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21194  * @singleton
21195  */
21196 Roo.dd.Registry = function(){
21197     var elements = {}; 
21198     var handles = {}; 
21199     var autoIdSeed = 0;
21200
21201     var getId = function(el, autogen){
21202         if(typeof el == "string"){
21203             return el;
21204         }
21205         var id = el.id;
21206         if(!id && autogen !== false){
21207             id = "roodd-" + (++autoIdSeed);
21208             el.id = id;
21209         }
21210         return id;
21211     };
21212     
21213     return {
21214     /**
21215      * Register a drag drop element
21216      * @param {String|HTMLElement} element The id or DOM node to register
21217      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21218      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21219      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21220      * populated in the data object (if applicable):
21221      * <pre>
21222 Value      Description<br />
21223 ---------  ------------------------------------------<br />
21224 handles    Array of DOM nodes that trigger dragging<br />
21225            for the element being registered<br />
21226 isHandle   True if the element passed in triggers<br />
21227            dragging itself, else false
21228 </pre>
21229      */
21230         register : function(el, data){
21231             data = data || {};
21232             if(typeof el == "string"){
21233                 el = document.getElementById(el);
21234             }
21235             data.ddel = el;
21236             elements[getId(el)] = data;
21237             if(data.isHandle !== false){
21238                 handles[data.ddel.id] = data;
21239             }
21240             if(data.handles){
21241                 var hs = data.handles;
21242                 for(var i = 0, len = hs.length; i < len; i++){
21243                         handles[getId(hs[i])] = data;
21244                 }
21245             }
21246         },
21247
21248     /**
21249      * Unregister a drag drop element
21250      * @param {String|HTMLElement}  element The id or DOM node to unregister
21251      */
21252         unregister : function(el){
21253             var id = getId(el, false);
21254             var data = elements[id];
21255             if(data){
21256                 delete elements[id];
21257                 if(data.handles){
21258                     var hs = data.handles;
21259                     for(var i = 0, len = hs.length; i < len; i++){
21260                         delete handles[getId(hs[i], false)];
21261                     }
21262                 }
21263             }
21264         },
21265
21266     /**
21267      * Returns the handle registered for a DOM Node by id
21268      * @param {String|HTMLElement} id The DOM node or id to look up
21269      * @return {Object} handle The custom handle data
21270      */
21271         getHandle : function(id){
21272             if(typeof id != "string"){ // must be element?
21273                 id = id.id;
21274             }
21275             return handles[id];
21276         },
21277
21278     /**
21279      * Returns the handle that is registered for the DOM node that is the target of the event
21280      * @param {Event} e The event
21281      * @return {Object} handle The custom handle data
21282      */
21283         getHandleFromEvent : function(e){
21284             var t = Roo.lib.Event.getTarget(e);
21285             return t ? handles[t.id] : null;
21286         },
21287
21288     /**
21289      * Returns a custom data object that is registered for a DOM node by id
21290      * @param {String|HTMLElement} id The DOM node or id to look up
21291      * @return {Object} data The custom data
21292      */
21293         getTarget : function(id){
21294             if(typeof id != "string"){ // must be element?
21295                 id = id.id;
21296             }
21297             return elements[id];
21298         },
21299
21300     /**
21301      * Returns a custom data object that is registered for the DOM node that is the target of the event
21302      * @param {Event} e The event
21303      * @return {Object} data The custom data
21304      */
21305         getTargetFromEvent : function(e){
21306             var t = Roo.lib.Event.getTarget(e);
21307             return t ? elements[t.id] || handles[t.id] : null;
21308         }
21309     };
21310 }();/*
21311  * Based on:
21312  * Ext JS Library 1.1.1
21313  * Copyright(c) 2006-2007, Ext JS, LLC.
21314  *
21315  * Originally Released Under LGPL - original licence link has changed is not relivant.
21316  *
21317  * Fork - LGPL
21318  * <script type="text/javascript">
21319  */
21320  
21321
21322 /**
21323  * @class Roo.dd.StatusProxy
21324  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21325  * default drag proxy used by all Roo.dd components.
21326  * @constructor
21327  * @param {Object} config
21328  */
21329 Roo.dd.StatusProxy = function(config){
21330     Roo.apply(this, config);
21331     this.id = this.id || Roo.id();
21332     this.el = new Roo.Layer({
21333         dh: {
21334             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21335                 {tag: "div", cls: "x-dd-drop-icon"},
21336                 {tag: "div", cls: "x-dd-drag-ghost"}
21337             ]
21338         }, 
21339         shadow: !config || config.shadow !== false
21340     });
21341     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21342     this.dropStatus = this.dropNotAllowed;
21343 };
21344
21345 Roo.dd.StatusProxy.prototype = {
21346     /**
21347      * @cfg {String} dropAllowed
21348      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21349      */
21350     dropAllowed : "x-dd-drop-ok",
21351     /**
21352      * @cfg {String} dropNotAllowed
21353      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21354      */
21355     dropNotAllowed : "x-dd-drop-nodrop",
21356
21357     /**
21358      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21359      * over the current target element.
21360      * @param {String} cssClass The css class for the new drop status indicator image
21361      */
21362     setStatus : function(cssClass){
21363         cssClass = cssClass || this.dropNotAllowed;
21364         if(this.dropStatus != cssClass){
21365             this.el.replaceClass(this.dropStatus, cssClass);
21366             this.dropStatus = cssClass;
21367         }
21368     },
21369
21370     /**
21371      * Resets the status indicator to the default dropNotAllowed value
21372      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21373      */
21374     reset : function(clearGhost){
21375         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21376         this.dropStatus = this.dropNotAllowed;
21377         if(clearGhost){
21378             this.ghost.update("");
21379         }
21380     },
21381
21382     /**
21383      * Updates the contents of the ghost element
21384      * @param {String} html The html that will replace the current innerHTML of the ghost element
21385      */
21386     update : function(html){
21387         if(typeof html == "string"){
21388             this.ghost.update(html);
21389         }else{
21390             this.ghost.update("");
21391             html.style.margin = "0";
21392             this.ghost.dom.appendChild(html);
21393         }
21394         // ensure float = none set?? cant remember why though.
21395         var el = this.ghost.dom.firstChild;
21396                 if(el){
21397                         Roo.fly(el).setStyle('float', 'none');
21398                 }
21399     },
21400     
21401     /**
21402      * Returns the underlying proxy {@link Roo.Layer}
21403      * @return {Roo.Layer} el
21404     */
21405     getEl : function(){
21406         return this.el;
21407     },
21408
21409     /**
21410      * Returns the ghost element
21411      * @return {Roo.Element} el
21412      */
21413     getGhost : function(){
21414         return this.ghost;
21415     },
21416
21417     /**
21418      * Hides the proxy
21419      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21420      */
21421     hide : function(clear){
21422         this.el.hide();
21423         if(clear){
21424             this.reset(true);
21425         }
21426     },
21427
21428     /**
21429      * Stops the repair animation if it's currently running
21430      */
21431     stop : function(){
21432         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21433             this.anim.stop();
21434         }
21435     },
21436
21437     /**
21438      * Displays this proxy
21439      */
21440     show : function(){
21441         this.el.show();
21442     },
21443
21444     /**
21445      * Force the Layer to sync its shadow and shim positions to the element
21446      */
21447     sync : function(){
21448         this.el.sync();
21449     },
21450
21451     /**
21452      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21453      * invalid drop operation by the item being dragged.
21454      * @param {Array} xy The XY position of the element ([x, y])
21455      * @param {Function} callback The function to call after the repair is complete
21456      * @param {Object} scope The scope in which to execute the callback
21457      */
21458     repair : function(xy, callback, scope){
21459         this.callback = callback;
21460         this.scope = scope;
21461         if(xy && this.animRepair !== false){
21462             this.el.addClass("x-dd-drag-repair");
21463             this.el.hideUnders(true);
21464             this.anim = this.el.shift({
21465                 duration: this.repairDuration || .5,
21466                 easing: 'easeOut',
21467                 xy: xy,
21468                 stopFx: true,
21469                 callback: this.afterRepair,
21470                 scope: this
21471             });
21472         }else{
21473             this.afterRepair();
21474         }
21475     },
21476
21477     // private
21478     afterRepair : function(){
21479         this.hide(true);
21480         if(typeof this.callback == "function"){
21481             this.callback.call(this.scope || this);
21482         }
21483         this.callback = null;
21484         this.scope = null;
21485     }
21486 };/*
21487  * Based on:
21488  * Ext JS Library 1.1.1
21489  * Copyright(c) 2006-2007, Ext JS, LLC.
21490  *
21491  * Originally Released Under LGPL - original licence link has changed is not relivant.
21492  *
21493  * Fork - LGPL
21494  * <script type="text/javascript">
21495  */
21496
21497 /**
21498  * @class Roo.dd.DragSource
21499  * @extends Roo.dd.DDProxy
21500  * A simple class that provides the basic implementation needed to make any element draggable.
21501  * @constructor
21502  * @param {String/HTMLElement/Element} el The container element
21503  * @param {Object} config
21504  */
21505 Roo.dd.DragSource = function(el, config){
21506     this.el = Roo.get(el);
21507     this.dragData = {};
21508     
21509     Roo.apply(this, config);
21510     
21511     if(!this.proxy){
21512         this.proxy = new Roo.dd.StatusProxy();
21513     }
21514
21515     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21516           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21517     
21518     this.dragging = false;
21519 };
21520
21521 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21522     /**
21523      * @cfg {String} dropAllowed
21524      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21525      */
21526     dropAllowed : "x-dd-drop-ok",
21527     /**
21528      * @cfg {String} dropNotAllowed
21529      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21530      */
21531     dropNotAllowed : "x-dd-drop-nodrop",
21532
21533     /**
21534      * Returns the data object associated with this drag source
21535      * @return {Object} data An object containing arbitrary data
21536      */
21537     getDragData : function(e){
21538         return this.dragData;
21539     },
21540
21541     // private
21542     onDragEnter : function(e, id){
21543         var target = Roo.dd.DragDropMgr.getDDById(id);
21544         this.cachedTarget = target;
21545         if(this.beforeDragEnter(target, e, id) !== false){
21546             if(target.isNotifyTarget){
21547                 var status = target.notifyEnter(this, e, this.dragData);
21548                 this.proxy.setStatus(status);
21549             }else{
21550                 this.proxy.setStatus(this.dropAllowed);
21551             }
21552             
21553             if(this.afterDragEnter){
21554                 /**
21555                  * An empty function by default, but provided so that you can perform a custom action
21556                  * when the dragged item enters the drop target by providing an implementation.
21557                  * @param {Roo.dd.DragDrop} target The drop target
21558                  * @param {Event} e The event object
21559                  * @param {String} id The id of the dragged element
21560                  * @method afterDragEnter
21561                  */
21562                 this.afterDragEnter(target, e, id);
21563             }
21564         }
21565     },
21566
21567     /**
21568      * An empty function by default, but provided so that you can perform a custom action
21569      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21570      * @param {Roo.dd.DragDrop} target The drop target
21571      * @param {Event} e The event object
21572      * @param {String} id The id of the dragged element
21573      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21574      */
21575     beforeDragEnter : function(target, e, id){
21576         return true;
21577     },
21578
21579     // private
21580     alignElWithMouse: function() {
21581         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21582         this.proxy.sync();
21583     },
21584
21585     // private
21586     onDragOver : function(e, id){
21587         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21588         if(this.beforeDragOver(target, e, id) !== false){
21589             if(target.isNotifyTarget){
21590                 var status = target.notifyOver(this, e, this.dragData);
21591                 this.proxy.setStatus(status);
21592             }
21593
21594             if(this.afterDragOver){
21595                 /**
21596                  * An empty function by default, but provided so that you can perform a custom action
21597                  * while the dragged item is over the drop target by providing an implementation.
21598                  * @param {Roo.dd.DragDrop} target The drop target
21599                  * @param {Event} e The event object
21600                  * @param {String} id The id of the dragged element
21601                  * @method afterDragOver
21602                  */
21603                 this.afterDragOver(target, e, id);
21604             }
21605         }
21606     },
21607
21608     /**
21609      * An empty function by default, but provided so that you can perform a custom action
21610      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21611      * @param {Roo.dd.DragDrop} target The drop target
21612      * @param {Event} e The event object
21613      * @param {String} id The id of the dragged element
21614      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21615      */
21616     beforeDragOver : function(target, e, id){
21617         return true;
21618     },
21619
21620     // private
21621     onDragOut : function(e, id){
21622         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21623         if(this.beforeDragOut(target, e, id) !== false){
21624             if(target.isNotifyTarget){
21625                 target.notifyOut(this, e, this.dragData);
21626             }
21627             this.proxy.reset();
21628             if(this.afterDragOut){
21629                 /**
21630                  * An empty function by default, but provided so that you can perform a custom action
21631                  * after the dragged item is dragged out of the target without dropping.
21632                  * @param {Roo.dd.DragDrop} target The drop target
21633                  * @param {Event} e The event object
21634                  * @param {String} id The id of the dragged element
21635                  * @method afterDragOut
21636                  */
21637                 this.afterDragOut(target, e, id);
21638             }
21639         }
21640         this.cachedTarget = null;
21641     },
21642
21643     /**
21644      * An empty function by default, but provided so that you can perform a custom action before the dragged
21645      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21646      * @param {Roo.dd.DragDrop} target The drop target
21647      * @param {Event} e The event object
21648      * @param {String} id The id of the dragged element
21649      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21650      */
21651     beforeDragOut : function(target, e, id){
21652         return true;
21653     },
21654     
21655     // private
21656     onDragDrop : function(e, id){
21657         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21658         if(this.beforeDragDrop(target, e, id) !== false){
21659             if(target.isNotifyTarget){
21660                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21661                     this.onValidDrop(target, e, id);
21662                 }else{
21663                     this.onInvalidDrop(target, e, id);
21664                 }
21665             }else{
21666                 this.onValidDrop(target, e, id);
21667             }
21668             
21669             if(this.afterDragDrop){
21670                 /**
21671                  * An empty function by default, but provided so that you can perform a custom action
21672                  * after a valid drag drop has occurred by providing an implementation.
21673                  * @param {Roo.dd.DragDrop} target The drop target
21674                  * @param {Event} e The event object
21675                  * @param {String} id The id of the dropped element
21676                  * @method afterDragDrop
21677                  */
21678                 this.afterDragDrop(target, e, id);
21679             }
21680         }
21681         delete this.cachedTarget;
21682     },
21683
21684     /**
21685      * An empty function by default, but provided so that you can perform a custom action before the dragged
21686      * item is dropped onto the target and optionally cancel the onDragDrop.
21687      * @param {Roo.dd.DragDrop} target The drop target
21688      * @param {Event} e The event object
21689      * @param {String} id The id of the dragged element
21690      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21691      */
21692     beforeDragDrop : function(target, e, id){
21693         return true;
21694     },
21695
21696     // private
21697     onValidDrop : function(target, e, id){
21698         this.hideProxy();
21699         if(this.afterValidDrop){
21700             /**
21701              * An empty function by default, but provided so that you can perform a custom action
21702              * after a valid drop has occurred by providing an implementation.
21703              * @param {Object} target The target DD 
21704              * @param {Event} e The event object
21705              * @param {String} id The id of the dropped element
21706              * @method afterInvalidDrop
21707              */
21708             this.afterValidDrop(target, e, id);
21709         }
21710     },
21711
21712     // private
21713     getRepairXY : function(e, data){
21714         return this.el.getXY();  
21715     },
21716
21717     // private
21718     onInvalidDrop : function(target, e, id){
21719         this.beforeInvalidDrop(target, e, id);
21720         if(this.cachedTarget){
21721             if(this.cachedTarget.isNotifyTarget){
21722                 this.cachedTarget.notifyOut(this, e, this.dragData);
21723             }
21724             this.cacheTarget = null;
21725         }
21726         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21727
21728         if(this.afterInvalidDrop){
21729             /**
21730              * An empty function by default, but provided so that you can perform a custom action
21731              * after an invalid drop has occurred by providing an implementation.
21732              * @param {Event} e The event object
21733              * @param {String} id The id of the dropped element
21734              * @method afterInvalidDrop
21735              */
21736             this.afterInvalidDrop(e, id);
21737         }
21738     },
21739
21740     // private
21741     afterRepair : function(){
21742         if(Roo.enableFx){
21743             this.el.highlight(this.hlColor || "c3daf9");
21744         }
21745         this.dragging = false;
21746     },
21747
21748     /**
21749      * An empty function by default, but provided so that you can perform a custom action after an invalid
21750      * drop has occurred.
21751      * @param {Roo.dd.DragDrop} target The drop target
21752      * @param {Event} e The event object
21753      * @param {String} id The id of the dragged element
21754      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21755      */
21756     beforeInvalidDrop : function(target, e, id){
21757         return true;
21758     },
21759
21760     // private
21761     handleMouseDown : function(e){
21762         if(this.dragging) {
21763             return;
21764         }
21765         var data = this.getDragData(e);
21766         if(data && this.onBeforeDrag(data, e) !== false){
21767             this.dragData = data;
21768             this.proxy.stop();
21769             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21770         } 
21771     },
21772
21773     /**
21774      * An empty function by default, but provided so that you can perform a custom action before the initial
21775      * drag event begins and optionally cancel it.
21776      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21777      * @param {Event} e The event object
21778      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21779      */
21780     onBeforeDrag : function(data, e){
21781         return true;
21782     },
21783
21784     /**
21785      * An empty function by default, but provided so that you can perform a custom action once the initial
21786      * drag event has begun.  The drag cannot be canceled from this function.
21787      * @param {Number} x The x position of the click on the dragged object
21788      * @param {Number} y The y position of the click on the dragged object
21789      */
21790     onStartDrag : Roo.emptyFn,
21791
21792     // private - YUI override
21793     startDrag : function(x, y){
21794         this.proxy.reset();
21795         this.dragging = true;
21796         this.proxy.update("");
21797         this.onInitDrag(x, y);
21798         this.proxy.show();
21799     },
21800
21801     // private
21802     onInitDrag : function(x, y){
21803         var clone = this.el.dom.cloneNode(true);
21804         clone.id = Roo.id(); // prevent duplicate ids
21805         this.proxy.update(clone);
21806         this.onStartDrag(x, y);
21807         return true;
21808     },
21809
21810     /**
21811      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21812      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21813      */
21814     getProxy : function(){
21815         return this.proxy;  
21816     },
21817
21818     /**
21819      * Hides the drag source's {@link Roo.dd.StatusProxy}
21820      */
21821     hideProxy : function(){
21822         this.proxy.hide();  
21823         this.proxy.reset(true);
21824         this.dragging = false;
21825     },
21826
21827     // private
21828     triggerCacheRefresh : function(){
21829         Roo.dd.DDM.refreshCache(this.groups);
21830     },
21831
21832     // private - override to prevent hiding
21833     b4EndDrag: function(e) {
21834     },
21835
21836     // private - override to prevent moving
21837     endDrag : function(e){
21838         this.onEndDrag(this.dragData, e);
21839     },
21840
21841     // private
21842     onEndDrag : function(data, e){
21843     },
21844     
21845     // private - pin to cursor
21846     autoOffset : function(x, y) {
21847         this.setDelta(-12, -20);
21848     }    
21849 });/*
21850  * Based on:
21851  * Ext JS Library 1.1.1
21852  * Copyright(c) 2006-2007, Ext JS, LLC.
21853  *
21854  * Originally Released Under LGPL - original licence link has changed is not relivant.
21855  *
21856  * Fork - LGPL
21857  * <script type="text/javascript">
21858  */
21859
21860
21861 /**
21862  * @class Roo.dd.DropTarget
21863  * @extends Roo.dd.DDTarget
21864  * A simple class that provides the basic implementation needed to make any element a drop target that can have
21865  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
21866  * @constructor
21867  * @param {String/HTMLElement/Element} el The container element
21868  * @param {Object} config
21869  */
21870 Roo.dd.DropTarget = function(el, config){
21871     this.el = Roo.get(el);
21872     
21873     var listeners = false; ;
21874     if (config && config.listeners) {
21875         listeners= config.listeners;
21876         delete config.listeners;
21877     }
21878     Roo.apply(this, config);
21879     
21880     if(this.containerScroll){
21881         Roo.dd.ScrollManager.register(this.el);
21882     }
21883     this.addEvents( {
21884          /**
21885          * @scope Roo.dd.DropTarget
21886          */
21887          
21888          /**
21889          * @event enter
21890          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
21891          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
21892          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
21893          * 
21894          * IMPORTANT : it should set this.overClass and this.dropAllowed
21895          * 
21896          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21897          * @param {Event} e The event
21898          * @param {Object} data An object containing arbitrary data supplied by the drag source
21899          */
21900         "enter" : true,
21901         
21902          /**
21903          * @event over
21904          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
21905          * This method will be called on every mouse movement while the drag source is over the drop target.
21906          * This default implementation simply returns the dropAllowed config value.
21907          * 
21908          * IMPORTANT : it should set this.dropAllowed
21909          * 
21910          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21911          * @param {Event} e The event
21912          * @param {Object} data An object containing arbitrary data supplied by the drag source
21913          
21914          */
21915         "over" : true,
21916         /**
21917          * @event out
21918          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
21919          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
21920          * overClass (if any) from the drop element.
21921          * 
21922          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21923          * @param {Event} e The event
21924          * @param {Object} data An object containing arbitrary data supplied by the drag source
21925          */
21926          "out" : true,
21927          
21928         /**
21929          * @event drop
21930          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
21931          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
21932          * implementation that does something to process the drop event and returns true so that the drag source's
21933          * repair action does not run.
21934          * 
21935          * IMPORTANT : it should set this.success
21936          * 
21937          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21938          * @param {Event} e The event
21939          * @param {Object} data An object containing arbitrary data supplied by the drag source
21940         */
21941          "drop" : true
21942     });
21943             
21944      
21945     Roo.dd.DropTarget.superclass.constructor.call(  this, 
21946         this.el.dom, 
21947         this.ddGroup || this.group,
21948         {
21949             isTarget: true,
21950             listeners : listeners || {} 
21951            
21952         
21953         }
21954     );
21955
21956 };
21957
21958 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
21959     /**
21960      * @cfg {String} overClass
21961      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
21962      */
21963      /**
21964      * @cfg {String} ddGroup
21965      * The drag drop group to handle drop events for
21966      */
21967      
21968     /**
21969      * @cfg {String} dropAllowed
21970      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21971      */
21972     dropAllowed : "x-dd-drop-ok",
21973     /**
21974      * @cfg {String} dropNotAllowed
21975      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21976      */
21977     dropNotAllowed : "x-dd-drop-nodrop",
21978     /**
21979      * @cfg {boolean} success
21980      * set this after drop listener.. 
21981      */
21982     success : false,
21983     /**
21984      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
21985      * if the drop point is valid for over/enter..
21986      */
21987     valid : false,
21988     // private
21989     isTarget : true,
21990
21991     // private
21992     isNotifyTarget : true,
21993     
21994     /**
21995      * @hide
21996      */
21997     notifyEnter : function(dd, e, data)
21998     {
21999         this.valid = true;
22000         this.fireEvent('enter', dd, e, data);
22001         if(this.overClass){
22002             this.el.addClass(this.overClass);
22003         }
22004         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22005             this.valid ? this.dropAllowed : this.dropNotAllowed
22006         );
22007     },
22008
22009     /**
22010      * @hide
22011      */
22012     notifyOver : function(dd, e, data)
22013     {
22014         this.valid = true;
22015         this.fireEvent('over', dd, e, data);
22016         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22017             this.valid ? this.dropAllowed : this.dropNotAllowed
22018         );
22019     },
22020
22021     /**
22022      * @hide
22023      */
22024     notifyOut : function(dd, e, data)
22025     {
22026         this.fireEvent('out', dd, e, data);
22027         if(this.overClass){
22028             this.el.removeClass(this.overClass);
22029         }
22030     },
22031
22032     /**
22033      * @hide
22034      */
22035     notifyDrop : function(dd, e, data)
22036     {
22037         this.success = false;
22038         this.fireEvent('drop', dd, e, data);
22039         return this.success;
22040     }
22041 });/*
22042  * Based on:
22043  * Ext JS Library 1.1.1
22044  * Copyright(c) 2006-2007, Ext JS, LLC.
22045  *
22046  * Originally Released Under LGPL - original licence link has changed is not relivant.
22047  *
22048  * Fork - LGPL
22049  * <script type="text/javascript">
22050  */
22051
22052
22053 /**
22054  * @class Roo.dd.DragZone
22055  * @extends Roo.dd.DragSource
22056  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22057  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22058  * @constructor
22059  * @param {String/HTMLElement/Element} el The container element
22060  * @param {Object} config
22061  */
22062 Roo.dd.DragZone = function(el, config){
22063     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22064     if(this.containerScroll){
22065         Roo.dd.ScrollManager.register(this.el);
22066     }
22067 };
22068
22069 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22070     /**
22071      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22072      * for auto scrolling during drag operations.
22073      */
22074     /**
22075      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22076      * method after a failed drop (defaults to "c3daf9" - light blue)
22077      */
22078
22079     /**
22080      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22081      * for a valid target to drag based on the mouse down. Override this method
22082      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22083      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22084      * @param {EventObject} e The mouse down event
22085      * @return {Object} The dragData
22086      */
22087     getDragData : function(e){
22088         return Roo.dd.Registry.getHandleFromEvent(e);
22089     },
22090     
22091     /**
22092      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22093      * this.dragData.ddel
22094      * @param {Number} x The x position of the click on the dragged object
22095      * @param {Number} y The y position of the click on the dragged object
22096      * @return {Boolean} true to continue the drag, false to cancel
22097      */
22098     onInitDrag : function(x, y){
22099         this.proxy.update(this.dragData.ddel.cloneNode(true));
22100         this.onStartDrag(x, y);
22101         return true;
22102     },
22103     
22104     /**
22105      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22106      */
22107     afterRepair : function(){
22108         if(Roo.enableFx){
22109             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22110         }
22111         this.dragging = false;
22112     },
22113
22114     /**
22115      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22116      * the XY of this.dragData.ddel
22117      * @param {EventObject} e The mouse up event
22118      * @return {Array} The xy location (e.g. [100, 200])
22119      */
22120     getRepairXY : function(e){
22121         return Roo.Element.fly(this.dragData.ddel).getXY();  
22122     }
22123 });/*
22124  * Based on:
22125  * Ext JS Library 1.1.1
22126  * Copyright(c) 2006-2007, Ext JS, LLC.
22127  *
22128  * Originally Released Under LGPL - original licence link has changed is not relivant.
22129  *
22130  * Fork - LGPL
22131  * <script type="text/javascript">
22132  */
22133 /**
22134  * @class Roo.dd.DropZone
22135  * @extends Roo.dd.DropTarget
22136  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22137  * By default, this class requires that child nodes accepting drop 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.DropZone = function(el, config){
22143     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22144 };
22145
22146 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22147     /**
22148      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22149      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22150      * provide your own custom lookup.
22151      * @param {Event} e The event
22152      * @return {Object} data The custom data
22153      */
22154     getTargetFromEvent : function(e){
22155         return Roo.dd.Registry.getTargetFromEvent(e);
22156     },
22157
22158     /**
22159      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22160      * that it has registered.  This method has no default implementation and should be overridden to provide
22161      * node-specific processing if necessary.
22162      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22163      * {@link #getTargetFromEvent} for this node)
22164      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22165      * @param {Event} e The event
22166      * @param {Object} data An object containing arbitrary data supplied by the drag source
22167      */
22168     onNodeEnter : function(n, dd, e, data){
22169         
22170     },
22171
22172     /**
22173      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22174      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22175      * overridden to provide the proper feedback.
22176      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22177      * {@link #getTargetFromEvent} for this node)
22178      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22179      * @param {Event} e The event
22180      * @param {Object} data An object containing arbitrary data supplied by the drag source
22181      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22182      * underlying {@link Roo.dd.StatusProxy} can be updated
22183      */
22184     onNodeOver : function(n, dd, e, data){
22185         return this.dropAllowed;
22186     },
22187
22188     /**
22189      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22190      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22191      * node-specific processing if necessary.
22192      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22193      * {@link #getTargetFromEvent} for this node)
22194      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22195      * @param {Event} e The event
22196      * @param {Object} data An object containing arbitrary data supplied by the drag source
22197      */
22198     onNodeOut : function(n, dd, e, data){
22199         
22200     },
22201
22202     /**
22203      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22204      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22205      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22206      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22207      * {@link #getTargetFromEvent} for this node)
22208      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22209      * @param {Event} e The event
22210      * @param {Object} data An object containing arbitrary data supplied by the drag source
22211      * @return {Boolean} True if the drop was valid, else false
22212      */
22213     onNodeDrop : function(n, dd, e, data){
22214         return false;
22215     },
22216
22217     /**
22218      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22219      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22220      * it should be overridden to provide the proper feedback if necessary.
22221      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22222      * @param {Event} e The event
22223      * @param {Object} data An object containing arbitrary data supplied by the drag source
22224      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22225      * underlying {@link Roo.dd.StatusProxy} can be updated
22226      */
22227     onContainerOver : function(dd, e, data){
22228         return this.dropNotAllowed;
22229     },
22230
22231     /**
22232      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22233      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22234      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22235      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22236      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22237      * @param {Event} e The event
22238      * @param {Object} data An object containing arbitrary data supplied by the drag source
22239      * @return {Boolean} True if the drop was valid, else false
22240      */
22241     onContainerDrop : function(dd, e, data){
22242         return false;
22243     },
22244
22245     /**
22246      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22247      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22248      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22249      * you should override this method and provide a custom implementation.
22250      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22251      * @param {Event} e The event
22252      * @param {Object} data An object containing arbitrary data supplied by the drag source
22253      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22254      * underlying {@link Roo.dd.StatusProxy} can be updated
22255      */
22256     notifyEnter : function(dd, e, data){
22257         return this.dropNotAllowed;
22258     },
22259
22260     /**
22261      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22262      * This method will be called on every mouse movement while the drag source is over the drop zone.
22263      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22264      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22265      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22266      * registered node, it will call {@link #onContainerOver}.
22267      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22268      * @param {Event} e The event
22269      * @param {Object} data An object containing arbitrary data supplied by the drag source
22270      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22271      * underlying {@link Roo.dd.StatusProxy} can be updated
22272      */
22273     notifyOver : function(dd, e, data){
22274         var n = this.getTargetFromEvent(e);
22275         if(!n){ // not over valid drop target
22276             if(this.lastOverNode){
22277                 this.onNodeOut(this.lastOverNode, dd, e, data);
22278                 this.lastOverNode = null;
22279             }
22280             return this.onContainerOver(dd, e, data);
22281         }
22282         if(this.lastOverNode != n){
22283             if(this.lastOverNode){
22284                 this.onNodeOut(this.lastOverNode, dd, e, data);
22285             }
22286             this.onNodeEnter(n, dd, e, data);
22287             this.lastOverNode = n;
22288         }
22289         return this.onNodeOver(n, dd, e, data);
22290     },
22291
22292     /**
22293      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22294      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22295      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22296      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22297      * @param {Event} e The event
22298      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22299      */
22300     notifyOut : function(dd, e, data){
22301         if(this.lastOverNode){
22302             this.onNodeOut(this.lastOverNode, dd, e, data);
22303             this.lastOverNode = null;
22304         }
22305     },
22306
22307     /**
22308      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22309      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22310      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22311      * otherwise it will call {@link #onContainerDrop}.
22312      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22313      * @param {Event} e The event
22314      * @param {Object} data An object containing arbitrary data supplied by the drag source
22315      * @return {Boolean} True if the drop was valid, else false
22316      */
22317     notifyDrop : function(dd, e, data){
22318         if(this.lastOverNode){
22319             this.onNodeOut(this.lastOverNode, dd, e, data);
22320             this.lastOverNode = null;
22321         }
22322         var n = this.getTargetFromEvent(e);
22323         return n ?
22324             this.onNodeDrop(n, dd, e, data) :
22325             this.onContainerDrop(dd, e, data);
22326     },
22327
22328     // private
22329     triggerCacheRefresh : function(){
22330         Roo.dd.DDM.refreshCache(this.groups);
22331     }  
22332 });/*
22333  * Based on:
22334  * Ext JS Library 1.1.1
22335  * Copyright(c) 2006-2007, Ext JS, LLC.
22336  *
22337  * Originally Released Under LGPL - original licence link has changed is not relivant.
22338  *
22339  * Fork - LGPL
22340  * <script type="text/javascript">
22341  */
22342
22343
22344 /**
22345  * @class Roo.data.SortTypes
22346  * @singleton
22347  * Defines the default sorting (casting?) comparison functions used when sorting data.
22348  */
22349 Roo.data.SortTypes = {
22350     /**
22351      * Default sort that does nothing
22352      * @param {Mixed} s The value being converted
22353      * @return {Mixed} The comparison value
22354      */
22355     none : function(s){
22356         return s;
22357     },
22358     
22359     /**
22360      * The regular expression used to strip tags
22361      * @type {RegExp}
22362      * @property
22363      */
22364     stripTagsRE : /<\/?[^>]+>/gi,
22365     
22366     /**
22367      * Strips all HTML tags to sort on text only
22368      * @param {Mixed} s The value being converted
22369      * @return {String} The comparison value
22370      */
22371     asText : function(s){
22372         return String(s).replace(this.stripTagsRE, "");
22373     },
22374     
22375     /**
22376      * Strips all HTML tags to sort on text only - Case insensitive
22377      * @param {Mixed} s The value being converted
22378      * @return {String} The comparison value
22379      */
22380     asUCText : function(s){
22381         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22382     },
22383     
22384     /**
22385      * Case insensitive string
22386      * @param {Mixed} s The value being converted
22387      * @return {String} The comparison value
22388      */
22389     asUCString : function(s) {
22390         return String(s).toUpperCase();
22391     },
22392     
22393     /**
22394      * Date sorting
22395      * @param {Mixed} s The value being converted
22396      * @return {Number} The comparison value
22397      */
22398     asDate : function(s) {
22399         if(!s){
22400             return 0;
22401         }
22402         if(s instanceof Date){
22403             return s.getTime();
22404         }
22405         return Date.parse(String(s));
22406     },
22407     
22408     /**
22409      * Float sorting
22410      * @param {Mixed} s The value being converted
22411      * @return {Float} The comparison value
22412      */
22413     asFloat : function(s) {
22414         var val = parseFloat(String(s).replace(/,/g, ""));
22415         if(isNaN(val)) {
22416             val = 0;
22417         }
22418         return val;
22419     },
22420     
22421     /**
22422      * Integer sorting
22423      * @param {Mixed} s The value being converted
22424      * @return {Number} The comparison value
22425      */
22426     asInt : function(s) {
22427         var val = parseInt(String(s).replace(/,/g, ""));
22428         if(isNaN(val)) {
22429             val = 0;
22430         }
22431         return val;
22432     }
22433 };/*
22434  * Based on:
22435  * Ext JS Library 1.1.1
22436  * Copyright(c) 2006-2007, Ext JS, LLC.
22437  *
22438  * Originally Released Under LGPL - original licence link has changed is not relivant.
22439  *
22440  * Fork - LGPL
22441  * <script type="text/javascript">
22442  */
22443
22444 /**
22445 * @class Roo.data.Record
22446  * Instances of this class encapsulate both record <em>definition</em> information, and record
22447  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22448  * to access Records cached in an {@link Roo.data.Store} object.<br>
22449  * <p>
22450  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22451  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22452  * objects.<br>
22453  * <p>
22454  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22455  * @constructor
22456  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22457  * {@link #create}. The parameters are the same.
22458  * @param {Array} data An associative Array of data values keyed by the field name.
22459  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22460  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22461  * not specified an integer id is generated.
22462  */
22463 Roo.data.Record = function(data, id){
22464     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22465     this.data = data;
22466 };
22467
22468 /**
22469  * Generate a constructor for a specific record layout.
22470  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22471  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22472  * Each field definition object may contain the following properties: <ul>
22473  * <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,
22474  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22475  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22476  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22477  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22478  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22479  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22480  * this may be omitted.</p></li>
22481  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22482  * <ul><li>auto (Default, implies no conversion)</li>
22483  * <li>string</li>
22484  * <li>int</li>
22485  * <li>float</li>
22486  * <li>boolean</li>
22487  * <li>date</li></ul></p></li>
22488  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22489  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22490  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22491  * by the Reader into an object that will be stored in the Record. It is passed the
22492  * following parameters:<ul>
22493  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22494  * </ul></p></li>
22495  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22496  * </ul>
22497  * <br>usage:<br><pre><code>
22498 var TopicRecord = Roo.data.Record.create(
22499     {name: 'title', mapping: 'topic_title'},
22500     {name: 'author', mapping: 'username'},
22501     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22502     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22503     {name: 'lastPoster', mapping: 'user2'},
22504     {name: 'excerpt', mapping: 'post_text'}
22505 );
22506
22507 var myNewRecord = new TopicRecord({
22508     title: 'Do my job please',
22509     author: 'noobie',
22510     totalPosts: 1,
22511     lastPost: new Date(),
22512     lastPoster: 'Animal',
22513     excerpt: 'No way dude!'
22514 });
22515 myStore.add(myNewRecord);
22516 </code></pre>
22517  * @method create
22518  * @static
22519  */
22520 Roo.data.Record.create = function(o){
22521     var f = function(){
22522         f.superclass.constructor.apply(this, arguments);
22523     };
22524     Roo.extend(f, Roo.data.Record);
22525     var p = f.prototype;
22526     p.fields = new Roo.util.MixedCollection(false, function(field){
22527         return field.name;
22528     });
22529     for(var i = 0, len = o.length; i < len; i++){
22530         p.fields.add(new Roo.data.Field(o[i]));
22531     }
22532     f.getField = function(name){
22533         return p.fields.get(name);  
22534     };
22535     return f;
22536 };
22537
22538 Roo.data.Record.AUTO_ID = 1000;
22539 Roo.data.Record.EDIT = 'edit';
22540 Roo.data.Record.REJECT = 'reject';
22541 Roo.data.Record.COMMIT = 'commit';
22542
22543 Roo.data.Record.prototype = {
22544     /**
22545      * Readonly flag - true if this record has been modified.
22546      * @type Boolean
22547      */
22548     dirty : false,
22549     editing : false,
22550     error: null,
22551     modified: null,
22552
22553     // private
22554     join : function(store){
22555         this.store = store;
22556     },
22557
22558     /**
22559      * Set the named field to the specified value.
22560      * @param {String} name The name of the field to set.
22561      * @param {Object} value The value to set the field to.
22562      */
22563     set : function(name, value){
22564         if(this.data[name] == value){
22565             return;
22566         }
22567         this.dirty = true;
22568         if(!this.modified){
22569             this.modified = {};
22570         }
22571         if(typeof this.modified[name] == 'undefined'){
22572             this.modified[name] = this.data[name];
22573         }
22574         this.data[name] = value;
22575         if(!this.editing && this.store){
22576             this.store.afterEdit(this);
22577         }       
22578     },
22579
22580     /**
22581      * Get the value of the named field.
22582      * @param {String} name The name of the field to get the value of.
22583      * @return {Object} The value of the field.
22584      */
22585     get : function(name){
22586         return this.data[name]; 
22587     },
22588
22589     // private
22590     beginEdit : function(){
22591         this.editing = true;
22592         this.modified = {}; 
22593     },
22594
22595     // private
22596     cancelEdit : function(){
22597         this.editing = false;
22598         delete this.modified;
22599     },
22600
22601     // private
22602     endEdit : function(){
22603         this.editing = false;
22604         if(this.dirty && this.store){
22605             this.store.afterEdit(this);
22606         }
22607     },
22608
22609     /**
22610      * Usually called by the {@link Roo.data.Store} which owns the Record.
22611      * Rejects all changes made to the Record since either creation, or the last commit operation.
22612      * Modified fields are reverted to their original values.
22613      * <p>
22614      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22615      * of reject operations.
22616      */
22617     reject : function(){
22618         var m = this.modified;
22619         for(var n in m){
22620             if(typeof m[n] != "function"){
22621                 this.data[n] = m[n];
22622             }
22623         }
22624         this.dirty = false;
22625         delete this.modified;
22626         this.editing = false;
22627         if(this.store){
22628             this.store.afterReject(this);
22629         }
22630     },
22631
22632     /**
22633      * Usually called by the {@link Roo.data.Store} which owns the Record.
22634      * Commits all changes made to the Record since either creation, or the last commit operation.
22635      * <p>
22636      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22637      * of commit operations.
22638      */
22639     commit : function(){
22640         this.dirty = false;
22641         delete this.modified;
22642         this.editing = false;
22643         if(this.store){
22644             this.store.afterCommit(this);
22645         }
22646     },
22647
22648     // private
22649     hasError : function(){
22650         return this.error != null;
22651     },
22652
22653     // private
22654     clearError : function(){
22655         this.error = null;
22656     },
22657
22658     /**
22659      * Creates a copy of this record.
22660      * @param {String} id (optional) A new record id if you don't want to use this record's id
22661      * @return {Record}
22662      */
22663     copy : function(newId) {
22664         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22665     }
22666 };/*
22667  * Based on:
22668  * Ext JS Library 1.1.1
22669  * Copyright(c) 2006-2007, Ext JS, LLC.
22670  *
22671  * Originally Released Under LGPL - original licence link has changed is not relivant.
22672  *
22673  * Fork - LGPL
22674  * <script type="text/javascript">
22675  */
22676
22677
22678
22679 /**
22680  * @class Roo.data.Store
22681  * @extends Roo.util.Observable
22682  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22683  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22684  * <p>
22685  * 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
22686  * has no knowledge of the format of the data returned by the Proxy.<br>
22687  * <p>
22688  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22689  * instances from the data object. These records are cached and made available through accessor functions.
22690  * @constructor
22691  * Creates a new Store.
22692  * @param {Object} config A config object containing the objects needed for the Store to access data,
22693  * and read the data into Records.
22694  */
22695 Roo.data.Store = function(config){
22696     this.data = new Roo.util.MixedCollection(false);
22697     this.data.getKey = function(o){
22698         return o.id;
22699     };
22700     this.baseParams = {};
22701     // private
22702     this.paramNames = {
22703         "start" : "start",
22704         "limit" : "limit",
22705         "sort" : "sort",
22706         "dir" : "dir",
22707         "multisort" : "_multisort"
22708     };
22709
22710     if(config && config.data){
22711         this.inlineData = config.data;
22712         delete config.data;
22713     }
22714
22715     Roo.apply(this, config);
22716     
22717     if(this.reader){ // reader passed
22718         this.reader = Roo.factory(this.reader, Roo.data);
22719         this.reader.xmodule = this.xmodule || false;
22720         if(!this.recordType){
22721             this.recordType = this.reader.recordType;
22722         }
22723         if(this.reader.onMetaChange){
22724             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22725         }
22726     }
22727
22728     if(this.recordType){
22729         this.fields = this.recordType.prototype.fields;
22730     }
22731     this.modified = [];
22732
22733     this.addEvents({
22734         /**
22735          * @event datachanged
22736          * Fires when the data cache has changed, and a widget which is using this Store
22737          * as a Record cache should refresh its view.
22738          * @param {Store} this
22739          */
22740         datachanged : true,
22741         /**
22742          * @event metachange
22743          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22744          * @param {Store} this
22745          * @param {Object} meta The JSON metadata
22746          */
22747         metachange : true,
22748         /**
22749          * @event add
22750          * Fires when Records have been added to the Store
22751          * @param {Store} this
22752          * @param {Roo.data.Record[]} records The array of Records added
22753          * @param {Number} index The index at which the record(s) were added
22754          */
22755         add : true,
22756         /**
22757          * @event remove
22758          * Fires when a Record has been removed from the Store
22759          * @param {Store} this
22760          * @param {Roo.data.Record} record The Record that was removed
22761          * @param {Number} index The index at which the record was removed
22762          */
22763         remove : true,
22764         /**
22765          * @event update
22766          * Fires when a Record has been updated
22767          * @param {Store} this
22768          * @param {Roo.data.Record} record The Record that was updated
22769          * @param {String} operation The update operation being performed.  Value may be one of:
22770          * <pre><code>
22771  Roo.data.Record.EDIT
22772  Roo.data.Record.REJECT
22773  Roo.data.Record.COMMIT
22774          * </code></pre>
22775          */
22776         update : true,
22777         /**
22778          * @event clear
22779          * Fires when the data cache has been cleared.
22780          * @param {Store} this
22781          */
22782         clear : true,
22783         /**
22784          * @event beforeload
22785          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22786          * the load action will be canceled.
22787          * @param {Store} this
22788          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22789          */
22790         beforeload : true,
22791         /**
22792          * @event beforeloadadd
22793          * Fires after a new set of Records has been loaded.
22794          * @param {Store} this
22795          * @param {Roo.data.Record[]} records The Records that were loaded
22796          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22797          */
22798         beforeloadadd : true,
22799         /**
22800          * @event load
22801          * Fires after a new set of Records has been loaded, before they are added to the store.
22802          * @param {Store} this
22803          * @param {Roo.data.Record[]} records The Records that were loaded
22804          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22805          * @params {Object} return from reader
22806          */
22807         load : true,
22808         /**
22809          * @event loadexception
22810          * Fires if an exception occurs in the Proxy during loading.
22811          * Called with the signature of the Proxy's "loadexception" event.
22812          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22813          * 
22814          * @param {Proxy} 
22815          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22816          * @param {Object} load options 
22817          * @param {Object} jsonData from your request (normally this contains the Exception)
22818          */
22819         loadexception : true
22820     });
22821     
22822     if(this.proxy){
22823         this.proxy = Roo.factory(this.proxy, Roo.data);
22824         this.proxy.xmodule = this.xmodule || false;
22825         this.relayEvents(this.proxy,  ["loadexception"]);
22826     }
22827     this.sortToggle = {};
22828     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22829
22830     Roo.data.Store.superclass.constructor.call(this);
22831
22832     if(this.inlineData){
22833         this.loadData(this.inlineData);
22834         delete this.inlineData;
22835     }
22836 };
22837
22838 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22839      /**
22840     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22841     * without a remote query - used by combo/forms at present.
22842     */
22843     
22844     /**
22845     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
22846     */
22847     /**
22848     * @cfg {Array} data Inline data to be loaded when the store is initialized.
22849     */
22850     /**
22851     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
22852     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
22853     */
22854     /**
22855     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
22856     * on any HTTP request
22857     */
22858     /**
22859     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
22860     */
22861     /**
22862     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
22863     */
22864     multiSort: false,
22865     /**
22866     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
22867     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
22868     */
22869     remoteSort : false,
22870
22871     /**
22872     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
22873      * loaded or when a record is removed. (defaults to false).
22874     */
22875     pruneModifiedRecords : false,
22876
22877     // private
22878     lastOptions : null,
22879
22880     /**
22881      * Add Records to the Store and fires the add event.
22882      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22883      */
22884     add : function(records){
22885         records = [].concat(records);
22886         for(var i = 0, len = records.length; i < len; i++){
22887             records[i].join(this);
22888         }
22889         var index = this.data.length;
22890         this.data.addAll(records);
22891         this.fireEvent("add", this, records, index);
22892     },
22893
22894     /**
22895      * Remove a Record from the Store and fires the remove event.
22896      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
22897      */
22898     remove : function(record){
22899         var index = this.data.indexOf(record);
22900         this.data.removeAt(index);
22901         if(this.pruneModifiedRecords){
22902             this.modified.remove(record);
22903         }
22904         this.fireEvent("remove", this, record, index);
22905     },
22906
22907     /**
22908      * Remove all Records from the Store and fires the clear event.
22909      */
22910     removeAll : function(){
22911         this.data.clear();
22912         if(this.pruneModifiedRecords){
22913             this.modified = [];
22914         }
22915         this.fireEvent("clear", this);
22916     },
22917
22918     /**
22919      * Inserts Records to the Store at the given index and fires the add event.
22920      * @param {Number} index The start index at which to insert the passed Records.
22921      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22922      */
22923     insert : function(index, records){
22924         records = [].concat(records);
22925         for(var i = 0, len = records.length; i < len; i++){
22926             this.data.insert(index, records[i]);
22927             records[i].join(this);
22928         }
22929         this.fireEvent("add", this, records, index);
22930     },
22931
22932     /**
22933      * Get the index within the cache of the passed Record.
22934      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
22935      * @return {Number} The index of the passed Record. Returns -1 if not found.
22936      */
22937     indexOf : function(record){
22938         return this.data.indexOf(record);
22939     },
22940
22941     /**
22942      * Get the index within the cache of the Record with the passed id.
22943      * @param {String} id The id of the Record to find.
22944      * @return {Number} The index of the Record. Returns -1 if not found.
22945      */
22946     indexOfId : function(id){
22947         return this.data.indexOfKey(id);
22948     },
22949
22950     /**
22951      * Get the Record with the specified id.
22952      * @param {String} id The id of the Record to find.
22953      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
22954      */
22955     getById : function(id){
22956         return this.data.key(id);
22957     },
22958
22959     /**
22960      * Get the Record at the specified index.
22961      * @param {Number} index The index of the Record to find.
22962      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
22963      */
22964     getAt : function(index){
22965         return this.data.itemAt(index);
22966     },
22967
22968     /**
22969      * Returns a range of Records between specified indices.
22970      * @param {Number} startIndex (optional) The starting index (defaults to 0)
22971      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
22972      * @return {Roo.data.Record[]} An array of Records
22973      */
22974     getRange : function(start, end){
22975         return this.data.getRange(start, end);
22976     },
22977
22978     // private
22979     storeOptions : function(o){
22980         o = Roo.apply({}, o);
22981         delete o.callback;
22982         delete o.scope;
22983         this.lastOptions = o;
22984     },
22985
22986     /**
22987      * Loads the Record cache from the configured Proxy using the configured Reader.
22988      * <p>
22989      * If using remote paging, then the first load call must specify the <em>start</em>
22990      * and <em>limit</em> properties in the options.params property to establish the initial
22991      * position within the dataset, and the number of Records to cache on each read from the Proxy.
22992      * <p>
22993      * <strong>It is important to note that for remote data sources, loading is asynchronous,
22994      * and this call will return before the new data has been loaded. Perform any post-processing
22995      * in a callback function, or in a "load" event handler.</strong>
22996      * <p>
22997      * @param {Object} options An object containing properties which control loading options:<ul>
22998      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
22999      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23000      * passed the following arguments:<ul>
23001      * <li>r : Roo.data.Record[]</li>
23002      * <li>options: Options object from the load call</li>
23003      * <li>success: Boolean success indicator</li></ul></li>
23004      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23005      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23006      * </ul>
23007      */
23008     load : function(options){
23009         options = options || {};
23010         if(this.fireEvent("beforeload", this, options) !== false){
23011             this.storeOptions(options);
23012             var p = Roo.apply(options.params || {}, this.baseParams);
23013             // if meta was not loaded from remote source.. try requesting it.
23014             if (!this.reader.metaFromRemote) {
23015                 p._requestMeta = 1;
23016             }
23017             if(this.sortInfo && this.remoteSort){
23018                 var pn = this.paramNames;
23019                 p[pn["sort"]] = this.sortInfo.field;
23020                 p[pn["dir"]] = this.sortInfo.direction;
23021             }
23022             if (this.multiSort) {
23023                 var pn = this.paramNames;
23024                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23025             }
23026             
23027             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23028         }
23029     },
23030
23031     /**
23032      * Reloads the Record cache from the configured Proxy using the configured Reader and
23033      * the options from the last load operation performed.
23034      * @param {Object} options (optional) An object containing properties which may override the options
23035      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23036      * the most recently used options are reused).
23037      */
23038     reload : function(options){
23039         this.load(Roo.applyIf(options||{}, this.lastOptions));
23040     },
23041
23042     // private
23043     // Called as a callback by the Reader during a load operation.
23044     loadRecords : function(o, options, success){
23045         if(!o || success === false){
23046             if(success !== false){
23047                 this.fireEvent("load", this, [], options, o);
23048             }
23049             if(options.callback){
23050                 options.callback.call(options.scope || this, [], options, false);
23051             }
23052             return;
23053         }
23054         // if data returned failure - throw an exception.
23055         if (o.success === false) {
23056             // show a message if no listener is registered.
23057             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23058                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23059             }
23060             // loadmask wil be hooked into this..
23061             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23062             return;
23063         }
23064         var r = o.records, t = o.totalRecords || r.length;
23065         
23066         this.fireEvent("beforeloadadd", this, r, options, o);
23067         
23068         if(!options || options.add !== true){
23069             if(this.pruneModifiedRecords){
23070                 this.modified = [];
23071             }
23072             for(var i = 0, len = r.length; i < len; i++){
23073                 r[i].join(this);
23074             }
23075             if(this.snapshot){
23076                 this.data = this.snapshot;
23077                 delete this.snapshot;
23078             }
23079             this.data.clear();
23080             this.data.addAll(r);
23081             this.totalLength = t;
23082             this.applySort();
23083             this.fireEvent("datachanged", this);
23084         }else{
23085             this.totalLength = Math.max(t, this.data.length+r.length);
23086             this.add(r);
23087         }
23088         
23089         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23090                 
23091             var e = new Roo.data.Record({});
23092
23093             e.set(this.parent.displayField, this.parent.emptyTitle);
23094             e.set(this.parent.valueField, '');
23095
23096             this.insert(0, e);
23097         }
23098             
23099         this.fireEvent("load", this, r, options, o);
23100         if(options.callback){
23101             options.callback.call(options.scope || this, r, options, true);
23102         }
23103     },
23104
23105
23106     /**
23107      * Loads data from a passed data block. A Reader which understands the format of the data
23108      * must have been configured in the constructor.
23109      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23110      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23111      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23112      */
23113     loadData : function(o, append){
23114         var r = this.reader.readRecords(o);
23115         this.loadRecords(r, {add: append}, true);
23116     },
23117
23118     /**
23119      * Gets the number of cached records.
23120      * <p>
23121      * <em>If using paging, this may not be the total size of the dataset. If the data object
23122      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23123      * the data set size</em>
23124      */
23125     getCount : function(){
23126         return this.data.length || 0;
23127     },
23128
23129     /**
23130      * Gets the total number of records in the dataset as returned by the server.
23131      * <p>
23132      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23133      * the dataset size</em>
23134      */
23135     getTotalCount : function(){
23136         return this.totalLength || 0;
23137     },
23138
23139     /**
23140      * Returns the sort state of the Store as an object with two properties:
23141      * <pre><code>
23142  field {String} The name of the field by which the Records are sorted
23143  direction {String} The sort order, "ASC" or "DESC"
23144      * </code></pre>
23145      */
23146     getSortState : function(){
23147         return this.sortInfo;
23148     },
23149
23150     // private
23151     applySort : function(){
23152         if(this.sortInfo && !this.remoteSort){
23153             var s = this.sortInfo, f = s.field;
23154             var st = this.fields.get(f).sortType;
23155             var fn = function(r1, r2){
23156                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23157                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23158             };
23159             this.data.sort(s.direction, fn);
23160             if(this.snapshot && this.snapshot != this.data){
23161                 this.snapshot.sort(s.direction, fn);
23162             }
23163         }
23164     },
23165
23166     /**
23167      * Sets the default sort column and order to be used by the next load operation.
23168      * @param {String} fieldName The name of the field to sort by.
23169      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23170      */
23171     setDefaultSort : function(field, dir){
23172         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23173     },
23174
23175     /**
23176      * Sort the Records.
23177      * If remote sorting is used, the sort is performed on the server, and the cache is
23178      * reloaded. If local sorting is used, the cache is sorted internally.
23179      * @param {String} fieldName The name of the field to sort by.
23180      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23181      */
23182     sort : function(fieldName, dir){
23183         var f = this.fields.get(fieldName);
23184         if(!dir){
23185             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23186             
23187             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23188                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23189             }else{
23190                 dir = f.sortDir;
23191             }
23192         }
23193         this.sortToggle[f.name] = dir;
23194         this.sortInfo = {field: f.name, direction: dir};
23195         if(!this.remoteSort){
23196             this.applySort();
23197             this.fireEvent("datachanged", this);
23198         }else{
23199             this.load(this.lastOptions);
23200         }
23201     },
23202
23203     /**
23204      * Calls the specified function for each of the Records in the cache.
23205      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23206      * Returning <em>false</em> aborts and exits the iteration.
23207      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23208      */
23209     each : function(fn, scope){
23210         this.data.each(fn, scope);
23211     },
23212
23213     /**
23214      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23215      * (e.g., during paging).
23216      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23217      */
23218     getModifiedRecords : function(){
23219         return this.modified;
23220     },
23221
23222     // private
23223     createFilterFn : function(property, value, anyMatch){
23224         if(!value.exec){ // not a regex
23225             value = String(value);
23226             if(value.length == 0){
23227                 return false;
23228             }
23229             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23230         }
23231         return function(r){
23232             return value.test(r.data[property]);
23233         };
23234     },
23235
23236     /**
23237      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23238      * @param {String} property A field on your records
23239      * @param {Number} start The record index to start at (defaults to 0)
23240      * @param {Number} end The last record index to include (defaults to length - 1)
23241      * @return {Number} The sum
23242      */
23243     sum : function(property, start, end){
23244         var rs = this.data.items, v = 0;
23245         start = start || 0;
23246         end = (end || end === 0) ? end : rs.length-1;
23247
23248         for(var i = start; i <= end; i++){
23249             v += (rs[i].data[property] || 0);
23250         }
23251         return v;
23252     },
23253
23254     /**
23255      * Filter the records by a specified property.
23256      * @param {String} field A field on your records
23257      * @param {String/RegExp} value Either a string that the field
23258      * should start with or a RegExp to test against the field
23259      * @param {Boolean} anyMatch True to match any part not just the beginning
23260      */
23261     filter : function(property, value, anyMatch){
23262         var fn = this.createFilterFn(property, value, anyMatch);
23263         return fn ? this.filterBy(fn) : this.clearFilter();
23264     },
23265
23266     /**
23267      * Filter by a function. The specified function will be called with each
23268      * record in this data source. If the function returns true the record is included,
23269      * otherwise it is filtered.
23270      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23271      * @param {Object} scope (optional) The scope of the function (defaults to this)
23272      */
23273     filterBy : function(fn, scope){
23274         this.snapshot = this.snapshot || this.data;
23275         this.data = this.queryBy(fn, scope||this);
23276         this.fireEvent("datachanged", this);
23277     },
23278
23279     /**
23280      * Query the records by a specified property.
23281      * @param {String} field A field on your records
23282      * @param {String/RegExp} value Either a string that the field
23283      * should start with or a RegExp to test against the field
23284      * @param {Boolean} anyMatch True to match any part not just the beginning
23285      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23286      */
23287     query : function(property, value, anyMatch){
23288         var fn = this.createFilterFn(property, value, anyMatch);
23289         return fn ? this.queryBy(fn) : this.data.clone();
23290     },
23291
23292     /**
23293      * Query by a function. The specified function will be called with each
23294      * record in this data source. If the function returns true the record is included
23295      * in the results.
23296      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23297      * @param {Object} scope (optional) The scope of the function (defaults to this)
23298       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23299      **/
23300     queryBy : function(fn, scope){
23301         var data = this.snapshot || this.data;
23302         return data.filterBy(fn, scope||this);
23303     },
23304
23305     /**
23306      * Collects unique values for a particular dataIndex from this store.
23307      * @param {String} dataIndex The property to collect
23308      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23309      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23310      * @return {Array} An array of the unique values
23311      **/
23312     collect : function(dataIndex, allowNull, bypassFilter){
23313         var d = (bypassFilter === true && this.snapshot) ?
23314                 this.snapshot.items : this.data.items;
23315         var v, sv, r = [], l = {};
23316         for(var i = 0, len = d.length; i < len; i++){
23317             v = d[i].data[dataIndex];
23318             sv = String(v);
23319             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23320                 l[sv] = true;
23321                 r[r.length] = v;
23322             }
23323         }
23324         return r;
23325     },
23326
23327     /**
23328      * Revert to a view of the Record cache with no filtering applied.
23329      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23330      */
23331     clearFilter : function(suppressEvent){
23332         if(this.snapshot && this.snapshot != this.data){
23333             this.data = this.snapshot;
23334             delete this.snapshot;
23335             if(suppressEvent !== true){
23336                 this.fireEvent("datachanged", this);
23337             }
23338         }
23339     },
23340
23341     // private
23342     afterEdit : function(record){
23343         if(this.modified.indexOf(record) == -1){
23344             this.modified.push(record);
23345         }
23346         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23347     },
23348     
23349     // private
23350     afterReject : function(record){
23351         this.modified.remove(record);
23352         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23353     },
23354
23355     // private
23356     afterCommit : function(record){
23357         this.modified.remove(record);
23358         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23359     },
23360
23361     /**
23362      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23363      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23364      */
23365     commitChanges : function(){
23366         var m = this.modified.slice(0);
23367         this.modified = [];
23368         for(var i = 0, len = m.length; i < len; i++){
23369             m[i].commit();
23370         }
23371     },
23372
23373     /**
23374      * Cancel outstanding changes on all changed records.
23375      */
23376     rejectChanges : function(){
23377         var m = this.modified.slice(0);
23378         this.modified = [];
23379         for(var i = 0, len = m.length; i < len; i++){
23380             m[i].reject();
23381         }
23382     },
23383
23384     onMetaChange : function(meta, rtype, o){
23385         this.recordType = rtype;
23386         this.fields = rtype.prototype.fields;
23387         delete this.snapshot;
23388         this.sortInfo = meta.sortInfo || this.sortInfo;
23389         this.modified = [];
23390         this.fireEvent('metachange', this, this.reader.meta);
23391     },
23392     
23393     moveIndex : function(data, type)
23394     {
23395         var index = this.indexOf(data);
23396         
23397         var newIndex = index + type;
23398         
23399         this.remove(data);
23400         
23401         this.insert(newIndex, data);
23402         
23403     }
23404 });/*
23405  * Based on:
23406  * Ext JS Library 1.1.1
23407  * Copyright(c) 2006-2007, Ext JS, LLC.
23408  *
23409  * Originally Released Under LGPL - original licence link has changed is not relivant.
23410  *
23411  * Fork - LGPL
23412  * <script type="text/javascript">
23413  */
23414
23415 /**
23416  * @class Roo.data.SimpleStore
23417  * @extends Roo.data.Store
23418  * Small helper class to make creating Stores from Array data easier.
23419  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23420  * @cfg {Array} fields An array of field definition objects, or field name strings.
23421  * @cfg {Array} data The multi-dimensional array of data
23422  * @constructor
23423  * @param {Object} config
23424  */
23425 Roo.data.SimpleStore = function(config){
23426     Roo.data.SimpleStore.superclass.constructor.call(this, {
23427         isLocal : true,
23428         reader: new Roo.data.ArrayReader({
23429                 id: config.id
23430             },
23431             Roo.data.Record.create(config.fields)
23432         ),
23433         proxy : new Roo.data.MemoryProxy(config.data)
23434     });
23435     this.load();
23436 };
23437 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23438  * Based on:
23439  * Ext JS Library 1.1.1
23440  * Copyright(c) 2006-2007, Ext JS, LLC.
23441  *
23442  * Originally Released Under LGPL - original licence link has changed is not relivant.
23443  *
23444  * Fork - LGPL
23445  * <script type="text/javascript">
23446  */
23447
23448 /**
23449 /**
23450  * @extends Roo.data.Store
23451  * @class Roo.data.JsonStore
23452  * Small helper class to make creating Stores for JSON data easier. <br/>
23453 <pre><code>
23454 var store = new Roo.data.JsonStore({
23455     url: 'get-images.php',
23456     root: 'images',
23457     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23458 });
23459 </code></pre>
23460  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23461  * JsonReader and HttpProxy (unless inline data is provided).</b>
23462  * @cfg {Array} fields An array of field definition objects, or field name strings.
23463  * @constructor
23464  * @param {Object} config
23465  */
23466 Roo.data.JsonStore = function(c){
23467     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23468         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23469         reader: new Roo.data.JsonReader(c, c.fields)
23470     }));
23471 };
23472 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23473  * Based on:
23474  * Ext JS Library 1.1.1
23475  * Copyright(c) 2006-2007, Ext JS, LLC.
23476  *
23477  * Originally Released Under LGPL - original licence link has changed is not relivant.
23478  *
23479  * Fork - LGPL
23480  * <script type="text/javascript">
23481  */
23482
23483  
23484 Roo.data.Field = function(config){
23485     if(typeof config == "string"){
23486         config = {name: config};
23487     }
23488     Roo.apply(this, config);
23489     
23490     if(!this.type){
23491         this.type = "auto";
23492     }
23493     
23494     var st = Roo.data.SortTypes;
23495     // named sortTypes are supported, here we look them up
23496     if(typeof this.sortType == "string"){
23497         this.sortType = st[this.sortType];
23498     }
23499     
23500     // set default sortType for strings and dates
23501     if(!this.sortType){
23502         switch(this.type){
23503             case "string":
23504                 this.sortType = st.asUCString;
23505                 break;
23506             case "date":
23507                 this.sortType = st.asDate;
23508                 break;
23509             default:
23510                 this.sortType = st.none;
23511         }
23512     }
23513
23514     // define once
23515     var stripRe = /[\$,%]/g;
23516
23517     // prebuilt conversion function for this field, instead of
23518     // switching every time we're reading a value
23519     if(!this.convert){
23520         var cv, dateFormat = this.dateFormat;
23521         switch(this.type){
23522             case "":
23523             case "auto":
23524             case undefined:
23525                 cv = function(v){ return v; };
23526                 break;
23527             case "string":
23528                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23529                 break;
23530             case "int":
23531                 cv = function(v){
23532                     return v !== undefined && v !== null && v !== '' ?
23533                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23534                     };
23535                 break;
23536             case "float":
23537                 cv = function(v){
23538                     return v !== undefined && v !== null && v !== '' ?
23539                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23540                     };
23541                 break;
23542             case "bool":
23543             case "boolean":
23544                 cv = function(v){ return v === true || v === "true" || v == 1; };
23545                 break;
23546             case "date":
23547                 cv = function(v){
23548                     if(!v){
23549                         return '';
23550                     }
23551                     if(v instanceof Date){
23552                         return v;
23553                     }
23554                     if(dateFormat){
23555                         if(dateFormat == "timestamp"){
23556                             return new Date(v*1000);
23557                         }
23558                         return Date.parseDate(v, dateFormat);
23559                     }
23560                     var parsed = Date.parse(v);
23561                     return parsed ? new Date(parsed) : null;
23562                 };
23563              break;
23564             
23565         }
23566         this.convert = cv;
23567     }
23568 };
23569
23570 Roo.data.Field.prototype = {
23571     dateFormat: null,
23572     defaultValue: "",
23573     mapping: null,
23574     sortType : null,
23575     sortDir : "ASC"
23576 };/*
23577  * Based on:
23578  * Ext JS Library 1.1.1
23579  * Copyright(c) 2006-2007, Ext JS, LLC.
23580  *
23581  * Originally Released Under LGPL - original licence link has changed is not relivant.
23582  *
23583  * Fork - LGPL
23584  * <script type="text/javascript">
23585  */
23586  
23587 // Base class for reading structured data from a data source.  This class is intended to be
23588 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23589
23590 /**
23591  * @class Roo.data.DataReader
23592  * Base class for reading structured data from a data source.  This class is intended to be
23593  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23594  */
23595
23596 Roo.data.DataReader = function(meta, recordType){
23597     
23598     this.meta = meta;
23599     
23600     this.recordType = recordType instanceof Array ? 
23601         Roo.data.Record.create(recordType) : recordType;
23602 };
23603
23604 Roo.data.DataReader.prototype = {
23605      /**
23606      * Create an empty record
23607      * @param {Object} data (optional) - overlay some values
23608      * @return {Roo.data.Record} record created.
23609      */
23610     newRow :  function(d) {
23611         var da =  {};
23612         this.recordType.prototype.fields.each(function(c) {
23613             switch( c.type) {
23614                 case 'int' : da[c.name] = 0; break;
23615                 case 'date' : da[c.name] = new Date(); break;
23616                 case 'float' : da[c.name] = 0.0; break;
23617                 case 'boolean' : da[c.name] = false; break;
23618                 default : da[c.name] = ""; break;
23619             }
23620             
23621         });
23622         return new this.recordType(Roo.apply(da, d));
23623     }
23624     
23625 };/*
23626  * Based on:
23627  * Ext JS Library 1.1.1
23628  * Copyright(c) 2006-2007, Ext JS, LLC.
23629  *
23630  * Originally Released Under LGPL - original licence link has changed is not relivant.
23631  *
23632  * Fork - LGPL
23633  * <script type="text/javascript">
23634  */
23635
23636 /**
23637  * @class Roo.data.DataProxy
23638  * @extends Roo.data.Observable
23639  * This class is an abstract base class for implementations which provide retrieval of
23640  * unformatted data objects.<br>
23641  * <p>
23642  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23643  * (of the appropriate type which knows how to parse the data object) to provide a block of
23644  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23645  * <p>
23646  * Custom implementations must implement the load method as described in
23647  * {@link Roo.data.HttpProxy#load}.
23648  */
23649 Roo.data.DataProxy = function(){
23650     this.addEvents({
23651         /**
23652          * @event beforeload
23653          * Fires before a network request is made to retrieve a data object.
23654          * @param {Object} This DataProxy object.
23655          * @param {Object} params The params parameter to the load function.
23656          */
23657         beforeload : true,
23658         /**
23659          * @event load
23660          * Fires before the load method's callback is called.
23661          * @param {Object} This DataProxy object.
23662          * @param {Object} o The data object.
23663          * @param {Object} arg The callback argument object passed to the load function.
23664          */
23665         load : true,
23666         /**
23667          * @event loadexception
23668          * Fires if an Exception occurs during data retrieval.
23669          * @param {Object} This DataProxy object.
23670          * @param {Object} o The data object.
23671          * @param {Object} arg The callback argument object passed to the load function.
23672          * @param {Object} e The Exception.
23673          */
23674         loadexception : true
23675     });
23676     Roo.data.DataProxy.superclass.constructor.call(this);
23677 };
23678
23679 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23680
23681     /**
23682      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23683      */
23684 /*
23685  * Based on:
23686  * Ext JS Library 1.1.1
23687  * Copyright(c) 2006-2007, Ext JS, LLC.
23688  *
23689  * Originally Released Under LGPL - original licence link has changed is not relivant.
23690  *
23691  * Fork - LGPL
23692  * <script type="text/javascript">
23693  */
23694 /**
23695  * @class Roo.data.MemoryProxy
23696  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23697  * to the Reader when its load method is called.
23698  * @constructor
23699  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23700  */
23701 Roo.data.MemoryProxy = function(data){
23702     if (data.data) {
23703         data = data.data;
23704     }
23705     Roo.data.MemoryProxy.superclass.constructor.call(this);
23706     this.data = data;
23707 };
23708
23709 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23710     
23711     /**
23712      * Load data from the requested source (in this case an in-memory
23713      * data object passed to the constructor), read the data object into
23714      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23715      * process that block using the passed callback.
23716      * @param {Object} params This parameter is not used by the MemoryProxy class.
23717      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23718      * object into a block of Roo.data.Records.
23719      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23720      * The function must be passed <ul>
23721      * <li>The Record block object</li>
23722      * <li>The "arg" argument from the load function</li>
23723      * <li>A boolean success indicator</li>
23724      * </ul>
23725      * @param {Object} scope The scope in which to call the callback
23726      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23727      */
23728     load : function(params, reader, callback, scope, arg){
23729         params = params || {};
23730         var result;
23731         try {
23732             result = reader.readRecords(this.data);
23733         }catch(e){
23734             this.fireEvent("loadexception", this, arg, null, e);
23735             callback.call(scope, null, arg, false);
23736             return;
23737         }
23738         callback.call(scope, result, arg, true);
23739     },
23740     
23741     // private
23742     update : function(params, records){
23743         
23744     }
23745 });/*
23746  * Based on:
23747  * Ext JS Library 1.1.1
23748  * Copyright(c) 2006-2007, Ext JS, LLC.
23749  *
23750  * Originally Released Under LGPL - original licence link has changed is not relivant.
23751  *
23752  * Fork - LGPL
23753  * <script type="text/javascript">
23754  */
23755 /**
23756  * @class Roo.data.HttpProxy
23757  * @extends Roo.data.DataProxy
23758  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23759  * configured to reference a certain URL.<br><br>
23760  * <p>
23761  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23762  * from which the running page was served.<br><br>
23763  * <p>
23764  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23765  * <p>
23766  * Be aware that to enable the browser to parse an XML document, the server must set
23767  * the Content-Type header in the HTTP response to "text/xml".
23768  * @constructor
23769  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23770  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23771  * will be used to make the request.
23772  */
23773 Roo.data.HttpProxy = function(conn){
23774     Roo.data.HttpProxy.superclass.constructor.call(this);
23775     // is conn a conn config or a real conn?
23776     this.conn = conn;
23777     this.useAjax = !conn || !conn.events;
23778   
23779 };
23780
23781 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23782     // thse are take from connection...
23783     
23784     /**
23785      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23786      */
23787     /**
23788      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23789      * extra parameters to each request made by this object. (defaults to undefined)
23790      */
23791     /**
23792      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23793      *  to each request made by this object. (defaults to undefined)
23794      */
23795     /**
23796      * @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)
23797      */
23798     /**
23799      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23800      */
23801      /**
23802      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23803      * @type Boolean
23804      */
23805   
23806
23807     /**
23808      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23809      * @type Boolean
23810      */
23811     /**
23812      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23813      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23814      * a finer-grained basis than the DataProxy events.
23815      */
23816     getConnection : function(){
23817         return this.useAjax ? Roo.Ajax : this.conn;
23818     },
23819
23820     /**
23821      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23822      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23823      * process that block using the passed callback.
23824      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23825      * for the request to the remote server.
23826      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23827      * object into a block of Roo.data.Records.
23828      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23829      * The function must be passed <ul>
23830      * <li>The Record block object</li>
23831      * <li>The "arg" argument from the load function</li>
23832      * <li>A boolean success indicator</li>
23833      * </ul>
23834      * @param {Object} scope The scope in which to call the callback
23835      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23836      */
23837     load : function(params, reader, callback, scope, arg){
23838         if(this.fireEvent("beforeload", this, params) !== false){
23839             var  o = {
23840                 params : params || {},
23841                 request: {
23842                     callback : callback,
23843                     scope : scope,
23844                     arg : arg
23845                 },
23846                 reader: reader,
23847                 callback : this.loadResponse,
23848                 scope: this
23849             };
23850             if(this.useAjax){
23851                 Roo.applyIf(o, this.conn);
23852                 if(this.activeRequest){
23853                     Roo.Ajax.abort(this.activeRequest);
23854                 }
23855                 this.activeRequest = Roo.Ajax.request(o);
23856             }else{
23857                 this.conn.request(o);
23858             }
23859         }else{
23860             callback.call(scope||this, null, arg, false);
23861         }
23862     },
23863
23864     // private
23865     loadResponse : function(o, success, response){
23866         delete this.activeRequest;
23867         if(!success){
23868             this.fireEvent("loadexception", this, o, response);
23869             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23870             return;
23871         }
23872         var result;
23873         try {
23874             result = o.reader.read(response);
23875         }catch(e){
23876             this.fireEvent("loadexception", this, o, response, e);
23877             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23878             return;
23879         }
23880         
23881         this.fireEvent("load", this, o, o.request.arg);
23882         o.request.callback.call(o.request.scope, result, o.request.arg, true);
23883     },
23884
23885     // private
23886     update : function(dataSet){
23887
23888     },
23889
23890     // private
23891     updateResponse : function(dataSet){
23892
23893     }
23894 });/*
23895  * Based on:
23896  * Ext JS Library 1.1.1
23897  * Copyright(c) 2006-2007, Ext JS, LLC.
23898  *
23899  * Originally Released Under LGPL - original licence link has changed is not relivant.
23900  *
23901  * Fork - LGPL
23902  * <script type="text/javascript">
23903  */
23904
23905 /**
23906  * @class Roo.data.ScriptTagProxy
23907  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
23908  * other than the originating domain of the running page.<br><br>
23909  * <p>
23910  * <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
23911  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
23912  * <p>
23913  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
23914  * source code that is used as the source inside a &lt;script> tag.<br><br>
23915  * <p>
23916  * In order for the browser to process the returned data, the server must wrap the data object
23917  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
23918  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
23919  * depending on whether the callback name was passed:
23920  * <p>
23921  * <pre><code>
23922 boolean scriptTag = false;
23923 String cb = request.getParameter("callback");
23924 if (cb != null) {
23925     scriptTag = true;
23926     response.setContentType("text/javascript");
23927 } else {
23928     response.setContentType("application/x-json");
23929 }
23930 Writer out = response.getWriter();
23931 if (scriptTag) {
23932     out.write(cb + "(");
23933 }
23934 out.print(dataBlock.toJsonString());
23935 if (scriptTag) {
23936     out.write(");");
23937 }
23938 </pre></code>
23939  *
23940  * @constructor
23941  * @param {Object} config A configuration object.
23942  */
23943 Roo.data.ScriptTagProxy = function(config){
23944     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
23945     Roo.apply(this, config);
23946     this.head = document.getElementsByTagName("head")[0];
23947 };
23948
23949 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
23950
23951 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
23952     /**
23953      * @cfg {String} url The URL from which to request the data object.
23954      */
23955     /**
23956      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
23957      */
23958     timeout : 30000,
23959     /**
23960      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
23961      * the server the name of the callback function set up by the load call to process the returned data object.
23962      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
23963      * javascript output which calls this named function passing the data object as its only parameter.
23964      */
23965     callbackParam : "callback",
23966     /**
23967      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
23968      * name to the request.
23969      */
23970     nocache : true,
23971
23972     /**
23973      * Load data from the configured URL, read the data object into
23974      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23975      * process that block using the passed callback.
23976      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23977      * for the request to the remote server.
23978      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23979      * object into a block of Roo.data.Records.
23980      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23981      * The function must be passed <ul>
23982      * <li>The Record block object</li>
23983      * <li>The "arg" argument from the load function</li>
23984      * <li>A boolean success indicator</li>
23985      * </ul>
23986      * @param {Object} scope The scope in which to call the callback
23987      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23988      */
23989     load : function(params, reader, callback, scope, arg){
23990         if(this.fireEvent("beforeload", this, params) !== false){
23991
23992             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
23993
23994             var url = this.url;
23995             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
23996             if(this.nocache){
23997                 url += "&_dc=" + (new Date().getTime());
23998             }
23999             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24000             var trans = {
24001                 id : transId,
24002                 cb : "stcCallback"+transId,
24003                 scriptId : "stcScript"+transId,
24004                 params : params,
24005                 arg : arg,
24006                 url : url,
24007                 callback : callback,
24008                 scope : scope,
24009                 reader : reader
24010             };
24011             var conn = this;
24012
24013             window[trans.cb] = function(o){
24014                 conn.handleResponse(o, trans);
24015             };
24016
24017             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24018
24019             if(this.autoAbort !== false){
24020                 this.abort();
24021             }
24022
24023             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24024
24025             var script = document.createElement("script");
24026             script.setAttribute("src", url);
24027             script.setAttribute("type", "text/javascript");
24028             script.setAttribute("id", trans.scriptId);
24029             this.head.appendChild(script);
24030
24031             this.trans = trans;
24032         }else{
24033             callback.call(scope||this, null, arg, false);
24034         }
24035     },
24036
24037     // private
24038     isLoading : function(){
24039         return this.trans ? true : false;
24040     },
24041
24042     /**
24043      * Abort the current server request.
24044      */
24045     abort : function(){
24046         if(this.isLoading()){
24047             this.destroyTrans(this.trans);
24048         }
24049     },
24050
24051     // private
24052     destroyTrans : function(trans, isLoaded){
24053         this.head.removeChild(document.getElementById(trans.scriptId));
24054         clearTimeout(trans.timeoutId);
24055         if(isLoaded){
24056             window[trans.cb] = undefined;
24057             try{
24058                 delete window[trans.cb];
24059             }catch(e){}
24060         }else{
24061             // if hasn't been loaded, wait for load to remove it to prevent script error
24062             window[trans.cb] = function(){
24063                 window[trans.cb] = undefined;
24064                 try{
24065                     delete window[trans.cb];
24066                 }catch(e){}
24067             };
24068         }
24069     },
24070
24071     // private
24072     handleResponse : function(o, trans){
24073         this.trans = false;
24074         this.destroyTrans(trans, true);
24075         var result;
24076         try {
24077             result = trans.reader.readRecords(o);
24078         }catch(e){
24079             this.fireEvent("loadexception", this, o, trans.arg, e);
24080             trans.callback.call(trans.scope||window, null, trans.arg, false);
24081             return;
24082         }
24083         this.fireEvent("load", this, o, trans.arg);
24084         trans.callback.call(trans.scope||window, result, trans.arg, true);
24085     },
24086
24087     // private
24088     handleFailure : function(trans){
24089         this.trans = false;
24090         this.destroyTrans(trans, false);
24091         this.fireEvent("loadexception", this, null, trans.arg);
24092         trans.callback.call(trans.scope||window, null, trans.arg, false);
24093     }
24094 });/*
24095  * Based on:
24096  * Ext JS Library 1.1.1
24097  * Copyright(c) 2006-2007, Ext JS, LLC.
24098  *
24099  * Originally Released Under LGPL - original licence link has changed is not relivant.
24100  *
24101  * Fork - LGPL
24102  * <script type="text/javascript">
24103  */
24104
24105 /**
24106  * @class Roo.data.JsonReader
24107  * @extends Roo.data.DataReader
24108  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24109  * based on mappings in a provided Roo.data.Record constructor.
24110  * 
24111  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24112  * in the reply previously. 
24113  * 
24114  * <p>
24115  * Example code:
24116  * <pre><code>
24117 var RecordDef = Roo.data.Record.create([
24118     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24119     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24120 ]);
24121 var myReader = new Roo.data.JsonReader({
24122     totalProperty: "results",    // The property which contains the total dataset size (optional)
24123     root: "rows",                // The property which contains an Array of row objects
24124     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24125 }, RecordDef);
24126 </code></pre>
24127  * <p>
24128  * This would consume a JSON file like this:
24129  * <pre><code>
24130 { 'results': 2, 'rows': [
24131     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24132     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24133 }
24134 </code></pre>
24135  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24136  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24137  * paged from the remote server.
24138  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24139  * @cfg {String} root name of the property which contains the Array of row objects.
24140  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24141  * @cfg {Array} fields Array of field definition objects
24142  * @constructor
24143  * Create a new JsonReader
24144  * @param {Object} meta Metadata configuration options
24145  * @param {Object} recordType Either an Array of field definition objects,
24146  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24147  */
24148 Roo.data.JsonReader = function(meta, recordType){
24149     
24150     meta = meta || {};
24151     // set some defaults:
24152     Roo.applyIf(meta, {
24153         totalProperty: 'total',
24154         successProperty : 'success',
24155         root : 'data',
24156         id : 'id'
24157     });
24158     
24159     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24160 };
24161 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24162     
24163     /**
24164      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24165      * Used by Store query builder to append _requestMeta to params.
24166      * 
24167      */
24168     metaFromRemote : false,
24169     /**
24170      * This method is only used by a DataProxy which has retrieved data from a remote server.
24171      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24172      * @return {Object} data A data block which is used by an Roo.data.Store object as
24173      * a cache of Roo.data.Records.
24174      */
24175     read : function(response){
24176         var json = response.responseText;
24177        
24178         var o = /* eval:var:o */ eval("("+json+")");
24179         if(!o) {
24180             throw {message: "JsonReader.read: Json object not found"};
24181         }
24182         
24183         if(o.metaData){
24184             
24185             delete this.ef;
24186             this.metaFromRemote = true;
24187             this.meta = o.metaData;
24188             this.recordType = Roo.data.Record.create(o.metaData.fields);
24189             this.onMetaChange(this.meta, this.recordType, o);
24190         }
24191         return this.readRecords(o);
24192     },
24193
24194     // private function a store will implement
24195     onMetaChange : function(meta, recordType, o){
24196
24197     },
24198
24199     /**
24200          * @ignore
24201          */
24202     simpleAccess: function(obj, subsc) {
24203         return obj[subsc];
24204     },
24205
24206         /**
24207          * @ignore
24208          */
24209     getJsonAccessor: function(){
24210         var re = /[\[\.]/;
24211         return function(expr) {
24212             try {
24213                 return(re.test(expr))
24214                     ? new Function("obj", "return obj." + expr)
24215                     : function(obj){
24216                         return obj[expr];
24217                     };
24218             } catch(e){}
24219             return Roo.emptyFn;
24220         };
24221     }(),
24222
24223     /**
24224      * Create a data block containing Roo.data.Records from an XML document.
24225      * @param {Object} o An object which contains an Array of row objects in the property specified
24226      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24227      * which contains the total size of the dataset.
24228      * @return {Object} data A data block which is used by an Roo.data.Store object as
24229      * a cache of Roo.data.Records.
24230      */
24231     readRecords : function(o){
24232         /**
24233          * After any data loads, the raw JSON data is available for further custom processing.
24234          * @type Object
24235          */
24236         this.o = o;
24237         var s = this.meta, Record = this.recordType,
24238             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24239
24240 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24241         if (!this.ef) {
24242             if(s.totalProperty) {
24243                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24244                 }
24245                 if(s.successProperty) {
24246                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24247                 }
24248                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24249                 if (s.id) {
24250                         var g = this.getJsonAccessor(s.id);
24251                         this.getId = function(rec) {
24252                                 var r = g(rec);  
24253                                 return (r === undefined || r === "") ? null : r;
24254                         };
24255                 } else {
24256                         this.getId = function(){return null;};
24257                 }
24258             this.ef = [];
24259             for(var jj = 0; jj < fl; jj++){
24260                 f = fi[jj];
24261                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24262                 this.ef[jj] = this.getJsonAccessor(map);
24263             }
24264         }
24265
24266         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24267         if(s.totalProperty){
24268             var vt = parseInt(this.getTotal(o), 10);
24269             if(!isNaN(vt)){
24270                 totalRecords = vt;
24271             }
24272         }
24273         if(s.successProperty){
24274             var vs = this.getSuccess(o);
24275             if(vs === false || vs === 'false'){
24276                 success = false;
24277             }
24278         }
24279         var records = [];
24280         for(var i = 0; i < c; i++){
24281                 var n = root[i];
24282             var values = {};
24283             var id = this.getId(n);
24284             for(var j = 0; j < fl; j++){
24285                 f = fi[j];
24286             var v = this.ef[j](n);
24287             if (!f.convert) {
24288                 Roo.log('missing convert for ' + f.name);
24289                 Roo.log(f);
24290                 continue;
24291             }
24292             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24293             }
24294             var record = new Record(values, id);
24295             record.json = n;
24296             records[i] = record;
24297         }
24298         return {
24299             raw : o,
24300             success : success,
24301             records : records,
24302             totalRecords : totalRecords
24303         };
24304     }
24305 });/*
24306  * Based on:
24307  * Ext JS Library 1.1.1
24308  * Copyright(c) 2006-2007, Ext JS, LLC.
24309  *
24310  * Originally Released Under LGPL - original licence link has changed is not relivant.
24311  *
24312  * Fork - LGPL
24313  * <script type="text/javascript">
24314  */
24315
24316 /**
24317  * @class Roo.data.XmlReader
24318  * @extends Roo.data.DataReader
24319  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24320  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24321  * <p>
24322  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24323  * header in the HTTP response must be set to "text/xml".</em>
24324  * <p>
24325  * Example code:
24326  * <pre><code>
24327 var RecordDef = Roo.data.Record.create([
24328    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24329    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24330 ]);
24331 var myReader = new Roo.data.XmlReader({
24332    totalRecords: "results", // The element which contains the total dataset size (optional)
24333    record: "row",           // The repeated element which contains row information
24334    id: "id"                 // The element within the row that provides an ID for the record (optional)
24335 }, RecordDef);
24336 </code></pre>
24337  * <p>
24338  * This would consume an XML file like this:
24339  * <pre><code>
24340 &lt;?xml?>
24341 &lt;dataset>
24342  &lt;results>2&lt;/results>
24343  &lt;row>
24344    &lt;id>1&lt;/id>
24345    &lt;name>Bill&lt;/name>
24346    &lt;occupation>Gardener&lt;/occupation>
24347  &lt;/row>
24348  &lt;row>
24349    &lt;id>2&lt;/id>
24350    &lt;name>Ben&lt;/name>
24351    &lt;occupation>Horticulturalist&lt;/occupation>
24352  &lt;/row>
24353 &lt;/dataset>
24354 </code></pre>
24355  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24356  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24357  * paged from the remote server.
24358  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24359  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24360  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24361  * a record identifier value.
24362  * @constructor
24363  * Create a new XmlReader
24364  * @param {Object} meta Metadata configuration options
24365  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24366  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24367  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24368  */
24369 Roo.data.XmlReader = function(meta, recordType){
24370     meta = meta || {};
24371     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24372 };
24373 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24374     /**
24375      * This method is only used by a DataProxy which has retrieved data from a remote server.
24376          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24377          * to contain a method called 'responseXML' that returns an XML document object.
24378      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24379      * a cache of Roo.data.Records.
24380      */
24381     read : function(response){
24382         var doc = response.responseXML;
24383         if(!doc) {
24384             throw {message: "XmlReader.read: XML Document not available"};
24385         }
24386         return this.readRecords(doc);
24387     },
24388
24389     /**
24390      * Create a data block containing Roo.data.Records from an XML document.
24391          * @param {Object} doc A parsed XML document.
24392      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24393      * a cache of Roo.data.Records.
24394      */
24395     readRecords : function(doc){
24396         /**
24397          * After any data loads/reads, the raw XML Document is available for further custom processing.
24398          * @type XMLDocument
24399          */
24400         this.xmlData = doc;
24401         var root = doc.documentElement || doc;
24402         var q = Roo.DomQuery;
24403         var recordType = this.recordType, fields = recordType.prototype.fields;
24404         var sid = this.meta.id;
24405         var totalRecords = 0, success = true;
24406         if(this.meta.totalRecords){
24407             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24408         }
24409         
24410         if(this.meta.success){
24411             var sv = q.selectValue(this.meta.success, root, true);
24412             success = sv !== false && sv !== 'false';
24413         }
24414         var records = [];
24415         var ns = q.select(this.meta.record, root);
24416         for(var i = 0, len = ns.length; i < len; i++) {
24417                 var n = ns[i];
24418                 var values = {};
24419                 var id = sid ? q.selectValue(sid, n) : undefined;
24420                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24421                     var f = fields.items[j];
24422                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24423                     v = f.convert(v);
24424                     values[f.name] = v;
24425                 }
24426                 var record = new recordType(values, id);
24427                 record.node = n;
24428                 records[records.length] = record;
24429             }
24430
24431             return {
24432                 success : success,
24433                 records : records,
24434                 totalRecords : totalRecords || records.length
24435             };
24436     }
24437 });/*
24438  * Based on:
24439  * Ext JS Library 1.1.1
24440  * Copyright(c) 2006-2007, Ext JS, LLC.
24441  *
24442  * Originally Released Under LGPL - original licence link has changed is not relivant.
24443  *
24444  * Fork - LGPL
24445  * <script type="text/javascript">
24446  */
24447
24448 /**
24449  * @class Roo.data.ArrayReader
24450  * @extends Roo.data.DataReader
24451  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24452  * Each element of that Array represents a row of data fields. The
24453  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24454  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24455  * <p>
24456  * Example code:.
24457  * <pre><code>
24458 var RecordDef = Roo.data.Record.create([
24459     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24460     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24461 ]);
24462 var myReader = new Roo.data.ArrayReader({
24463     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24464 }, RecordDef);
24465 </code></pre>
24466  * <p>
24467  * This would consume an Array like this:
24468  * <pre><code>
24469 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24470   </code></pre>
24471  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
24472  * @constructor
24473  * Create a new JsonReader
24474  * @param {Object} meta Metadata configuration options.
24475  * @param {Object} recordType Either an Array of field definition objects
24476  * as specified to {@link Roo.data.Record#create},
24477  * or an {@link Roo.data.Record} object
24478  * created using {@link Roo.data.Record#create}.
24479  */
24480 Roo.data.ArrayReader = function(meta, recordType){
24481     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
24482 };
24483
24484 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24485     /**
24486      * Create a data block containing Roo.data.Records from an XML document.
24487      * @param {Object} o An Array of row objects which represents the dataset.
24488      * @return {Object} data A data block which is used by an Roo.data.Store object as
24489      * a cache of Roo.data.Records.
24490      */
24491     readRecords : function(o){
24492         var sid = this.meta ? this.meta.id : null;
24493         var recordType = this.recordType, fields = recordType.prototype.fields;
24494         var records = [];
24495         var root = o;
24496             for(var i = 0; i < root.length; i++){
24497                     var n = root[i];
24498                 var values = {};
24499                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24500                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24501                 var f = fields.items[j];
24502                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24503                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24504                 v = f.convert(v);
24505                 values[f.name] = v;
24506             }
24507                 var record = new recordType(values, id);
24508                 record.json = n;
24509                 records[records.length] = record;
24510             }
24511             return {
24512                 records : records,
24513                 totalRecords : records.length
24514             };
24515     }
24516 });/*
24517  * Based on:
24518  * Ext JS Library 1.1.1
24519  * Copyright(c) 2006-2007, Ext JS, LLC.
24520  *
24521  * Originally Released Under LGPL - original licence link has changed is not relivant.
24522  *
24523  * Fork - LGPL
24524  * <script type="text/javascript">
24525  */
24526
24527
24528 /**
24529  * @class Roo.data.Tree
24530  * @extends Roo.util.Observable
24531  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24532  * in the tree have most standard DOM functionality.
24533  * @constructor
24534  * @param {Node} root (optional) The root node
24535  */
24536 Roo.data.Tree = function(root){
24537    this.nodeHash = {};
24538    /**
24539     * The root node for this tree
24540     * @type Node
24541     */
24542    this.root = null;
24543    if(root){
24544        this.setRootNode(root);
24545    }
24546    this.addEvents({
24547        /**
24548         * @event append
24549         * Fires when a new child node is appended to a node in this tree.
24550         * @param {Tree} tree The owner tree
24551         * @param {Node} parent The parent node
24552         * @param {Node} node The newly appended node
24553         * @param {Number} index The index of the newly appended node
24554         */
24555        "append" : true,
24556        /**
24557         * @event remove
24558         * Fires when a child node is removed from a node in this tree.
24559         * @param {Tree} tree The owner tree
24560         * @param {Node} parent The parent node
24561         * @param {Node} node The child node removed
24562         */
24563        "remove" : true,
24564        /**
24565         * @event move
24566         * Fires when a node is moved to a new location in the tree
24567         * @param {Tree} tree The owner tree
24568         * @param {Node} node The node moved
24569         * @param {Node} oldParent The old parent of this node
24570         * @param {Node} newParent The new parent of this node
24571         * @param {Number} index The index it was moved to
24572         */
24573        "move" : true,
24574        /**
24575         * @event insert
24576         * Fires when a new child node is inserted in a node in this tree.
24577         * @param {Tree} tree The owner tree
24578         * @param {Node} parent The parent node
24579         * @param {Node} node The child node inserted
24580         * @param {Node} refNode The child node the node was inserted before
24581         */
24582        "insert" : true,
24583        /**
24584         * @event beforeappend
24585         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24586         * @param {Tree} tree The owner tree
24587         * @param {Node} parent The parent node
24588         * @param {Node} node The child node to be appended
24589         */
24590        "beforeappend" : true,
24591        /**
24592         * @event beforeremove
24593         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24594         * @param {Tree} tree The owner tree
24595         * @param {Node} parent The parent node
24596         * @param {Node} node The child node to be removed
24597         */
24598        "beforeremove" : true,
24599        /**
24600         * @event beforemove
24601         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24602         * @param {Tree} tree The owner tree
24603         * @param {Node} node The node being moved
24604         * @param {Node} oldParent The parent of the node
24605         * @param {Node} newParent The new parent the node is moving to
24606         * @param {Number} index The index it is being moved to
24607         */
24608        "beforemove" : true,
24609        /**
24610         * @event beforeinsert
24611         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24612         * @param {Tree} tree The owner tree
24613         * @param {Node} parent The parent node
24614         * @param {Node} node The child node to be inserted
24615         * @param {Node} refNode The child node the node is being inserted before
24616         */
24617        "beforeinsert" : true
24618    });
24619
24620     Roo.data.Tree.superclass.constructor.call(this);
24621 };
24622
24623 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24624     pathSeparator: "/",
24625
24626     proxyNodeEvent : function(){
24627         return this.fireEvent.apply(this, arguments);
24628     },
24629
24630     /**
24631      * Returns the root node for this tree.
24632      * @return {Node}
24633      */
24634     getRootNode : function(){
24635         return this.root;
24636     },
24637
24638     /**
24639      * Sets the root node for this tree.
24640      * @param {Node} node
24641      * @return {Node}
24642      */
24643     setRootNode : function(node){
24644         this.root = node;
24645         node.ownerTree = this;
24646         node.isRoot = true;
24647         this.registerNode(node);
24648         return node;
24649     },
24650
24651     /**
24652      * Gets a node in this tree by its id.
24653      * @param {String} id
24654      * @return {Node}
24655      */
24656     getNodeById : function(id){
24657         return this.nodeHash[id];
24658     },
24659
24660     registerNode : function(node){
24661         this.nodeHash[node.id] = node;
24662     },
24663
24664     unregisterNode : function(node){
24665         delete this.nodeHash[node.id];
24666     },
24667
24668     toString : function(){
24669         return "[Tree"+(this.id?" "+this.id:"")+"]";
24670     }
24671 });
24672
24673 /**
24674  * @class Roo.data.Node
24675  * @extends Roo.util.Observable
24676  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24677  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24678  * @constructor
24679  * @param {Object} attributes The attributes/config for the node
24680  */
24681 Roo.data.Node = function(attributes){
24682     /**
24683      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24684      * @type {Object}
24685      */
24686     this.attributes = attributes || {};
24687     this.leaf = this.attributes.leaf;
24688     /**
24689      * The node id. @type String
24690      */
24691     this.id = this.attributes.id;
24692     if(!this.id){
24693         this.id = Roo.id(null, "ynode-");
24694         this.attributes.id = this.id;
24695     }
24696      
24697     
24698     /**
24699      * All child nodes of this node. @type Array
24700      */
24701     this.childNodes = [];
24702     if(!this.childNodes.indexOf){ // indexOf is a must
24703         this.childNodes.indexOf = function(o){
24704             for(var i = 0, len = this.length; i < len; i++){
24705                 if(this[i] == o) {
24706                     return i;
24707                 }
24708             }
24709             return -1;
24710         };
24711     }
24712     /**
24713      * The parent node for this node. @type Node
24714      */
24715     this.parentNode = null;
24716     /**
24717      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24718      */
24719     this.firstChild = null;
24720     /**
24721      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24722      */
24723     this.lastChild = null;
24724     /**
24725      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24726      */
24727     this.previousSibling = null;
24728     /**
24729      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24730      */
24731     this.nextSibling = null;
24732
24733     this.addEvents({
24734        /**
24735         * @event append
24736         * Fires when a new child node is appended
24737         * @param {Tree} tree The owner tree
24738         * @param {Node} this This node
24739         * @param {Node} node The newly appended node
24740         * @param {Number} index The index of the newly appended node
24741         */
24742        "append" : true,
24743        /**
24744         * @event remove
24745         * Fires when a child node is removed
24746         * @param {Tree} tree The owner tree
24747         * @param {Node} this This node
24748         * @param {Node} node The removed node
24749         */
24750        "remove" : true,
24751        /**
24752         * @event move
24753         * Fires when this node is moved to a new location in the tree
24754         * @param {Tree} tree The owner tree
24755         * @param {Node} this This node
24756         * @param {Node} oldParent The old parent of this node
24757         * @param {Node} newParent The new parent of this node
24758         * @param {Number} index The index it was moved to
24759         */
24760        "move" : true,
24761        /**
24762         * @event insert
24763         * Fires when a new child node is inserted.
24764         * @param {Tree} tree The owner tree
24765         * @param {Node} this This node
24766         * @param {Node} node The child node inserted
24767         * @param {Node} refNode The child node the node was inserted before
24768         */
24769        "insert" : true,
24770        /**
24771         * @event beforeappend
24772         * Fires before a new child is appended, return false to cancel the append.
24773         * @param {Tree} tree The owner tree
24774         * @param {Node} this This node
24775         * @param {Node} node The child node to be appended
24776         */
24777        "beforeappend" : true,
24778        /**
24779         * @event beforeremove
24780         * Fires before a child is removed, return false to cancel the remove.
24781         * @param {Tree} tree The owner tree
24782         * @param {Node} this This node
24783         * @param {Node} node The child node to be removed
24784         */
24785        "beforeremove" : true,
24786        /**
24787         * @event beforemove
24788         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24789         * @param {Tree} tree The owner tree
24790         * @param {Node} this This node
24791         * @param {Node} oldParent The parent of this node
24792         * @param {Node} newParent The new parent this node is moving to
24793         * @param {Number} index The index it is being moved to
24794         */
24795        "beforemove" : true,
24796        /**
24797         * @event beforeinsert
24798         * Fires before a new child is inserted, return false to cancel the insert.
24799         * @param {Tree} tree The owner tree
24800         * @param {Node} this This node
24801         * @param {Node} node The child node to be inserted
24802         * @param {Node} refNode The child node the node is being inserted before
24803         */
24804        "beforeinsert" : true
24805    });
24806     this.listeners = this.attributes.listeners;
24807     Roo.data.Node.superclass.constructor.call(this);
24808 };
24809
24810 Roo.extend(Roo.data.Node, Roo.util.Observable, {
24811     fireEvent : function(evtName){
24812         // first do standard event for this node
24813         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
24814             return false;
24815         }
24816         // then bubble it up to the tree if the event wasn't cancelled
24817         var ot = this.getOwnerTree();
24818         if(ot){
24819             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
24820                 return false;
24821             }
24822         }
24823         return true;
24824     },
24825
24826     /**
24827      * Returns true if this node is a leaf
24828      * @return {Boolean}
24829      */
24830     isLeaf : function(){
24831         return this.leaf === true;
24832     },
24833
24834     // private
24835     setFirstChild : function(node){
24836         this.firstChild = node;
24837     },
24838
24839     //private
24840     setLastChild : function(node){
24841         this.lastChild = node;
24842     },
24843
24844
24845     /**
24846      * Returns true if this node is the last child of its parent
24847      * @return {Boolean}
24848      */
24849     isLast : function(){
24850        return (!this.parentNode ? true : this.parentNode.lastChild == this);
24851     },
24852
24853     /**
24854      * Returns true if this node is the first child of its parent
24855      * @return {Boolean}
24856      */
24857     isFirst : function(){
24858        return (!this.parentNode ? true : this.parentNode.firstChild == this);
24859     },
24860
24861     hasChildNodes : function(){
24862         return !this.isLeaf() && this.childNodes.length > 0;
24863     },
24864
24865     /**
24866      * Insert node(s) as the last child node of this node.
24867      * @param {Node/Array} node The node or Array of nodes to append
24868      * @return {Node} The appended node if single append, or null if an array was passed
24869      */
24870     appendChild : function(node){
24871         var multi = false;
24872         if(node instanceof Array){
24873             multi = node;
24874         }else if(arguments.length > 1){
24875             multi = arguments;
24876         }
24877         // if passed an array or multiple args do them one by one
24878         if(multi){
24879             for(var i = 0, len = multi.length; i < len; i++) {
24880                 this.appendChild(multi[i]);
24881             }
24882         }else{
24883             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
24884                 return false;
24885             }
24886             var index = this.childNodes.length;
24887             var oldParent = node.parentNode;
24888             // it's a move, make sure we move it cleanly
24889             if(oldParent){
24890                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
24891                     return false;
24892                 }
24893                 oldParent.removeChild(node);
24894             }
24895             index = this.childNodes.length;
24896             if(index == 0){
24897                 this.setFirstChild(node);
24898             }
24899             this.childNodes.push(node);
24900             node.parentNode = this;
24901             var ps = this.childNodes[index-1];
24902             if(ps){
24903                 node.previousSibling = ps;
24904                 ps.nextSibling = node;
24905             }else{
24906                 node.previousSibling = null;
24907             }
24908             node.nextSibling = null;
24909             this.setLastChild(node);
24910             node.setOwnerTree(this.getOwnerTree());
24911             this.fireEvent("append", this.ownerTree, this, node, index);
24912             if(oldParent){
24913                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
24914             }
24915             return node;
24916         }
24917     },
24918
24919     /**
24920      * Removes a child node from this node.
24921      * @param {Node} node The node to remove
24922      * @return {Node} The removed node
24923      */
24924     removeChild : function(node){
24925         var index = this.childNodes.indexOf(node);
24926         if(index == -1){
24927             return false;
24928         }
24929         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
24930             return false;
24931         }
24932
24933         // remove it from childNodes collection
24934         this.childNodes.splice(index, 1);
24935
24936         // update siblings
24937         if(node.previousSibling){
24938             node.previousSibling.nextSibling = node.nextSibling;
24939         }
24940         if(node.nextSibling){
24941             node.nextSibling.previousSibling = node.previousSibling;
24942         }
24943
24944         // update child refs
24945         if(this.firstChild == node){
24946             this.setFirstChild(node.nextSibling);
24947         }
24948         if(this.lastChild == node){
24949             this.setLastChild(node.previousSibling);
24950         }
24951
24952         node.setOwnerTree(null);
24953         // clear any references from the node
24954         node.parentNode = null;
24955         node.previousSibling = null;
24956         node.nextSibling = null;
24957         this.fireEvent("remove", this.ownerTree, this, node);
24958         return node;
24959     },
24960
24961     /**
24962      * Inserts the first node before the second node in this nodes childNodes collection.
24963      * @param {Node} node The node to insert
24964      * @param {Node} refNode The node to insert before (if null the node is appended)
24965      * @return {Node} The inserted node
24966      */
24967     insertBefore : function(node, refNode){
24968         if(!refNode){ // like standard Dom, refNode can be null for append
24969             return this.appendChild(node);
24970         }
24971         // nothing to do
24972         if(node == refNode){
24973             return false;
24974         }
24975
24976         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
24977             return false;
24978         }
24979         var index = this.childNodes.indexOf(refNode);
24980         var oldParent = node.parentNode;
24981         var refIndex = index;
24982
24983         // when moving internally, indexes will change after remove
24984         if(oldParent == this && this.childNodes.indexOf(node) < index){
24985             refIndex--;
24986         }
24987
24988         // it's a move, make sure we move it cleanly
24989         if(oldParent){
24990             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
24991                 return false;
24992             }
24993             oldParent.removeChild(node);
24994         }
24995         if(refIndex == 0){
24996             this.setFirstChild(node);
24997         }
24998         this.childNodes.splice(refIndex, 0, node);
24999         node.parentNode = this;
25000         var ps = this.childNodes[refIndex-1];
25001         if(ps){
25002             node.previousSibling = ps;
25003             ps.nextSibling = node;
25004         }else{
25005             node.previousSibling = null;
25006         }
25007         node.nextSibling = refNode;
25008         refNode.previousSibling = node;
25009         node.setOwnerTree(this.getOwnerTree());
25010         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25011         if(oldParent){
25012             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25013         }
25014         return node;
25015     },
25016
25017     /**
25018      * Returns the child node at the specified index.
25019      * @param {Number} index
25020      * @return {Node}
25021      */
25022     item : function(index){
25023         return this.childNodes[index];
25024     },
25025
25026     /**
25027      * Replaces one child node in this node with another.
25028      * @param {Node} newChild The replacement node
25029      * @param {Node} oldChild The node to replace
25030      * @return {Node} The replaced node
25031      */
25032     replaceChild : function(newChild, oldChild){
25033         this.insertBefore(newChild, oldChild);
25034         this.removeChild(oldChild);
25035         return oldChild;
25036     },
25037
25038     /**
25039      * Returns the index of a child node
25040      * @param {Node} node
25041      * @return {Number} The index of the node or -1 if it was not found
25042      */
25043     indexOf : function(child){
25044         return this.childNodes.indexOf(child);
25045     },
25046
25047     /**
25048      * Returns the tree this node is in.
25049      * @return {Tree}
25050      */
25051     getOwnerTree : function(){
25052         // if it doesn't have one, look for one
25053         if(!this.ownerTree){
25054             var p = this;
25055             while(p){
25056                 if(p.ownerTree){
25057                     this.ownerTree = p.ownerTree;
25058                     break;
25059                 }
25060                 p = p.parentNode;
25061             }
25062         }
25063         return this.ownerTree;
25064     },
25065
25066     /**
25067      * Returns depth of this node (the root node has a depth of 0)
25068      * @return {Number}
25069      */
25070     getDepth : function(){
25071         var depth = 0;
25072         var p = this;
25073         while(p.parentNode){
25074             ++depth;
25075             p = p.parentNode;
25076         }
25077         return depth;
25078     },
25079
25080     // private
25081     setOwnerTree : function(tree){
25082         // if it's move, we need to update everyone
25083         if(tree != this.ownerTree){
25084             if(this.ownerTree){
25085                 this.ownerTree.unregisterNode(this);
25086             }
25087             this.ownerTree = tree;
25088             var cs = this.childNodes;
25089             for(var i = 0, len = cs.length; i < len; i++) {
25090                 cs[i].setOwnerTree(tree);
25091             }
25092             if(tree){
25093                 tree.registerNode(this);
25094             }
25095         }
25096     },
25097
25098     /**
25099      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25100      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25101      * @return {String} The path
25102      */
25103     getPath : function(attr){
25104         attr = attr || "id";
25105         var p = this.parentNode;
25106         var b = [this.attributes[attr]];
25107         while(p){
25108             b.unshift(p.attributes[attr]);
25109             p = p.parentNode;
25110         }
25111         var sep = this.getOwnerTree().pathSeparator;
25112         return sep + b.join(sep);
25113     },
25114
25115     /**
25116      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25117      * function call will be the scope provided or the current node. The arguments to the function
25118      * will be the args provided or the current node. If the function returns false at any point,
25119      * the bubble is stopped.
25120      * @param {Function} fn The function to call
25121      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25122      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25123      */
25124     bubble : function(fn, scope, args){
25125         var p = this;
25126         while(p){
25127             if(fn.call(scope || p, args || p) === false){
25128                 break;
25129             }
25130             p = p.parentNode;
25131         }
25132     },
25133
25134     /**
25135      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25136      * function call will be the scope provided or the current node. The arguments to the function
25137      * will be the args provided or the current node. If the function returns false at any point,
25138      * the cascade is stopped on that branch.
25139      * @param {Function} fn The function to call
25140      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25141      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25142      */
25143     cascade : function(fn, scope, args){
25144         if(fn.call(scope || this, args || this) !== false){
25145             var cs = this.childNodes;
25146             for(var i = 0, len = cs.length; i < len; i++) {
25147                 cs[i].cascade(fn, scope, args);
25148             }
25149         }
25150     },
25151
25152     /**
25153      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25154      * function call will be the scope provided or the current node. The arguments to the function
25155      * will be the args provided or the current node. If the function returns false at any point,
25156      * the iteration stops.
25157      * @param {Function} fn The function to call
25158      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25159      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25160      */
25161     eachChild : function(fn, scope, args){
25162         var cs = this.childNodes;
25163         for(var i = 0, len = cs.length; i < len; i++) {
25164                 if(fn.call(scope || this, args || cs[i]) === false){
25165                     break;
25166                 }
25167         }
25168     },
25169
25170     /**
25171      * Finds the first child that has the attribute with the specified value.
25172      * @param {String} attribute The attribute name
25173      * @param {Mixed} value The value to search for
25174      * @return {Node} The found child or null if none was found
25175      */
25176     findChild : function(attribute, value){
25177         var cs = this.childNodes;
25178         for(var i = 0, len = cs.length; i < len; i++) {
25179                 if(cs[i].attributes[attribute] == value){
25180                     return cs[i];
25181                 }
25182         }
25183         return null;
25184     },
25185
25186     /**
25187      * Finds the first child by a custom function. The child matches if the function passed
25188      * returns true.
25189      * @param {Function} fn
25190      * @param {Object} scope (optional)
25191      * @return {Node} The found child or null if none was found
25192      */
25193     findChildBy : function(fn, scope){
25194         var cs = this.childNodes;
25195         for(var i = 0, len = cs.length; i < len; i++) {
25196                 if(fn.call(scope||cs[i], cs[i]) === true){
25197                     return cs[i];
25198                 }
25199         }
25200         return null;
25201     },
25202
25203     /**
25204      * Sorts this nodes children using the supplied sort function
25205      * @param {Function} fn
25206      * @param {Object} scope (optional)
25207      */
25208     sort : function(fn, scope){
25209         var cs = this.childNodes;
25210         var len = cs.length;
25211         if(len > 0){
25212             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25213             cs.sort(sortFn);
25214             for(var i = 0; i < len; i++){
25215                 var n = cs[i];
25216                 n.previousSibling = cs[i-1];
25217                 n.nextSibling = cs[i+1];
25218                 if(i == 0){
25219                     this.setFirstChild(n);
25220                 }
25221                 if(i == len-1){
25222                     this.setLastChild(n);
25223                 }
25224             }
25225         }
25226     },
25227
25228     /**
25229      * Returns true if this node is an ancestor (at any point) of the passed node.
25230      * @param {Node} node
25231      * @return {Boolean}
25232      */
25233     contains : function(node){
25234         return node.isAncestor(this);
25235     },
25236
25237     /**
25238      * Returns true if the passed node is an ancestor (at any point) of this node.
25239      * @param {Node} node
25240      * @return {Boolean}
25241      */
25242     isAncestor : function(node){
25243         var p = this.parentNode;
25244         while(p){
25245             if(p == node){
25246                 return true;
25247             }
25248             p = p.parentNode;
25249         }
25250         return false;
25251     },
25252
25253     toString : function(){
25254         return "[Node"+(this.id?" "+this.id:"")+"]";
25255     }
25256 });/*
25257  * Based on:
25258  * Ext JS Library 1.1.1
25259  * Copyright(c) 2006-2007, Ext JS, LLC.
25260  *
25261  * Originally Released Under LGPL - original licence link has changed is not relivant.
25262  *
25263  * Fork - LGPL
25264  * <script type="text/javascript">
25265  */
25266  (function(){ 
25267 /**
25268  * @class Roo.Layer
25269  * @extends Roo.Element
25270  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25271  * automatic maintaining of shadow/shim positions.
25272  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25273  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25274  * you can pass a string with a CSS class name. False turns off the shadow.
25275  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25276  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25277  * @cfg {String} cls CSS class to add to the element
25278  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25279  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25280  * @constructor
25281  * @param {Object} config An object with config options.
25282  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25283  */
25284
25285 Roo.Layer = function(config, existingEl){
25286     config = config || {};
25287     var dh = Roo.DomHelper;
25288     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25289     if(existingEl){
25290         this.dom = Roo.getDom(existingEl);
25291     }
25292     if(!this.dom){
25293         var o = config.dh || {tag: "div", cls: "x-layer"};
25294         this.dom = dh.append(pel, o);
25295     }
25296     if(config.cls){
25297         this.addClass(config.cls);
25298     }
25299     this.constrain = config.constrain !== false;
25300     this.visibilityMode = Roo.Element.VISIBILITY;
25301     if(config.id){
25302         this.id = this.dom.id = config.id;
25303     }else{
25304         this.id = Roo.id(this.dom);
25305     }
25306     this.zindex = config.zindex || this.getZIndex();
25307     this.position("absolute", this.zindex);
25308     if(config.shadow){
25309         this.shadowOffset = config.shadowOffset || 4;
25310         this.shadow = new Roo.Shadow({
25311             offset : this.shadowOffset,
25312             mode : config.shadow
25313         });
25314     }else{
25315         this.shadowOffset = 0;
25316     }
25317     this.useShim = config.shim !== false && Roo.useShims;
25318     this.useDisplay = config.useDisplay;
25319     this.hide();
25320 };
25321
25322 var supr = Roo.Element.prototype;
25323
25324 // shims are shared among layer to keep from having 100 iframes
25325 var shims = [];
25326
25327 Roo.extend(Roo.Layer, Roo.Element, {
25328
25329     getZIndex : function(){
25330         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25331     },
25332
25333     getShim : function(){
25334         if(!this.useShim){
25335             return null;
25336         }
25337         if(this.shim){
25338             return this.shim;
25339         }
25340         var shim = shims.shift();
25341         if(!shim){
25342             shim = this.createShim();
25343             shim.enableDisplayMode('block');
25344             shim.dom.style.display = 'none';
25345             shim.dom.style.visibility = 'visible';
25346         }
25347         var pn = this.dom.parentNode;
25348         if(shim.dom.parentNode != pn){
25349             pn.insertBefore(shim.dom, this.dom);
25350         }
25351         shim.setStyle('z-index', this.getZIndex()-2);
25352         this.shim = shim;
25353         return shim;
25354     },
25355
25356     hideShim : function(){
25357         if(this.shim){
25358             this.shim.setDisplayed(false);
25359             shims.push(this.shim);
25360             delete this.shim;
25361         }
25362     },
25363
25364     disableShadow : function(){
25365         if(this.shadow){
25366             this.shadowDisabled = true;
25367             this.shadow.hide();
25368             this.lastShadowOffset = this.shadowOffset;
25369             this.shadowOffset = 0;
25370         }
25371     },
25372
25373     enableShadow : function(show){
25374         if(this.shadow){
25375             this.shadowDisabled = false;
25376             this.shadowOffset = this.lastShadowOffset;
25377             delete this.lastShadowOffset;
25378             if(show){
25379                 this.sync(true);
25380             }
25381         }
25382     },
25383
25384     // private
25385     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25386     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25387     sync : function(doShow){
25388         var sw = this.shadow;
25389         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25390             var sh = this.getShim();
25391
25392             var w = this.getWidth(),
25393                 h = this.getHeight();
25394
25395             var l = this.getLeft(true),
25396                 t = this.getTop(true);
25397
25398             if(sw && !this.shadowDisabled){
25399                 if(doShow && !sw.isVisible()){
25400                     sw.show(this);
25401                 }else{
25402                     sw.realign(l, t, w, h);
25403                 }
25404                 if(sh){
25405                     if(doShow){
25406                        sh.show();
25407                     }
25408                     // fit the shim behind the shadow, so it is shimmed too
25409                     var a = sw.adjusts, s = sh.dom.style;
25410                     s.left = (Math.min(l, l+a.l))+"px";
25411                     s.top = (Math.min(t, t+a.t))+"px";
25412                     s.width = (w+a.w)+"px";
25413                     s.height = (h+a.h)+"px";
25414                 }
25415             }else if(sh){
25416                 if(doShow){
25417                    sh.show();
25418                 }
25419                 sh.setSize(w, h);
25420                 sh.setLeftTop(l, t);
25421             }
25422             
25423         }
25424     },
25425
25426     // private
25427     destroy : function(){
25428         this.hideShim();
25429         if(this.shadow){
25430             this.shadow.hide();
25431         }
25432         this.removeAllListeners();
25433         var pn = this.dom.parentNode;
25434         if(pn){
25435             pn.removeChild(this.dom);
25436         }
25437         Roo.Element.uncache(this.id);
25438     },
25439
25440     remove : function(){
25441         this.destroy();
25442     },
25443
25444     // private
25445     beginUpdate : function(){
25446         this.updating = true;
25447     },
25448
25449     // private
25450     endUpdate : function(){
25451         this.updating = false;
25452         this.sync(true);
25453     },
25454
25455     // private
25456     hideUnders : function(negOffset){
25457         if(this.shadow){
25458             this.shadow.hide();
25459         }
25460         this.hideShim();
25461     },
25462
25463     // private
25464     constrainXY : function(){
25465         if(this.constrain){
25466             var vw = Roo.lib.Dom.getViewWidth(),
25467                 vh = Roo.lib.Dom.getViewHeight();
25468             var s = Roo.get(document).getScroll();
25469
25470             var xy = this.getXY();
25471             var x = xy[0], y = xy[1];   
25472             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25473             // only move it if it needs it
25474             var moved = false;
25475             // first validate right/bottom
25476             if((x + w) > vw+s.left){
25477                 x = vw - w - this.shadowOffset;
25478                 moved = true;
25479             }
25480             if((y + h) > vh+s.top){
25481                 y = vh - h - this.shadowOffset;
25482                 moved = true;
25483             }
25484             // then make sure top/left isn't negative
25485             if(x < s.left){
25486                 x = s.left;
25487                 moved = true;
25488             }
25489             if(y < s.top){
25490                 y = s.top;
25491                 moved = true;
25492             }
25493             if(moved){
25494                 if(this.avoidY){
25495                     var ay = this.avoidY;
25496                     if(y <= ay && (y+h) >= ay){
25497                         y = ay-h-5;   
25498                     }
25499                 }
25500                 xy = [x, y];
25501                 this.storeXY(xy);
25502                 supr.setXY.call(this, xy);
25503                 this.sync();
25504             }
25505         }
25506     },
25507
25508     isVisible : function(){
25509         return this.visible;    
25510     },
25511
25512     // private
25513     showAction : function(){
25514         this.visible = true; // track visibility to prevent getStyle calls
25515         if(this.useDisplay === true){
25516             this.setDisplayed("");
25517         }else if(this.lastXY){
25518             supr.setXY.call(this, this.lastXY);
25519         }else if(this.lastLT){
25520             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25521         }
25522     },
25523
25524     // private
25525     hideAction : function(){
25526         this.visible = false;
25527         if(this.useDisplay === true){
25528             this.setDisplayed(false);
25529         }else{
25530             this.setLeftTop(-10000,-10000);
25531         }
25532     },
25533
25534     // overridden Element method
25535     setVisible : function(v, a, d, c, e){
25536         if(v){
25537             this.showAction();
25538         }
25539         if(a && v){
25540             var cb = function(){
25541                 this.sync(true);
25542                 if(c){
25543                     c();
25544                 }
25545             }.createDelegate(this);
25546             supr.setVisible.call(this, true, true, d, cb, e);
25547         }else{
25548             if(!v){
25549                 this.hideUnders(true);
25550             }
25551             var cb = c;
25552             if(a){
25553                 cb = function(){
25554                     this.hideAction();
25555                     if(c){
25556                         c();
25557                     }
25558                 }.createDelegate(this);
25559             }
25560             supr.setVisible.call(this, v, a, d, cb, e);
25561             if(v){
25562                 this.sync(true);
25563             }else if(!a){
25564                 this.hideAction();
25565             }
25566         }
25567     },
25568
25569     storeXY : function(xy){
25570         delete this.lastLT;
25571         this.lastXY = xy;
25572     },
25573
25574     storeLeftTop : function(left, top){
25575         delete this.lastXY;
25576         this.lastLT = [left, top];
25577     },
25578
25579     // private
25580     beforeFx : function(){
25581         this.beforeAction();
25582         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25583     },
25584
25585     // private
25586     afterFx : function(){
25587         Roo.Layer.superclass.afterFx.apply(this, arguments);
25588         this.sync(this.isVisible());
25589     },
25590
25591     // private
25592     beforeAction : function(){
25593         if(!this.updating && this.shadow){
25594             this.shadow.hide();
25595         }
25596     },
25597
25598     // overridden Element method
25599     setLeft : function(left){
25600         this.storeLeftTop(left, this.getTop(true));
25601         supr.setLeft.apply(this, arguments);
25602         this.sync();
25603     },
25604
25605     setTop : function(top){
25606         this.storeLeftTop(this.getLeft(true), top);
25607         supr.setTop.apply(this, arguments);
25608         this.sync();
25609     },
25610
25611     setLeftTop : function(left, top){
25612         this.storeLeftTop(left, top);
25613         supr.setLeftTop.apply(this, arguments);
25614         this.sync();
25615     },
25616
25617     setXY : function(xy, a, d, c, e){
25618         this.fixDisplay();
25619         this.beforeAction();
25620         this.storeXY(xy);
25621         var cb = this.createCB(c);
25622         supr.setXY.call(this, xy, a, d, cb, e);
25623         if(!a){
25624             cb();
25625         }
25626     },
25627
25628     // private
25629     createCB : function(c){
25630         var el = this;
25631         return function(){
25632             el.constrainXY();
25633             el.sync(true);
25634             if(c){
25635                 c();
25636             }
25637         };
25638     },
25639
25640     // overridden Element method
25641     setX : function(x, a, d, c, e){
25642         this.setXY([x, this.getY()], a, d, c, e);
25643     },
25644
25645     // overridden Element method
25646     setY : function(y, a, d, c, e){
25647         this.setXY([this.getX(), y], a, d, c, e);
25648     },
25649
25650     // overridden Element method
25651     setSize : function(w, h, a, d, c, e){
25652         this.beforeAction();
25653         var cb = this.createCB(c);
25654         supr.setSize.call(this, w, h, a, d, cb, e);
25655         if(!a){
25656             cb();
25657         }
25658     },
25659
25660     // overridden Element method
25661     setWidth : function(w, a, d, c, e){
25662         this.beforeAction();
25663         var cb = this.createCB(c);
25664         supr.setWidth.call(this, w, a, d, cb, e);
25665         if(!a){
25666             cb();
25667         }
25668     },
25669
25670     // overridden Element method
25671     setHeight : function(h, a, d, c, e){
25672         this.beforeAction();
25673         var cb = this.createCB(c);
25674         supr.setHeight.call(this, h, a, d, cb, e);
25675         if(!a){
25676             cb();
25677         }
25678     },
25679
25680     // overridden Element method
25681     setBounds : function(x, y, w, h, a, d, c, e){
25682         this.beforeAction();
25683         var cb = this.createCB(c);
25684         if(!a){
25685             this.storeXY([x, y]);
25686             supr.setXY.call(this, [x, y]);
25687             supr.setSize.call(this, w, h, a, d, cb, e);
25688             cb();
25689         }else{
25690             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25691         }
25692         return this;
25693     },
25694     
25695     /**
25696      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25697      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25698      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25699      * @param {Number} zindex The new z-index to set
25700      * @return {this} The Layer
25701      */
25702     setZIndex : function(zindex){
25703         this.zindex = zindex;
25704         this.setStyle("z-index", zindex + 2);
25705         if(this.shadow){
25706             this.shadow.setZIndex(zindex + 1);
25707         }
25708         if(this.shim){
25709             this.shim.setStyle("z-index", zindex);
25710         }
25711     }
25712 });
25713 })();/*
25714  * Based on:
25715  * Ext JS Library 1.1.1
25716  * Copyright(c) 2006-2007, Ext JS, LLC.
25717  *
25718  * Originally Released Under LGPL - original licence link has changed is not relivant.
25719  *
25720  * Fork - LGPL
25721  * <script type="text/javascript">
25722  */
25723
25724
25725 /**
25726  * @class Roo.Shadow
25727  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25728  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25729  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25730  * @constructor
25731  * Create a new Shadow
25732  * @param {Object} config The config object
25733  */
25734 Roo.Shadow = function(config){
25735     Roo.apply(this, config);
25736     if(typeof this.mode != "string"){
25737         this.mode = this.defaultMode;
25738     }
25739     var o = this.offset, a = {h: 0};
25740     var rad = Math.floor(this.offset/2);
25741     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25742         case "drop":
25743             a.w = 0;
25744             a.l = a.t = o;
25745             a.t -= 1;
25746             if(Roo.isIE){
25747                 a.l -= this.offset + rad;
25748                 a.t -= this.offset + rad;
25749                 a.w -= rad;
25750                 a.h -= rad;
25751                 a.t += 1;
25752             }
25753         break;
25754         case "sides":
25755             a.w = (o*2);
25756             a.l = -o;
25757             a.t = o-1;
25758             if(Roo.isIE){
25759                 a.l -= (this.offset - rad);
25760                 a.t -= this.offset + rad;
25761                 a.l += 1;
25762                 a.w -= (this.offset - rad)*2;
25763                 a.w -= rad + 1;
25764                 a.h -= 1;
25765             }
25766         break;
25767         case "frame":
25768             a.w = a.h = (o*2);
25769             a.l = a.t = -o;
25770             a.t += 1;
25771             a.h -= 2;
25772             if(Roo.isIE){
25773                 a.l -= (this.offset - rad);
25774                 a.t -= (this.offset - rad);
25775                 a.l += 1;
25776                 a.w -= (this.offset + rad + 1);
25777                 a.h -= (this.offset + rad);
25778                 a.h += 1;
25779             }
25780         break;
25781     };
25782
25783     this.adjusts = a;
25784 };
25785
25786 Roo.Shadow.prototype = {
25787     /**
25788      * @cfg {String} mode
25789      * The shadow display mode.  Supports the following options:<br />
25790      * sides: Shadow displays on both sides and bottom only<br />
25791      * frame: Shadow displays equally on all four sides<br />
25792      * drop: Traditional bottom-right drop shadow (default)
25793      */
25794     /**
25795      * @cfg {String} offset
25796      * The number of pixels to offset the shadow from the element (defaults to 4)
25797      */
25798     offset: 4,
25799
25800     // private
25801     defaultMode: "drop",
25802
25803     /**
25804      * Displays the shadow under the target element
25805      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25806      */
25807     show : function(target){
25808         target = Roo.get(target);
25809         if(!this.el){
25810             this.el = Roo.Shadow.Pool.pull();
25811             if(this.el.dom.nextSibling != target.dom){
25812                 this.el.insertBefore(target);
25813             }
25814         }
25815         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25816         if(Roo.isIE){
25817             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25818         }
25819         this.realign(
25820             target.getLeft(true),
25821             target.getTop(true),
25822             target.getWidth(),
25823             target.getHeight()
25824         );
25825         this.el.dom.style.display = "block";
25826     },
25827
25828     /**
25829      * Returns true if the shadow is visible, else false
25830      */
25831     isVisible : function(){
25832         return this.el ? true : false;  
25833     },
25834
25835     /**
25836      * Direct alignment when values are already available. Show must be called at least once before
25837      * calling this method to ensure it is initialized.
25838      * @param {Number} left The target element left position
25839      * @param {Number} top The target element top position
25840      * @param {Number} width The target element width
25841      * @param {Number} height The target element height
25842      */
25843     realign : function(l, t, w, h){
25844         if(!this.el){
25845             return;
25846         }
25847         var a = this.adjusts, d = this.el.dom, s = d.style;
25848         var iea = 0;
25849         s.left = (l+a.l)+"px";
25850         s.top = (t+a.t)+"px";
25851         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
25852  
25853         if(s.width != sws || s.height != shs){
25854             s.width = sws;
25855             s.height = shs;
25856             if(!Roo.isIE){
25857                 var cn = d.childNodes;
25858                 var sww = Math.max(0, (sw-12))+"px";
25859                 cn[0].childNodes[1].style.width = sww;
25860                 cn[1].childNodes[1].style.width = sww;
25861                 cn[2].childNodes[1].style.width = sww;
25862                 cn[1].style.height = Math.max(0, (sh-12))+"px";
25863             }
25864         }
25865     },
25866
25867     /**
25868      * Hides this shadow
25869      */
25870     hide : function(){
25871         if(this.el){
25872             this.el.dom.style.display = "none";
25873             Roo.Shadow.Pool.push(this.el);
25874             delete this.el;
25875         }
25876     },
25877
25878     /**
25879      * Adjust the z-index of this shadow
25880      * @param {Number} zindex The new z-index
25881      */
25882     setZIndex : function(z){
25883         this.zIndex = z;
25884         if(this.el){
25885             this.el.setStyle("z-index", z);
25886         }
25887     }
25888 };
25889
25890 // Private utility class that manages the internal Shadow cache
25891 Roo.Shadow.Pool = function(){
25892     var p = [];
25893     var markup = Roo.isIE ?
25894                  '<div class="x-ie-shadow"></div>' :
25895                  '<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>';
25896     return {
25897         pull : function(){
25898             var sh = p.shift();
25899             if(!sh){
25900                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
25901                 sh.autoBoxAdjust = false;
25902             }
25903             return sh;
25904         },
25905
25906         push : function(sh){
25907             p.push(sh);
25908         }
25909     };
25910 }();/*
25911  * Based on:
25912  * Ext JS Library 1.1.1
25913  * Copyright(c) 2006-2007, Ext JS, LLC.
25914  *
25915  * Originally Released Under LGPL - original licence link has changed is not relivant.
25916  *
25917  * Fork - LGPL
25918  * <script type="text/javascript">
25919  */
25920
25921
25922 /**
25923  * @class Roo.SplitBar
25924  * @extends Roo.util.Observable
25925  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
25926  * <br><br>
25927  * Usage:
25928  * <pre><code>
25929 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
25930                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
25931 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
25932 split.minSize = 100;
25933 split.maxSize = 600;
25934 split.animate = true;
25935 split.on('moved', splitterMoved);
25936 </code></pre>
25937  * @constructor
25938  * Create a new SplitBar
25939  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
25940  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
25941  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25942  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
25943                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
25944                         position of the SplitBar).
25945  */
25946 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
25947     
25948     /** @private */
25949     this.el = Roo.get(dragElement, true);
25950     this.el.dom.unselectable = "on";
25951     /** @private */
25952     this.resizingEl = Roo.get(resizingElement, true);
25953
25954     /**
25955      * @private
25956      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25957      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
25958      * @type Number
25959      */
25960     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
25961     
25962     /**
25963      * The minimum size of the resizing element. (Defaults to 0)
25964      * @type Number
25965      */
25966     this.minSize = 0;
25967     
25968     /**
25969      * The maximum size of the resizing element. (Defaults to 2000)
25970      * @type Number
25971      */
25972     this.maxSize = 2000;
25973     
25974     /**
25975      * Whether to animate the transition to the new size
25976      * @type Boolean
25977      */
25978     this.animate = false;
25979     
25980     /**
25981      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
25982      * @type Boolean
25983      */
25984     this.useShim = false;
25985     
25986     /** @private */
25987     this.shim = null;
25988     
25989     if(!existingProxy){
25990         /** @private */
25991         this.proxy = Roo.SplitBar.createProxy(this.orientation);
25992     }else{
25993         this.proxy = Roo.get(existingProxy).dom;
25994     }
25995     /** @private */
25996     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
25997     
25998     /** @private */
25999     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26000     
26001     /** @private */
26002     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26003     
26004     /** @private */
26005     this.dragSpecs = {};
26006     
26007     /**
26008      * @private The adapter to use to positon and resize elements
26009      */
26010     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26011     this.adapter.init(this);
26012     
26013     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26014         /** @private */
26015         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26016         this.el.addClass("x-splitbar-h");
26017     }else{
26018         /** @private */
26019         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26020         this.el.addClass("x-splitbar-v");
26021     }
26022     
26023     this.addEvents({
26024         /**
26025          * @event resize
26026          * Fires when the splitter is moved (alias for {@link #event-moved})
26027          * @param {Roo.SplitBar} this
26028          * @param {Number} newSize the new width or height
26029          */
26030         "resize" : true,
26031         /**
26032          * @event moved
26033          * Fires when the splitter is moved
26034          * @param {Roo.SplitBar} this
26035          * @param {Number} newSize the new width or height
26036          */
26037         "moved" : true,
26038         /**
26039          * @event beforeresize
26040          * Fires before the splitter is dragged
26041          * @param {Roo.SplitBar} this
26042          */
26043         "beforeresize" : true,
26044
26045         "beforeapply" : true
26046     });
26047
26048     Roo.util.Observable.call(this);
26049 };
26050
26051 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26052     onStartProxyDrag : function(x, y){
26053         this.fireEvent("beforeresize", this);
26054         if(!this.overlay){
26055             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26056             o.unselectable();
26057             o.enableDisplayMode("block");
26058             // all splitbars share the same overlay
26059             Roo.SplitBar.prototype.overlay = o;
26060         }
26061         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26062         this.overlay.show();
26063         Roo.get(this.proxy).setDisplayed("block");
26064         var size = this.adapter.getElementSize(this);
26065         this.activeMinSize = this.getMinimumSize();;
26066         this.activeMaxSize = this.getMaximumSize();;
26067         var c1 = size - this.activeMinSize;
26068         var c2 = Math.max(this.activeMaxSize - size, 0);
26069         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26070             this.dd.resetConstraints();
26071             this.dd.setXConstraint(
26072                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26073                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26074             );
26075             this.dd.setYConstraint(0, 0);
26076         }else{
26077             this.dd.resetConstraints();
26078             this.dd.setXConstraint(0, 0);
26079             this.dd.setYConstraint(
26080                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26081                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26082             );
26083          }
26084         this.dragSpecs.startSize = size;
26085         this.dragSpecs.startPoint = [x, y];
26086         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26087     },
26088     
26089     /** 
26090      * @private Called after the drag operation by the DDProxy
26091      */
26092     onEndProxyDrag : function(e){
26093         Roo.get(this.proxy).setDisplayed(false);
26094         var endPoint = Roo.lib.Event.getXY(e);
26095         if(this.overlay){
26096             this.overlay.hide();
26097         }
26098         var newSize;
26099         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26100             newSize = this.dragSpecs.startSize + 
26101                 (this.placement == Roo.SplitBar.LEFT ?
26102                     endPoint[0] - this.dragSpecs.startPoint[0] :
26103                     this.dragSpecs.startPoint[0] - endPoint[0]
26104                 );
26105         }else{
26106             newSize = this.dragSpecs.startSize + 
26107                 (this.placement == Roo.SplitBar.TOP ?
26108                     endPoint[1] - this.dragSpecs.startPoint[1] :
26109                     this.dragSpecs.startPoint[1] - endPoint[1]
26110                 );
26111         }
26112         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26113         if(newSize != this.dragSpecs.startSize){
26114             if(this.fireEvent('beforeapply', this, newSize) !== false){
26115                 this.adapter.setElementSize(this, newSize);
26116                 this.fireEvent("moved", this, newSize);
26117                 this.fireEvent("resize", this, newSize);
26118             }
26119         }
26120     },
26121     
26122     /**
26123      * Get the adapter this SplitBar uses
26124      * @return The adapter object
26125      */
26126     getAdapter : function(){
26127         return this.adapter;
26128     },
26129     
26130     /**
26131      * Set the adapter this SplitBar uses
26132      * @param {Object} adapter A SplitBar adapter object
26133      */
26134     setAdapter : function(adapter){
26135         this.adapter = adapter;
26136         this.adapter.init(this);
26137     },
26138     
26139     /**
26140      * Gets the minimum size for the resizing element
26141      * @return {Number} The minimum size
26142      */
26143     getMinimumSize : function(){
26144         return this.minSize;
26145     },
26146     
26147     /**
26148      * Sets the minimum size for the resizing element
26149      * @param {Number} minSize The minimum size
26150      */
26151     setMinimumSize : function(minSize){
26152         this.minSize = minSize;
26153     },
26154     
26155     /**
26156      * Gets the maximum size for the resizing element
26157      * @return {Number} The maximum size
26158      */
26159     getMaximumSize : function(){
26160         return this.maxSize;
26161     },
26162     
26163     /**
26164      * Sets the maximum size for the resizing element
26165      * @param {Number} maxSize The maximum size
26166      */
26167     setMaximumSize : function(maxSize){
26168         this.maxSize = maxSize;
26169     },
26170     
26171     /**
26172      * Sets the initialize size for the resizing element
26173      * @param {Number} size The initial size
26174      */
26175     setCurrentSize : function(size){
26176         var oldAnimate = this.animate;
26177         this.animate = false;
26178         this.adapter.setElementSize(this, size);
26179         this.animate = oldAnimate;
26180     },
26181     
26182     /**
26183      * Destroy this splitbar. 
26184      * @param {Boolean} removeEl True to remove the element
26185      */
26186     destroy : function(removeEl){
26187         if(this.shim){
26188             this.shim.remove();
26189         }
26190         this.dd.unreg();
26191         this.proxy.parentNode.removeChild(this.proxy);
26192         if(removeEl){
26193             this.el.remove();
26194         }
26195     }
26196 });
26197
26198 /**
26199  * @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.
26200  */
26201 Roo.SplitBar.createProxy = function(dir){
26202     var proxy = new Roo.Element(document.createElement("div"));
26203     proxy.unselectable();
26204     var cls = 'x-splitbar-proxy';
26205     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26206     document.body.appendChild(proxy.dom);
26207     return proxy.dom;
26208 };
26209
26210 /** 
26211  * @class Roo.SplitBar.BasicLayoutAdapter
26212  * Default Adapter. It assumes the splitter and resizing element are not positioned
26213  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26214  */
26215 Roo.SplitBar.BasicLayoutAdapter = function(){
26216 };
26217
26218 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26219     // do nothing for now
26220     init : function(s){
26221     
26222     },
26223     /**
26224      * Called before drag operations to get the current size of the resizing element. 
26225      * @param {Roo.SplitBar} s The SplitBar using this adapter
26226      */
26227      getElementSize : function(s){
26228         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26229             return s.resizingEl.getWidth();
26230         }else{
26231             return s.resizingEl.getHeight();
26232         }
26233     },
26234     
26235     /**
26236      * Called after drag operations to set the size of the resizing element.
26237      * @param {Roo.SplitBar} s The SplitBar using this adapter
26238      * @param {Number} newSize The new size to set
26239      * @param {Function} onComplete A function to be invoked when resizing is complete
26240      */
26241     setElementSize : function(s, newSize, onComplete){
26242         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26243             if(!s.animate){
26244                 s.resizingEl.setWidth(newSize);
26245                 if(onComplete){
26246                     onComplete(s, newSize);
26247                 }
26248             }else{
26249                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26250             }
26251         }else{
26252             
26253             if(!s.animate){
26254                 s.resizingEl.setHeight(newSize);
26255                 if(onComplete){
26256                     onComplete(s, newSize);
26257                 }
26258             }else{
26259                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26260             }
26261         }
26262     }
26263 };
26264
26265 /** 
26266  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26267  * @extends Roo.SplitBar.BasicLayoutAdapter
26268  * Adapter that  moves the splitter element to align with the resized sizing element. 
26269  * Used with an absolute positioned SplitBar.
26270  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26271  * document.body, make sure you assign an id to the body element.
26272  */
26273 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26274     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26275     this.container = Roo.get(container);
26276 };
26277
26278 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26279     init : function(s){
26280         this.basic.init(s);
26281     },
26282     
26283     getElementSize : function(s){
26284         return this.basic.getElementSize(s);
26285     },
26286     
26287     setElementSize : function(s, newSize, onComplete){
26288         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26289     },
26290     
26291     moveSplitter : function(s){
26292         var yes = Roo.SplitBar;
26293         switch(s.placement){
26294             case yes.LEFT:
26295                 s.el.setX(s.resizingEl.getRight());
26296                 break;
26297             case yes.RIGHT:
26298                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26299                 break;
26300             case yes.TOP:
26301                 s.el.setY(s.resizingEl.getBottom());
26302                 break;
26303             case yes.BOTTOM:
26304                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26305                 break;
26306         }
26307     }
26308 };
26309
26310 /**
26311  * Orientation constant - Create a vertical SplitBar
26312  * @static
26313  * @type Number
26314  */
26315 Roo.SplitBar.VERTICAL = 1;
26316
26317 /**
26318  * Orientation constant - Create a horizontal SplitBar
26319  * @static
26320  * @type Number
26321  */
26322 Roo.SplitBar.HORIZONTAL = 2;
26323
26324 /**
26325  * Placement constant - The resizing element is to the left of the splitter element
26326  * @static
26327  * @type Number
26328  */
26329 Roo.SplitBar.LEFT = 1;
26330
26331 /**
26332  * Placement constant - The resizing element is to the right of the splitter element
26333  * @static
26334  * @type Number
26335  */
26336 Roo.SplitBar.RIGHT = 2;
26337
26338 /**
26339  * Placement constant - The resizing element is positioned above the splitter element
26340  * @static
26341  * @type Number
26342  */
26343 Roo.SplitBar.TOP = 3;
26344
26345 /**
26346  * Placement constant - The resizing element is positioned under splitter element
26347  * @static
26348  * @type Number
26349  */
26350 Roo.SplitBar.BOTTOM = 4;
26351 /*
26352  * Based on:
26353  * Ext JS Library 1.1.1
26354  * Copyright(c) 2006-2007, Ext JS, LLC.
26355  *
26356  * Originally Released Under LGPL - original licence link has changed is not relivant.
26357  *
26358  * Fork - LGPL
26359  * <script type="text/javascript">
26360  */
26361
26362 /**
26363  * @class Roo.View
26364  * @extends Roo.util.Observable
26365  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26366  * This class also supports single and multi selection modes. <br>
26367  * Create a data model bound view:
26368  <pre><code>
26369  var store = new Roo.data.Store(...);
26370
26371  var view = new Roo.View({
26372     el : "my-element",
26373     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26374  
26375     singleSelect: true,
26376     selectedClass: "ydataview-selected",
26377     store: store
26378  });
26379
26380  // listen for node click?
26381  view.on("click", function(vw, index, node, e){
26382  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26383  });
26384
26385  // load XML data
26386  dataModel.load("foobar.xml");
26387  </code></pre>
26388  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26389  * <br><br>
26390  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26391  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26392  * 
26393  * Note: old style constructor is still suported (container, template, config)
26394  * 
26395  * @constructor
26396  * Create a new View
26397  * @param {Object} config The config object
26398  * 
26399  */
26400 Roo.View = function(config, depreciated_tpl, depreciated_config){
26401     
26402     this.parent = false;
26403     
26404     if (typeof(depreciated_tpl) == 'undefined') {
26405         // new way.. - universal constructor.
26406         Roo.apply(this, config);
26407         this.el  = Roo.get(this.el);
26408     } else {
26409         // old format..
26410         this.el  = Roo.get(config);
26411         this.tpl = depreciated_tpl;
26412         Roo.apply(this, depreciated_config);
26413     }
26414     this.wrapEl  = this.el.wrap().wrap();
26415     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26416     
26417     
26418     if(typeof(this.tpl) == "string"){
26419         this.tpl = new Roo.Template(this.tpl);
26420     } else {
26421         // support xtype ctors..
26422         this.tpl = new Roo.factory(this.tpl, Roo);
26423     }
26424     
26425     
26426     this.tpl.compile();
26427     
26428     /** @private */
26429     this.addEvents({
26430         /**
26431          * @event beforeclick
26432          * Fires before a click is processed. Returns false to cancel the default action.
26433          * @param {Roo.View} this
26434          * @param {Number} index The index of the target node
26435          * @param {HTMLElement} node The target node
26436          * @param {Roo.EventObject} e The raw event object
26437          */
26438             "beforeclick" : true,
26439         /**
26440          * @event click
26441          * Fires when a template node is clicked.
26442          * @param {Roo.View} this
26443          * @param {Number} index The index of the target node
26444          * @param {HTMLElement} node The target node
26445          * @param {Roo.EventObject} e The raw event object
26446          */
26447             "click" : true,
26448         /**
26449          * @event dblclick
26450          * Fires when a template node is double clicked.
26451          * @param {Roo.View} this
26452          * @param {Number} index The index of the target node
26453          * @param {HTMLElement} node The target node
26454          * @param {Roo.EventObject} e The raw event object
26455          */
26456             "dblclick" : true,
26457         /**
26458          * @event contextmenu
26459          * Fires when a template node is right clicked.
26460          * @param {Roo.View} this
26461          * @param {Number} index The index of the target node
26462          * @param {HTMLElement} node The target node
26463          * @param {Roo.EventObject} e The raw event object
26464          */
26465             "contextmenu" : true,
26466         /**
26467          * @event selectionchange
26468          * Fires when the selected nodes change.
26469          * @param {Roo.View} this
26470          * @param {Array} selections Array of the selected nodes
26471          */
26472             "selectionchange" : true,
26473     
26474         /**
26475          * @event beforeselect
26476          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26477          * @param {Roo.View} this
26478          * @param {HTMLElement} node The node to be selected
26479          * @param {Array} selections Array of currently selected nodes
26480          */
26481             "beforeselect" : true,
26482         /**
26483          * @event preparedata
26484          * Fires on every row to render, to allow you to change the data.
26485          * @param {Roo.View} this
26486          * @param {Object} data to be rendered (change this)
26487          */
26488           "preparedata" : true
26489           
26490           
26491         });
26492
26493
26494
26495     this.el.on({
26496         "click": this.onClick,
26497         "dblclick": this.onDblClick,
26498         "contextmenu": this.onContextMenu,
26499         scope:this
26500     });
26501
26502     this.selections = [];
26503     this.nodes = [];
26504     this.cmp = new Roo.CompositeElementLite([]);
26505     if(this.store){
26506         this.store = Roo.factory(this.store, Roo.data);
26507         this.setStore(this.store, true);
26508     }
26509     
26510     if ( this.footer && this.footer.xtype) {
26511            
26512          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26513         
26514         this.footer.dataSource = this.store;
26515         this.footer.container = fctr;
26516         this.footer = Roo.factory(this.footer, Roo);
26517         fctr.insertFirst(this.el);
26518         
26519         // this is a bit insane - as the paging toolbar seems to detach the el..
26520 //        dom.parentNode.parentNode.parentNode
26521          // they get detached?
26522     }
26523     
26524     
26525     Roo.View.superclass.constructor.call(this);
26526     
26527     
26528 };
26529
26530 Roo.extend(Roo.View, Roo.util.Observable, {
26531     
26532      /**
26533      * @cfg {Roo.data.Store} store Data store to load data from.
26534      */
26535     store : false,
26536     
26537     /**
26538      * @cfg {String|Roo.Element} el The container element.
26539      */
26540     el : '',
26541     
26542     /**
26543      * @cfg {String|Roo.Template} tpl The template used by this View 
26544      */
26545     tpl : false,
26546     /**
26547      * @cfg {String} dataName the named area of the template to use as the data area
26548      *                          Works with domtemplates roo-name="name"
26549      */
26550     dataName: false,
26551     /**
26552      * @cfg {String} selectedClass The css class to add to selected nodes
26553      */
26554     selectedClass : "x-view-selected",
26555      /**
26556      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26557      */
26558     emptyText : "",
26559     
26560     /**
26561      * @cfg {String} text to display on mask (default Loading)
26562      */
26563     mask : false,
26564     /**
26565      * @cfg {Boolean} multiSelect Allow multiple selection
26566      */
26567     multiSelect : false,
26568     /**
26569      * @cfg {Boolean} singleSelect Allow single selection
26570      */
26571     singleSelect:  false,
26572     
26573     /**
26574      * @cfg {Boolean} toggleSelect - selecting 
26575      */
26576     toggleSelect : false,
26577     
26578     /**
26579      * @cfg {Boolean} tickable - selecting 
26580      */
26581     tickable : false,
26582     
26583     /**
26584      * Returns the element this view is bound to.
26585      * @return {Roo.Element}
26586      */
26587     getEl : function(){
26588         return this.wrapEl;
26589     },
26590     
26591     
26592
26593     /**
26594      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26595      */
26596     refresh : function(){
26597         //Roo.log('refresh');
26598         var t = this.tpl;
26599         
26600         // if we are using something like 'domtemplate', then
26601         // the what gets used is:
26602         // t.applySubtemplate(NAME, data, wrapping data..)
26603         // the outer template then get' applied with
26604         //     the store 'extra data'
26605         // and the body get's added to the
26606         //      roo-name="data" node?
26607         //      <span class='roo-tpl-{name}'></span> ?????
26608         
26609         
26610         
26611         this.clearSelections();
26612         this.el.update("");
26613         var html = [];
26614         var records = this.store.getRange();
26615         if(records.length < 1) {
26616             
26617             // is this valid??  = should it render a template??
26618             
26619             this.el.update(this.emptyText);
26620             return;
26621         }
26622         var el = this.el;
26623         if (this.dataName) {
26624             this.el.update(t.apply(this.store.meta)); //????
26625             el = this.el.child('.roo-tpl-' + this.dataName);
26626         }
26627         
26628         for(var i = 0, len = records.length; i < len; i++){
26629             var data = this.prepareData(records[i].data, i, records[i]);
26630             this.fireEvent("preparedata", this, data, i, records[i]);
26631             
26632             var d = Roo.apply({}, data);
26633             
26634             if(this.tickable){
26635                 Roo.apply(d, {'roo-id' : Roo.id()});
26636                 
26637                 var _this = this;
26638             
26639                 Roo.each(this.parent.item, function(item){
26640                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26641                         return;
26642                     }
26643                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26644                 });
26645             }
26646             
26647             html[html.length] = Roo.util.Format.trim(
26648                 this.dataName ?
26649                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26650                     t.apply(d)
26651             );
26652         }
26653         
26654         
26655         
26656         el.update(html.join(""));
26657         this.nodes = el.dom.childNodes;
26658         this.updateIndexes(0);
26659     },
26660     
26661
26662     /**
26663      * Function to override to reformat the data that is sent to
26664      * the template for each node.
26665      * DEPRICATED - use the preparedata event handler.
26666      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26667      * a JSON object for an UpdateManager bound view).
26668      */
26669     prepareData : function(data, index, record)
26670     {
26671         this.fireEvent("preparedata", this, data, index, record);
26672         return data;
26673     },
26674
26675     onUpdate : function(ds, record){
26676         // Roo.log('on update');   
26677         this.clearSelections();
26678         var index = this.store.indexOf(record);
26679         var n = this.nodes[index];
26680         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26681         n.parentNode.removeChild(n);
26682         this.updateIndexes(index, index);
26683     },
26684
26685     
26686     
26687 // --------- FIXME     
26688     onAdd : function(ds, records, index)
26689     {
26690         //Roo.log(['on Add', ds, records, index] );        
26691         this.clearSelections();
26692         if(this.nodes.length == 0){
26693             this.refresh();
26694             return;
26695         }
26696         var n = this.nodes[index];
26697         for(var i = 0, len = records.length; i < len; i++){
26698             var d = this.prepareData(records[i].data, i, records[i]);
26699             if(n){
26700                 this.tpl.insertBefore(n, d);
26701             }else{
26702                 
26703                 this.tpl.append(this.el, d);
26704             }
26705         }
26706         this.updateIndexes(index);
26707     },
26708
26709     onRemove : function(ds, record, index){
26710        // Roo.log('onRemove');
26711         this.clearSelections();
26712         var el = this.dataName  ?
26713             this.el.child('.roo-tpl-' + this.dataName) :
26714             this.el; 
26715         
26716         el.dom.removeChild(this.nodes[index]);
26717         this.updateIndexes(index);
26718     },
26719
26720     /**
26721      * Refresh an individual node.
26722      * @param {Number} index
26723      */
26724     refreshNode : function(index){
26725         this.onUpdate(this.store, this.store.getAt(index));
26726     },
26727
26728     updateIndexes : function(startIndex, endIndex){
26729         var ns = this.nodes;
26730         startIndex = startIndex || 0;
26731         endIndex = endIndex || ns.length - 1;
26732         for(var i = startIndex; i <= endIndex; i++){
26733             ns[i].nodeIndex = i;
26734         }
26735     },
26736
26737     /**
26738      * Changes the data store this view uses and refresh the view.
26739      * @param {Store} store
26740      */
26741     setStore : function(store, initial){
26742         if(!initial && this.store){
26743             this.store.un("datachanged", this.refresh);
26744             this.store.un("add", this.onAdd);
26745             this.store.un("remove", this.onRemove);
26746             this.store.un("update", this.onUpdate);
26747             this.store.un("clear", this.refresh);
26748             this.store.un("beforeload", this.onBeforeLoad);
26749             this.store.un("load", this.onLoad);
26750             this.store.un("loadexception", this.onLoad);
26751         }
26752         if(store){
26753           
26754             store.on("datachanged", this.refresh, this);
26755             store.on("add", this.onAdd, this);
26756             store.on("remove", this.onRemove, this);
26757             store.on("update", this.onUpdate, this);
26758             store.on("clear", this.refresh, this);
26759             store.on("beforeload", this.onBeforeLoad, this);
26760             store.on("load", this.onLoad, this);
26761             store.on("loadexception", this.onLoad, this);
26762         }
26763         
26764         if(store){
26765             this.refresh();
26766         }
26767     },
26768     /**
26769      * onbeforeLoad - masks the loading area.
26770      *
26771      */
26772     onBeforeLoad : function(store,opts)
26773     {
26774          //Roo.log('onBeforeLoad');   
26775         if (!opts.add) {
26776             this.el.update("");
26777         }
26778         this.el.mask(this.mask ? this.mask : "Loading" ); 
26779     },
26780     onLoad : function ()
26781     {
26782         this.el.unmask();
26783     },
26784     
26785
26786     /**
26787      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26788      * @param {HTMLElement} node
26789      * @return {HTMLElement} The template node
26790      */
26791     findItemFromChild : function(node){
26792         var el = this.dataName  ?
26793             this.el.child('.roo-tpl-' + this.dataName,true) :
26794             this.el.dom; 
26795         
26796         if(!node || node.parentNode == el){
26797                     return node;
26798             }
26799             var p = node.parentNode;
26800             while(p && p != el){
26801             if(p.parentNode == el){
26802                 return p;
26803             }
26804             p = p.parentNode;
26805         }
26806             return null;
26807     },
26808
26809     /** @ignore */
26810     onClick : function(e){
26811         var item = this.findItemFromChild(e.getTarget());
26812         if(item){
26813             var index = this.indexOf(item);
26814             if(this.onItemClick(item, index, e) !== false){
26815                 this.fireEvent("click", this, index, item, e);
26816             }
26817         }else{
26818             this.clearSelections();
26819         }
26820     },
26821
26822     /** @ignore */
26823     onContextMenu : function(e){
26824         var item = this.findItemFromChild(e.getTarget());
26825         if(item){
26826             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
26827         }
26828     },
26829
26830     /** @ignore */
26831     onDblClick : function(e){
26832         var item = this.findItemFromChild(e.getTarget());
26833         if(item){
26834             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
26835         }
26836     },
26837
26838     onItemClick : function(item, index, e)
26839     {
26840         if(this.fireEvent("beforeclick", this, index, item, e) === false){
26841             return false;
26842         }
26843         if (this.toggleSelect) {
26844             var m = this.isSelected(item) ? 'unselect' : 'select';
26845             //Roo.log(m);
26846             var _t = this;
26847             _t[m](item, true, false);
26848             return true;
26849         }
26850         if(this.multiSelect || this.singleSelect){
26851             if(this.multiSelect && e.shiftKey && this.lastSelection){
26852                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
26853             }else{
26854                 this.select(item, this.multiSelect && e.ctrlKey);
26855                 this.lastSelection = item;
26856             }
26857             
26858             if(!this.tickable){
26859                 e.preventDefault();
26860             }
26861             
26862         }
26863         return true;
26864     },
26865
26866     /**
26867      * Get the number of selected nodes.
26868      * @return {Number}
26869      */
26870     getSelectionCount : function(){
26871         return this.selections.length;
26872     },
26873
26874     /**
26875      * Get the currently selected nodes.
26876      * @return {Array} An array of HTMLElements
26877      */
26878     getSelectedNodes : function(){
26879         return this.selections;
26880     },
26881
26882     /**
26883      * Get the indexes of the selected nodes.
26884      * @return {Array}
26885      */
26886     getSelectedIndexes : function(){
26887         var indexes = [], s = this.selections;
26888         for(var i = 0, len = s.length; i < len; i++){
26889             indexes.push(s[i].nodeIndex);
26890         }
26891         return indexes;
26892     },
26893
26894     /**
26895      * Clear all selections
26896      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
26897      */
26898     clearSelections : function(suppressEvent){
26899         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
26900             this.cmp.elements = this.selections;
26901             this.cmp.removeClass(this.selectedClass);
26902             this.selections = [];
26903             if(!suppressEvent){
26904                 this.fireEvent("selectionchange", this, this.selections);
26905             }
26906         }
26907     },
26908
26909     /**
26910      * Returns true if the passed node is selected
26911      * @param {HTMLElement/Number} node The node or node index
26912      * @return {Boolean}
26913      */
26914     isSelected : function(node){
26915         var s = this.selections;
26916         if(s.length < 1){
26917             return false;
26918         }
26919         node = this.getNode(node);
26920         return s.indexOf(node) !== -1;
26921     },
26922
26923     /**
26924      * Selects nodes.
26925      * @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
26926      * @param {Boolean} keepExisting (optional) true to keep existing selections
26927      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26928      */
26929     select : function(nodeInfo, keepExisting, suppressEvent){
26930         if(nodeInfo instanceof Array){
26931             if(!keepExisting){
26932                 this.clearSelections(true);
26933             }
26934             for(var i = 0, len = nodeInfo.length; i < len; i++){
26935                 this.select(nodeInfo[i], true, true);
26936             }
26937             return;
26938         } 
26939         var node = this.getNode(nodeInfo);
26940         if(!node || this.isSelected(node)){
26941             return; // already selected.
26942         }
26943         if(!keepExisting){
26944             this.clearSelections(true);
26945         }
26946         
26947         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
26948             Roo.fly(node).addClass(this.selectedClass);
26949             this.selections.push(node);
26950             if(!suppressEvent){
26951                 this.fireEvent("selectionchange", this, this.selections);
26952             }
26953         }
26954         
26955         
26956     },
26957       /**
26958      * Unselects nodes.
26959      * @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
26960      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
26961      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26962      */
26963     unselect : function(nodeInfo, keepExisting, suppressEvent)
26964     {
26965         if(nodeInfo instanceof Array){
26966             Roo.each(this.selections, function(s) {
26967                 this.unselect(s, nodeInfo);
26968             }, this);
26969             return;
26970         }
26971         var node = this.getNode(nodeInfo);
26972         if(!node || !this.isSelected(node)){
26973             //Roo.log("not selected");
26974             return; // not selected.
26975         }
26976         // fireevent???
26977         var ns = [];
26978         Roo.each(this.selections, function(s) {
26979             if (s == node ) {
26980                 Roo.fly(node).removeClass(this.selectedClass);
26981
26982                 return;
26983             }
26984             ns.push(s);
26985         },this);
26986         
26987         this.selections= ns;
26988         this.fireEvent("selectionchange", this, this.selections);
26989     },
26990
26991     /**
26992      * Gets a template node.
26993      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
26994      * @return {HTMLElement} The node or null if it wasn't found
26995      */
26996     getNode : function(nodeInfo){
26997         if(typeof nodeInfo == "string"){
26998             return document.getElementById(nodeInfo);
26999         }else if(typeof nodeInfo == "number"){
27000             return this.nodes[nodeInfo];
27001         }
27002         return nodeInfo;
27003     },
27004
27005     /**
27006      * Gets a range template nodes.
27007      * @param {Number} startIndex
27008      * @param {Number} endIndex
27009      * @return {Array} An array of nodes
27010      */
27011     getNodes : function(start, end){
27012         var ns = this.nodes;
27013         start = start || 0;
27014         end = typeof end == "undefined" ? ns.length - 1 : end;
27015         var nodes = [];
27016         if(start <= end){
27017             for(var i = start; i <= end; i++){
27018                 nodes.push(ns[i]);
27019             }
27020         } else{
27021             for(var i = start; i >= end; i--){
27022                 nodes.push(ns[i]);
27023             }
27024         }
27025         return nodes;
27026     },
27027
27028     /**
27029      * Finds the index of the passed node
27030      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27031      * @return {Number} The index of the node or -1
27032      */
27033     indexOf : function(node){
27034         node = this.getNode(node);
27035         if(typeof node.nodeIndex == "number"){
27036             return node.nodeIndex;
27037         }
27038         var ns = this.nodes;
27039         for(var i = 0, len = ns.length; i < len; i++){
27040             if(ns[i] == node){
27041                 return i;
27042             }
27043         }
27044         return -1;
27045     }
27046 });
27047 /*
27048  * Based on:
27049  * Ext JS Library 1.1.1
27050  * Copyright(c) 2006-2007, Ext JS, LLC.
27051  *
27052  * Originally Released Under LGPL - original licence link has changed is not relivant.
27053  *
27054  * Fork - LGPL
27055  * <script type="text/javascript">
27056  */
27057
27058 /**
27059  * @class Roo.JsonView
27060  * @extends Roo.View
27061  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27062 <pre><code>
27063 var view = new Roo.JsonView({
27064     container: "my-element",
27065     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27066     multiSelect: true, 
27067     jsonRoot: "data" 
27068 });
27069
27070 // listen for node click?
27071 view.on("click", function(vw, index, node, e){
27072     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27073 });
27074
27075 // direct load of JSON data
27076 view.load("foobar.php");
27077
27078 // Example from my blog list
27079 var tpl = new Roo.Template(
27080     '&lt;div class="entry"&gt;' +
27081     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27082     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27083     "&lt;/div&gt;&lt;hr /&gt;"
27084 );
27085
27086 var moreView = new Roo.JsonView({
27087     container :  "entry-list", 
27088     template : tpl,
27089     jsonRoot: "posts"
27090 });
27091 moreView.on("beforerender", this.sortEntries, this);
27092 moreView.load({
27093     url: "/blog/get-posts.php",
27094     params: "allposts=true",
27095     text: "Loading Blog Entries..."
27096 });
27097 </code></pre>
27098
27099 * Note: old code is supported with arguments : (container, template, config)
27100
27101
27102  * @constructor
27103  * Create a new JsonView
27104  * 
27105  * @param {Object} config The config object
27106  * 
27107  */
27108 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27109     
27110     
27111     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27112
27113     var um = this.el.getUpdateManager();
27114     um.setRenderer(this);
27115     um.on("update", this.onLoad, this);
27116     um.on("failure", this.onLoadException, this);
27117
27118     /**
27119      * @event beforerender
27120      * Fires before rendering of the downloaded JSON data.
27121      * @param {Roo.JsonView} this
27122      * @param {Object} data The JSON data loaded
27123      */
27124     /**
27125      * @event load
27126      * Fires when data is loaded.
27127      * @param {Roo.JsonView} this
27128      * @param {Object} data The JSON data loaded
27129      * @param {Object} response The raw Connect response object
27130      */
27131     /**
27132      * @event loadexception
27133      * Fires when loading fails.
27134      * @param {Roo.JsonView} this
27135      * @param {Object} response The raw Connect response object
27136      */
27137     this.addEvents({
27138         'beforerender' : true,
27139         'load' : true,
27140         'loadexception' : true
27141     });
27142 };
27143 Roo.extend(Roo.JsonView, Roo.View, {
27144     /**
27145      * @type {String} The root property in the loaded JSON object that contains the data
27146      */
27147     jsonRoot : "",
27148
27149     /**
27150      * Refreshes the view.
27151      */
27152     refresh : function(){
27153         this.clearSelections();
27154         this.el.update("");
27155         var html = [];
27156         var o = this.jsonData;
27157         if(o && o.length > 0){
27158             for(var i = 0, len = o.length; i < len; i++){
27159                 var data = this.prepareData(o[i], i, o);
27160                 html[html.length] = this.tpl.apply(data);
27161             }
27162         }else{
27163             html.push(this.emptyText);
27164         }
27165         this.el.update(html.join(""));
27166         this.nodes = this.el.dom.childNodes;
27167         this.updateIndexes(0);
27168     },
27169
27170     /**
27171      * 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.
27172      * @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:
27173      <pre><code>
27174      view.load({
27175          url: "your-url.php",
27176          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27177          callback: yourFunction,
27178          scope: yourObject, //(optional scope)
27179          discardUrl: false,
27180          nocache: false,
27181          text: "Loading...",
27182          timeout: 30,
27183          scripts: false
27184      });
27185      </code></pre>
27186      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27187      * 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.
27188      * @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}
27189      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27190      * @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.
27191      */
27192     load : function(){
27193         var um = this.el.getUpdateManager();
27194         um.update.apply(um, arguments);
27195     },
27196
27197     // note - render is a standard framework call...
27198     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27199     render : function(el, response){
27200         
27201         this.clearSelections();
27202         this.el.update("");
27203         var o;
27204         try{
27205             if (response != '') {
27206                 o = Roo.util.JSON.decode(response.responseText);
27207                 if(this.jsonRoot){
27208                     
27209                     o = o[this.jsonRoot];
27210                 }
27211             }
27212         } catch(e){
27213         }
27214         /**
27215          * The current JSON data or null
27216          */
27217         this.jsonData = o;
27218         this.beforeRender();
27219         this.refresh();
27220     },
27221
27222 /**
27223  * Get the number of records in the current JSON dataset
27224  * @return {Number}
27225  */
27226     getCount : function(){
27227         return this.jsonData ? this.jsonData.length : 0;
27228     },
27229
27230 /**
27231  * Returns the JSON object for the specified node(s)
27232  * @param {HTMLElement/Array} node The node or an array of nodes
27233  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27234  * you get the JSON object for the node
27235  */
27236     getNodeData : function(node){
27237         if(node instanceof Array){
27238             var data = [];
27239             for(var i = 0, len = node.length; i < len; i++){
27240                 data.push(this.getNodeData(node[i]));
27241             }
27242             return data;
27243         }
27244         return this.jsonData[this.indexOf(node)] || null;
27245     },
27246
27247     beforeRender : function(){
27248         this.snapshot = this.jsonData;
27249         if(this.sortInfo){
27250             this.sort.apply(this, this.sortInfo);
27251         }
27252         this.fireEvent("beforerender", this, this.jsonData);
27253     },
27254
27255     onLoad : function(el, o){
27256         this.fireEvent("load", this, this.jsonData, o);
27257     },
27258
27259     onLoadException : function(el, o){
27260         this.fireEvent("loadexception", this, o);
27261     },
27262
27263 /**
27264  * Filter the data by a specific property.
27265  * @param {String} property A property on your JSON objects
27266  * @param {String/RegExp} value Either string that the property values
27267  * should start with, or a RegExp to test against the property
27268  */
27269     filter : function(property, value){
27270         if(this.jsonData){
27271             var data = [];
27272             var ss = this.snapshot;
27273             if(typeof value == "string"){
27274                 var vlen = value.length;
27275                 if(vlen == 0){
27276                     this.clearFilter();
27277                     return;
27278                 }
27279                 value = value.toLowerCase();
27280                 for(var i = 0, len = ss.length; i < len; i++){
27281                     var o = ss[i];
27282                     if(o[property].substr(0, vlen).toLowerCase() == value){
27283                         data.push(o);
27284                     }
27285                 }
27286             } else if(value.exec){ // regex?
27287                 for(var i = 0, len = ss.length; i < len; i++){
27288                     var o = ss[i];
27289                     if(value.test(o[property])){
27290                         data.push(o);
27291                     }
27292                 }
27293             } else{
27294                 return;
27295             }
27296             this.jsonData = data;
27297             this.refresh();
27298         }
27299     },
27300
27301 /**
27302  * Filter by a function. The passed function will be called with each
27303  * object in the current dataset. If the function returns true the value is kept,
27304  * otherwise it is filtered.
27305  * @param {Function} fn
27306  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27307  */
27308     filterBy : function(fn, scope){
27309         if(this.jsonData){
27310             var data = [];
27311             var ss = this.snapshot;
27312             for(var i = 0, len = ss.length; i < len; i++){
27313                 var o = ss[i];
27314                 if(fn.call(scope || this, o)){
27315                     data.push(o);
27316                 }
27317             }
27318             this.jsonData = data;
27319             this.refresh();
27320         }
27321     },
27322
27323 /**
27324  * Clears the current filter.
27325  */
27326     clearFilter : function(){
27327         if(this.snapshot && this.jsonData != this.snapshot){
27328             this.jsonData = this.snapshot;
27329             this.refresh();
27330         }
27331     },
27332
27333
27334 /**
27335  * Sorts the data for this view and refreshes it.
27336  * @param {String} property A property on your JSON objects to sort on
27337  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27338  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27339  */
27340     sort : function(property, dir, sortType){
27341         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27342         if(this.jsonData){
27343             var p = property;
27344             var dsc = dir && dir.toLowerCase() == "desc";
27345             var f = function(o1, o2){
27346                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27347                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27348                 ;
27349                 if(v1 < v2){
27350                     return dsc ? +1 : -1;
27351                 } else if(v1 > v2){
27352                     return dsc ? -1 : +1;
27353                 } else{
27354                     return 0;
27355                 }
27356             };
27357             this.jsonData.sort(f);
27358             this.refresh();
27359             if(this.jsonData != this.snapshot){
27360                 this.snapshot.sort(f);
27361             }
27362         }
27363     }
27364 });/*
27365  * Based on:
27366  * Ext JS Library 1.1.1
27367  * Copyright(c) 2006-2007, Ext JS, LLC.
27368  *
27369  * Originally Released Under LGPL - original licence link has changed is not relivant.
27370  *
27371  * Fork - LGPL
27372  * <script type="text/javascript">
27373  */
27374  
27375
27376 /**
27377  * @class Roo.ColorPalette
27378  * @extends Roo.Component
27379  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27380  * Here's an example of typical usage:
27381  * <pre><code>
27382 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27383 cp.render('my-div');
27384
27385 cp.on('select', function(palette, selColor){
27386     // do something with selColor
27387 });
27388 </code></pre>
27389  * @constructor
27390  * Create a new ColorPalette
27391  * @param {Object} config The config object
27392  */
27393 Roo.ColorPalette = function(config){
27394     Roo.ColorPalette.superclass.constructor.call(this, config);
27395     this.addEvents({
27396         /**
27397              * @event select
27398              * Fires when a color is selected
27399              * @param {ColorPalette} this
27400              * @param {String} color The 6-digit color hex code (without the # symbol)
27401              */
27402         select: true
27403     });
27404
27405     if(this.handler){
27406         this.on("select", this.handler, this.scope, true);
27407     }
27408 };
27409 Roo.extend(Roo.ColorPalette, Roo.Component, {
27410     /**
27411      * @cfg {String} itemCls
27412      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27413      */
27414     itemCls : "x-color-palette",
27415     /**
27416      * @cfg {String} value
27417      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27418      * the hex codes are case-sensitive.
27419      */
27420     value : null,
27421     clickEvent:'click',
27422     // private
27423     ctype: "Roo.ColorPalette",
27424
27425     /**
27426      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27427      */
27428     allowReselect : false,
27429
27430     /**
27431      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27432      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27433      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27434      * of colors with the width setting until the box is symmetrical.</p>
27435      * <p>You can override individual colors if needed:</p>
27436      * <pre><code>
27437 var cp = new Roo.ColorPalette();
27438 cp.colors[0] = "FF0000";  // change the first box to red
27439 </code></pre>
27440
27441 Or you can provide a custom array of your own for complete control:
27442 <pre><code>
27443 var cp = new Roo.ColorPalette();
27444 cp.colors = ["000000", "993300", "333300"];
27445 </code></pre>
27446      * @type Array
27447      */
27448     colors : [
27449         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27450         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27451         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27452         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27453         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27454     ],
27455
27456     // private
27457     onRender : function(container, position){
27458         var t = new Roo.MasterTemplate(
27459             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27460         );
27461         var c = this.colors;
27462         for(var i = 0, len = c.length; i < len; i++){
27463             t.add([c[i]]);
27464         }
27465         var el = document.createElement("div");
27466         el.className = this.itemCls;
27467         t.overwrite(el);
27468         container.dom.insertBefore(el, position);
27469         this.el = Roo.get(el);
27470         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27471         if(this.clickEvent != 'click'){
27472             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27473         }
27474     },
27475
27476     // private
27477     afterRender : function(){
27478         Roo.ColorPalette.superclass.afterRender.call(this);
27479         if(this.value){
27480             var s = this.value;
27481             this.value = null;
27482             this.select(s);
27483         }
27484     },
27485
27486     // private
27487     handleClick : function(e, t){
27488         e.preventDefault();
27489         if(!this.disabled){
27490             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27491             this.select(c.toUpperCase());
27492         }
27493     },
27494
27495     /**
27496      * Selects the specified color in the palette (fires the select event)
27497      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27498      */
27499     select : function(color){
27500         color = color.replace("#", "");
27501         if(color != this.value || this.allowReselect){
27502             var el = this.el;
27503             if(this.value){
27504                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27505             }
27506             el.child("a.color-"+color).addClass("x-color-palette-sel");
27507             this.value = color;
27508             this.fireEvent("select", this, color);
27509         }
27510     }
27511 });/*
27512  * Based on:
27513  * Ext JS Library 1.1.1
27514  * Copyright(c) 2006-2007, Ext JS, LLC.
27515  *
27516  * Originally Released Under LGPL - original licence link has changed is not relivant.
27517  *
27518  * Fork - LGPL
27519  * <script type="text/javascript">
27520  */
27521  
27522 /**
27523  * @class Roo.DatePicker
27524  * @extends Roo.Component
27525  * Simple date picker class.
27526  * @constructor
27527  * Create a new DatePicker
27528  * @param {Object} config The config object
27529  */
27530 Roo.DatePicker = function(config){
27531     Roo.DatePicker.superclass.constructor.call(this, config);
27532
27533     this.value = config && config.value ?
27534                  config.value.clearTime() : new Date().clearTime();
27535
27536     this.addEvents({
27537         /**
27538              * @event select
27539              * Fires when a date is selected
27540              * @param {DatePicker} this
27541              * @param {Date} date The selected date
27542              */
27543         'select': true,
27544         /**
27545              * @event monthchange
27546              * Fires when the displayed month changes 
27547              * @param {DatePicker} this
27548              * @param {Date} date The selected month
27549              */
27550         'monthchange': true
27551     });
27552
27553     if(this.handler){
27554         this.on("select", this.handler,  this.scope || this);
27555     }
27556     // build the disabledDatesRE
27557     if(!this.disabledDatesRE && this.disabledDates){
27558         var dd = this.disabledDates;
27559         var re = "(?:";
27560         for(var i = 0; i < dd.length; i++){
27561             re += dd[i];
27562             if(i != dd.length-1) {
27563                 re += "|";
27564             }
27565         }
27566         this.disabledDatesRE = new RegExp(re + ")");
27567     }
27568 };
27569
27570 Roo.extend(Roo.DatePicker, Roo.Component, {
27571     /**
27572      * @cfg {String} todayText
27573      * The text to display on the button that selects the current date (defaults to "Today")
27574      */
27575     todayText : "Today",
27576     /**
27577      * @cfg {String} okText
27578      * The text to display on the ok button
27579      */
27580     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27581     /**
27582      * @cfg {String} cancelText
27583      * The text to display on the cancel button
27584      */
27585     cancelText : "Cancel",
27586     /**
27587      * @cfg {String} todayTip
27588      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27589      */
27590     todayTip : "{0} (Spacebar)",
27591     /**
27592      * @cfg {Date} minDate
27593      * Minimum allowable date (JavaScript date object, defaults to null)
27594      */
27595     minDate : null,
27596     /**
27597      * @cfg {Date} maxDate
27598      * Maximum allowable date (JavaScript date object, defaults to null)
27599      */
27600     maxDate : null,
27601     /**
27602      * @cfg {String} minText
27603      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27604      */
27605     minText : "This date is before the minimum date",
27606     /**
27607      * @cfg {String} maxText
27608      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27609      */
27610     maxText : "This date is after the maximum date",
27611     /**
27612      * @cfg {String} format
27613      * The default date format string which can be overriden for localization support.  The format must be
27614      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27615      */
27616     format : "m/d/y",
27617     /**
27618      * @cfg {Array} disabledDays
27619      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27620      */
27621     disabledDays : null,
27622     /**
27623      * @cfg {String} disabledDaysText
27624      * The tooltip to display when the date falls on a disabled day (defaults to "")
27625      */
27626     disabledDaysText : "",
27627     /**
27628      * @cfg {RegExp} disabledDatesRE
27629      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27630      */
27631     disabledDatesRE : null,
27632     /**
27633      * @cfg {String} disabledDatesText
27634      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27635      */
27636     disabledDatesText : "",
27637     /**
27638      * @cfg {Boolean} constrainToViewport
27639      * True to constrain the date picker to the viewport (defaults to true)
27640      */
27641     constrainToViewport : true,
27642     /**
27643      * @cfg {Array} monthNames
27644      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27645      */
27646     monthNames : Date.monthNames,
27647     /**
27648      * @cfg {Array} dayNames
27649      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27650      */
27651     dayNames : Date.dayNames,
27652     /**
27653      * @cfg {String} nextText
27654      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27655      */
27656     nextText: 'Next Month (Control+Right)',
27657     /**
27658      * @cfg {String} prevText
27659      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27660      */
27661     prevText: 'Previous Month (Control+Left)',
27662     /**
27663      * @cfg {String} monthYearText
27664      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27665      */
27666     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27667     /**
27668      * @cfg {Number} startDay
27669      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27670      */
27671     startDay : 0,
27672     /**
27673      * @cfg {Bool} showClear
27674      * Show a clear button (usefull for date form elements that can be blank.)
27675      */
27676     
27677     showClear: false,
27678     
27679     /**
27680      * Sets the value of the date field
27681      * @param {Date} value The date to set
27682      */
27683     setValue : function(value){
27684         var old = this.value;
27685         
27686         if (typeof(value) == 'string') {
27687          
27688             value = Date.parseDate(value, this.format);
27689         }
27690         if (!value) {
27691             value = new Date();
27692         }
27693         
27694         this.value = value.clearTime(true);
27695         if(this.el){
27696             this.update(this.value);
27697         }
27698     },
27699
27700     /**
27701      * Gets the current selected value of the date field
27702      * @return {Date} The selected date
27703      */
27704     getValue : function(){
27705         return this.value;
27706     },
27707
27708     // private
27709     focus : function(){
27710         if(this.el){
27711             this.update(this.activeDate);
27712         }
27713     },
27714
27715     // privateval
27716     onRender : function(container, position){
27717         
27718         var m = [
27719              '<table cellspacing="0">',
27720                 '<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>',
27721                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27722         var dn = this.dayNames;
27723         for(var i = 0; i < 7; i++){
27724             var d = this.startDay+i;
27725             if(d > 6){
27726                 d = d-7;
27727             }
27728             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27729         }
27730         m[m.length] = "</tr></thead><tbody><tr>";
27731         for(var i = 0; i < 42; i++) {
27732             if(i % 7 == 0 && i != 0){
27733                 m[m.length] = "</tr><tr>";
27734             }
27735             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27736         }
27737         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27738             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27739
27740         var el = document.createElement("div");
27741         el.className = "x-date-picker";
27742         el.innerHTML = m.join("");
27743
27744         container.dom.insertBefore(el, position);
27745
27746         this.el = Roo.get(el);
27747         this.eventEl = Roo.get(el.firstChild);
27748
27749         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27750             handler: this.showPrevMonth,
27751             scope: this,
27752             preventDefault:true,
27753             stopDefault:true
27754         });
27755
27756         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27757             handler: this.showNextMonth,
27758             scope: this,
27759             preventDefault:true,
27760             stopDefault:true
27761         });
27762
27763         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27764
27765         this.monthPicker = this.el.down('div.x-date-mp');
27766         this.monthPicker.enableDisplayMode('block');
27767         
27768         var kn = new Roo.KeyNav(this.eventEl, {
27769             "left" : function(e){
27770                 e.ctrlKey ?
27771                     this.showPrevMonth() :
27772                     this.update(this.activeDate.add("d", -1));
27773             },
27774
27775             "right" : function(e){
27776                 e.ctrlKey ?
27777                     this.showNextMonth() :
27778                     this.update(this.activeDate.add("d", 1));
27779             },
27780
27781             "up" : function(e){
27782                 e.ctrlKey ?
27783                     this.showNextYear() :
27784                     this.update(this.activeDate.add("d", -7));
27785             },
27786
27787             "down" : function(e){
27788                 e.ctrlKey ?
27789                     this.showPrevYear() :
27790                     this.update(this.activeDate.add("d", 7));
27791             },
27792
27793             "pageUp" : function(e){
27794                 this.showNextMonth();
27795             },
27796
27797             "pageDown" : function(e){
27798                 this.showPrevMonth();
27799             },
27800
27801             "enter" : function(e){
27802                 e.stopPropagation();
27803                 return true;
27804             },
27805
27806             scope : this
27807         });
27808
27809         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27810
27811         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27812
27813         this.el.unselectable();
27814         
27815         this.cells = this.el.select("table.x-date-inner tbody td");
27816         this.textNodes = this.el.query("table.x-date-inner tbody span");
27817
27818         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27819             text: "&#160;",
27820             tooltip: this.monthYearText
27821         });
27822
27823         this.mbtn.on('click', this.showMonthPicker, this);
27824         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27825
27826
27827         var today = (new Date()).dateFormat(this.format);
27828         
27829         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
27830         if (this.showClear) {
27831             baseTb.add( new Roo.Toolbar.Fill());
27832         }
27833         baseTb.add({
27834             text: String.format(this.todayText, today),
27835             tooltip: String.format(this.todayTip, today),
27836             handler: this.selectToday,
27837             scope: this
27838         });
27839         
27840         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
27841             
27842         //});
27843         if (this.showClear) {
27844             
27845             baseTb.add( new Roo.Toolbar.Fill());
27846             baseTb.add({
27847                 text: '&#160;',
27848                 cls: 'x-btn-icon x-btn-clear',
27849                 handler: function() {
27850                     //this.value = '';
27851                     this.fireEvent("select", this, '');
27852                 },
27853                 scope: this
27854             });
27855         }
27856         
27857         
27858         if(Roo.isIE){
27859             this.el.repaint();
27860         }
27861         this.update(this.value);
27862     },
27863
27864     createMonthPicker : function(){
27865         if(!this.monthPicker.dom.firstChild){
27866             var buf = ['<table border="0" cellspacing="0">'];
27867             for(var i = 0; i < 6; i++){
27868                 buf.push(
27869                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
27870                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
27871                     i == 0 ?
27872                     '<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>' :
27873                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
27874                 );
27875             }
27876             buf.push(
27877                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
27878                     this.okText,
27879                     '</button><button type="button" class="x-date-mp-cancel">',
27880                     this.cancelText,
27881                     '</button></td></tr>',
27882                 '</table>'
27883             );
27884             this.monthPicker.update(buf.join(''));
27885             this.monthPicker.on('click', this.onMonthClick, this);
27886             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
27887
27888             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
27889             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
27890
27891             this.mpMonths.each(function(m, a, i){
27892                 i += 1;
27893                 if((i%2) == 0){
27894                     m.dom.xmonth = 5 + Math.round(i * .5);
27895                 }else{
27896                     m.dom.xmonth = Math.round((i-1) * .5);
27897                 }
27898             });
27899         }
27900     },
27901
27902     showMonthPicker : function(){
27903         this.createMonthPicker();
27904         var size = this.el.getSize();
27905         this.monthPicker.setSize(size);
27906         this.monthPicker.child('table').setSize(size);
27907
27908         this.mpSelMonth = (this.activeDate || this.value).getMonth();
27909         this.updateMPMonth(this.mpSelMonth);
27910         this.mpSelYear = (this.activeDate || this.value).getFullYear();
27911         this.updateMPYear(this.mpSelYear);
27912
27913         this.monthPicker.slideIn('t', {duration:.2});
27914     },
27915
27916     updateMPYear : function(y){
27917         this.mpyear = y;
27918         var ys = this.mpYears.elements;
27919         for(var i = 1; i <= 10; i++){
27920             var td = ys[i-1], y2;
27921             if((i%2) == 0){
27922                 y2 = y + Math.round(i * .5);
27923                 td.firstChild.innerHTML = y2;
27924                 td.xyear = y2;
27925             }else{
27926                 y2 = y - (5-Math.round(i * .5));
27927                 td.firstChild.innerHTML = y2;
27928                 td.xyear = y2;
27929             }
27930             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
27931         }
27932     },
27933
27934     updateMPMonth : function(sm){
27935         this.mpMonths.each(function(m, a, i){
27936             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
27937         });
27938     },
27939
27940     selectMPMonth: function(m){
27941         
27942     },
27943
27944     onMonthClick : function(e, t){
27945         e.stopEvent();
27946         var el = new Roo.Element(t), pn;
27947         if(el.is('button.x-date-mp-cancel')){
27948             this.hideMonthPicker();
27949         }
27950         else if(el.is('button.x-date-mp-ok')){
27951             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27952             this.hideMonthPicker();
27953         }
27954         else if(pn = el.up('td.x-date-mp-month', 2)){
27955             this.mpMonths.removeClass('x-date-mp-sel');
27956             pn.addClass('x-date-mp-sel');
27957             this.mpSelMonth = pn.dom.xmonth;
27958         }
27959         else if(pn = el.up('td.x-date-mp-year', 2)){
27960             this.mpYears.removeClass('x-date-mp-sel');
27961             pn.addClass('x-date-mp-sel');
27962             this.mpSelYear = pn.dom.xyear;
27963         }
27964         else if(el.is('a.x-date-mp-prev')){
27965             this.updateMPYear(this.mpyear-10);
27966         }
27967         else if(el.is('a.x-date-mp-next')){
27968             this.updateMPYear(this.mpyear+10);
27969         }
27970     },
27971
27972     onMonthDblClick : function(e, t){
27973         e.stopEvent();
27974         var el = new Roo.Element(t), pn;
27975         if(pn = el.up('td.x-date-mp-month', 2)){
27976             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
27977             this.hideMonthPicker();
27978         }
27979         else if(pn = el.up('td.x-date-mp-year', 2)){
27980             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27981             this.hideMonthPicker();
27982         }
27983     },
27984
27985     hideMonthPicker : function(disableAnim){
27986         if(this.monthPicker){
27987             if(disableAnim === true){
27988                 this.monthPicker.hide();
27989             }else{
27990                 this.monthPicker.slideOut('t', {duration:.2});
27991             }
27992         }
27993     },
27994
27995     // private
27996     showPrevMonth : function(e){
27997         this.update(this.activeDate.add("mo", -1));
27998     },
27999
28000     // private
28001     showNextMonth : function(e){
28002         this.update(this.activeDate.add("mo", 1));
28003     },
28004
28005     // private
28006     showPrevYear : function(){
28007         this.update(this.activeDate.add("y", -1));
28008     },
28009
28010     // private
28011     showNextYear : function(){
28012         this.update(this.activeDate.add("y", 1));
28013     },
28014
28015     // private
28016     handleMouseWheel : function(e){
28017         var delta = e.getWheelDelta();
28018         if(delta > 0){
28019             this.showPrevMonth();
28020             e.stopEvent();
28021         } else if(delta < 0){
28022             this.showNextMonth();
28023             e.stopEvent();
28024         }
28025     },
28026
28027     // private
28028     handleDateClick : function(e, t){
28029         e.stopEvent();
28030         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28031             this.setValue(new Date(t.dateValue));
28032             this.fireEvent("select", this, this.value);
28033         }
28034     },
28035
28036     // private
28037     selectToday : function(){
28038         this.setValue(new Date().clearTime());
28039         this.fireEvent("select", this, this.value);
28040     },
28041
28042     // private
28043     update : function(date)
28044     {
28045         var vd = this.activeDate;
28046         this.activeDate = date;
28047         if(vd && this.el){
28048             var t = date.getTime();
28049             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28050                 this.cells.removeClass("x-date-selected");
28051                 this.cells.each(function(c){
28052                    if(c.dom.firstChild.dateValue == t){
28053                        c.addClass("x-date-selected");
28054                        setTimeout(function(){
28055                             try{c.dom.firstChild.focus();}catch(e){}
28056                        }, 50);
28057                        return false;
28058                    }
28059                 });
28060                 return;
28061             }
28062         }
28063         
28064         var days = date.getDaysInMonth();
28065         var firstOfMonth = date.getFirstDateOfMonth();
28066         var startingPos = firstOfMonth.getDay()-this.startDay;
28067
28068         if(startingPos <= this.startDay){
28069             startingPos += 7;
28070         }
28071
28072         var pm = date.add("mo", -1);
28073         var prevStart = pm.getDaysInMonth()-startingPos;
28074
28075         var cells = this.cells.elements;
28076         var textEls = this.textNodes;
28077         days += startingPos;
28078
28079         // convert everything to numbers so it's fast
28080         var day = 86400000;
28081         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28082         var today = new Date().clearTime().getTime();
28083         var sel = date.clearTime().getTime();
28084         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28085         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28086         var ddMatch = this.disabledDatesRE;
28087         var ddText = this.disabledDatesText;
28088         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28089         var ddaysText = this.disabledDaysText;
28090         var format = this.format;
28091
28092         var setCellClass = function(cal, cell){
28093             cell.title = "";
28094             var t = d.getTime();
28095             cell.firstChild.dateValue = t;
28096             if(t == today){
28097                 cell.className += " x-date-today";
28098                 cell.title = cal.todayText;
28099             }
28100             if(t == sel){
28101                 cell.className += " x-date-selected";
28102                 setTimeout(function(){
28103                     try{cell.firstChild.focus();}catch(e){}
28104                 }, 50);
28105             }
28106             // disabling
28107             if(t < min) {
28108                 cell.className = " x-date-disabled";
28109                 cell.title = cal.minText;
28110                 return;
28111             }
28112             if(t > max) {
28113                 cell.className = " x-date-disabled";
28114                 cell.title = cal.maxText;
28115                 return;
28116             }
28117             if(ddays){
28118                 if(ddays.indexOf(d.getDay()) != -1){
28119                     cell.title = ddaysText;
28120                     cell.className = " x-date-disabled";
28121                 }
28122             }
28123             if(ddMatch && format){
28124                 var fvalue = d.dateFormat(format);
28125                 if(ddMatch.test(fvalue)){
28126                     cell.title = ddText.replace("%0", fvalue);
28127                     cell.className = " x-date-disabled";
28128                 }
28129             }
28130         };
28131
28132         var i = 0;
28133         for(; i < startingPos; i++) {
28134             textEls[i].innerHTML = (++prevStart);
28135             d.setDate(d.getDate()+1);
28136             cells[i].className = "x-date-prevday";
28137             setCellClass(this, cells[i]);
28138         }
28139         for(; i < days; i++){
28140             intDay = i - startingPos + 1;
28141             textEls[i].innerHTML = (intDay);
28142             d.setDate(d.getDate()+1);
28143             cells[i].className = "x-date-active";
28144             setCellClass(this, cells[i]);
28145         }
28146         var extraDays = 0;
28147         for(; i < 42; i++) {
28148              textEls[i].innerHTML = (++extraDays);
28149              d.setDate(d.getDate()+1);
28150              cells[i].className = "x-date-nextday";
28151              setCellClass(this, cells[i]);
28152         }
28153
28154         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28155         this.fireEvent('monthchange', this, date);
28156         
28157         if(!this.internalRender){
28158             var main = this.el.dom.firstChild;
28159             var w = main.offsetWidth;
28160             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28161             Roo.fly(main).setWidth(w);
28162             this.internalRender = true;
28163             // opera does not respect the auto grow header center column
28164             // then, after it gets a width opera refuses to recalculate
28165             // without a second pass
28166             if(Roo.isOpera && !this.secondPass){
28167                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28168                 this.secondPass = true;
28169                 this.update.defer(10, this, [date]);
28170             }
28171         }
28172         
28173         
28174     }
28175 });        /*
28176  * Based on:
28177  * Ext JS Library 1.1.1
28178  * Copyright(c) 2006-2007, Ext JS, LLC.
28179  *
28180  * Originally Released Under LGPL - original licence link has changed is not relivant.
28181  *
28182  * Fork - LGPL
28183  * <script type="text/javascript">
28184  */
28185 /**
28186  * @class Roo.TabPanel
28187  * @extends Roo.util.Observable
28188  * A lightweight tab container.
28189  * <br><br>
28190  * Usage:
28191  * <pre><code>
28192 // basic tabs 1, built from existing content
28193 var tabs = new Roo.TabPanel("tabs1");
28194 tabs.addTab("script", "View Script");
28195 tabs.addTab("markup", "View Markup");
28196 tabs.activate("script");
28197
28198 // more advanced tabs, built from javascript
28199 var jtabs = new Roo.TabPanel("jtabs");
28200 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28201
28202 // set up the UpdateManager
28203 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28204 var updater = tab2.getUpdateManager();
28205 updater.setDefaultUrl("ajax1.htm");
28206 tab2.on('activate', updater.refresh, updater, true);
28207
28208 // Use setUrl for Ajax loading
28209 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28210 tab3.setUrl("ajax2.htm", null, true);
28211
28212 // Disabled tab
28213 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28214 tab4.disable();
28215
28216 jtabs.activate("jtabs-1");
28217  * </code></pre>
28218  * @constructor
28219  * Create a new TabPanel.
28220  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28221  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28222  */
28223 Roo.TabPanel = function(container, config){
28224     /**
28225     * The container element for this TabPanel.
28226     * @type Roo.Element
28227     */
28228     this.el = Roo.get(container, true);
28229     if(config){
28230         if(typeof config == "boolean"){
28231             this.tabPosition = config ? "bottom" : "top";
28232         }else{
28233             Roo.apply(this, config);
28234         }
28235     }
28236     if(this.tabPosition == "bottom"){
28237         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28238         this.el.addClass("x-tabs-bottom");
28239     }
28240     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28241     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28242     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28243     if(Roo.isIE){
28244         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28245     }
28246     if(this.tabPosition != "bottom"){
28247         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28248          * @type Roo.Element
28249          */
28250         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28251         this.el.addClass("x-tabs-top");
28252     }
28253     this.items = [];
28254
28255     this.bodyEl.setStyle("position", "relative");
28256
28257     this.active = null;
28258     this.activateDelegate = this.activate.createDelegate(this);
28259
28260     this.addEvents({
28261         /**
28262          * @event tabchange
28263          * Fires when the active tab changes
28264          * @param {Roo.TabPanel} this
28265          * @param {Roo.TabPanelItem} activePanel The new active tab
28266          */
28267         "tabchange": true,
28268         /**
28269          * @event beforetabchange
28270          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28271          * @param {Roo.TabPanel} this
28272          * @param {Object} e Set cancel to true on this object to cancel the tab change
28273          * @param {Roo.TabPanelItem} tab The tab being changed to
28274          */
28275         "beforetabchange" : true
28276     });
28277
28278     Roo.EventManager.onWindowResize(this.onResize, this);
28279     this.cpad = this.el.getPadding("lr");
28280     this.hiddenCount = 0;
28281
28282
28283     // toolbar on the tabbar support...
28284     if (this.toolbar) {
28285         var tcfg = this.toolbar;
28286         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28287         this.toolbar = new Roo.Toolbar(tcfg);
28288         if (Roo.isSafari) {
28289             var tbl = tcfg.container.child('table', true);
28290             tbl.setAttribute('width', '100%');
28291         }
28292         
28293     }
28294    
28295
28296
28297     Roo.TabPanel.superclass.constructor.call(this);
28298 };
28299
28300 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28301     /*
28302      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28303      */
28304     tabPosition : "top",
28305     /*
28306      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28307      */
28308     currentTabWidth : 0,
28309     /*
28310      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28311      */
28312     minTabWidth : 40,
28313     /*
28314      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28315      */
28316     maxTabWidth : 250,
28317     /*
28318      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28319      */
28320     preferredTabWidth : 175,
28321     /*
28322      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28323      */
28324     resizeTabs : false,
28325     /*
28326      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28327      */
28328     monitorResize : true,
28329     /*
28330      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28331      */
28332     toolbar : false,
28333
28334     /**
28335      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28336      * @param {String} id The id of the div to use <b>or create</b>
28337      * @param {String} text The text for the tab
28338      * @param {String} content (optional) Content to put in the TabPanelItem body
28339      * @param {Boolean} closable (optional) True to create a close icon on the tab
28340      * @return {Roo.TabPanelItem} The created TabPanelItem
28341      */
28342     addTab : function(id, text, content, closable){
28343         var item = new Roo.TabPanelItem(this, id, text, closable);
28344         this.addTabItem(item);
28345         if(content){
28346             item.setContent(content);
28347         }
28348         return item;
28349     },
28350
28351     /**
28352      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28353      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28354      * @return {Roo.TabPanelItem}
28355      */
28356     getTab : function(id){
28357         return this.items[id];
28358     },
28359
28360     /**
28361      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28362      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28363      */
28364     hideTab : function(id){
28365         var t = this.items[id];
28366         if(!t.isHidden()){
28367            t.setHidden(true);
28368            this.hiddenCount++;
28369            this.autoSizeTabs();
28370         }
28371     },
28372
28373     /**
28374      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28375      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28376      */
28377     unhideTab : function(id){
28378         var t = this.items[id];
28379         if(t.isHidden()){
28380            t.setHidden(false);
28381            this.hiddenCount--;
28382            this.autoSizeTabs();
28383         }
28384     },
28385
28386     /**
28387      * Adds an existing {@link Roo.TabPanelItem}.
28388      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28389      */
28390     addTabItem : function(item){
28391         this.items[item.id] = item;
28392         this.items.push(item);
28393         if(this.resizeTabs){
28394            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28395            this.autoSizeTabs();
28396         }else{
28397             item.autoSize();
28398         }
28399     },
28400
28401     /**
28402      * Removes a {@link Roo.TabPanelItem}.
28403      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28404      */
28405     removeTab : function(id){
28406         var items = this.items;
28407         var tab = items[id];
28408         if(!tab) { return; }
28409         var index = items.indexOf(tab);
28410         if(this.active == tab && items.length > 1){
28411             var newTab = this.getNextAvailable(index);
28412             if(newTab) {
28413                 newTab.activate();
28414             }
28415         }
28416         this.stripEl.dom.removeChild(tab.pnode.dom);
28417         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28418             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28419         }
28420         items.splice(index, 1);
28421         delete this.items[tab.id];
28422         tab.fireEvent("close", tab);
28423         tab.purgeListeners();
28424         this.autoSizeTabs();
28425     },
28426
28427     getNextAvailable : function(start){
28428         var items = this.items;
28429         var index = start;
28430         // look for a next tab that will slide over to
28431         // replace the one being removed
28432         while(index < items.length){
28433             var item = items[++index];
28434             if(item && !item.isHidden()){
28435                 return item;
28436             }
28437         }
28438         // if one isn't found select the previous tab (on the left)
28439         index = start;
28440         while(index >= 0){
28441             var item = items[--index];
28442             if(item && !item.isHidden()){
28443                 return item;
28444             }
28445         }
28446         return null;
28447     },
28448
28449     /**
28450      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28451      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28452      */
28453     disableTab : function(id){
28454         var tab = this.items[id];
28455         if(tab && this.active != tab){
28456             tab.disable();
28457         }
28458     },
28459
28460     /**
28461      * Enables a {@link Roo.TabPanelItem} that is disabled.
28462      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28463      */
28464     enableTab : function(id){
28465         var tab = this.items[id];
28466         tab.enable();
28467     },
28468
28469     /**
28470      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28471      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28472      * @return {Roo.TabPanelItem} The TabPanelItem.
28473      */
28474     activate : function(id){
28475         var tab = this.items[id];
28476         if(!tab){
28477             return null;
28478         }
28479         if(tab == this.active || tab.disabled){
28480             return tab;
28481         }
28482         var e = {};
28483         this.fireEvent("beforetabchange", this, e, tab);
28484         if(e.cancel !== true && !tab.disabled){
28485             if(this.active){
28486                 this.active.hide();
28487             }
28488             this.active = this.items[id];
28489             this.active.show();
28490             this.fireEvent("tabchange", this, this.active);
28491         }
28492         return tab;
28493     },
28494
28495     /**
28496      * Gets the active {@link Roo.TabPanelItem}.
28497      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28498      */
28499     getActiveTab : function(){
28500         return this.active;
28501     },
28502
28503     /**
28504      * Updates the tab body element to fit the height of the container element
28505      * for overflow scrolling
28506      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28507      */
28508     syncHeight : function(targetHeight){
28509         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28510         var bm = this.bodyEl.getMargins();
28511         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28512         this.bodyEl.setHeight(newHeight);
28513         return newHeight;
28514     },
28515
28516     onResize : function(){
28517         if(this.monitorResize){
28518             this.autoSizeTabs();
28519         }
28520     },
28521
28522     /**
28523      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28524      */
28525     beginUpdate : function(){
28526         this.updating = true;
28527     },
28528
28529     /**
28530      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28531      */
28532     endUpdate : function(){
28533         this.updating = false;
28534         this.autoSizeTabs();
28535     },
28536
28537     /**
28538      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28539      */
28540     autoSizeTabs : function(){
28541         var count = this.items.length;
28542         var vcount = count - this.hiddenCount;
28543         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28544             return;
28545         }
28546         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28547         var availWidth = Math.floor(w / vcount);
28548         var b = this.stripBody;
28549         if(b.getWidth() > w){
28550             var tabs = this.items;
28551             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28552             if(availWidth < this.minTabWidth){
28553                 /*if(!this.sleft){    // incomplete scrolling code
28554                     this.createScrollButtons();
28555                 }
28556                 this.showScroll();
28557                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28558             }
28559         }else{
28560             if(this.currentTabWidth < this.preferredTabWidth){
28561                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28562             }
28563         }
28564     },
28565
28566     /**
28567      * Returns the number of tabs in this TabPanel.
28568      * @return {Number}
28569      */
28570      getCount : function(){
28571          return this.items.length;
28572      },
28573
28574     /**
28575      * Resizes all the tabs to the passed width
28576      * @param {Number} The new width
28577      */
28578     setTabWidth : function(width){
28579         this.currentTabWidth = width;
28580         for(var i = 0, len = this.items.length; i < len; i++) {
28581                 if(!this.items[i].isHidden()) {
28582                 this.items[i].setWidth(width);
28583             }
28584         }
28585     },
28586
28587     /**
28588      * Destroys this TabPanel
28589      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28590      */
28591     destroy : function(removeEl){
28592         Roo.EventManager.removeResizeListener(this.onResize, this);
28593         for(var i = 0, len = this.items.length; i < len; i++){
28594             this.items[i].purgeListeners();
28595         }
28596         if(removeEl === true){
28597             this.el.update("");
28598             this.el.remove();
28599         }
28600     }
28601 });
28602
28603 /**
28604  * @class Roo.TabPanelItem
28605  * @extends Roo.util.Observable
28606  * Represents an individual item (tab plus body) in a TabPanel.
28607  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28608  * @param {String} id The id of this TabPanelItem
28609  * @param {String} text The text for the tab of this TabPanelItem
28610  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28611  */
28612 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28613     /**
28614      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28615      * @type Roo.TabPanel
28616      */
28617     this.tabPanel = tabPanel;
28618     /**
28619      * The id for this TabPanelItem
28620      * @type String
28621      */
28622     this.id = id;
28623     /** @private */
28624     this.disabled = false;
28625     /** @private */
28626     this.text = text;
28627     /** @private */
28628     this.loaded = false;
28629     this.closable = closable;
28630
28631     /**
28632      * The body element for this TabPanelItem.
28633      * @type Roo.Element
28634      */
28635     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28636     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28637     this.bodyEl.setStyle("display", "block");
28638     this.bodyEl.setStyle("zoom", "1");
28639     this.hideAction();
28640
28641     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28642     /** @private */
28643     this.el = Roo.get(els.el, true);
28644     this.inner = Roo.get(els.inner, true);
28645     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28646     this.pnode = Roo.get(els.el.parentNode, true);
28647     this.el.on("mousedown", this.onTabMouseDown, this);
28648     this.el.on("click", this.onTabClick, this);
28649     /** @private */
28650     if(closable){
28651         var c = Roo.get(els.close, true);
28652         c.dom.title = this.closeText;
28653         c.addClassOnOver("close-over");
28654         c.on("click", this.closeClick, this);
28655      }
28656
28657     this.addEvents({
28658          /**
28659          * @event activate
28660          * Fires when this tab becomes the active tab.
28661          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28662          * @param {Roo.TabPanelItem} this
28663          */
28664         "activate": true,
28665         /**
28666          * @event beforeclose
28667          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28668          * @param {Roo.TabPanelItem} this
28669          * @param {Object} e Set cancel to true on this object to cancel the close.
28670          */
28671         "beforeclose": true,
28672         /**
28673          * @event close
28674          * Fires when this tab is closed.
28675          * @param {Roo.TabPanelItem} this
28676          */
28677          "close": true,
28678         /**
28679          * @event deactivate
28680          * Fires when this tab is no longer the active tab.
28681          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28682          * @param {Roo.TabPanelItem} this
28683          */
28684          "deactivate" : true
28685     });
28686     this.hidden = false;
28687
28688     Roo.TabPanelItem.superclass.constructor.call(this);
28689 };
28690
28691 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28692     purgeListeners : function(){
28693        Roo.util.Observable.prototype.purgeListeners.call(this);
28694        this.el.removeAllListeners();
28695     },
28696     /**
28697      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28698      */
28699     show : function(){
28700         this.pnode.addClass("on");
28701         this.showAction();
28702         if(Roo.isOpera){
28703             this.tabPanel.stripWrap.repaint();
28704         }
28705         this.fireEvent("activate", this.tabPanel, this);
28706     },
28707
28708     /**
28709      * Returns true if this tab is the active tab.
28710      * @return {Boolean}
28711      */
28712     isActive : function(){
28713         return this.tabPanel.getActiveTab() == this;
28714     },
28715
28716     /**
28717      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28718      */
28719     hide : function(){
28720         this.pnode.removeClass("on");
28721         this.hideAction();
28722         this.fireEvent("deactivate", this.tabPanel, this);
28723     },
28724
28725     hideAction : function(){
28726         this.bodyEl.hide();
28727         this.bodyEl.setStyle("position", "absolute");
28728         this.bodyEl.setLeft("-20000px");
28729         this.bodyEl.setTop("-20000px");
28730     },
28731
28732     showAction : function(){
28733         this.bodyEl.setStyle("position", "relative");
28734         this.bodyEl.setTop("");
28735         this.bodyEl.setLeft("");
28736         this.bodyEl.show();
28737     },
28738
28739     /**
28740      * Set the tooltip for the tab.
28741      * @param {String} tooltip The tab's tooltip
28742      */
28743     setTooltip : function(text){
28744         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28745             this.textEl.dom.qtip = text;
28746             this.textEl.dom.removeAttribute('title');
28747         }else{
28748             this.textEl.dom.title = text;
28749         }
28750     },
28751
28752     onTabClick : function(e){
28753         e.preventDefault();
28754         this.tabPanel.activate(this.id);
28755     },
28756
28757     onTabMouseDown : function(e){
28758         e.preventDefault();
28759         this.tabPanel.activate(this.id);
28760     },
28761
28762     getWidth : function(){
28763         return this.inner.getWidth();
28764     },
28765
28766     setWidth : function(width){
28767         var iwidth = width - this.pnode.getPadding("lr");
28768         this.inner.setWidth(iwidth);
28769         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28770         this.pnode.setWidth(width);
28771     },
28772
28773     /**
28774      * Show or hide the tab
28775      * @param {Boolean} hidden True to hide or false to show.
28776      */
28777     setHidden : function(hidden){
28778         this.hidden = hidden;
28779         this.pnode.setStyle("display", hidden ? "none" : "");
28780     },
28781
28782     /**
28783      * Returns true if this tab is "hidden"
28784      * @return {Boolean}
28785      */
28786     isHidden : function(){
28787         return this.hidden;
28788     },
28789
28790     /**
28791      * Returns the text for this tab
28792      * @return {String}
28793      */
28794     getText : function(){
28795         return this.text;
28796     },
28797
28798     autoSize : function(){
28799         //this.el.beginMeasure();
28800         this.textEl.setWidth(1);
28801         /*
28802          *  #2804 [new] Tabs in Roojs
28803          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28804          */
28805         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28806         //this.el.endMeasure();
28807     },
28808
28809     /**
28810      * Sets the text for the tab (Note: this also sets the tooltip text)
28811      * @param {String} text The tab's text and tooltip
28812      */
28813     setText : function(text){
28814         this.text = text;
28815         this.textEl.update(text);
28816         this.setTooltip(text);
28817         if(!this.tabPanel.resizeTabs){
28818             this.autoSize();
28819         }
28820     },
28821     /**
28822      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28823      */
28824     activate : function(){
28825         this.tabPanel.activate(this.id);
28826     },
28827
28828     /**
28829      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
28830      */
28831     disable : function(){
28832         if(this.tabPanel.active != this){
28833             this.disabled = true;
28834             this.pnode.addClass("disabled");
28835         }
28836     },
28837
28838     /**
28839      * Enables this TabPanelItem if it was previously disabled.
28840      */
28841     enable : function(){
28842         this.disabled = false;
28843         this.pnode.removeClass("disabled");
28844     },
28845
28846     /**
28847      * Sets the content for this TabPanelItem.
28848      * @param {String} content The content
28849      * @param {Boolean} loadScripts true to look for and load scripts
28850      */
28851     setContent : function(content, loadScripts){
28852         this.bodyEl.update(content, loadScripts);
28853     },
28854
28855     /**
28856      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
28857      * @return {Roo.UpdateManager} The UpdateManager
28858      */
28859     getUpdateManager : function(){
28860         return this.bodyEl.getUpdateManager();
28861     },
28862
28863     /**
28864      * Set a URL to be used to load the content for this TabPanelItem.
28865      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
28866      * @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)
28867      * @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)
28868      * @return {Roo.UpdateManager} The UpdateManager
28869      */
28870     setUrl : function(url, params, loadOnce){
28871         if(this.refreshDelegate){
28872             this.un('activate', this.refreshDelegate);
28873         }
28874         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
28875         this.on("activate", this.refreshDelegate);
28876         return this.bodyEl.getUpdateManager();
28877     },
28878
28879     /** @private */
28880     _handleRefresh : function(url, params, loadOnce){
28881         if(!loadOnce || !this.loaded){
28882             var updater = this.bodyEl.getUpdateManager();
28883             updater.update(url, params, this._setLoaded.createDelegate(this));
28884         }
28885     },
28886
28887     /**
28888      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
28889      *   Will fail silently if the setUrl method has not been called.
28890      *   This does not activate the panel, just updates its content.
28891      */
28892     refresh : function(){
28893         if(this.refreshDelegate){
28894            this.loaded = false;
28895            this.refreshDelegate();
28896         }
28897     },
28898
28899     /** @private */
28900     _setLoaded : function(){
28901         this.loaded = true;
28902     },
28903
28904     /** @private */
28905     closeClick : function(e){
28906         var o = {};
28907         e.stopEvent();
28908         this.fireEvent("beforeclose", this, o);
28909         if(o.cancel !== true){
28910             this.tabPanel.removeTab(this.id);
28911         }
28912     },
28913     /**
28914      * The text displayed in the tooltip for the close icon.
28915      * @type String
28916      */
28917     closeText : "Close this tab"
28918 });
28919
28920 /** @private */
28921 Roo.TabPanel.prototype.createStrip = function(container){
28922     var strip = document.createElement("div");
28923     strip.className = "x-tabs-wrap";
28924     container.appendChild(strip);
28925     return strip;
28926 };
28927 /** @private */
28928 Roo.TabPanel.prototype.createStripList = function(strip){
28929     // div wrapper for retard IE
28930     // returns the "tr" element.
28931     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
28932         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
28933         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
28934     return strip.firstChild.firstChild.firstChild.firstChild;
28935 };
28936 /** @private */
28937 Roo.TabPanel.prototype.createBody = function(container){
28938     var body = document.createElement("div");
28939     Roo.id(body, "tab-body");
28940     Roo.fly(body).addClass("x-tabs-body");
28941     container.appendChild(body);
28942     return body;
28943 };
28944 /** @private */
28945 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
28946     var body = Roo.getDom(id);
28947     if(!body){
28948         body = document.createElement("div");
28949         body.id = id;
28950     }
28951     Roo.fly(body).addClass("x-tabs-item-body");
28952     bodyEl.insertBefore(body, bodyEl.firstChild);
28953     return body;
28954 };
28955 /** @private */
28956 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
28957     var td = document.createElement("td");
28958     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
28959     //stripEl.appendChild(td);
28960     if(closable){
28961         td.className = "x-tabs-closable";
28962         if(!this.closeTpl){
28963             this.closeTpl = new Roo.Template(
28964                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28965                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
28966                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
28967             );
28968         }
28969         var el = this.closeTpl.overwrite(td, {"text": text});
28970         var close = el.getElementsByTagName("div")[0];
28971         var inner = el.getElementsByTagName("em")[0];
28972         return {"el": el, "close": close, "inner": inner};
28973     } else {
28974         if(!this.tabTpl){
28975             this.tabTpl = new Roo.Template(
28976                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28977                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
28978             );
28979         }
28980         var el = this.tabTpl.overwrite(td, {"text": text});
28981         var inner = el.getElementsByTagName("em")[0];
28982         return {"el": el, "inner": inner};
28983     }
28984 };/*
28985  * Based on:
28986  * Ext JS Library 1.1.1
28987  * Copyright(c) 2006-2007, Ext JS, LLC.
28988  *
28989  * Originally Released Under LGPL - original licence link has changed is not relivant.
28990  *
28991  * Fork - LGPL
28992  * <script type="text/javascript">
28993  */
28994
28995 /**
28996  * @class Roo.Button
28997  * @extends Roo.util.Observable
28998  * Simple Button class
28999  * @cfg {String} text The button text
29000  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29001  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29002  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29003  * @cfg {Object} scope The scope of the handler
29004  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29005  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29006  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29007  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29008  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29009  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29010    applies if enableToggle = true)
29011  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29012  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29013   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29014  * @constructor
29015  * Create a new button
29016  * @param {Object} config The config object
29017  */
29018 Roo.Button = function(renderTo, config)
29019 {
29020     if (!config) {
29021         config = renderTo;
29022         renderTo = config.renderTo || false;
29023     }
29024     
29025     Roo.apply(this, config);
29026     this.addEvents({
29027         /**
29028              * @event click
29029              * Fires when this button is clicked
29030              * @param {Button} this
29031              * @param {EventObject} e The click event
29032              */
29033             "click" : true,
29034         /**
29035              * @event toggle
29036              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29037              * @param {Button} this
29038              * @param {Boolean} pressed
29039              */
29040             "toggle" : true,
29041         /**
29042              * @event mouseover
29043              * Fires when the mouse hovers over the button
29044              * @param {Button} this
29045              * @param {Event} e The event object
29046              */
29047         'mouseover' : true,
29048         /**
29049              * @event mouseout
29050              * Fires when the mouse exits the button
29051              * @param {Button} this
29052              * @param {Event} e The event object
29053              */
29054         'mouseout': true,
29055          /**
29056              * @event render
29057              * Fires when the button is rendered
29058              * @param {Button} this
29059              */
29060         'render': true
29061     });
29062     if(this.menu){
29063         this.menu = Roo.menu.MenuMgr.get(this.menu);
29064     }
29065     // register listeners first!!  - so render can be captured..
29066     Roo.util.Observable.call(this);
29067     if(renderTo){
29068         this.render(renderTo);
29069     }
29070     
29071   
29072 };
29073
29074 Roo.extend(Roo.Button, Roo.util.Observable, {
29075     /**
29076      * 
29077      */
29078     
29079     /**
29080      * Read-only. True if this button is hidden
29081      * @type Boolean
29082      */
29083     hidden : false,
29084     /**
29085      * Read-only. True if this button is disabled
29086      * @type Boolean
29087      */
29088     disabled : false,
29089     /**
29090      * Read-only. True if this button is pressed (only if enableToggle = true)
29091      * @type Boolean
29092      */
29093     pressed : false,
29094
29095     /**
29096      * @cfg {Number} tabIndex 
29097      * The DOM tabIndex for this button (defaults to undefined)
29098      */
29099     tabIndex : undefined,
29100
29101     /**
29102      * @cfg {Boolean} enableToggle
29103      * True to enable pressed/not pressed toggling (defaults to false)
29104      */
29105     enableToggle: false,
29106     /**
29107      * @cfg {Mixed} menu
29108      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29109      */
29110     menu : undefined,
29111     /**
29112      * @cfg {String} menuAlign
29113      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29114      */
29115     menuAlign : "tl-bl?",
29116
29117     /**
29118      * @cfg {String} iconCls
29119      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29120      */
29121     iconCls : undefined,
29122     /**
29123      * @cfg {String} type
29124      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29125      */
29126     type : 'button',
29127
29128     // private
29129     menuClassTarget: 'tr',
29130
29131     /**
29132      * @cfg {String} clickEvent
29133      * The type of event to map to the button's event handler (defaults to 'click')
29134      */
29135     clickEvent : 'click',
29136
29137     /**
29138      * @cfg {Boolean} handleMouseEvents
29139      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29140      */
29141     handleMouseEvents : true,
29142
29143     /**
29144      * @cfg {String} tooltipType
29145      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29146      */
29147     tooltipType : 'qtip',
29148
29149     /**
29150      * @cfg {String} cls
29151      * A CSS class to apply to the button's main element.
29152      */
29153     
29154     /**
29155      * @cfg {Roo.Template} template (Optional)
29156      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29157      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29158      * require code modifications if required elements (e.g. a button) aren't present.
29159      */
29160
29161     // private
29162     render : function(renderTo){
29163         var btn;
29164         if(this.hideParent){
29165             this.parentEl = Roo.get(renderTo);
29166         }
29167         if(!this.dhconfig){
29168             if(!this.template){
29169                 if(!Roo.Button.buttonTemplate){
29170                     // hideous table template
29171                     Roo.Button.buttonTemplate = new Roo.Template(
29172                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29173                         '<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>',
29174                         "</tr></tbody></table>");
29175                 }
29176                 this.template = Roo.Button.buttonTemplate;
29177             }
29178             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29179             var btnEl = btn.child("button:first");
29180             btnEl.on('focus', this.onFocus, this);
29181             btnEl.on('blur', this.onBlur, this);
29182             if(this.cls){
29183                 btn.addClass(this.cls);
29184             }
29185             if(this.icon){
29186                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29187             }
29188             if(this.iconCls){
29189                 btnEl.addClass(this.iconCls);
29190                 if(!this.cls){
29191                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29192                 }
29193             }
29194             if(this.tabIndex !== undefined){
29195                 btnEl.dom.tabIndex = this.tabIndex;
29196             }
29197             if(this.tooltip){
29198                 if(typeof this.tooltip == 'object'){
29199                     Roo.QuickTips.tips(Roo.apply({
29200                           target: btnEl.id
29201                     }, this.tooltip));
29202                 } else {
29203                     btnEl.dom[this.tooltipType] = this.tooltip;
29204                 }
29205             }
29206         }else{
29207             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29208         }
29209         this.el = btn;
29210         if(this.id){
29211             this.el.dom.id = this.el.id = this.id;
29212         }
29213         if(this.menu){
29214             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29215             this.menu.on("show", this.onMenuShow, this);
29216             this.menu.on("hide", this.onMenuHide, this);
29217         }
29218         btn.addClass("x-btn");
29219         if(Roo.isIE && !Roo.isIE7){
29220             this.autoWidth.defer(1, this);
29221         }else{
29222             this.autoWidth();
29223         }
29224         if(this.handleMouseEvents){
29225             btn.on("mouseover", this.onMouseOver, this);
29226             btn.on("mouseout", this.onMouseOut, this);
29227             btn.on("mousedown", this.onMouseDown, this);
29228         }
29229         btn.on(this.clickEvent, this.onClick, this);
29230         //btn.on("mouseup", this.onMouseUp, this);
29231         if(this.hidden){
29232             this.hide();
29233         }
29234         if(this.disabled){
29235             this.disable();
29236         }
29237         Roo.ButtonToggleMgr.register(this);
29238         if(this.pressed){
29239             this.el.addClass("x-btn-pressed");
29240         }
29241         if(this.repeat){
29242             var repeater = new Roo.util.ClickRepeater(btn,
29243                 typeof this.repeat == "object" ? this.repeat : {}
29244             );
29245             repeater.on("click", this.onClick,  this);
29246         }
29247         
29248         this.fireEvent('render', this);
29249         
29250     },
29251     /**
29252      * Returns the button's underlying element
29253      * @return {Roo.Element} The element
29254      */
29255     getEl : function(){
29256         return this.el;  
29257     },
29258     
29259     /**
29260      * Destroys this Button and removes any listeners.
29261      */
29262     destroy : function(){
29263         Roo.ButtonToggleMgr.unregister(this);
29264         this.el.removeAllListeners();
29265         this.purgeListeners();
29266         this.el.remove();
29267     },
29268
29269     // private
29270     autoWidth : function(){
29271         if(this.el){
29272             this.el.setWidth("auto");
29273             if(Roo.isIE7 && Roo.isStrict){
29274                 var ib = this.el.child('button');
29275                 if(ib && ib.getWidth() > 20){
29276                     ib.clip();
29277                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29278                 }
29279             }
29280             if(this.minWidth){
29281                 if(this.hidden){
29282                     this.el.beginMeasure();
29283                 }
29284                 if(this.el.getWidth() < this.minWidth){
29285                     this.el.setWidth(this.minWidth);
29286                 }
29287                 if(this.hidden){
29288                     this.el.endMeasure();
29289                 }
29290             }
29291         }
29292     },
29293
29294     /**
29295      * Assigns this button's click handler
29296      * @param {Function} handler The function to call when the button is clicked
29297      * @param {Object} scope (optional) Scope for the function passed in
29298      */
29299     setHandler : function(handler, scope){
29300         this.handler = handler;
29301         this.scope = scope;  
29302     },
29303     
29304     /**
29305      * Sets this button's text
29306      * @param {String} text The button text
29307      */
29308     setText : function(text){
29309         this.text = text;
29310         if(this.el){
29311             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29312         }
29313         this.autoWidth();
29314     },
29315     
29316     /**
29317      * Gets the text for this button
29318      * @return {String} The button text
29319      */
29320     getText : function(){
29321         return this.text;  
29322     },
29323     
29324     /**
29325      * Show this button
29326      */
29327     show: function(){
29328         this.hidden = false;
29329         if(this.el){
29330             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29331         }
29332     },
29333     
29334     /**
29335      * Hide this button
29336      */
29337     hide: function(){
29338         this.hidden = true;
29339         if(this.el){
29340             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29341         }
29342     },
29343     
29344     /**
29345      * Convenience function for boolean show/hide
29346      * @param {Boolean} visible True to show, false to hide
29347      */
29348     setVisible: function(visible){
29349         if(visible) {
29350             this.show();
29351         }else{
29352             this.hide();
29353         }
29354     },
29355     
29356     /**
29357      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29358      * @param {Boolean} state (optional) Force a particular state
29359      */
29360     toggle : function(state){
29361         state = state === undefined ? !this.pressed : state;
29362         if(state != this.pressed){
29363             if(state){
29364                 this.el.addClass("x-btn-pressed");
29365                 this.pressed = true;
29366                 this.fireEvent("toggle", this, true);
29367             }else{
29368                 this.el.removeClass("x-btn-pressed");
29369                 this.pressed = false;
29370                 this.fireEvent("toggle", this, false);
29371             }
29372             if(this.toggleHandler){
29373                 this.toggleHandler.call(this.scope || this, this, state);
29374             }
29375         }
29376     },
29377     
29378     /**
29379      * Focus the button
29380      */
29381     focus : function(){
29382         this.el.child('button:first').focus();
29383     },
29384     
29385     /**
29386      * Disable this button
29387      */
29388     disable : function(){
29389         if(this.el){
29390             this.el.addClass("x-btn-disabled");
29391         }
29392         this.disabled = true;
29393     },
29394     
29395     /**
29396      * Enable this button
29397      */
29398     enable : function(){
29399         if(this.el){
29400             this.el.removeClass("x-btn-disabled");
29401         }
29402         this.disabled = false;
29403     },
29404
29405     /**
29406      * Convenience function for boolean enable/disable
29407      * @param {Boolean} enabled True to enable, false to disable
29408      */
29409     setDisabled : function(v){
29410         this[v !== true ? "enable" : "disable"]();
29411     },
29412
29413     // private
29414     onClick : function(e)
29415     {
29416         if(e){
29417             e.preventDefault();
29418         }
29419         if(e.button != 0){
29420             return;
29421         }
29422         if(!this.disabled){
29423             if(this.enableToggle){
29424                 this.toggle();
29425             }
29426             if(this.menu && !this.menu.isVisible()){
29427                 this.menu.show(this.el, this.menuAlign);
29428             }
29429             this.fireEvent("click", this, e);
29430             if(this.handler){
29431                 this.el.removeClass("x-btn-over");
29432                 this.handler.call(this.scope || this, this, e);
29433             }
29434         }
29435     },
29436     // private
29437     onMouseOver : function(e){
29438         if(!this.disabled){
29439             this.el.addClass("x-btn-over");
29440             this.fireEvent('mouseover', this, e);
29441         }
29442     },
29443     // private
29444     onMouseOut : function(e){
29445         if(!e.within(this.el,  true)){
29446             this.el.removeClass("x-btn-over");
29447             this.fireEvent('mouseout', this, e);
29448         }
29449     },
29450     // private
29451     onFocus : function(e){
29452         if(!this.disabled){
29453             this.el.addClass("x-btn-focus");
29454         }
29455     },
29456     // private
29457     onBlur : function(e){
29458         this.el.removeClass("x-btn-focus");
29459     },
29460     // private
29461     onMouseDown : function(e){
29462         if(!this.disabled && e.button == 0){
29463             this.el.addClass("x-btn-click");
29464             Roo.get(document).on('mouseup', this.onMouseUp, this);
29465         }
29466     },
29467     // private
29468     onMouseUp : function(e){
29469         if(e.button == 0){
29470             this.el.removeClass("x-btn-click");
29471             Roo.get(document).un('mouseup', this.onMouseUp, this);
29472         }
29473     },
29474     // private
29475     onMenuShow : function(e){
29476         this.el.addClass("x-btn-menu-active");
29477     },
29478     // private
29479     onMenuHide : function(e){
29480         this.el.removeClass("x-btn-menu-active");
29481     }   
29482 });
29483
29484 // Private utility class used by Button
29485 Roo.ButtonToggleMgr = function(){
29486    var groups = {};
29487    
29488    function toggleGroup(btn, state){
29489        if(state){
29490            var g = groups[btn.toggleGroup];
29491            for(var i = 0, l = g.length; i < l; i++){
29492                if(g[i] != btn){
29493                    g[i].toggle(false);
29494                }
29495            }
29496        }
29497    }
29498    
29499    return {
29500        register : function(btn){
29501            if(!btn.toggleGroup){
29502                return;
29503            }
29504            var g = groups[btn.toggleGroup];
29505            if(!g){
29506                g = groups[btn.toggleGroup] = [];
29507            }
29508            g.push(btn);
29509            btn.on("toggle", toggleGroup);
29510        },
29511        
29512        unregister : function(btn){
29513            if(!btn.toggleGroup){
29514                return;
29515            }
29516            var g = groups[btn.toggleGroup];
29517            if(g){
29518                g.remove(btn);
29519                btn.un("toggle", toggleGroup);
29520            }
29521        }
29522    };
29523 }();/*
29524  * Based on:
29525  * Ext JS Library 1.1.1
29526  * Copyright(c) 2006-2007, Ext JS, LLC.
29527  *
29528  * Originally Released Under LGPL - original licence link has changed is not relivant.
29529  *
29530  * Fork - LGPL
29531  * <script type="text/javascript">
29532  */
29533  
29534 /**
29535  * @class Roo.SplitButton
29536  * @extends Roo.Button
29537  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29538  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29539  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29540  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29541  * @cfg {String} arrowTooltip The title attribute of the arrow
29542  * @constructor
29543  * Create a new menu button
29544  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29545  * @param {Object} config The config object
29546  */
29547 Roo.SplitButton = function(renderTo, config){
29548     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29549     /**
29550      * @event arrowclick
29551      * Fires when this button's arrow is clicked
29552      * @param {SplitButton} this
29553      * @param {EventObject} e The click event
29554      */
29555     this.addEvents({"arrowclick":true});
29556 };
29557
29558 Roo.extend(Roo.SplitButton, Roo.Button, {
29559     render : function(renderTo){
29560         // this is one sweet looking template!
29561         var tpl = new Roo.Template(
29562             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29563             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29564             '<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>',
29565             "</tbody></table></td><td>",
29566             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29567             '<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>',
29568             "</tbody></table></td></tr></table>"
29569         );
29570         var btn = tpl.append(renderTo, [this.text, this.type], true);
29571         var btnEl = btn.child("button");
29572         if(this.cls){
29573             btn.addClass(this.cls);
29574         }
29575         if(this.icon){
29576             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29577         }
29578         if(this.iconCls){
29579             btnEl.addClass(this.iconCls);
29580             if(!this.cls){
29581                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29582             }
29583         }
29584         this.el = btn;
29585         if(this.handleMouseEvents){
29586             btn.on("mouseover", this.onMouseOver, this);
29587             btn.on("mouseout", this.onMouseOut, this);
29588             btn.on("mousedown", this.onMouseDown, this);
29589             btn.on("mouseup", this.onMouseUp, this);
29590         }
29591         btn.on(this.clickEvent, this.onClick, this);
29592         if(this.tooltip){
29593             if(typeof this.tooltip == 'object'){
29594                 Roo.QuickTips.tips(Roo.apply({
29595                       target: btnEl.id
29596                 }, this.tooltip));
29597             } else {
29598                 btnEl.dom[this.tooltipType] = this.tooltip;
29599             }
29600         }
29601         if(this.arrowTooltip){
29602             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29603         }
29604         if(this.hidden){
29605             this.hide();
29606         }
29607         if(this.disabled){
29608             this.disable();
29609         }
29610         if(this.pressed){
29611             this.el.addClass("x-btn-pressed");
29612         }
29613         if(Roo.isIE && !Roo.isIE7){
29614             this.autoWidth.defer(1, this);
29615         }else{
29616             this.autoWidth();
29617         }
29618         if(this.menu){
29619             this.menu.on("show", this.onMenuShow, this);
29620             this.menu.on("hide", this.onMenuHide, this);
29621         }
29622         this.fireEvent('render', this);
29623     },
29624
29625     // private
29626     autoWidth : function(){
29627         if(this.el){
29628             var tbl = this.el.child("table:first");
29629             var tbl2 = this.el.child("table:last");
29630             this.el.setWidth("auto");
29631             tbl.setWidth("auto");
29632             if(Roo.isIE7 && Roo.isStrict){
29633                 var ib = this.el.child('button:first');
29634                 if(ib && ib.getWidth() > 20){
29635                     ib.clip();
29636                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29637                 }
29638             }
29639             if(this.minWidth){
29640                 if(this.hidden){
29641                     this.el.beginMeasure();
29642                 }
29643                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29644                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29645                 }
29646                 if(this.hidden){
29647                     this.el.endMeasure();
29648                 }
29649             }
29650             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29651         } 
29652     },
29653     /**
29654      * Sets this button's click handler
29655      * @param {Function} handler The function to call when the button is clicked
29656      * @param {Object} scope (optional) Scope for the function passed above
29657      */
29658     setHandler : function(handler, scope){
29659         this.handler = handler;
29660         this.scope = scope;  
29661     },
29662     
29663     /**
29664      * Sets this button's arrow click handler
29665      * @param {Function} handler The function to call when the arrow is clicked
29666      * @param {Object} scope (optional) Scope for the function passed above
29667      */
29668     setArrowHandler : function(handler, scope){
29669         this.arrowHandler = handler;
29670         this.scope = scope;  
29671     },
29672     
29673     /**
29674      * Focus the button
29675      */
29676     focus : function(){
29677         if(this.el){
29678             this.el.child("button:first").focus();
29679         }
29680     },
29681
29682     // private
29683     onClick : function(e){
29684         e.preventDefault();
29685         if(!this.disabled){
29686             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29687                 if(this.menu && !this.menu.isVisible()){
29688                     this.menu.show(this.el, this.menuAlign);
29689                 }
29690                 this.fireEvent("arrowclick", this, e);
29691                 if(this.arrowHandler){
29692                     this.arrowHandler.call(this.scope || this, this, e);
29693                 }
29694             }else{
29695                 this.fireEvent("click", this, e);
29696                 if(this.handler){
29697                     this.handler.call(this.scope || this, this, e);
29698                 }
29699             }
29700         }
29701     },
29702     // private
29703     onMouseDown : function(e){
29704         if(!this.disabled){
29705             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29706         }
29707     },
29708     // private
29709     onMouseUp : function(e){
29710         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29711     }   
29712 });
29713
29714
29715 // backwards compat
29716 Roo.MenuButton = Roo.SplitButton;/*
29717  * Based on:
29718  * Ext JS Library 1.1.1
29719  * Copyright(c) 2006-2007, Ext JS, LLC.
29720  *
29721  * Originally Released Under LGPL - original licence link has changed is not relivant.
29722  *
29723  * Fork - LGPL
29724  * <script type="text/javascript">
29725  */
29726
29727 /**
29728  * @class Roo.Toolbar
29729  * Basic Toolbar class.
29730  * @constructor
29731  * Creates a new Toolbar
29732  * @param {Object} container The config object
29733  */ 
29734 Roo.Toolbar = function(container, buttons, config)
29735 {
29736     /// old consturctor format still supported..
29737     if(container instanceof Array){ // omit the container for later rendering
29738         buttons = container;
29739         config = buttons;
29740         container = null;
29741     }
29742     if (typeof(container) == 'object' && container.xtype) {
29743         config = container;
29744         container = config.container;
29745         buttons = config.buttons || []; // not really - use items!!
29746     }
29747     var xitems = [];
29748     if (config && config.items) {
29749         xitems = config.items;
29750         delete config.items;
29751     }
29752     Roo.apply(this, config);
29753     this.buttons = buttons;
29754     
29755     if(container){
29756         this.render(container);
29757     }
29758     this.xitems = xitems;
29759     Roo.each(xitems, function(b) {
29760         this.add(b);
29761     }, this);
29762     
29763 };
29764
29765 Roo.Toolbar.prototype = {
29766     /**
29767      * @cfg {Array} items
29768      * array of button configs or elements to add (will be converted to a MixedCollection)
29769      */
29770     
29771     /**
29772      * @cfg {String/HTMLElement/Element} container
29773      * The id or element that will contain the toolbar
29774      */
29775     // private
29776     render : function(ct){
29777         this.el = Roo.get(ct);
29778         if(this.cls){
29779             this.el.addClass(this.cls);
29780         }
29781         // using a table allows for vertical alignment
29782         // 100% width is needed by Safari...
29783         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29784         this.tr = this.el.child("tr", true);
29785         var autoId = 0;
29786         this.items = new Roo.util.MixedCollection(false, function(o){
29787             return o.id || ("item" + (++autoId));
29788         });
29789         if(this.buttons){
29790             this.add.apply(this, this.buttons);
29791             delete this.buttons;
29792         }
29793     },
29794
29795     /**
29796      * Adds element(s) to the toolbar -- this function takes a variable number of 
29797      * arguments of mixed type and adds them to the toolbar.
29798      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29799      * <ul>
29800      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29801      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29802      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29803      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29804      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29805      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29806      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29807      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29808      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29809      * </ul>
29810      * @param {Mixed} arg2
29811      * @param {Mixed} etc.
29812      */
29813     add : function(){
29814         var a = arguments, l = a.length;
29815         for(var i = 0; i < l; i++){
29816             this._add(a[i]);
29817         }
29818     },
29819     // private..
29820     _add : function(el) {
29821         
29822         if (el.xtype) {
29823             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29824         }
29825         
29826         if (el.applyTo){ // some kind of form field
29827             return this.addField(el);
29828         } 
29829         if (el.render){ // some kind of Toolbar.Item
29830             return this.addItem(el);
29831         }
29832         if (typeof el == "string"){ // string
29833             if(el == "separator" || el == "-"){
29834                 return this.addSeparator();
29835             }
29836             if (el == " "){
29837                 return this.addSpacer();
29838             }
29839             if(el == "->"){
29840                 return this.addFill();
29841             }
29842             return this.addText(el);
29843             
29844         }
29845         if(el.tagName){ // element
29846             return this.addElement(el);
29847         }
29848         if(typeof el == "object"){ // must be button config?
29849             return this.addButton(el);
29850         }
29851         // and now what?!?!
29852         return false;
29853         
29854     },
29855     
29856     /**
29857      * Add an Xtype element
29858      * @param {Object} xtype Xtype Object
29859      * @return {Object} created Object
29860      */
29861     addxtype : function(e){
29862         return this.add(e);  
29863     },
29864     
29865     /**
29866      * Returns the Element for this toolbar.
29867      * @return {Roo.Element}
29868      */
29869     getEl : function(){
29870         return this.el;  
29871     },
29872     
29873     /**
29874      * Adds a separator
29875      * @return {Roo.Toolbar.Item} The separator item
29876      */
29877     addSeparator : function(){
29878         return this.addItem(new Roo.Toolbar.Separator());
29879     },
29880
29881     /**
29882      * Adds a spacer element
29883      * @return {Roo.Toolbar.Spacer} The spacer item
29884      */
29885     addSpacer : function(){
29886         return this.addItem(new Roo.Toolbar.Spacer());
29887     },
29888
29889     /**
29890      * Adds a fill element that forces subsequent additions to the right side of the toolbar
29891      * @return {Roo.Toolbar.Fill} The fill item
29892      */
29893     addFill : function(){
29894         return this.addItem(new Roo.Toolbar.Fill());
29895     },
29896
29897     /**
29898      * Adds any standard HTML element to the toolbar
29899      * @param {String/HTMLElement/Element} el The element or id of the element to add
29900      * @return {Roo.Toolbar.Item} The element's item
29901      */
29902     addElement : function(el){
29903         return this.addItem(new Roo.Toolbar.Item(el));
29904     },
29905     /**
29906      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
29907      * @type Roo.util.MixedCollection  
29908      */
29909     items : false,
29910      
29911     /**
29912      * Adds any Toolbar.Item or subclass
29913      * @param {Roo.Toolbar.Item} item
29914      * @return {Roo.Toolbar.Item} The item
29915      */
29916     addItem : function(item){
29917         var td = this.nextBlock();
29918         item.render(td);
29919         this.items.add(item);
29920         return item;
29921     },
29922     
29923     /**
29924      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
29925      * @param {Object/Array} config A button config or array of configs
29926      * @return {Roo.Toolbar.Button/Array}
29927      */
29928     addButton : function(config){
29929         if(config instanceof Array){
29930             var buttons = [];
29931             for(var i = 0, len = config.length; i < len; i++) {
29932                 buttons.push(this.addButton(config[i]));
29933             }
29934             return buttons;
29935         }
29936         var b = config;
29937         if(!(config instanceof Roo.Toolbar.Button)){
29938             b = config.split ?
29939                 new Roo.Toolbar.SplitButton(config) :
29940                 new Roo.Toolbar.Button(config);
29941         }
29942         var td = this.nextBlock();
29943         b.render(td);
29944         this.items.add(b);
29945         return b;
29946     },
29947     
29948     /**
29949      * Adds text to the toolbar
29950      * @param {String} text The text to add
29951      * @return {Roo.Toolbar.Item} The element's item
29952      */
29953     addText : function(text){
29954         return this.addItem(new Roo.Toolbar.TextItem(text));
29955     },
29956     
29957     /**
29958      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
29959      * @param {Number} index The index where the item is to be inserted
29960      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
29961      * @return {Roo.Toolbar.Button/Item}
29962      */
29963     insertButton : function(index, item){
29964         if(item instanceof Array){
29965             var buttons = [];
29966             for(var i = 0, len = item.length; i < len; i++) {
29967                buttons.push(this.insertButton(index + i, item[i]));
29968             }
29969             return buttons;
29970         }
29971         if (!(item instanceof Roo.Toolbar.Button)){
29972            item = new Roo.Toolbar.Button(item);
29973         }
29974         var td = document.createElement("td");
29975         this.tr.insertBefore(td, this.tr.childNodes[index]);
29976         item.render(td);
29977         this.items.insert(index, item);
29978         return item;
29979     },
29980     
29981     /**
29982      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
29983      * @param {Object} config
29984      * @return {Roo.Toolbar.Item} The element's item
29985      */
29986     addDom : function(config, returnEl){
29987         var td = this.nextBlock();
29988         Roo.DomHelper.overwrite(td, config);
29989         var ti = new Roo.Toolbar.Item(td.firstChild);
29990         ti.render(td);
29991         this.items.add(ti);
29992         return ti;
29993     },
29994
29995     /**
29996      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
29997      * @type Roo.util.MixedCollection  
29998      */
29999     fields : false,
30000     
30001     /**
30002      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30003      * Note: the field should not have been rendered yet. For a field that has already been
30004      * rendered, use {@link #addElement}.
30005      * @param {Roo.form.Field} field
30006      * @return {Roo.ToolbarItem}
30007      */
30008      
30009       
30010     addField : function(field) {
30011         if (!this.fields) {
30012             var autoId = 0;
30013             this.fields = new Roo.util.MixedCollection(false, function(o){
30014                 return o.id || ("item" + (++autoId));
30015             });
30016
30017         }
30018         
30019         var td = this.nextBlock();
30020         field.render(td);
30021         var ti = new Roo.Toolbar.Item(td.firstChild);
30022         ti.render(td);
30023         this.items.add(ti);
30024         this.fields.add(field);
30025         return ti;
30026     },
30027     /**
30028      * Hide the toolbar
30029      * @method hide
30030      */
30031      
30032       
30033     hide : function()
30034     {
30035         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30036         this.el.child('div').hide();
30037     },
30038     /**
30039      * Show the toolbar
30040      * @method show
30041      */
30042     show : function()
30043     {
30044         this.el.child('div').show();
30045     },
30046       
30047     // private
30048     nextBlock : function(){
30049         var td = document.createElement("td");
30050         this.tr.appendChild(td);
30051         return td;
30052     },
30053
30054     // private
30055     destroy : function(){
30056         if(this.items){ // rendered?
30057             Roo.destroy.apply(Roo, this.items.items);
30058         }
30059         if(this.fields){ // rendered?
30060             Roo.destroy.apply(Roo, this.fields.items);
30061         }
30062         Roo.Element.uncache(this.el, this.tr);
30063     }
30064 };
30065
30066 /**
30067  * @class Roo.Toolbar.Item
30068  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30069  * @constructor
30070  * Creates a new Item
30071  * @param {HTMLElement} el 
30072  */
30073 Roo.Toolbar.Item = function(el){
30074     var cfg = {};
30075     if (typeof (el.xtype) != 'undefined') {
30076         cfg = el;
30077         el = cfg.el;
30078     }
30079     
30080     this.el = Roo.getDom(el);
30081     this.id = Roo.id(this.el);
30082     this.hidden = false;
30083     
30084     this.addEvents({
30085          /**
30086              * @event render
30087              * Fires when the button is rendered
30088              * @param {Button} this
30089              */
30090         'render': true
30091     });
30092     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30093 };
30094 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30095 //Roo.Toolbar.Item.prototype = {
30096     
30097     /**
30098      * Get this item's HTML Element
30099      * @return {HTMLElement}
30100      */
30101     getEl : function(){
30102        return this.el;  
30103     },
30104
30105     // private
30106     render : function(td){
30107         
30108          this.td = td;
30109         td.appendChild(this.el);
30110         
30111         this.fireEvent('render', this);
30112     },
30113     
30114     /**
30115      * Removes and destroys this item.
30116      */
30117     destroy : function(){
30118         this.td.parentNode.removeChild(this.td);
30119     },
30120     
30121     /**
30122      * Shows this item.
30123      */
30124     show: function(){
30125         this.hidden = false;
30126         this.td.style.display = "";
30127     },
30128     
30129     /**
30130      * Hides this item.
30131      */
30132     hide: function(){
30133         this.hidden = true;
30134         this.td.style.display = "none";
30135     },
30136     
30137     /**
30138      * Convenience function for boolean show/hide.
30139      * @param {Boolean} visible true to show/false to hide
30140      */
30141     setVisible: function(visible){
30142         if(visible) {
30143             this.show();
30144         }else{
30145             this.hide();
30146         }
30147     },
30148     
30149     /**
30150      * Try to focus this item.
30151      */
30152     focus : function(){
30153         Roo.fly(this.el).focus();
30154     },
30155     
30156     /**
30157      * Disables this item.
30158      */
30159     disable : function(){
30160         Roo.fly(this.td).addClass("x-item-disabled");
30161         this.disabled = true;
30162         this.el.disabled = true;
30163     },
30164     
30165     /**
30166      * Enables this item.
30167      */
30168     enable : function(){
30169         Roo.fly(this.td).removeClass("x-item-disabled");
30170         this.disabled = false;
30171         this.el.disabled = false;
30172     }
30173 });
30174
30175
30176 /**
30177  * @class Roo.Toolbar.Separator
30178  * @extends Roo.Toolbar.Item
30179  * A simple toolbar separator class
30180  * @constructor
30181  * Creates a new Separator
30182  */
30183 Roo.Toolbar.Separator = function(cfg){
30184     
30185     var s = document.createElement("span");
30186     s.className = "ytb-sep";
30187     if (cfg) {
30188         cfg.el = s;
30189     }
30190     
30191     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30192 };
30193 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30194     enable:Roo.emptyFn,
30195     disable:Roo.emptyFn,
30196     focus:Roo.emptyFn
30197 });
30198
30199 /**
30200  * @class Roo.Toolbar.Spacer
30201  * @extends Roo.Toolbar.Item
30202  * A simple element that adds extra horizontal space to a toolbar.
30203  * @constructor
30204  * Creates a new Spacer
30205  */
30206 Roo.Toolbar.Spacer = function(cfg){
30207     var s = document.createElement("div");
30208     s.className = "ytb-spacer";
30209     if (cfg) {
30210         cfg.el = s;
30211     }
30212     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30213 };
30214 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30215     enable:Roo.emptyFn,
30216     disable:Roo.emptyFn,
30217     focus:Roo.emptyFn
30218 });
30219
30220 /**
30221  * @class Roo.Toolbar.Fill
30222  * @extends Roo.Toolbar.Spacer
30223  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30224  * @constructor
30225  * Creates a new Spacer
30226  */
30227 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30228     // private
30229     render : function(td){
30230         td.style.width = '100%';
30231         Roo.Toolbar.Fill.superclass.render.call(this, td);
30232     }
30233 });
30234
30235 /**
30236  * @class Roo.Toolbar.TextItem
30237  * @extends Roo.Toolbar.Item
30238  * A simple class that renders text directly into a toolbar.
30239  * @constructor
30240  * Creates a new TextItem
30241  * @param {String} text
30242  */
30243 Roo.Toolbar.TextItem = function(cfg){
30244     var  text = cfg || "";
30245     if (typeof(cfg) == 'object') {
30246         text = cfg.text || "";
30247     }  else {
30248         cfg = null;
30249     }
30250     var s = document.createElement("span");
30251     s.className = "ytb-text";
30252     s.innerHTML = text;
30253     if (cfg) {
30254         cfg.el  = s;
30255     }
30256     
30257     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30258 };
30259 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30260     
30261      
30262     enable:Roo.emptyFn,
30263     disable:Roo.emptyFn,
30264     focus:Roo.emptyFn
30265 });
30266
30267 /**
30268  * @class Roo.Toolbar.Button
30269  * @extends Roo.Button
30270  * A button that renders into a toolbar.
30271  * @constructor
30272  * Creates a new Button
30273  * @param {Object} config A standard {@link Roo.Button} config object
30274  */
30275 Roo.Toolbar.Button = function(config){
30276     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30277 };
30278 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30279     render : function(td){
30280         this.td = td;
30281         Roo.Toolbar.Button.superclass.render.call(this, td);
30282     },
30283     
30284     /**
30285      * Removes and destroys this button
30286      */
30287     destroy : function(){
30288         Roo.Toolbar.Button.superclass.destroy.call(this);
30289         this.td.parentNode.removeChild(this.td);
30290     },
30291     
30292     /**
30293      * Shows this button
30294      */
30295     show: function(){
30296         this.hidden = false;
30297         this.td.style.display = "";
30298     },
30299     
30300     /**
30301      * Hides this button
30302      */
30303     hide: function(){
30304         this.hidden = true;
30305         this.td.style.display = "none";
30306     },
30307
30308     /**
30309      * Disables this item
30310      */
30311     disable : function(){
30312         Roo.fly(this.td).addClass("x-item-disabled");
30313         this.disabled = true;
30314     },
30315
30316     /**
30317      * Enables this item
30318      */
30319     enable : function(){
30320         Roo.fly(this.td).removeClass("x-item-disabled");
30321         this.disabled = false;
30322     }
30323 });
30324 // backwards compat
30325 Roo.ToolbarButton = Roo.Toolbar.Button;
30326
30327 /**
30328  * @class Roo.Toolbar.SplitButton
30329  * @extends Roo.SplitButton
30330  * A menu button that renders into a toolbar.
30331  * @constructor
30332  * Creates a new SplitButton
30333  * @param {Object} config A standard {@link Roo.SplitButton} config object
30334  */
30335 Roo.Toolbar.SplitButton = function(config){
30336     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30337 };
30338 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30339     render : function(td){
30340         this.td = td;
30341         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30342     },
30343     
30344     /**
30345      * Removes and destroys this button
30346      */
30347     destroy : function(){
30348         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30349         this.td.parentNode.removeChild(this.td);
30350     },
30351     
30352     /**
30353      * Shows this button
30354      */
30355     show: function(){
30356         this.hidden = false;
30357         this.td.style.display = "";
30358     },
30359     
30360     /**
30361      * Hides this button
30362      */
30363     hide: function(){
30364         this.hidden = true;
30365         this.td.style.display = "none";
30366     }
30367 });
30368
30369 // backwards compat
30370 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30371  * Based on:
30372  * Ext JS Library 1.1.1
30373  * Copyright(c) 2006-2007, Ext JS, LLC.
30374  *
30375  * Originally Released Under LGPL - original licence link has changed is not relivant.
30376  *
30377  * Fork - LGPL
30378  * <script type="text/javascript">
30379  */
30380  
30381 /**
30382  * @class Roo.PagingToolbar
30383  * @extends Roo.Toolbar
30384  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30385  * @constructor
30386  * Create a new PagingToolbar
30387  * @param {Object} config The config object
30388  */
30389 Roo.PagingToolbar = function(el, ds, config)
30390 {
30391     // old args format still supported... - xtype is prefered..
30392     if (typeof(el) == 'object' && el.xtype) {
30393         // created from xtype...
30394         config = el;
30395         ds = el.dataSource;
30396         el = config.container;
30397     }
30398     var items = [];
30399     if (config.items) {
30400         items = config.items;
30401         config.items = [];
30402     }
30403     
30404     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30405     this.ds = ds;
30406     this.cursor = 0;
30407     this.renderButtons(this.el);
30408     this.bind(ds);
30409     
30410     // supprot items array.
30411    
30412     Roo.each(items, function(e) {
30413         this.add(Roo.factory(e));
30414     },this);
30415     
30416 };
30417
30418 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30419     /**
30420      * @cfg {Roo.data.Store} dataSource
30421      * The underlying data store providing the paged data
30422      */
30423     /**
30424      * @cfg {String/HTMLElement/Element} container
30425      * container The id or element that will contain the toolbar
30426      */
30427     /**
30428      * @cfg {Boolean} displayInfo
30429      * True to display the displayMsg (defaults to false)
30430      */
30431     /**
30432      * @cfg {Number} pageSize
30433      * The number of records to display per page (defaults to 20)
30434      */
30435     pageSize: 20,
30436     /**
30437      * @cfg {String} displayMsg
30438      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30439      */
30440     displayMsg : 'Displaying {0} - {1} of {2}',
30441     /**
30442      * @cfg {String} emptyMsg
30443      * The message to display when no records are found (defaults to "No data to display")
30444      */
30445     emptyMsg : 'No data to display',
30446     /**
30447      * Customizable piece of the default paging text (defaults to "Page")
30448      * @type String
30449      */
30450     beforePageText : "Page",
30451     /**
30452      * Customizable piece of the default paging text (defaults to "of %0")
30453      * @type String
30454      */
30455     afterPageText : "of {0}",
30456     /**
30457      * Customizable piece of the default paging text (defaults to "First Page")
30458      * @type String
30459      */
30460     firstText : "First Page",
30461     /**
30462      * Customizable piece of the default paging text (defaults to "Previous Page")
30463      * @type String
30464      */
30465     prevText : "Previous Page",
30466     /**
30467      * Customizable piece of the default paging text (defaults to "Next Page")
30468      * @type String
30469      */
30470     nextText : "Next Page",
30471     /**
30472      * Customizable piece of the default paging text (defaults to "Last Page")
30473      * @type String
30474      */
30475     lastText : "Last Page",
30476     /**
30477      * Customizable piece of the default paging text (defaults to "Refresh")
30478      * @type String
30479      */
30480     refreshText : "Refresh",
30481
30482     // private
30483     renderButtons : function(el){
30484         Roo.PagingToolbar.superclass.render.call(this, el);
30485         this.first = this.addButton({
30486             tooltip: this.firstText,
30487             cls: "x-btn-icon x-grid-page-first",
30488             disabled: true,
30489             handler: this.onClick.createDelegate(this, ["first"])
30490         });
30491         this.prev = this.addButton({
30492             tooltip: this.prevText,
30493             cls: "x-btn-icon x-grid-page-prev",
30494             disabled: true,
30495             handler: this.onClick.createDelegate(this, ["prev"])
30496         });
30497         //this.addSeparator();
30498         this.add(this.beforePageText);
30499         this.field = Roo.get(this.addDom({
30500            tag: "input",
30501            type: "text",
30502            size: "3",
30503            value: "1",
30504            cls: "x-grid-page-number"
30505         }).el);
30506         this.field.on("keydown", this.onPagingKeydown, this);
30507         this.field.on("focus", function(){this.dom.select();});
30508         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30509         this.field.setHeight(18);
30510         //this.addSeparator();
30511         this.next = this.addButton({
30512             tooltip: this.nextText,
30513             cls: "x-btn-icon x-grid-page-next",
30514             disabled: true,
30515             handler: this.onClick.createDelegate(this, ["next"])
30516         });
30517         this.last = this.addButton({
30518             tooltip: this.lastText,
30519             cls: "x-btn-icon x-grid-page-last",
30520             disabled: true,
30521             handler: this.onClick.createDelegate(this, ["last"])
30522         });
30523         //this.addSeparator();
30524         this.loading = this.addButton({
30525             tooltip: this.refreshText,
30526             cls: "x-btn-icon x-grid-loading",
30527             handler: this.onClick.createDelegate(this, ["refresh"])
30528         });
30529
30530         if(this.displayInfo){
30531             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30532         }
30533     },
30534
30535     // private
30536     updateInfo : function(){
30537         if(this.displayEl){
30538             var count = this.ds.getCount();
30539             var msg = count == 0 ?
30540                 this.emptyMsg :
30541                 String.format(
30542                     this.displayMsg,
30543                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30544                 );
30545             this.displayEl.update(msg);
30546         }
30547     },
30548
30549     // private
30550     onLoad : function(ds, r, o){
30551        this.cursor = o.params ? o.params.start : 0;
30552        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30553
30554        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30555        this.field.dom.value = ap;
30556        this.first.setDisabled(ap == 1);
30557        this.prev.setDisabled(ap == 1);
30558        this.next.setDisabled(ap == ps);
30559        this.last.setDisabled(ap == ps);
30560        this.loading.enable();
30561        this.updateInfo();
30562     },
30563
30564     // private
30565     getPageData : function(){
30566         var total = this.ds.getTotalCount();
30567         return {
30568             total : total,
30569             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30570             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30571         };
30572     },
30573
30574     // private
30575     onLoadError : function(){
30576         this.loading.enable();
30577     },
30578
30579     // private
30580     onPagingKeydown : function(e){
30581         var k = e.getKey();
30582         var d = this.getPageData();
30583         if(k == e.RETURN){
30584             var v = this.field.dom.value, pageNum;
30585             if(!v || isNaN(pageNum = parseInt(v, 10))){
30586                 this.field.dom.value = d.activePage;
30587                 return;
30588             }
30589             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30590             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30591             e.stopEvent();
30592         }
30593         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))
30594         {
30595           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30596           this.field.dom.value = pageNum;
30597           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30598           e.stopEvent();
30599         }
30600         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30601         {
30602           var v = this.field.dom.value, pageNum; 
30603           var increment = (e.shiftKey) ? 10 : 1;
30604           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30605             increment *= -1;
30606           }
30607           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30608             this.field.dom.value = d.activePage;
30609             return;
30610           }
30611           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30612           {
30613             this.field.dom.value = parseInt(v, 10) + increment;
30614             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30615             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30616           }
30617           e.stopEvent();
30618         }
30619     },
30620
30621     // private
30622     beforeLoad : function(){
30623         if(this.loading){
30624             this.loading.disable();
30625         }
30626     },
30627
30628     // private
30629     onClick : function(which){
30630         var ds = this.ds;
30631         switch(which){
30632             case "first":
30633                 ds.load({params:{start: 0, limit: this.pageSize}});
30634             break;
30635             case "prev":
30636                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30637             break;
30638             case "next":
30639                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30640             break;
30641             case "last":
30642                 var total = ds.getTotalCount();
30643                 var extra = total % this.pageSize;
30644                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30645                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30646             break;
30647             case "refresh":
30648                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30649             break;
30650         }
30651     },
30652
30653     /**
30654      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30655      * @param {Roo.data.Store} store The data store to unbind
30656      */
30657     unbind : function(ds){
30658         ds.un("beforeload", this.beforeLoad, this);
30659         ds.un("load", this.onLoad, this);
30660         ds.un("loadexception", this.onLoadError, this);
30661         ds.un("remove", this.updateInfo, this);
30662         ds.un("add", this.updateInfo, this);
30663         this.ds = undefined;
30664     },
30665
30666     /**
30667      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30668      * @param {Roo.data.Store} store The data store to bind
30669      */
30670     bind : function(ds){
30671         ds.on("beforeload", this.beforeLoad, this);
30672         ds.on("load", this.onLoad, this);
30673         ds.on("loadexception", this.onLoadError, this);
30674         ds.on("remove", this.updateInfo, this);
30675         ds.on("add", this.updateInfo, this);
30676         this.ds = ds;
30677     }
30678 });/*
30679  * Based on:
30680  * Ext JS Library 1.1.1
30681  * Copyright(c) 2006-2007, Ext JS, LLC.
30682  *
30683  * Originally Released Under LGPL - original licence link has changed is not relivant.
30684  *
30685  * Fork - LGPL
30686  * <script type="text/javascript">
30687  */
30688
30689 /**
30690  * @class Roo.Resizable
30691  * @extends Roo.util.Observable
30692  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30693  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30694  * 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
30695  * the element will be wrapped for you automatically.</p>
30696  * <p>Here is the list of valid resize handles:</p>
30697  * <pre>
30698 Value   Description
30699 ------  -------------------
30700  'n'     north
30701  's'     south
30702  'e'     east
30703  'w'     west
30704  'nw'    northwest
30705  'sw'    southwest
30706  'se'    southeast
30707  'ne'    northeast
30708  'hd'    horizontal drag
30709  'all'   all
30710 </pre>
30711  * <p>Here's an example showing the creation of a typical Resizable:</p>
30712  * <pre><code>
30713 var resizer = new Roo.Resizable("element-id", {
30714     handles: 'all',
30715     minWidth: 200,
30716     minHeight: 100,
30717     maxWidth: 500,
30718     maxHeight: 400,
30719     pinned: true
30720 });
30721 resizer.on("resize", myHandler);
30722 </code></pre>
30723  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30724  * resizer.east.setDisplayed(false);</p>
30725  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30726  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30727  * resize operation's new size (defaults to [0, 0])
30728  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30729  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30730  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30731  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30732  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30733  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30734  * @cfg {Number} width The width of the element in pixels (defaults to null)
30735  * @cfg {Number} height The height of the element in pixels (defaults to null)
30736  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30737  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30738  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30739  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30740  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30741  * in favor of the handles config option (defaults to false)
30742  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30743  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30744  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30745  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30746  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30747  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30748  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30749  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30750  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30751  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30752  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30753  * @constructor
30754  * Create a new resizable component
30755  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30756  * @param {Object} config configuration options
30757   */
30758 Roo.Resizable = function(el, config)
30759 {
30760     this.el = Roo.get(el);
30761
30762     if(config && config.wrap){
30763         config.resizeChild = this.el;
30764         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30765         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30766         this.el.setStyle("overflow", "hidden");
30767         this.el.setPositioning(config.resizeChild.getPositioning());
30768         config.resizeChild.clearPositioning();
30769         if(!config.width || !config.height){
30770             var csize = config.resizeChild.getSize();
30771             this.el.setSize(csize.width, csize.height);
30772         }
30773         if(config.pinned && !config.adjustments){
30774             config.adjustments = "auto";
30775         }
30776     }
30777
30778     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30779     this.proxy.unselectable();
30780     this.proxy.enableDisplayMode('block');
30781
30782     Roo.apply(this, config);
30783
30784     if(this.pinned){
30785         this.disableTrackOver = true;
30786         this.el.addClass("x-resizable-pinned");
30787     }
30788     // if the element isn't positioned, make it relative
30789     var position = this.el.getStyle("position");
30790     if(position != "absolute" && position != "fixed"){
30791         this.el.setStyle("position", "relative");
30792     }
30793     if(!this.handles){ // no handles passed, must be legacy style
30794         this.handles = 's,e,se';
30795         if(this.multiDirectional){
30796             this.handles += ',n,w';
30797         }
30798     }
30799     if(this.handles == "all"){
30800         this.handles = "n s e w ne nw se sw";
30801     }
30802     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30803     var ps = Roo.Resizable.positions;
30804     for(var i = 0, len = hs.length; i < len; i++){
30805         if(hs[i] && ps[hs[i]]){
30806             var pos = ps[hs[i]];
30807             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30808         }
30809     }
30810     // legacy
30811     this.corner = this.southeast;
30812     
30813     // updateBox = the box can move..
30814     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30815         this.updateBox = true;
30816     }
30817
30818     this.activeHandle = null;
30819
30820     if(this.resizeChild){
30821         if(typeof this.resizeChild == "boolean"){
30822             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30823         }else{
30824             this.resizeChild = Roo.get(this.resizeChild, true);
30825         }
30826     }
30827     
30828     if(this.adjustments == "auto"){
30829         var rc = this.resizeChild;
30830         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
30831         if(rc && (hw || hn)){
30832             rc.position("relative");
30833             rc.setLeft(hw ? hw.el.getWidth() : 0);
30834             rc.setTop(hn ? hn.el.getHeight() : 0);
30835         }
30836         this.adjustments = [
30837             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
30838             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
30839         ];
30840     }
30841
30842     if(this.draggable){
30843         this.dd = this.dynamic ?
30844             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
30845         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
30846     }
30847
30848     // public events
30849     this.addEvents({
30850         /**
30851          * @event beforeresize
30852          * Fired before resize is allowed. Set enabled to false to cancel resize.
30853          * @param {Roo.Resizable} this
30854          * @param {Roo.EventObject} e The mousedown event
30855          */
30856         "beforeresize" : true,
30857         /**
30858          * @event resizing
30859          * Fired a resizing.
30860          * @param {Roo.Resizable} this
30861          * @param {Number} x The new x position
30862          * @param {Number} y The new y position
30863          * @param {Number} w The new w width
30864          * @param {Number} h The new h hight
30865          * @param {Roo.EventObject} e The mouseup event
30866          */
30867         "resizing" : true,
30868         /**
30869          * @event resize
30870          * Fired after a resize.
30871          * @param {Roo.Resizable} this
30872          * @param {Number} width The new width
30873          * @param {Number} height The new height
30874          * @param {Roo.EventObject} e The mouseup event
30875          */
30876         "resize" : true
30877     });
30878
30879     if(this.width !== null && this.height !== null){
30880         this.resizeTo(this.width, this.height);
30881     }else{
30882         this.updateChildSize();
30883     }
30884     if(Roo.isIE){
30885         this.el.dom.style.zoom = 1;
30886     }
30887     Roo.Resizable.superclass.constructor.call(this);
30888 };
30889
30890 Roo.extend(Roo.Resizable, Roo.util.Observable, {
30891         resizeChild : false,
30892         adjustments : [0, 0],
30893         minWidth : 5,
30894         minHeight : 5,
30895         maxWidth : 10000,
30896         maxHeight : 10000,
30897         enabled : true,
30898         animate : false,
30899         duration : .35,
30900         dynamic : false,
30901         handles : false,
30902         multiDirectional : false,
30903         disableTrackOver : false,
30904         easing : 'easeOutStrong',
30905         widthIncrement : 0,
30906         heightIncrement : 0,
30907         pinned : false,
30908         width : null,
30909         height : null,
30910         preserveRatio : false,
30911         transparent: false,
30912         minX: 0,
30913         minY: 0,
30914         draggable: false,
30915
30916         /**
30917          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
30918          */
30919         constrainTo: undefined,
30920         /**
30921          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
30922          */
30923         resizeRegion: undefined,
30924
30925
30926     /**
30927      * Perform a manual resize
30928      * @param {Number} width
30929      * @param {Number} height
30930      */
30931     resizeTo : function(width, height){
30932         this.el.setSize(width, height);
30933         this.updateChildSize();
30934         this.fireEvent("resize", this, width, height, null);
30935     },
30936
30937     // private
30938     startSizing : function(e, handle){
30939         this.fireEvent("beforeresize", this, e);
30940         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
30941
30942             if(!this.overlay){
30943                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
30944                 this.overlay.unselectable();
30945                 this.overlay.enableDisplayMode("block");
30946                 this.overlay.on("mousemove", this.onMouseMove, this);
30947                 this.overlay.on("mouseup", this.onMouseUp, this);
30948             }
30949             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
30950
30951             this.resizing = true;
30952             this.startBox = this.el.getBox();
30953             this.startPoint = e.getXY();
30954             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
30955                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
30956
30957             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30958             this.overlay.show();
30959
30960             if(this.constrainTo) {
30961                 var ct = Roo.get(this.constrainTo);
30962                 this.resizeRegion = ct.getRegion().adjust(
30963                     ct.getFrameWidth('t'),
30964                     ct.getFrameWidth('l'),
30965                     -ct.getFrameWidth('b'),
30966                     -ct.getFrameWidth('r')
30967                 );
30968             }
30969
30970             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
30971             this.proxy.show();
30972             this.proxy.setBox(this.startBox);
30973             if(!this.dynamic){
30974                 this.proxy.setStyle('visibility', 'visible');
30975             }
30976         }
30977     },
30978
30979     // private
30980     onMouseDown : function(handle, e){
30981         if(this.enabled){
30982             e.stopEvent();
30983             this.activeHandle = handle;
30984             this.startSizing(e, handle);
30985         }
30986     },
30987
30988     // private
30989     onMouseUp : function(e){
30990         var size = this.resizeElement();
30991         this.resizing = false;
30992         this.handleOut();
30993         this.overlay.hide();
30994         this.proxy.hide();
30995         this.fireEvent("resize", this, size.width, size.height, e);
30996     },
30997
30998     // private
30999     updateChildSize : function(){
31000         
31001         if(this.resizeChild){
31002             var el = this.el;
31003             var child = this.resizeChild;
31004             var adj = this.adjustments;
31005             if(el.dom.offsetWidth){
31006                 var b = el.getSize(true);
31007                 child.setSize(b.width+adj[0], b.height+adj[1]);
31008             }
31009             // Second call here for IE
31010             // The first call enables instant resizing and
31011             // the second call corrects scroll bars if they
31012             // exist
31013             if(Roo.isIE){
31014                 setTimeout(function(){
31015                     if(el.dom.offsetWidth){
31016                         var b = el.getSize(true);
31017                         child.setSize(b.width+adj[0], b.height+adj[1]);
31018                     }
31019                 }, 10);
31020             }
31021         }
31022     },
31023
31024     // private
31025     snap : function(value, inc, min){
31026         if(!inc || !value) {
31027             return value;
31028         }
31029         var newValue = value;
31030         var m = value % inc;
31031         if(m > 0){
31032             if(m > (inc/2)){
31033                 newValue = value + (inc-m);
31034             }else{
31035                 newValue = value - m;
31036             }
31037         }
31038         return Math.max(min, newValue);
31039     },
31040
31041     // private
31042     resizeElement : function(){
31043         var box = this.proxy.getBox();
31044         if(this.updateBox){
31045             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31046         }else{
31047             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31048         }
31049         this.updateChildSize();
31050         if(!this.dynamic){
31051             this.proxy.hide();
31052         }
31053         return box;
31054     },
31055
31056     // private
31057     constrain : function(v, diff, m, mx){
31058         if(v - diff < m){
31059             diff = v - m;
31060         }else if(v - diff > mx){
31061             diff = mx - v;
31062         }
31063         return diff;
31064     },
31065
31066     // private
31067     onMouseMove : function(e){
31068         
31069         if(this.enabled){
31070             try{// try catch so if something goes wrong the user doesn't get hung
31071
31072             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31073                 return;
31074             }
31075
31076             //var curXY = this.startPoint;
31077             var curSize = this.curSize || this.startBox;
31078             var x = this.startBox.x, y = this.startBox.y;
31079             var ox = x, oy = y;
31080             var w = curSize.width, h = curSize.height;
31081             var ow = w, oh = h;
31082             var mw = this.minWidth, mh = this.minHeight;
31083             var mxw = this.maxWidth, mxh = this.maxHeight;
31084             var wi = this.widthIncrement;
31085             var hi = this.heightIncrement;
31086
31087             var eventXY = e.getXY();
31088             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31089             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31090
31091             var pos = this.activeHandle.position;
31092
31093             switch(pos){
31094                 case "east":
31095                     w += diffX;
31096                     w = Math.min(Math.max(mw, w), mxw);
31097                     break;
31098              
31099                 case "south":
31100                     h += diffY;
31101                     h = Math.min(Math.max(mh, h), mxh);
31102                     break;
31103                 case "southeast":
31104                     w += diffX;
31105                     h += diffY;
31106                     w = Math.min(Math.max(mw, w), mxw);
31107                     h = Math.min(Math.max(mh, h), mxh);
31108                     break;
31109                 case "north":
31110                     diffY = this.constrain(h, diffY, mh, mxh);
31111                     y += diffY;
31112                     h -= diffY;
31113                     break;
31114                 case "hdrag":
31115                     
31116                     if (wi) {
31117                         var adiffX = Math.abs(diffX);
31118                         var sub = (adiffX % wi); // how much 
31119                         if (sub > (wi/2)) { // far enough to snap
31120                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31121                         } else {
31122                             // remove difference.. 
31123                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31124                         }
31125                     }
31126                     x += diffX;
31127                     x = Math.max(this.minX, x);
31128                     break;
31129                 case "west":
31130                     diffX = this.constrain(w, diffX, mw, mxw);
31131                     x += diffX;
31132                     w -= diffX;
31133                     break;
31134                 case "northeast":
31135                     w += diffX;
31136                     w = Math.min(Math.max(mw, w), mxw);
31137                     diffY = this.constrain(h, diffY, mh, mxh);
31138                     y += diffY;
31139                     h -= diffY;
31140                     break;
31141                 case "northwest":
31142                     diffX = this.constrain(w, diffX, mw, mxw);
31143                     diffY = this.constrain(h, diffY, mh, mxh);
31144                     y += diffY;
31145                     h -= diffY;
31146                     x += diffX;
31147                     w -= diffX;
31148                     break;
31149                case "southwest":
31150                     diffX = this.constrain(w, diffX, mw, mxw);
31151                     h += diffY;
31152                     h = Math.min(Math.max(mh, h), mxh);
31153                     x += diffX;
31154                     w -= diffX;
31155                     break;
31156             }
31157
31158             var sw = this.snap(w, wi, mw);
31159             var sh = this.snap(h, hi, mh);
31160             if(sw != w || sh != h){
31161                 switch(pos){
31162                     case "northeast":
31163                         y -= sh - h;
31164                     break;
31165                     case "north":
31166                         y -= sh - h;
31167                         break;
31168                     case "southwest":
31169                         x -= sw - w;
31170                     break;
31171                     case "west":
31172                         x -= sw - w;
31173                         break;
31174                     case "northwest":
31175                         x -= sw - w;
31176                         y -= sh - h;
31177                     break;
31178                 }
31179                 w = sw;
31180                 h = sh;
31181             }
31182
31183             if(this.preserveRatio){
31184                 switch(pos){
31185                     case "southeast":
31186                     case "east":
31187                         h = oh * (w/ow);
31188                         h = Math.min(Math.max(mh, h), mxh);
31189                         w = ow * (h/oh);
31190                        break;
31191                     case "south":
31192                         w = ow * (h/oh);
31193                         w = Math.min(Math.max(mw, w), mxw);
31194                         h = oh * (w/ow);
31195                         break;
31196                     case "northeast":
31197                         w = ow * (h/oh);
31198                         w = Math.min(Math.max(mw, w), mxw);
31199                         h = oh * (w/ow);
31200                     break;
31201                     case "north":
31202                         var tw = w;
31203                         w = ow * (h/oh);
31204                         w = Math.min(Math.max(mw, w), mxw);
31205                         h = oh * (w/ow);
31206                         x += (tw - w) / 2;
31207                         break;
31208                     case "southwest":
31209                         h = oh * (w/ow);
31210                         h = Math.min(Math.max(mh, h), mxh);
31211                         var tw = w;
31212                         w = ow * (h/oh);
31213                         x += tw - w;
31214                         break;
31215                     case "west":
31216                         var th = h;
31217                         h = oh * (w/ow);
31218                         h = Math.min(Math.max(mh, h), mxh);
31219                         y += (th - h) / 2;
31220                         var tw = w;
31221                         w = ow * (h/oh);
31222                         x += tw - w;
31223                        break;
31224                     case "northwest":
31225                         var tw = w;
31226                         var th = h;
31227                         h = oh * (w/ow);
31228                         h = Math.min(Math.max(mh, h), mxh);
31229                         w = ow * (h/oh);
31230                         y += th - h;
31231                         x += tw - w;
31232                        break;
31233
31234                 }
31235             }
31236             if (pos == 'hdrag') {
31237                 w = ow;
31238             }
31239             this.proxy.setBounds(x, y, w, h);
31240             if(this.dynamic){
31241                 this.resizeElement();
31242             }
31243             }catch(e){}
31244         }
31245         this.fireEvent("resizing", this, x, y, w, h, e);
31246     },
31247
31248     // private
31249     handleOver : function(){
31250         if(this.enabled){
31251             this.el.addClass("x-resizable-over");
31252         }
31253     },
31254
31255     // private
31256     handleOut : function(){
31257         if(!this.resizing){
31258             this.el.removeClass("x-resizable-over");
31259         }
31260     },
31261
31262     /**
31263      * Returns the element this component is bound to.
31264      * @return {Roo.Element}
31265      */
31266     getEl : function(){
31267         return this.el;
31268     },
31269
31270     /**
31271      * Returns the resizeChild element (or null).
31272      * @return {Roo.Element}
31273      */
31274     getResizeChild : function(){
31275         return this.resizeChild;
31276     },
31277     groupHandler : function()
31278     {
31279         
31280     },
31281     /**
31282      * Destroys this resizable. If the element was wrapped and
31283      * removeEl is not true then the element remains.
31284      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31285      */
31286     destroy : function(removeEl){
31287         this.proxy.remove();
31288         if(this.overlay){
31289             this.overlay.removeAllListeners();
31290             this.overlay.remove();
31291         }
31292         var ps = Roo.Resizable.positions;
31293         for(var k in ps){
31294             if(typeof ps[k] != "function" && this[ps[k]]){
31295                 var h = this[ps[k]];
31296                 h.el.removeAllListeners();
31297                 h.el.remove();
31298             }
31299         }
31300         if(removeEl){
31301             this.el.update("");
31302             this.el.remove();
31303         }
31304     }
31305 });
31306
31307 // private
31308 // hash to map config positions to true positions
31309 Roo.Resizable.positions = {
31310     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31311     hd: "hdrag"
31312 };
31313
31314 // private
31315 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31316     if(!this.tpl){
31317         // only initialize the template if resizable is used
31318         var tpl = Roo.DomHelper.createTemplate(
31319             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31320         );
31321         tpl.compile();
31322         Roo.Resizable.Handle.prototype.tpl = tpl;
31323     }
31324     this.position = pos;
31325     this.rz = rz;
31326     // show north drag fro topdra
31327     var handlepos = pos == 'hdrag' ? 'north' : pos;
31328     
31329     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31330     if (pos == 'hdrag') {
31331         this.el.setStyle('cursor', 'pointer');
31332     }
31333     this.el.unselectable();
31334     if(transparent){
31335         this.el.setOpacity(0);
31336     }
31337     this.el.on("mousedown", this.onMouseDown, this);
31338     if(!disableTrackOver){
31339         this.el.on("mouseover", this.onMouseOver, this);
31340         this.el.on("mouseout", this.onMouseOut, this);
31341     }
31342 };
31343
31344 // private
31345 Roo.Resizable.Handle.prototype = {
31346     afterResize : function(rz){
31347         Roo.log('after?');
31348         // do nothing
31349     },
31350     // private
31351     onMouseDown : function(e){
31352         this.rz.onMouseDown(this, e);
31353     },
31354     // private
31355     onMouseOver : function(e){
31356         this.rz.handleOver(this, e);
31357     },
31358     // private
31359     onMouseOut : function(e){
31360         this.rz.handleOut(this, e);
31361     }
31362 };/*
31363  * Based on:
31364  * Ext JS Library 1.1.1
31365  * Copyright(c) 2006-2007, Ext JS, LLC.
31366  *
31367  * Originally Released Under LGPL - original licence link has changed is not relivant.
31368  *
31369  * Fork - LGPL
31370  * <script type="text/javascript">
31371  */
31372
31373 /**
31374  * @class Roo.Editor
31375  * @extends Roo.Component
31376  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31377  * @constructor
31378  * Create a new Editor
31379  * @param {Roo.form.Field} field The Field object (or descendant)
31380  * @param {Object} config The config object
31381  */
31382 Roo.Editor = function(field, config){
31383     Roo.Editor.superclass.constructor.call(this, config);
31384     this.field = field;
31385     this.addEvents({
31386         /**
31387              * @event beforestartedit
31388              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31389              * false from the handler of this event.
31390              * @param {Editor} this
31391              * @param {Roo.Element} boundEl The underlying element bound to this editor
31392              * @param {Mixed} value The field value being set
31393              */
31394         "beforestartedit" : true,
31395         /**
31396              * @event startedit
31397              * Fires when this editor is displayed
31398              * @param {Roo.Element} boundEl The underlying element bound to this editor
31399              * @param {Mixed} value The starting field value
31400              */
31401         "startedit" : true,
31402         /**
31403              * @event beforecomplete
31404              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31405              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31406              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31407              * event will not fire since no edit actually occurred.
31408              * @param {Editor} this
31409              * @param {Mixed} value The current field value
31410              * @param {Mixed} startValue The original field value
31411              */
31412         "beforecomplete" : true,
31413         /**
31414              * @event complete
31415              * Fires after editing is complete and any changed value has been written to the underlying field.
31416              * @param {Editor} this
31417              * @param {Mixed} value The current field value
31418              * @param {Mixed} startValue The original field value
31419              */
31420         "complete" : true,
31421         /**
31422          * @event specialkey
31423          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31424          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31425          * @param {Roo.form.Field} this
31426          * @param {Roo.EventObject} e The event object
31427          */
31428         "specialkey" : true
31429     });
31430 };
31431
31432 Roo.extend(Roo.Editor, Roo.Component, {
31433     /**
31434      * @cfg {Boolean/String} autosize
31435      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31436      * or "height" to adopt the height only (defaults to false)
31437      */
31438     /**
31439      * @cfg {Boolean} revertInvalid
31440      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31441      * validation fails (defaults to true)
31442      */
31443     /**
31444      * @cfg {Boolean} ignoreNoChange
31445      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31446      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31447      * will never be ignored.
31448      */
31449     /**
31450      * @cfg {Boolean} hideEl
31451      * False to keep the bound element visible while the editor is displayed (defaults to true)
31452      */
31453     /**
31454      * @cfg {Mixed} value
31455      * The data value of the underlying field (defaults to "")
31456      */
31457     value : "",
31458     /**
31459      * @cfg {String} alignment
31460      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31461      */
31462     alignment: "c-c?",
31463     /**
31464      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31465      * for bottom-right shadow (defaults to "frame")
31466      */
31467     shadow : "frame",
31468     /**
31469      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31470      */
31471     constrain : false,
31472     /**
31473      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31474      */
31475     completeOnEnter : false,
31476     /**
31477      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31478      */
31479     cancelOnEsc : false,
31480     /**
31481      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31482      */
31483     updateEl : false,
31484
31485     // private
31486     onRender : function(ct, position){
31487         this.el = new Roo.Layer({
31488             shadow: this.shadow,
31489             cls: "x-editor",
31490             parentEl : ct,
31491             shim : this.shim,
31492             shadowOffset:4,
31493             id: this.id,
31494             constrain: this.constrain
31495         });
31496         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31497         if(this.field.msgTarget != 'title'){
31498             this.field.msgTarget = 'qtip';
31499         }
31500         this.field.render(this.el);
31501         if(Roo.isGecko){
31502             this.field.el.dom.setAttribute('autocomplete', 'off');
31503         }
31504         this.field.on("specialkey", this.onSpecialKey, this);
31505         if(this.swallowKeys){
31506             this.field.el.swallowEvent(['keydown','keypress']);
31507         }
31508         this.field.show();
31509         this.field.on("blur", this.onBlur, this);
31510         if(this.field.grow){
31511             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31512         }
31513     },
31514
31515     onSpecialKey : function(field, e)
31516     {
31517         //Roo.log('editor onSpecialKey');
31518         if(this.completeOnEnter && e.getKey() == e.ENTER){
31519             e.stopEvent();
31520             this.completeEdit();
31521             return;
31522         }
31523         // do not fire special key otherwise it might hide close the editor...
31524         if(e.getKey() == e.ENTER){    
31525             return;
31526         }
31527         if(this.cancelOnEsc && e.getKey() == e.ESC){
31528             this.cancelEdit();
31529             return;
31530         } 
31531         this.fireEvent('specialkey', field, e);
31532     
31533     },
31534
31535     /**
31536      * Starts the editing process and shows the editor.
31537      * @param {String/HTMLElement/Element} el The element to edit
31538      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31539       * to the innerHTML of el.
31540      */
31541     startEdit : function(el, value){
31542         if(this.editing){
31543             this.completeEdit();
31544         }
31545         this.boundEl = Roo.get(el);
31546         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31547         if(!this.rendered){
31548             this.render(this.parentEl || document.body);
31549         }
31550         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31551             return;
31552         }
31553         this.startValue = v;
31554         this.field.setValue(v);
31555         if(this.autoSize){
31556             var sz = this.boundEl.getSize();
31557             switch(this.autoSize){
31558                 case "width":
31559                 this.setSize(sz.width,  "");
31560                 break;
31561                 case "height":
31562                 this.setSize("",  sz.height);
31563                 break;
31564                 default:
31565                 this.setSize(sz.width,  sz.height);
31566             }
31567         }
31568         this.el.alignTo(this.boundEl, this.alignment);
31569         this.editing = true;
31570         if(Roo.QuickTips){
31571             Roo.QuickTips.disable();
31572         }
31573         this.show();
31574     },
31575
31576     /**
31577      * Sets the height and width of this editor.
31578      * @param {Number} width The new width
31579      * @param {Number} height The new height
31580      */
31581     setSize : function(w, h){
31582         this.field.setSize(w, h);
31583         if(this.el){
31584             this.el.sync();
31585         }
31586     },
31587
31588     /**
31589      * Realigns the editor to the bound field based on the current alignment config value.
31590      */
31591     realign : function(){
31592         this.el.alignTo(this.boundEl, this.alignment);
31593     },
31594
31595     /**
31596      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31597      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31598      */
31599     completeEdit : function(remainVisible){
31600         if(!this.editing){
31601             return;
31602         }
31603         var v = this.getValue();
31604         if(this.revertInvalid !== false && !this.field.isValid()){
31605             v = this.startValue;
31606             this.cancelEdit(true);
31607         }
31608         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31609             this.editing = false;
31610             this.hide();
31611             return;
31612         }
31613         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31614             this.editing = false;
31615             if(this.updateEl && this.boundEl){
31616                 this.boundEl.update(v);
31617             }
31618             if(remainVisible !== true){
31619                 this.hide();
31620             }
31621             this.fireEvent("complete", this, v, this.startValue);
31622         }
31623     },
31624
31625     // private
31626     onShow : function(){
31627         this.el.show();
31628         if(this.hideEl !== false){
31629             this.boundEl.hide();
31630         }
31631         this.field.show();
31632         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31633             this.fixIEFocus = true;
31634             this.deferredFocus.defer(50, this);
31635         }else{
31636             this.field.focus();
31637         }
31638         this.fireEvent("startedit", this.boundEl, this.startValue);
31639     },
31640
31641     deferredFocus : function(){
31642         if(this.editing){
31643             this.field.focus();
31644         }
31645     },
31646
31647     /**
31648      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31649      * reverted to the original starting value.
31650      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31651      * cancel (defaults to false)
31652      */
31653     cancelEdit : function(remainVisible){
31654         if(this.editing){
31655             this.setValue(this.startValue);
31656             if(remainVisible !== true){
31657                 this.hide();
31658             }
31659         }
31660     },
31661
31662     // private
31663     onBlur : function(){
31664         if(this.allowBlur !== true && this.editing){
31665             this.completeEdit();
31666         }
31667     },
31668
31669     // private
31670     onHide : function(){
31671         if(this.editing){
31672             this.completeEdit();
31673             return;
31674         }
31675         this.field.blur();
31676         if(this.field.collapse){
31677             this.field.collapse();
31678         }
31679         this.el.hide();
31680         if(this.hideEl !== false){
31681             this.boundEl.show();
31682         }
31683         if(Roo.QuickTips){
31684             Roo.QuickTips.enable();
31685         }
31686     },
31687
31688     /**
31689      * Sets the data value of the editor
31690      * @param {Mixed} value Any valid value supported by the underlying field
31691      */
31692     setValue : function(v){
31693         this.field.setValue(v);
31694     },
31695
31696     /**
31697      * Gets the data value of the editor
31698      * @return {Mixed} The data value
31699      */
31700     getValue : function(){
31701         return this.field.getValue();
31702     }
31703 });/*
31704  * Based on:
31705  * Ext JS Library 1.1.1
31706  * Copyright(c) 2006-2007, Ext JS, LLC.
31707  *
31708  * Originally Released Under LGPL - original licence link has changed is not relivant.
31709  *
31710  * Fork - LGPL
31711  * <script type="text/javascript">
31712  */
31713  
31714 /**
31715  * @class Roo.BasicDialog
31716  * @extends Roo.util.Observable
31717  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31718  * <pre><code>
31719 var dlg = new Roo.BasicDialog("my-dlg", {
31720     height: 200,
31721     width: 300,
31722     minHeight: 100,
31723     minWidth: 150,
31724     modal: true,
31725     proxyDrag: true,
31726     shadow: true
31727 });
31728 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31729 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31730 dlg.addButton('Cancel', dlg.hide, dlg);
31731 dlg.show();
31732 </code></pre>
31733   <b>A Dialog should always be a direct child of the body element.</b>
31734  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31735  * @cfg {String} title Default text to display in the title bar (defaults to null)
31736  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31737  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31738  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31739  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31740  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31741  * (defaults to null with no animation)
31742  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31743  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31744  * property for valid values (defaults to 'all')
31745  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31746  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31747  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31748  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31749  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31750  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31751  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31752  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31753  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31754  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31755  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31756  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31757  * draggable = true (defaults to false)
31758  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31759  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31760  * shadow (defaults to false)
31761  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31762  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31763  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31764  * @cfg {Array} buttons Array of buttons
31765  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31766  * @constructor
31767  * Create a new BasicDialog.
31768  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31769  * @param {Object} config Configuration options
31770  */
31771 Roo.BasicDialog = function(el, config){
31772     this.el = Roo.get(el);
31773     var dh = Roo.DomHelper;
31774     if(!this.el && config && config.autoCreate){
31775         if(typeof config.autoCreate == "object"){
31776             if(!config.autoCreate.id){
31777                 config.autoCreate.id = el;
31778             }
31779             this.el = dh.append(document.body,
31780                         config.autoCreate, true);
31781         }else{
31782             this.el = dh.append(document.body,
31783                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31784         }
31785     }
31786     el = this.el;
31787     el.setDisplayed(true);
31788     el.hide = this.hideAction;
31789     this.id = el.id;
31790     el.addClass("x-dlg");
31791
31792     Roo.apply(this, config);
31793
31794     this.proxy = el.createProxy("x-dlg-proxy");
31795     this.proxy.hide = this.hideAction;
31796     this.proxy.setOpacity(.5);
31797     this.proxy.hide();
31798
31799     if(config.width){
31800         el.setWidth(config.width);
31801     }
31802     if(config.height){
31803         el.setHeight(config.height);
31804     }
31805     this.size = el.getSize();
31806     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31807         this.xy = [config.x,config.y];
31808     }else{
31809         this.xy = el.getCenterXY(true);
31810     }
31811     /** The header element @type Roo.Element */
31812     this.header = el.child("> .x-dlg-hd");
31813     /** The body element @type Roo.Element */
31814     this.body = el.child("> .x-dlg-bd");
31815     /** The footer element @type Roo.Element */
31816     this.footer = el.child("> .x-dlg-ft");
31817
31818     if(!this.header){
31819         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31820     }
31821     if(!this.body){
31822         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31823     }
31824
31825     this.header.unselectable();
31826     if(this.title){
31827         this.header.update(this.title);
31828     }
31829     // this element allows the dialog to be focused for keyboard event
31830     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
31831     this.focusEl.swallowEvent("click", true);
31832
31833     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
31834
31835     // wrap the body and footer for special rendering
31836     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
31837     if(this.footer){
31838         this.bwrap.dom.appendChild(this.footer.dom);
31839     }
31840
31841     this.bg = this.el.createChild({
31842         tag: "div", cls:"x-dlg-bg",
31843         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
31844     });
31845     this.centerBg = this.bg.child("div.x-dlg-bg-center");
31846
31847
31848     if(this.autoScroll !== false && !this.autoTabs){
31849         this.body.setStyle("overflow", "auto");
31850     }
31851
31852     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
31853
31854     if(this.closable !== false){
31855         this.el.addClass("x-dlg-closable");
31856         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
31857         this.close.on("click", this.closeClick, this);
31858         this.close.addClassOnOver("x-dlg-close-over");
31859     }
31860     if(this.collapsible !== false){
31861         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
31862         this.collapseBtn.on("click", this.collapseClick, this);
31863         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
31864         this.header.on("dblclick", this.collapseClick, this);
31865     }
31866     if(this.resizable !== false){
31867         this.el.addClass("x-dlg-resizable");
31868         this.resizer = new Roo.Resizable(el, {
31869             minWidth: this.minWidth || 80,
31870             minHeight:this.minHeight || 80,
31871             handles: this.resizeHandles || "all",
31872             pinned: true
31873         });
31874         this.resizer.on("beforeresize", this.beforeResize, this);
31875         this.resizer.on("resize", this.onResize, this);
31876     }
31877     if(this.draggable !== false){
31878         el.addClass("x-dlg-draggable");
31879         if (!this.proxyDrag) {
31880             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
31881         }
31882         else {
31883             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
31884         }
31885         dd.setHandleElId(this.header.id);
31886         dd.endDrag = this.endMove.createDelegate(this);
31887         dd.startDrag = this.startMove.createDelegate(this);
31888         dd.onDrag = this.onDrag.createDelegate(this);
31889         dd.scroll = false;
31890         this.dd = dd;
31891     }
31892     if(this.modal){
31893         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
31894         this.mask.enableDisplayMode("block");
31895         this.mask.hide();
31896         this.el.addClass("x-dlg-modal");
31897     }
31898     if(this.shadow){
31899         this.shadow = new Roo.Shadow({
31900             mode : typeof this.shadow == "string" ? this.shadow : "sides",
31901             offset : this.shadowOffset
31902         });
31903     }else{
31904         this.shadowOffset = 0;
31905     }
31906     if(Roo.useShims && this.shim !== false){
31907         this.shim = this.el.createShim();
31908         this.shim.hide = this.hideAction;
31909         this.shim.hide();
31910     }else{
31911         this.shim = false;
31912     }
31913     if(this.autoTabs){
31914         this.initTabs();
31915     }
31916     if (this.buttons) { 
31917         var bts= this.buttons;
31918         this.buttons = [];
31919         Roo.each(bts, function(b) {
31920             this.addButton(b);
31921         }, this);
31922     }
31923     
31924     
31925     this.addEvents({
31926         /**
31927          * @event keydown
31928          * Fires when a key is pressed
31929          * @param {Roo.BasicDialog} this
31930          * @param {Roo.EventObject} e
31931          */
31932         "keydown" : true,
31933         /**
31934          * @event move
31935          * Fires when this dialog is moved by the user.
31936          * @param {Roo.BasicDialog} this
31937          * @param {Number} x The new page X
31938          * @param {Number} y The new page Y
31939          */
31940         "move" : true,
31941         /**
31942          * @event resize
31943          * Fires when this dialog is resized by the user.
31944          * @param {Roo.BasicDialog} this
31945          * @param {Number} width The new width
31946          * @param {Number} height The new height
31947          */
31948         "resize" : true,
31949         /**
31950          * @event beforehide
31951          * Fires before this dialog is hidden.
31952          * @param {Roo.BasicDialog} this
31953          */
31954         "beforehide" : true,
31955         /**
31956          * @event hide
31957          * Fires when this dialog is hidden.
31958          * @param {Roo.BasicDialog} this
31959          */
31960         "hide" : true,
31961         /**
31962          * @event beforeshow
31963          * Fires before this dialog is shown.
31964          * @param {Roo.BasicDialog} this
31965          */
31966         "beforeshow" : true,
31967         /**
31968          * @event show
31969          * Fires when this dialog is shown.
31970          * @param {Roo.BasicDialog} this
31971          */
31972         "show" : true
31973     });
31974     el.on("keydown", this.onKeyDown, this);
31975     el.on("mousedown", this.toFront, this);
31976     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
31977     this.el.hide();
31978     Roo.DialogManager.register(this);
31979     Roo.BasicDialog.superclass.constructor.call(this);
31980 };
31981
31982 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
31983     shadowOffset: Roo.isIE ? 6 : 5,
31984     minHeight: 80,
31985     minWidth: 200,
31986     minButtonWidth: 75,
31987     defaultButton: null,
31988     buttonAlign: "right",
31989     tabTag: 'div',
31990     firstShow: true,
31991
31992     /**
31993      * Sets the dialog title text
31994      * @param {String} text The title text to display
31995      * @return {Roo.BasicDialog} this
31996      */
31997     setTitle : function(text){
31998         this.header.update(text);
31999         return this;
32000     },
32001
32002     // private
32003     closeClick : function(){
32004         this.hide();
32005     },
32006
32007     // private
32008     collapseClick : function(){
32009         this[this.collapsed ? "expand" : "collapse"]();
32010     },
32011
32012     /**
32013      * Collapses the dialog to its minimized state (only the title bar is visible).
32014      * Equivalent to the user clicking the collapse dialog button.
32015      */
32016     collapse : function(){
32017         if(!this.collapsed){
32018             this.collapsed = true;
32019             this.el.addClass("x-dlg-collapsed");
32020             this.restoreHeight = this.el.getHeight();
32021             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32022         }
32023     },
32024
32025     /**
32026      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32027      * clicking the expand dialog button.
32028      */
32029     expand : function(){
32030         if(this.collapsed){
32031             this.collapsed = false;
32032             this.el.removeClass("x-dlg-collapsed");
32033             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32034         }
32035     },
32036
32037     /**
32038      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32039      * @return {Roo.TabPanel} The tabs component
32040      */
32041     initTabs : function(){
32042         var tabs = this.getTabs();
32043         while(tabs.getTab(0)){
32044             tabs.removeTab(0);
32045         }
32046         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32047             var dom = el.dom;
32048             tabs.addTab(Roo.id(dom), dom.title);
32049             dom.title = "";
32050         });
32051         tabs.activate(0);
32052         return tabs;
32053     },
32054
32055     // private
32056     beforeResize : function(){
32057         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32058     },
32059
32060     // private
32061     onResize : function(){
32062         this.refreshSize();
32063         this.syncBodyHeight();
32064         this.adjustAssets();
32065         this.focus();
32066         this.fireEvent("resize", this, this.size.width, this.size.height);
32067     },
32068
32069     // private
32070     onKeyDown : function(e){
32071         if(this.isVisible()){
32072             this.fireEvent("keydown", this, e);
32073         }
32074     },
32075
32076     /**
32077      * Resizes the dialog.
32078      * @param {Number} width
32079      * @param {Number} height
32080      * @return {Roo.BasicDialog} this
32081      */
32082     resizeTo : function(width, height){
32083         this.el.setSize(width, height);
32084         this.size = {width: width, height: height};
32085         this.syncBodyHeight();
32086         if(this.fixedcenter){
32087             this.center();
32088         }
32089         if(this.isVisible()){
32090             this.constrainXY();
32091             this.adjustAssets();
32092         }
32093         this.fireEvent("resize", this, width, height);
32094         return this;
32095     },
32096
32097
32098     /**
32099      * Resizes the dialog to fit the specified content size.
32100      * @param {Number} width
32101      * @param {Number} height
32102      * @return {Roo.BasicDialog} this
32103      */
32104     setContentSize : function(w, h){
32105         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32106         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32107         //if(!this.el.isBorderBox()){
32108             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32109             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32110         //}
32111         if(this.tabs){
32112             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32113             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32114         }
32115         this.resizeTo(w, h);
32116         return this;
32117     },
32118
32119     /**
32120      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32121      * executed in response to a particular key being pressed while the dialog is active.
32122      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32123      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32124      * @param {Function} fn The function to call
32125      * @param {Object} scope (optional) The scope of the function
32126      * @return {Roo.BasicDialog} this
32127      */
32128     addKeyListener : function(key, fn, scope){
32129         var keyCode, shift, ctrl, alt;
32130         if(typeof key == "object" && !(key instanceof Array)){
32131             keyCode = key["key"];
32132             shift = key["shift"];
32133             ctrl = key["ctrl"];
32134             alt = key["alt"];
32135         }else{
32136             keyCode = key;
32137         }
32138         var handler = function(dlg, e){
32139             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32140                 var k = e.getKey();
32141                 if(keyCode instanceof Array){
32142                     for(var i = 0, len = keyCode.length; i < len; i++){
32143                         if(keyCode[i] == k){
32144                           fn.call(scope || window, dlg, k, e);
32145                           return;
32146                         }
32147                     }
32148                 }else{
32149                     if(k == keyCode){
32150                         fn.call(scope || window, dlg, k, e);
32151                     }
32152                 }
32153             }
32154         };
32155         this.on("keydown", handler);
32156         return this;
32157     },
32158
32159     /**
32160      * Returns the TabPanel component (creates it if it doesn't exist).
32161      * Note: If you wish to simply check for the existence of tabs without creating them,
32162      * check for a null 'tabs' property.
32163      * @return {Roo.TabPanel} The tabs component
32164      */
32165     getTabs : function(){
32166         if(!this.tabs){
32167             this.el.addClass("x-dlg-auto-tabs");
32168             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32169             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32170         }
32171         return this.tabs;
32172     },
32173
32174     /**
32175      * Adds a button to the footer section of the dialog.
32176      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32177      * object or a valid Roo.DomHelper element config
32178      * @param {Function} handler The function called when the button is clicked
32179      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32180      * @return {Roo.Button} The new button
32181      */
32182     addButton : function(config, handler, scope){
32183         var dh = Roo.DomHelper;
32184         if(!this.footer){
32185             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32186         }
32187         if(!this.btnContainer){
32188             var tb = this.footer.createChild({
32189
32190                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32191                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32192             }, null, true);
32193             this.btnContainer = tb.firstChild.firstChild.firstChild;
32194         }
32195         var bconfig = {
32196             handler: handler,
32197             scope: scope,
32198             minWidth: this.minButtonWidth,
32199             hideParent:true
32200         };
32201         if(typeof config == "string"){
32202             bconfig.text = config;
32203         }else{
32204             if(config.tag){
32205                 bconfig.dhconfig = config;
32206             }else{
32207                 Roo.apply(bconfig, config);
32208             }
32209         }
32210         var fc = false;
32211         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32212             bconfig.position = Math.max(0, bconfig.position);
32213             fc = this.btnContainer.childNodes[bconfig.position];
32214         }
32215          
32216         var btn = new Roo.Button(
32217             fc ? 
32218                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32219                 : this.btnContainer.appendChild(document.createElement("td")),
32220             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32221             bconfig
32222         );
32223         this.syncBodyHeight();
32224         if(!this.buttons){
32225             /**
32226              * Array of all the buttons that have been added to this dialog via addButton
32227              * @type Array
32228              */
32229             this.buttons = [];
32230         }
32231         this.buttons.push(btn);
32232         return btn;
32233     },
32234
32235     /**
32236      * Sets the default button to be focused when the dialog is displayed.
32237      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32238      * @return {Roo.BasicDialog} this
32239      */
32240     setDefaultButton : function(btn){
32241         this.defaultButton = btn;
32242         return this;
32243     },
32244
32245     // private
32246     getHeaderFooterHeight : function(safe){
32247         var height = 0;
32248         if(this.header){
32249            height += this.header.getHeight();
32250         }
32251         if(this.footer){
32252            var fm = this.footer.getMargins();
32253             height += (this.footer.getHeight()+fm.top+fm.bottom);
32254         }
32255         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32256         height += this.centerBg.getPadding("tb");
32257         return height;
32258     },
32259
32260     // private
32261     syncBodyHeight : function()
32262     {
32263         var bd = this.body, // the text
32264             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32265             bw = this.bwrap;
32266         var height = this.size.height - this.getHeaderFooterHeight(false);
32267         bd.setHeight(height-bd.getMargins("tb"));
32268         var hh = this.header.getHeight();
32269         var h = this.size.height-hh;
32270         cb.setHeight(h);
32271         
32272         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32273         bw.setHeight(h-cb.getPadding("tb"));
32274         
32275         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32276         bd.setWidth(bw.getWidth(true));
32277         if(this.tabs){
32278             this.tabs.syncHeight();
32279             if(Roo.isIE){
32280                 this.tabs.el.repaint();
32281             }
32282         }
32283     },
32284
32285     /**
32286      * Restores the previous state of the dialog if Roo.state is configured.
32287      * @return {Roo.BasicDialog} this
32288      */
32289     restoreState : function(){
32290         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32291         if(box && box.width){
32292             this.xy = [box.x, box.y];
32293             this.resizeTo(box.width, box.height);
32294         }
32295         return this;
32296     },
32297
32298     // private
32299     beforeShow : function(){
32300         this.expand();
32301         if(this.fixedcenter){
32302             this.xy = this.el.getCenterXY(true);
32303         }
32304         if(this.modal){
32305             Roo.get(document.body).addClass("x-body-masked");
32306             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32307             this.mask.show();
32308         }
32309         this.constrainXY();
32310     },
32311
32312     // private
32313     animShow : function(){
32314         var b = Roo.get(this.animateTarget).getBox();
32315         this.proxy.setSize(b.width, b.height);
32316         this.proxy.setLocation(b.x, b.y);
32317         this.proxy.show();
32318         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32319                     true, .35, this.showEl.createDelegate(this));
32320     },
32321
32322     /**
32323      * Shows the dialog.
32324      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32325      * @return {Roo.BasicDialog} this
32326      */
32327     show : function(animateTarget){
32328         if (this.fireEvent("beforeshow", this) === false){
32329             return;
32330         }
32331         if(this.syncHeightBeforeShow){
32332             this.syncBodyHeight();
32333         }else if(this.firstShow){
32334             this.firstShow = false;
32335             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32336         }
32337         this.animateTarget = animateTarget || this.animateTarget;
32338         if(!this.el.isVisible()){
32339             this.beforeShow();
32340             if(this.animateTarget && Roo.get(this.animateTarget)){
32341                 this.animShow();
32342             }else{
32343                 this.showEl();
32344             }
32345         }
32346         return this;
32347     },
32348
32349     // private
32350     showEl : function(){
32351         this.proxy.hide();
32352         this.el.setXY(this.xy);
32353         this.el.show();
32354         this.adjustAssets(true);
32355         this.toFront();
32356         this.focus();
32357         // IE peekaboo bug - fix found by Dave Fenwick
32358         if(Roo.isIE){
32359             this.el.repaint();
32360         }
32361         this.fireEvent("show", this);
32362     },
32363
32364     /**
32365      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32366      * dialog itself will receive focus.
32367      */
32368     focus : function(){
32369         if(this.defaultButton){
32370             this.defaultButton.focus();
32371         }else{
32372             this.focusEl.focus();
32373         }
32374     },
32375
32376     // private
32377     constrainXY : function(){
32378         if(this.constraintoviewport !== false){
32379             if(!this.viewSize){
32380                 if(this.container){
32381                     var s = this.container.getSize();
32382                     this.viewSize = [s.width, s.height];
32383                 }else{
32384                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32385                 }
32386             }
32387             var s = Roo.get(this.container||document).getScroll();
32388
32389             var x = this.xy[0], y = this.xy[1];
32390             var w = this.size.width, h = this.size.height;
32391             var vw = this.viewSize[0], vh = this.viewSize[1];
32392             // only move it if it needs it
32393             var moved = false;
32394             // first validate right/bottom
32395             if(x + w > vw+s.left){
32396                 x = vw - w;
32397                 moved = true;
32398             }
32399             if(y + h > vh+s.top){
32400                 y = vh - h;
32401                 moved = true;
32402             }
32403             // then make sure top/left isn't negative
32404             if(x < s.left){
32405                 x = s.left;
32406                 moved = true;
32407             }
32408             if(y < s.top){
32409                 y = s.top;
32410                 moved = true;
32411             }
32412             if(moved){
32413                 // cache xy
32414                 this.xy = [x, y];
32415                 if(this.isVisible()){
32416                     this.el.setLocation(x, y);
32417                     this.adjustAssets();
32418                 }
32419             }
32420         }
32421     },
32422
32423     // private
32424     onDrag : function(){
32425         if(!this.proxyDrag){
32426             this.xy = this.el.getXY();
32427             this.adjustAssets();
32428         }
32429     },
32430
32431     // private
32432     adjustAssets : function(doShow){
32433         var x = this.xy[0], y = this.xy[1];
32434         var w = this.size.width, h = this.size.height;
32435         if(doShow === true){
32436             if(this.shadow){
32437                 this.shadow.show(this.el);
32438             }
32439             if(this.shim){
32440                 this.shim.show();
32441             }
32442         }
32443         if(this.shadow && this.shadow.isVisible()){
32444             this.shadow.show(this.el);
32445         }
32446         if(this.shim && this.shim.isVisible()){
32447             this.shim.setBounds(x, y, w, h);
32448         }
32449     },
32450
32451     // private
32452     adjustViewport : function(w, h){
32453         if(!w || !h){
32454             w = Roo.lib.Dom.getViewWidth();
32455             h = Roo.lib.Dom.getViewHeight();
32456         }
32457         // cache the size
32458         this.viewSize = [w, h];
32459         if(this.modal && this.mask.isVisible()){
32460             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32461             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32462         }
32463         if(this.isVisible()){
32464             this.constrainXY();
32465         }
32466     },
32467
32468     /**
32469      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32470      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32471      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32472      */
32473     destroy : function(removeEl){
32474         if(this.isVisible()){
32475             this.animateTarget = null;
32476             this.hide();
32477         }
32478         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32479         if(this.tabs){
32480             this.tabs.destroy(removeEl);
32481         }
32482         Roo.destroy(
32483              this.shim,
32484              this.proxy,
32485              this.resizer,
32486              this.close,
32487              this.mask
32488         );
32489         if(this.dd){
32490             this.dd.unreg();
32491         }
32492         if(this.buttons){
32493            for(var i = 0, len = this.buttons.length; i < len; i++){
32494                this.buttons[i].destroy();
32495            }
32496         }
32497         this.el.removeAllListeners();
32498         if(removeEl === true){
32499             this.el.update("");
32500             this.el.remove();
32501         }
32502         Roo.DialogManager.unregister(this);
32503     },
32504
32505     // private
32506     startMove : function(){
32507         if(this.proxyDrag){
32508             this.proxy.show();
32509         }
32510         if(this.constraintoviewport !== false){
32511             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32512         }
32513     },
32514
32515     // private
32516     endMove : function(){
32517         if(!this.proxyDrag){
32518             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32519         }else{
32520             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32521             this.proxy.hide();
32522         }
32523         this.refreshSize();
32524         this.adjustAssets();
32525         this.focus();
32526         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32527     },
32528
32529     /**
32530      * Brings this dialog to the front of any other visible dialogs
32531      * @return {Roo.BasicDialog} this
32532      */
32533     toFront : function(){
32534         Roo.DialogManager.bringToFront(this);
32535         return this;
32536     },
32537
32538     /**
32539      * Sends this dialog to the back (under) of any other visible dialogs
32540      * @return {Roo.BasicDialog} this
32541      */
32542     toBack : function(){
32543         Roo.DialogManager.sendToBack(this);
32544         return this;
32545     },
32546
32547     /**
32548      * Centers this dialog in the viewport
32549      * @return {Roo.BasicDialog} this
32550      */
32551     center : function(){
32552         var xy = this.el.getCenterXY(true);
32553         this.moveTo(xy[0], xy[1]);
32554         return this;
32555     },
32556
32557     /**
32558      * Moves the dialog's top-left corner to the specified point
32559      * @param {Number} x
32560      * @param {Number} y
32561      * @return {Roo.BasicDialog} this
32562      */
32563     moveTo : function(x, y){
32564         this.xy = [x,y];
32565         if(this.isVisible()){
32566             this.el.setXY(this.xy);
32567             this.adjustAssets();
32568         }
32569         return this;
32570     },
32571
32572     /**
32573      * Aligns the dialog to the specified element
32574      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32575      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32576      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32577      * @return {Roo.BasicDialog} this
32578      */
32579     alignTo : function(element, position, offsets){
32580         this.xy = this.el.getAlignToXY(element, position, offsets);
32581         if(this.isVisible()){
32582             this.el.setXY(this.xy);
32583             this.adjustAssets();
32584         }
32585         return this;
32586     },
32587
32588     /**
32589      * Anchors an element to another element and realigns it when the window is resized.
32590      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32591      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32592      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32593      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32594      * is a number, it is used as the buffer delay (defaults to 50ms).
32595      * @return {Roo.BasicDialog} this
32596      */
32597     anchorTo : function(el, alignment, offsets, monitorScroll){
32598         var action = function(){
32599             this.alignTo(el, alignment, offsets);
32600         };
32601         Roo.EventManager.onWindowResize(action, this);
32602         var tm = typeof monitorScroll;
32603         if(tm != 'undefined'){
32604             Roo.EventManager.on(window, 'scroll', action, this,
32605                 {buffer: tm == 'number' ? monitorScroll : 50});
32606         }
32607         action.call(this);
32608         return this;
32609     },
32610
32611     /**
32612      * Returns true if the dialog is visible
32613      * @return {Boolean}
32614      */
32615     isVisible : function(){
32616         return this.el.isVisible();
32617     },
32618
32619     // private
32620     animHide : function(callback){
32621         var b = Roo.get(this.animateTarget).getBox();
32622         this.proxy.show();
32623         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32624         this.el.hide();
32625         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32626                     this.hideEl.createDelegate(this, [callback]));
32627     },
32628
32629     /**
32630      * Hides the dialog.
32631      * @param {Function} callback (optional) Function to call when the dialog is hidden
32632      * @return {Roo.BasicDialog} this
32633      */
32634     hide : function(callback){
32635         if (this.fireEvent("beforehide", this) === false){
32636             return;
32637         }
32638         if(this.shadow){
32639             this.shadow.hide();
32640         }
32641         if(this.shim) {
32642           this.shim.hide();
32643         }
32644         // sometimes animateTarget seems to get set.. causing problems...
32645         // this just double checks..
32646         if(this.animateTarget && Roo.get(this.animateTarget)) {
32647            this.animHide(callback);
32648         }else{
32649             this.el.hide();
32650             this.hideEl(callback);
32651         }
32652         return this;
32653     },
32654
32655     // private
32656     hideEl : function(callback){
32657         this.proxy.hide();
32658         if(this.modal){
32659             this.mask.hide();
32660             Roo.get(document.body).removeClass("x-body-masked");
32661         }
32662         this.fireEvent("hide", this);
32663         if(typeof callback == "function"){
32664             callback();
32665         }
32666     },
32667
32668     // private
32669     hideAction : function(){
32670         this.setLeft("-10000px");
32671         this.setTop("-10000px");
32672         this.setStyle("visibility", "hidden");
32673     },
32674
32675     // private
32676     refreshSize : function(){
32677         this.size = this.el.getSize();
32678         this.xy = this.el.getXY();
32679         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32680     },
32681
32682     // private
32683     // z-index is managed by the DialogManager and may be overwritten at any time
32684     setZIndex : function(index){
32685         if(this.modal){
32686             this.mask.setStyle("z-index", index);
32687         }
32688         if(this.shim){
32689             this.shim.setStyle("z-index", ++index);
32690         }
32691         if(this.shadow){
32692             this.shadow.setZIndex(++index);
32693         }
32694         this.el.setStyle("z-index", ++index);
32695         if(this.proxy){
32696             this.proxy.setStyle("z-index", ++index);
32697         }
32698         if(this.resizer){
32699             this.resizer.proxy.setStyle("z-index", ++index);
32700         }
32701
32702         this.lastZIndex = index;
32703     },
32704
32705     /**
32706      * Returns the element for this dialog
32707      * @return {Roo.Element} The underlying dialog Element
32708      */
32709     getEl : function(){
32710         return this.el;
32711     }
32712 });
32713
32714 /**
32715  * @class Roo.DialogManager
32716  * Provides global access to BasicDialogs that have been created and
32717  * support for z-indexing (layering) multiple open dialogs.
32718  */
32719 Roo.DialogManager = function(){
32720     var list = {};
32721     var accessList = [];
32722     var front = null;
32723
32724     // private
32725     var sortDialogs = function(d1, d2){
32726         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32727     };
32728
32729     // private
32730     var orderDialogs = function(){
32731         accessList.sort(sortDialogs);
32732         var seed = Roo.DialogManager.zseed;
32733         for(var i = 0, len = accessList.length; i < len; i++){
32734             var dlg = accessList[i];
32735             if(dlg){
32736                 dlg.setZIndex(seed + (i*10));
32737             }
32738         }
32739     };
32740
32741     return {
32742         /**
32743          * The starting z-index for BasicDialogs (defaults to 9000)
32744          * @type Number The z-index value
32745          */
32746         zseed : 9000,
32747
32748         // private
32749         register : function(dlg){
32750             list[dlg.id] = dlg;
32751             accessList.push(dlg);
32752         },
32753
32754         // private
32755         unregister : function(dlg){
32756             delete list[dlg.id];
32757             var i=0;
32758             var len=0;
32759             if(!accessList.indexOf){
32760                 for(  i = 0, len = accessList.length; i < len; i++){
32761                     if(accessList[i] == dlg){
32762                         accessList.splice(i, 1);
32763                         return;
32764                     }
32765                 }
32766             }else{
32767                  i = accessList.indexOf(dlg);
32768                 if(i != -1){
32769                     accessList.splice(i, 1);
32770                 }
32771             }
32772         },
32773
32774         /**
32775          * Gets a registered dialog by id
32776          * @param {String/Object} id The id of the dialog or a dialog
32777          * @return {Roo.BasicDialog} this
32778          */
32779         get : function(id){
32780             return typeof id == "object" ? id : list[id];
32781         },
32782
32783         /**
32784          * Brings the specified dialog to the front
32785          * @param {String/Object} dlg The id of the dialog or a dialog
32786          * @return {Roo.BasicDialog} this
32787          */
32788         bringToFront : function(dlg){
32789             dlg = this.get(dlg);
32790             if(dlg != front){
32791                 front = dlg;
32792                 dlg._lastAccess = new Date().getTime();
32793                 orderDialogs();
32794             }
32795             return dlg;
32796         },
32797
32798         /**
32799          * Sends the specified dialog to the back
32800          * @param {String/Object} dlg The id of the dialog or a dialog
32801          * @return {Roo.BasicDialog} this
32802          */
32803         sendToBack : function(dlg){
32804             dlg = this.get(dlg);
32805             dlg._lastAccess = -(new Date().getTime());
32806             orderDialogs();
32807             return dlg;
32808         },
32809
32810         /**
32811          * Hides all dialogs
32812          */
32813         hideAll : function(){
32814             for(var id in list){
32815                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32816                     list[id].hide();
32817                 }
32818             }
32819         }
32820     };
32821 }();
32822
32823 /**
32824  * @class Roo.LayoutDialog
32825  * @extends Roo.BasicDialog
32826  * Dialog which provides adjustments for working with a layout in a Dialog.
32827  * Add your necessary layout config options to the dialog's config.<br>
32828  * Example usage (including a nested layout):
32829  * <pre><code>
32830 if(!dialog){
32831     dialog = new Roo.LayoutDialog("download-dlg", {
32832         modal: true,
32833         width:600,
32834         height:450,
32835         shadow:true,
32836         minWidth:500,
32837         minHeight:350,
32838         autoTabs:true,
32839         proxyDrag:true,
32840         // layout config merges with the dialog config
32841         center:{
32842             tabPosition: "top",
32843             alwaysShowTabs: true
32844         }
32845     });
32846     dialog.addKeyListener(27, dialog.hide, dialog);
32847     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
32848     dialog.addButton("Build It!", this.getDownload, this);
32849
32850     // we can even add nested layouts
32851     var innerLayout = new Roo.BorderLayout("dl-inner", {
32852         east: {
32853             initialSize: 200,
32854             autoScroll:true,
32855             split:true
32856         },
32857         center: {
32858             autoScroll:true
32859         }
32860     });
32861     innerLayout.beginUpdate();
32862     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
32863     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
32864     innerLayout.endUpdate(true);
32865
32866     var layout = dialog.getLayout();
32867     layout.beginUpdate();
32868     layout.add("center", new Roo.ContentPanel("standard-panel",
32869                         {title: "Download the Source", fitToFrame:true}));
32870     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
32871                {title: "Build your own roo.js"}));
32872     layout.getRegion("center").showPanel(sp);
32873     layout.endUpdate();
32874 }
32875 </code></pre>
32876     * @constructor
32877     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
32878     * @param {Object} config configuration options
32879   */
32880 Roo.LayoutDialog = function(el, cfg){
32881     
32882     var config=  cfg;
32883     if (typeof(cfg) == 'undefined') {
32884         config = Roo.apply({}, el);
32885         // not sure why we use documentElement here.. - it should always be body.
32886         // IE7 borks horribly if we use documentElement.
32887         // webkit also does not like documentElement - it creates a body element...
32888         el = Roo.get( document.body || document.documentElement ).createChild();
32889         //config.autoCreate = true;
32890     }
32891     
32892     
32893     config.autoTabs = false;
32894     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
32895     this.body.setStyle({overflow:"hidden", position:"relative"});
32896     this.layout = new Roo.BorderLayout(this.body.dom, config);
32897     this.layout.monitorWindowResize = false;
32898     this.el.addClass("x-dlg-auto-layout");
32899     // fix case when center region overwrites center function
32900     this.center = Roo.BasicDialog.prototype.center;
32901     this.on("show", this.layout.layout, this.layout, true);
32902     if (config.items) {
32903         var xitems = config.items;
32904         delete config.items;
32905         Roo.each(xitems, this.addxtype, this);
32906     }
32907     
32908     
32909 };
32910 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
32911     /**
32912      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
32913      * @deprecated
32914      */
32915     endUpdate : function(){
32916         this.layout.endUpdate();
32917     },
32918
32919     /**
32920      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
32921      *  @deprecated
32922      */
32923     beginUpdate : function(){
32924         this.layout.beginUpdate();
32925     },
32926
32927     /**
32928      * Get the BorderLayout for this dialog
32929      * @return {Roo.BorderLayout}
32930      */
32931     getLayout : function(){
32932         return this.layout;
32933     },
32934
32935     showEl : function(){
32936         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
32937         if(Roo.isIE7){
32938             this.layout.layout();
32939         }
32940     },
32941
32942     // private
32943     // Use the syncHeightBeforeShow config option to control this automatically
32944     syncBodyHeight : function(){
32945         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
32946         if(this.layout){this.layout.layout();}
32947     },
32948     
32949       /**
32950      * Add an xtype element (actually adds to the layout.)
32951      * @return {Object} xdata xtype object data.
32952      */
32953     
32954     addxtype : function(c) {
32955         return this.layout.addxtype(c);
32956     }
32957 });/*
32958  * Based on:
32959  * Ext JS Library 1.1.1
32960  * Copyright(c) 2006-2007, Ext JS, LLC.
32961  *
32962  * Originally Released Under LGPL - original licence link has changed is not relivant.
32963  *
32964  * Fork - LGPL
32965  * <script type="text/javascript">
32966  */
32967  
32968 /**
32969  * @class Roo.MessageBox
32970  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
32971  * Example usage:
32972  *<pre><code>
32973 // Basic alert:
32974 Roo.Msg.alert('Status', 'Changes saved successfully.');
32975
32976 // Prompt for user data:
32977 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
32978     if (btn == 'ok'){
32979         // process text value...
32980     }
32981 });
32982
32983 // Show a dialog using config options:
32984 Roo.Msg.show({
32985    title:'Save Changes?',
32986    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
32987    buttons: Roo.Msg.YESNOCANCEL,
32988    fn: processResult,
32989    animEl: 'elId'
32990 });
32991 </code></pre>
32992  * @singleton
32993  */
32994 Roo.MessageBox = function(){
32995     var dlg, opt, mask, waitTimer;
32996     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
32997     var buttons, activeTextEl, bwidth;
32998
32999     // private
33000     var handleButton = function(button){
33001         dlg.hide();
33002         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33003     };
33004
33005     // private
33006     var handleHide = function(){
33007         if(opt && opt.cls){
33008             dlg.el.removeClass(opt.cls);
33009         }
33010         if(waitTimer){
33011             Roo.TaskMgr.stop(waitTimer);
33012             waitTimer = null;
33013         }
33014     };
33015
33016     // private
33017     var updateButtons = function(b){
33018         var width = 0;
33019         if(!b){
33020             buttons["ok"].hide();
33021             buttons["cancel"].hide();
33022             buttons["yes"].hide();
33023             buttons["no"].hide();
33024             dlg.footer.dom.style.display = 'none';
33025             return width;
33026         }
33027         dlg.footer.dom.style.display = '';
33028         for(var k in buttons){
33029             if(typeof buttons[k] != "function"){
33030                 if(b[k]){
33031                     buttons[k].show();
33032                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33033                     width += buttons[k].el.getWidth()+15;
33034                 }else{
33035                     buttons[k].hide();
33036                 }
33037             }
33038         }
33039         return width;
33040     };
33041
33042     // private
33043     var handleEsc = function(d, k, e){
33044         if(opt && opt.closable !== false){
33045             dlg.hide();
33046         }
33047         if(e){
33048             e.stopEvent();
33049         }
33050     };
33051
33052     return {
33053         /**
33054          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33055          * @return {Roo.BasicDialog} The BasicDialog element
33056          */
33057         getDialog : function(){
33058            if(!dlg){
33059                 dlg = new Roo.BasicDialog("x-msg-box", {
33060                     autoCreate : true,
33061                     shadow: true,
33062                     draggable: true,
33063                     resizable:false,
33064                     constraintoviewport:false,
33065                     fixedcenter:true,
33066                     collapsible : false,
33067                     shim:true,
33068                     modal: true,
33069                     width:400, height:100,
33070                     buttonAlign:"center",
33071                     closeClick : function(){
33072                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33073                             handleButton("no");
33074                         }else{
33075                             handleButton("cancel");
33076                         }
33077                     }
33078                 });
33079                 dlg.on("hide", handleHide);
33080                 mask = dlg.mask;
33081                 dlg.addKeyListener(27, handleEsc);
33082                 buttons = {};
33083                 var bt = this.buttonText;
33084                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33085                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33086                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33087                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33088                 bodyEl = dlg.body.createChild({
33089
33090                     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>'
33091                 });
33092                 msgEl = bodyEl.dom.firstChild;
33093                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33094                 textboxEl.enableDisplayMode();
33095                 textboxEl.addKeyListener([10,13], function(){
33096                     if(dlg.isVisible() && opt && opt.buttons){
33097                         if(opt.buttons.ok){
33098                             handleButton("ok");
33099                         }else if(opt.buttons.yes){
33100                             handleButton("yes");
33101                         }
33102                     }
33103                 });
33104                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33105                 textareaEl.enableDisplayMode();
33106                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33107                 progressEl.enableDisplayMode();
33108                 var pf = progressEl.dom.firstChild;
33109                 if (pf) {
33110                     pp = Roo.get(pf.firstChild);
33111                     pp.setHeight(pf.offsetHeight);
33112                 }
33113                 
33114             }
33115             return dlg;
33116         },
33117
33118         /**
33119          * Updates the message box body text
33120          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33121          * the XHTML-compliant non-breaking space character '&amp;#160;')
33122          * @return {Roo.MessageBox} This message box
33123          */
33124         updateText : function(text){
33125             if(!dlg.isVisible() && !opt.width){
33126                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33127             }
33128             msgEl.innerHTML = text || '&#160;';
33129       
33130             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33131             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33132             var w = Math.max(
33133                     Math.min(opt.width || cw , this.maxWidth), 
33134                     Math.max(opt.minWidth || this.minWidth, bwidth)
33135             );
33136             if(opt.prompt){
33137                 activeTextEl.setWidth(w);
33138             }
33139             if(dlg.isVisible()){
33140                 dlg.fixedcenter = false;
33141             }
33142             // to big, make it scroll. = But as usual stupid IE does not support
33143             // !important..
33144             
33145             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33146                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33147                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33148             } else {
33149                 bodyEl.dom.style.height = '';
33150                 bodyEl.dom.style.overflowY = '';
33151             }
33152             if (cw > w) {
33153                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33154             } else {
33155                 bodyEl.dom.style.overflowX = '';
33156             }
33157             
33158             dlg.setContentSize(w, bodyEl.getHeight());
33159             if(dlg.isVisible()){
33160                 dlg.fixedcenter = true;
33161             }
33162             return this;
33163         },
33164
33165         /**
33166          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33167          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33168          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33169          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33170          * @return {Roo.MessageBox} This message box
33171          */
33172         updateProgress : function(value, text){
33173             if(text){
33174                 this.updateText(text);
33175             }
33176             if (pp) { // weird bug on my firefox - for some reason this is not defined
33177                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33178             }
33179             return this;
33180         },        
33181
33182         /**
33183          * Returns true if the message box is currently displayed
33184          * @return {Boolean} True if the message box is visible, else false
33185          */
33186         isVisible : function(){
33187             return dlg && dlg.isVisible();  
33188         },
33189
33190         /**
33191          * Hides the message box if it is displayed
33192          */
33193         hide : function(){
33194             if(this.isVisible()){
33195                 dlg.hide();
33196             }  
33197         },
33198
33199         /**
33200          * Displays a new message box, or reinitializes an existing message box, based on the config options
33201          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33202          * The following config object properties are supported:
33203          * <pre>
33204 Property    Type             Description
33205 ----------  ---------------  ------------------------------------------------------------------------------------
33206 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33207                                    closes (defaults to undefined)
33208 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33209                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33210 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33211                                    progress and wait dialogs will ignore this property and always hide the
33212                                    close button as they can only be closed programmatically.
33213 cls               String           A custom CSS class to apply to the message box element
33214 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33215                                    displayed (defaults to 75)
33216 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33217                                    function will be btn (the name of the button that was clicked, if applicable,
33218                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33219                                    Progress and wait dialogs will ignore this option since they do not respond to
33220                                    user actions and can only be closed programmatically, so any required function
33221                                    should be called by the same code after it closes the dialog.
33222 icon              String           A CSS class that provides a background image to be used as an icon for
33223                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33224 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33225 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33226 modal             Boolean          False to allow user interaction with the page while the message box is
33227                                    displayed (defaults to true)
33228 msg               String           A string that will replace the existing message box body text (defaults
33229                                    to the XHTML-compliant non-breaking space character '&#160;')
33230 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33231 progress          Boolean          True to display a progress bar (defaults to false)
33232 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33233 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33234 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33235 title             String           The title text
33236 value             String           The string value to set into the active textbox element if displayed
33237 wait              Boolean          True to display a progress bar (defaults to false)
33238 width             Number           The width of the dialog in pixels
33239 </pre>
33240          *
33241          * Example usage:
33242          * <pre><code>
33243 Roo.Msg.show({
33244    title: 'Address',
33245    msg: 'Please enter your address:',
33246    width: 300,
33247    buttons: Roo.MessageBox.OKCANCEL,
33248    multiline: true,
33249    fn: saveAddress,
33250    animEl: 'addAddressBtn'
33251 });
33252 </code></pre>
33253          * @param {Object} config Configuration options
33254          * @return {Roo.MessageBox} This message box
33255          */
33256         show : function(options)
33257         {
33258             
33259             // this causes nightmares if you show one dialog after another
33260             // especially on callbacks..
33261              
33262             if(this.isVisible()){
33263                 
33264                 this.hide();
33265                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33266                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33267                 Roo.log("New Dialog Message:" +  options.msg )
33268                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33269                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33270                 
33271             }
33272             var d = this.getDialog();
33273             opt = options;
33274             d.setTitle(opt.title || "&#160;");
33275             d.close.setDisplayed(opt.closable !== false);
33276             activeTextEl = textboxEl;
33277             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33278             if(opt.prompt){
33279                 if(opt.multiline){
33280                     textboxEl.hide();
33281                     textareaEl.show();
33282                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33283                         opt.multiline : this.defaultTextHeight);
33284                     activeTextEl = textareaEl;
33285                 }else{
33286                     textboxEl.show();
33287                     textareaEl.hide();
33288                 }
33289             }else{
33290                 textboxEl.hide();
33291                 textareaEl.hide();
33292             }
33293             progressEl.setDisplayed(opt.progress === true);
33294             this.updateProgress(0);
33295             activeTextEl.dom.value = opt.value || "";
33296             if(opt.prompt){
33297                 dlg.setDefaultButton(activeTextEl);
33298             }else{
33299                 var bs = opt.buttons;
33300                 var db = null;
33301                 if(bs && bs.ok){
33302                     db = buttons["ok"];
33303                 }else if(bs && bs.yes){
33304                     db = buttons["yes"];
33305                 }
33306                 dlg.setDefaultButton(db);
33307             }
33308             bwidth = updateButtons(opt.buttons);
33309             this.updateText(opt.msg);
33310             if(opt.cls){
33311                 d.el.addClass(opt.cls);
33312             }
33313             d.proxyDrag = opt.proxyDrag === true;
33314             d.modal = opt.modal !== false;
33315             d.mask = opt.modal !== false ? mask : false;
33316             if(!d.isVisible()){
33317                 // force it to the end of the z-index stack so it gets a cursor in FF
33318                 document.body.appendChild(dlg.el.dom);
33319                 d.animateTarget = null;
33320                 d.show(options.animEl);
33321             }
33322             return this;
33323         },
33324
33325         /**
33326          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33327          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33328          * and closing the message box when the process is complete.
33329          * @param {String} title The title bar text
33330          * @param {String} msg The message box body text
33331          * @return {Roo.MessageBox} This message box
33332          */
33333         progress : function(title, msg){
33334             this.show({
33335                 title : title,
33336                 msg : msg,
33337                 buttons: false,
33338                 progress:true,
33339                 closable:false,
33340                 minWidth: this.minProgressWidth,
33341                 modal : true
33342             });
33343             return this;
33344         },
33345
33346         /**
33347          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33348          * If a callback function is passed it will be called after the user clicks the button, and the
33349          * id of the button that was clicked will be passed as the only parameter to the callback
33350          * (could also be the top-right close button).
33351          * @param {String} title The title bar text
33352          * @param {String} msg The message box body text
33353          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33354          * @param {Object} scope (optional) The scope of the callback function
33355          * @return {Roo.MessageBox} This message box
33356          */
33357         alert : function(title, msg, fn, scope){
33358             this.show({
33359                 title : title,
33360                 msg : msg,
33361                 buttons: this.OK,
33362                 fn: fn,
33363                 scope : scope,
33364                 modal : true
33365             });
33366             return this;
33367         },
33368
33369         /**
33370          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33371          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33372          * You are responsible for closing the message box when the process is complete.
33373          * @param {String} msg The message box body text
33374          * @param {String} title (optional) The title bar text
33375          * @return {Roo.MessageBox} This message box
33376          */
33377         wait : function(msg, title){
33378             this.show({
33379                 title : title,
33380                 msg : msg,
33381                 buttons: false,
33382                 closable:false,
33383                 progress:true,
33384                 modal:true,
33385                 width:300,
33386                 wait:true
33387             });
33388             waitTimer = Roo.TaskMgr.start({
33389                 run: function(i){
33390                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33391                 },
33392                 interval: 1000
33393             });
33394             return this;
33395         },
33396
33397         /**
33398          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33399          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33400          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33401          * @param {String} title The title bar text
33402          * @param {String} msg The message box body text
33403          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33404          * @param {Object} scope (optional) The scope of the callback function
33405          * @return {Roo.MessageBox} This message box
33406          */
33407         confirm : function(title, msg, fn, scope){
33408             this.show({
33409                 title : title,
33410                 msg : msg,
33411                 buttons: this.YESNO,
33412                 fn: fn,
33413                 scope : scope,
33414                 modal : true
33415             });
33416             return this;
33417         },
33418
33419         /**
33420          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33421          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33422          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33423          * (could also be the top-right close button) and the text that was entered will be passed as the two
33424          * parameters to the callback.
33425          * @param {String} title The title bar text
33426          * @param {String} msg The message box body text
33427          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33428          * @param {Object} scope (optional) The scope of the callback function
33429          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33430          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33431          * @return {Roo.MessageBox} This message box
33432          */
33433         prompt : function(title, msg, fn, scope, multiline){
33434             this.show({
33435                 title : title,
33436                 msg : msg,
33437                 buttons: this.OKCANCEL,
33438                 fn: fn,
33439                 minWidth:250,
33440                 scope : scope,
33441                 prompt:true,
33442                 multiline: multiline,
33443                 modal : true
33444             });
33445             return this;
33446         },
33447
33448         /**
33449          * Button config that displays a single OK button
33450          * @type Object
33451          */
33452         OK : {ok:true},
33453         /**
33454          * Button config that displays Yes and No buttons
33455          * @type Object
33456          */
33457         YESNO : {yes:true, no:true},
33458         /**
33459          * Button config that displays OK and Cancel buttons
33460          * @type Object
33461          */
33462         OKCANCEL : {ok:true, cancel:true},
33463         /**
33464          * Button config that displays Yes, No and Cancel buttons
33465          * @type Object
33466          */
33467         YESNOCANCEL : {yes:true, no:true, cancel:true},
33468
33469         /**
33470          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33471          * @type Number
33472          */
33473         defaultTextHeight : 75,
33474         /**
33475          * The maximum width in pixels of the message box (defaults to 600)
33476          * @type Number
33477          */
33478         maxWidth : 600,
33479         /**
33480          * The minimum width in pixels of the message box (defaults to 100)
33481          * @type Number
33482          */
33483         minWidth : 100,
33484         /**
33485          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33486          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33487          * @type Number
33488          */
33489         minProgressWidth : 250,
33490         /**
33491          * An object containing the default button text strings that can be overriden for localized language support.
33492          * Supported properties are: ok, cancel, yes and no.
33493          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33494          * @type Object
33495          */
33496         buttonText : {
33497             ok : "OK",
33498             cancel : "Cancel",
33499             yes : "Yes",
33500             no : "No"
33501         }
33502     };
33503 }();
33504
33505 /**
33506  * Shorthand for {@link Roo.MessageBox}
33507  */
33508 Roo.Msg = Roo.MessageBox;/*
33509  * Based on:
33510  * Ext JS Library 1.1.1
33511  * Copyright(c) 2006-2007, Ext JS, LLC.
33512  *
33513  * Originally Released Under LGPL - original licence link has changed is not relivant.
33514  *
33515  * Fork - LGPL
33516  * <script type="text/javascript">
33517  */
33518 /**
33519  * @class Roo.QuickTips
33520  * Provides attractive and customizable tooltips for any element.
33521  * @singleton
33522  */
33523 Roo.QuickTips = function(){
33524     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33525     var ce, bd, xy, dd;
33526     var visible = false, disabled = true, inited = false;
33527     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33528     
33529     var onOver = function(e){
33530         if(disabled){
33531             return;
33532         }
33533         var t = e.getTarget();
33534         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33535             return;
33536         }
33537         if(ce && t == ce.el){
33538             clearTimeout(hideProc);
33539             return;
33540         }
33541         if(t && tagEls[t.id]){
33542             tagEls[t.id].el = t;
33543             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33544             return;
33545         }
33546         var ttp, et = Roo.fly(t);
33547         var ns = cfg.namespace;
33548         if(tm.interceptTitles && t.title){
33549             ttp = t.title;
33550             t.qtip = ttp;
33551             t.removeAttribute("title");
33552             e.preventDefault();
33553         }else{
33554             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33555         }
33556         if(ttp){
33557             showProc = show.defer(tm.showDelay, tm, [{
33558                 el: t, 
33559                 text: ttp, 
33560                 width: et.getAttributeNS(ns, cfg.width),
33561                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33562                 title: et.getAttributeNS(ns, cfg.title),
33563                     cls: et.getAttributeNS(ns, cfg.cls)
33564             }]);
33565         }
33566     };
33567     
33568     var onOut = function(e){
33569         clearTimeout(showProc);
33570         var t = e.getTarget();
33571         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33572             hideProc = setTimeout(hide, tm.hideDelay);
33573         }
33574     };
33575     
33576     var onMove = function(e){
33577         if(disabled){
33578             return;
33579         }
33580         xy = e.getXY();
33581         xy[1] += 18;
33582         if(tm.trackMouse && ce){
33583             el.setXY(xy);
33584         }
33585     };
33586     
33587     var onDown = function(e){
33588         clearTimeout(showProc);
33589         clearTimeout(hideProc);
33590         if(!e.within(el)){
33591             if(tm.hideOnClick){
33592                 hide();
33593                 tm.disable();
33594                 tm.enable.defer(100, tm);
33595             }
33596         }
33597     };
33598     
33599     var getPad = function(){
33600         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33601     };
33602
33603     var show = function(o){
33604         if(disabled){
33605             return;
33606         }
33607         clearTimeout(dismissProc);
33608         ce = o;
33609         if(removeCls){ // in case manually hidden
33610             el.removeClass(removeCls);
33611             removeCls = null;
33612         }
33613         if(ce.cls){
33614             el.addClass(ce.cls);
33615             removeCls = ce.cls;
33616         }
33617         if(ce.title){
33618             tipTitle.update(ce.title);
33619             tipTitle.show();
33620         }else{
33621             tipTitle.update('');
33622             tipTitle.hide();
33623         }
33624         el.dom.style.width  = tm.maxWidth+'px';
33625         //tipBody.dom.style.width = '';
33626         tipBodyText.update(o.text);
33627         var p = getPad(), w = ce.width;
33628         if(!w){
33629             var td = tipBodyText.dom;
33630             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33631             if(aw > tm.maxWidth){
33632                 w = tm.maxWidth;
33633             }else if(aw < tm.minWidth){
33634                 w = tm.minWidth;
33635             }else{
33636                 w = aw;
33637             }
33638         }
33639         //tipBody.setWidth(w);
33640         el.setWidth(parseInt(w, 10) + p);
33641         if(ce.autoHide === false){
33642             close.setDisplayed(true);
33643             if(dd){
33644                 dd.unlock();
33645             }
33646         }else{
33647             close.setDisplayed(false);
33648             if(dd){
33649                 dd.lock();
33650             }
33651         }
33652         if(xy){
33653             el.avoidY = xy[1]-18;
33654             el.setXY(xy);
33655         }
33656         if(tm.animate){
33657             el.setOpacity(.1);
33658             el.setStyle("visibility", "visible");
33659             el.fadeIn({callback: afterShow});
33660         }else{
33661             afterShow();
33662         }
33663     };
33664     
33665     var afterShow = function(){
33666         if(ce){
33667             el.show();
33668             esc.enable();
33669             if(tm.autoDismiss && ce.autoHide !== false){
33670                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33671             }
33672         }
33673     };
33674     
33675     var hide = function(noanim){
33676         clearTimeout(dismissProc);
33677         clearTimeout(hideProc);
33678         ce = null;
33679         if(el.isVisible()){
33680             esc.disable();
33681             if(noanim !== true && tm.animate){
33682                 el.fadeOut({callback: afterHide});
33683             }else{
33684                 afterHide();
33685             } 
33686         }
33687     };
33688     
33689     var afterHide = function(){
33690         el.hide();
33691         if(removeCls){
33692             el.removeClass(removeCls);
33693             removeCls = null;
33694         }
33695     };
33696     
33697     return {
33698         /**
33699         * @cfg {Number} minWidth
33700         * The minimum width of the quick tip (defaults to 40)
33701         */
33702        minWidth : 40,
33703         /**
33704         * @cfg {Number} maxWidth
33705         * The maximum width of the quick tip (defaults to 300)
33706         */
33707        maxWidth : 300,
33708         /**
33709         * @cfg {Boolean} interceptTitles
33710         * True to automatically use the element's DOM title value if available (defaults to false)
33711         */
33712        interceptTitles : false,
33713         /**
33714         * @cfg {Boolean} trackMouse
33715         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33716         */
33717        trackMouse : false,
33718         /**
33719         * @cfg {Boolean} hideOnClick
33720         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33721         */
33722        hideOnClick : true,
33723         /**
33724         * @cfg {Number} showDelay
33725         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33726         */
33727        showDelay : 500,
33728         /**
33729         * @cfg {Number} hideDelay
33730         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33731         */
33732        hideDelay : 200,
33733         /**
33734         * @cfg {Boolean} autoHide
33735         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33736         * Used in conjunction with hideDelay.
33737         */
33738        autoHide : true,
33739         /**
33740         * @cfg {Boolean}
33741         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33742         * (defaults to true).  Used in conjunction with autoDismissDelay.
33743         */
33744        autoDismiss : true,
33745         /**
33746         * @cfg {Number}
33747         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33748         */
33749        autoDismissDelay : 5000,
33750        /**
33751         * @cfg {Boolean} animate
33752         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33753         */
33754        animate : false,
33755
33756        /**
33757         * @cfg {String} title
33758         * Title text to display (defaults to '').  This can be any valid HTML markup.
33759         */
33760         title: '',
33761        /**
33762         * @cfg {String} text
33763         * Body text to display (defaults to '').  This can be any valid HTML markup.
33764         */
33765         text : '',
33766        /**
33767         * @cfg {String} cls
33768         * A CSS class to apply to the base quick tip element (defaults to '').
33769         */
33770         cls : '',
33771        /**
33772         * @cfg {Number} width
33773         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33774         * minWidth or maxWidth.
33775         */
33776         width : null,
33777
33778     /**
33779      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33780      * or display QuickTips in a page.
33781      */
33782        init : function(){
33783           tm = Roo.QuickTips;
33784           cfg = tm.tagConfig;
33785           if(!inited){
33786               if(!Roo.isReady){ // allow calling of init() before onReady
33787                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33788                   return;
33789               }
33790               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33791               el.fxDefaults = {stopFx: true};
33792               // maximum custom styling
33793               //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>');
33794               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>');              
33795               tipTitle = el.child('h3');
33796               tipTitle.enableDisplayMode("block");
33797               tipBody = el.child('div.x-tip-bd');
33798               tipBodyText = el.child('div.x-tip-bd-inner');
33799               //bdLeft = el.child('div.x-tip-bd-left');
33800               //bdRight = el.child('div.x-tip-bd-right');
33801               close = el.child('div.x-tip-close');
33802               close.enableDisplayMode("block");
33803               close.on("click", hide);
33804               var d = Roo.get(document);
33805               d.on("mousedown", onDown);
33806               d.on("mouseover", onOver);
33807               d.on("mouseout", onOut);
33808               d.on("mousemove", onMove);
33809               esc = d.addKeyListener(27, hide);
33810               esc.disable();
33811               if(Roo.dd.DD){
33812                   dd = el.initDD("default", null, {
33813                       onDrag : function(){
33814                           el.sync();  
33815                       }
33816                   });
33817                   dd.setHandleElId(tipTitle.id);
33818                   dd.lock();
33819               }
33820               inited = true;
33821           }
33822           this.enable(); 
33823        },
33824
33825     /**
33826      * Configures a new quick tip instance and assigns it to a target element.  The following config options
33827      * are supported:
33828      * <pre>
33829 Property    Type                   Description
33830 ----------  ---------------------  ------------------------------------------------------------------------
33831 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
33832      * </ul>
33833      * @param {Object} config The config object
33834      */
33835        register : function(config){
33836            var cs = config instanceof Array ? config : arguments;
33837            for(var i = 0, len = cs.length; i < len; i++) {
33838                var c = cs[i];
33839                var target = c.target;
33840                if(target){
33841                    if(target instanceof Array){
33842                        for(var j = 0, jlen = target.length; j < jlen; j++){
33843                            tagEls[target[j]] = c;
33844                        }
33845                    }else{
33846                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
33847                    }
33848                }
33849            }
33850        },
33851
33852     /**
33853      * Removes this quick tip from its element and destroys it.
33854      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
33855      */
33856        unregister : function(el){
33857            delete tagEls[Roo.id(el)];
33858        },
33859
33860     /**
33861      * Enable this quick tip.
33862      */
33863        enable : function(){
33864            if(inited && disabled){
33865                locks.pop();
33866                if(locks.length < 1){
33867                    disabled = false;
33868                }
33869            }
33870        },
33871
33872     /**
33873      * Disable this quick tip.
33874      */
33875        disable : function(){
33876           disabled = true;
33877           clearTimeout(showProc);
33878           clearTimeout(hideProc);
33879           clearTimeout(dismissProc);
33880           if(ce){
33881               hide(true);
33882           }
33883           locks.push(1);
33884        },
33885
33886     /**
33887      * Returns true if the quick tip is enabled, else false.
33888      */
33889        isEnabled : function(){
33890             return !disabled;
33891        },
33892
33893         // private
33894        tagConfig : {
33895            namespace : "roo", // was ext?? this may break..
33896            alt_namespace : "ext",
33897            attribute : "qtip",
33898            width : "width",
33899            target : "target",
33900            title : "qtitle",
33901            hide : "hide",
33902            cls : "qclass"
33903        }
33904    };
33905 }();
33906
33907 // backwards compat
33908 Roo.QuickTips.tips = Roo.QuickTips.register;/*
33909  * Based on:
33910  * Ext JS Library 1.1.1
33911  * Copyright(c) 2006-2007, Ext JS, LLC.
33912  *
33913  * Originally Released Under LGPL - original licence link has changed is not relivant.
33914  *
33915  * Fork - LGPL
33916  * <script type="text/javascript">
33917  */
33918  
33919
33920 /**
33921  * @class Roo.tree.TreePanel
33922  * @extends Roo.data.Tree
33923
33924  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
33925  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
33926  * @cfg {Boolean} enableDD true to enable drag and drop
33927  * @cfg {Boolean} enableDrag true to enable just drag
33928  * @cfg {Boolean} enableDrop true to enable just drop
33929  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
33930  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
33931  * @cfg {String} ddGroup The DD group this TreePanel belongs to
33932  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
33933  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
33934  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
33935  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
33936  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
33937  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
33938  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
33939  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
33940  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
33941  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
33942  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
33943  * @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>
33944  * @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>
33945  * 
33946  * @constructor
33947  * @param {String/HTMLElement/Element} el The container element
33948  * @param {Object} config
33949  */
33950 Roo.tree.TreePanel = function(el, config){
33951     var root = false;
33952     var loader = false;
33953     if (config.root) {
33954         root = config.root;
33955         delete config.root;
33956     }
33957     if (config.loader) {
33958         loader = config.loader;
33959         delete config.loader;
33960     }
33961     
33962     Roo.apply(this, config);
33963     Roo.tree.TreePanel.superclass.constructor.call(this);
33964     this.el = Roo.get(el);
33965     this.el.addClass('x-tree');
33966     //console.log(root);
33967     if (root) {
33968         this.setRootNode( Roo.factory(root, Roo.tree));
33969     }
33970     if (loader) {
33971         this.loader = Roo.factory(loader, Roo.tree);
33972     }
33973    /**
33974     * Read-only. The id of the container element becomes this TreePanel's id.
33975     */
33976     this.id = this.el.id;
33977     this.addEvents({
33978         /**
33979         * @event beforeload
33980         * Fires before a node is loaded, return false to cancel
33981         * @param {Node} node The node being loaded
33982         */
33983         "beforeload" : true,
33984         /**
33985         * @event load
33986         * Fires when a node is loaded
33987         * @param {Node} node The node that was loaded
33988         */
33989         "load" : true,
33990         /**
33991         * @event textchange
33992         * Fires when the text for a node is changed
33993         * @param {Node} node The node
33994         * @param {String} text The new text
33995         * @param {String} oldText The old text
33996         */
33997         "textchange" : true,
33998         /**
33999         * @event beforeexpand
34000         * Fires before a node is expanded, return false to cancel.
34001         * @param {Node} node The node
34002         * @param {Boolean} deep
34003         * @param {Boolean} anim
34004         */
34005         "beforeexpand" : true,
34006         /**
34007         * @event beforecollapse
34008         * Fires before a node is collapsed, return false to cancel.
34009         * @param {Node} node The node
34010         * @param {Boolean} deep
34011         * @param {Boolean} anim
34012         */
34013         "beforecollapse" : true,
34014         /**
34015         * @event expand
34016         * Fires when a node is expanded
34017         * @param {Node} node The node
34018         */
34019         "expand" : true,
34020         /**
34021         * @event disabledchange
34022         * Fires when the disabled status of a node changes
34023         * @param {Node} node The node
34024         * @param {Boolean} disabled
34025         */
34026         "disabledchange" : true,
34027         /**
34028         * @event collapse
34029         * Fires when a node is collapsed
34030         * @param {Node} node The node
34031         */
34032         "collapse" : true,
34033         /**
34034         * @event beforeclick
34035         * Fires before click processing on a node. Return false to cancel the default action.
34036         * @param {Node} node The node
34037         * @param {Roo.EventObject} e The event object
34038         */
34039         "beforeclick":true,
34040         /**
34041         * @event checkchange
34042         * Fires when a node with a checkbox's checked property changes
34043         * @param {Node} this This node
34044         * @param {Boolean} checked
34045         */
34046         "checkchange":true,
34047         /**
34048         * @event click
34049         * Fires when a node is clicked
34050         * @param {Node} node The node
34051         * @param {Roo.EventObject} e The event object
34052         */
34053         "click":true,
34054         /**
34055         * @event dblclick
34056         * Fires when a node is double clicked
34057         * @param {Node} node The node
34058         * @param {Roo.EventObject} e The event object
34059         */
34060         "dblclick":true,
34061         /**
34062         * @event contextmenu
34063         * Fires when a node is right clicked
34064         * @param {Node} node The node
34065         * @param {Roo.EventObject} e The event object
34066         */
34067         "contextmenu":true,
34068         /**
34069         * @event beforechildrenrendered
34070         * Fires right before the child nodes for a node are rendered
34071         * @param {Node} node The node
34072         */
34073         "beforechildrenrendered":true,
34074         /**
34075         * @event startdrag
34076         * Fires when a node starts being dragged
34077         * @param {Roo.tree.TreePanel} this
34078         * @param {Roo.tree.TreeNode} node
34079         * @param {event} e The raw browser event
34080         */ 
34081        "startdrag" : true,
34082        /**
34083         * @event enddrag
34084         * Fires when a drag operation is complete
34085         * @param {Roo.tree.TreePanel} this
34086         * @param {Roo.tree.TreeNode} node
34087         * @param {event} e The raw browser event
34088         */
34089        "enddrag" : true,
34090        /**
34091         * @event dragdrop
34092         * Fires when a dragged node is dropped on a valid DD target
34093         * @param {Roo.tree.TreePanel} this
34094         * @param {Roo.tree.TreeNode} node
34095         * @param {DD} dd The dd it was dropped on
34096         * @param {event} e The raw browser event
34097         */
34098        "dragdrop" : true,
34099        /**
34100         * @event beforenodedrop
34101         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34102         * passed to handlers has the following properties:<br />
34103         * <ul style="padding:5px;padding-left:16px;">
34104         * <li>tree - The TreePanel</li>
34105         * <li>target - The node being targeted for the drop</li>
34106         * <li>data - The drag data from the drag source</li>
34107         * <li>point - The point of the drop - append, above or below</li>
34108         * <li>source - The drag source</li>
34109         * <li>rawEvent - Raw mouse event</li>
34110         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34111         * to be inserted by setting them on this object.</li>
34112         * <li>cancel - Set this to true to cancel the drop.</li>
34113         * </ul>
34114         * @param {Object} dropEvent
34115         */
34116        "beforenodedrop" : true,
34117        /**
34118         * @event nodedrop
34119         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34120         * passed to handlers has the following properties:<br />
34121         * <ul style="padding:5px;padding-left:16px;">
34122         * <li>tree - The TreePanel</li>
34123         * <li>target - The node being targeted for the drop</li>
34124         * <li>data - The drag data from the drag source</li>
34125         * <li>point - The point of the drop - append, above or below</li>
34126         * <li>source - The drag source</li>
34127         * <li>rawEvent - Raw mouse event</li>
34128         * <li>dropNode - Dropped node(s).</li>
34129         * </ul>
34130         * @param {Object} dropEvent
34131         */
34132        "nodedrop" : true,
34133         /**
34134         * @event nodedragover
34135         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34136         * passed to handlers has the following properties:<br />
34137         * <ul style="padding:5px;padding-left:16px;">
34138         * <li>tree - The TreePanel</li>
34139         * <li>target - The node being targeted for the drop</li>
34140         * <li>data - The drag data from the drag source</li>
34141         * <li>point - The point of the drop - append, above or below</li>
34142         * <li>source - The drag source</li>
34143         * <li>rawEvent - Raw mouse event</li>
34144         * <li>dropNode - Drop node(s) provided by the source.</li>
34145         * <li>cancel - Set this to true to signal drop not allowed.</li>
34146         * </ul>
34147         * @param {Object} dragOverEvent
34148         */
34149        "nodedragover" : true
34150         
34151     });
34152     if(this.singleExpand){
34153        this.on("beforeexpand", this.restrictExpand, this);
34154     }
34155     if (this.editor) {
34156         this.editor.tree = this;
34157         this.editor = Roo.factory(this.editor, Roo.tree);
34158     }
34159     
34160     if (this.selModel) {
34161         this.selModel = Roo.factory(this.selModel, Roo.tree);
34162     }
34163    
34164 };
34165 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34166     rootVisible : true,
34167     animate: Roo.enableFx,
34168     lines : true,
34169     enableDD : false,
34170     hlDrop : Roo.enableFx,
34171   
34172     renderer: false,
34173     
34174     rendererTip: false,
34175     // private
34176     restrictExpand : function(node){
34177         var p = node.parentNode;
34178         if(p){
34179             if(p.expandedChild && p.expandedChild.parentNode == p){
34180                 p.expandedChild.collapse();
34181             }
34182             p.expandedChild = node;
34183         }
34184     },
34185
34186     // private override
34187     setRootNode : function(node){
34188         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34189         if(!this.rootVisible){
34190             node.ui = new Roo.tree.RootTreeNodeUI(node);
34191         }
34192         return node;
34193     },
34194
34195     /**
34196      * Returns the container element for this TreePanel
34197      */
34198     getEl : function(){
34199         return this.el;
34200     },
34201
34202     /**
34203      * Returns the default TreeLoader for this TreePanel
34204      */
34205     getLoader : function(){
34206         return this.loader;
34207     },
34208
34209     /**
34210      * Expand all nodes
34211      */
34212     expandAll : function(){
34213         this.root.expand(true);
34214     },
34215
34216     /**
34217      * Collapse all nodes
34218      */
34219     collapseAll : function(){
34220         this.root.collapse(true);
34221     },
34222
34223     /**
34224      * Returns the selection model used by this TreePanel
34225      */
34226     getSelectionModel : function(){
34227         if(!this.selModel){
34228             this.selModel = new Roo.tree.DefaultSelectionModel();
34229         }
34230         return this.selModel;
34231     },
34232
34233     /**
34234      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34235      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34236      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34237      * @return {Array}
34238      */
34239     getChecked : function(a, startNode){
34240         startNode = startNode || this.root;
34241         var r = [];
34242         var f = function(){
34243             if(this.attributes.checked){
34244                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34245             }
34246         }
34247         startNode.cascade(f);
34248         return r;
34249     },
34250
34251     /**
34252      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34253      * @param {String} path
34254      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34255      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34256      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34257      */
34258     expandPath : function(path, attr, callback){
34259         attr = attr || "id";
34260         var keys = path.split(this.pathSeparator);
34261         var curNode = this.root;
34262         if(curNode.attributes[attr] != keys[1]){ // invalid root
34263             if(callback){
34264                 callback(false, null);
34265             }
34266             return;
34267         }
34268         var index = 1;
34269         var f = function(){
34270             if(++index == keys.length){
34271                 if(callback){
34272                     callback(true, curNode);
34273                 }
34274                 return;
34275             }
34276             var c = curNode.findChild(attr, keys[index]);
34277             if(!c){
34278                 if(callback){
34279                     callback(false, curNode);
34280                 }
34281                 return;
34282             }
34283             curNode = c;
34284             c.expand(false, false, f);
34285         };
34286         curNode.expand(false, false, f);
34287     },
34288
34289     /**
34290      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34291      * @param {String} path
34292      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34293      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34294      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34295      */
34296     selectPath : function(path, attr, callback){
34297         attr = attr || "id";
34298         var keys = path.split(this.pathSeparator);
34299         var v = keys.pop();
34300         if(keys.length > 0){
34301             var f = function(success, node){
34302                 if(success && node){
34303                     var n = node.findChild(attr, v);
34304                     if(n){
34305                         n.select();
34306                         if(callback){
34307                             callback(true, n);
34308                         }
34309                     }else if(callback){
34310                         callback(false, n);
34311                     }
34312                 }else{
34313                     if(callback){
34314                         callback(false, n);
34315                     }
34316                 }
34317             };
34318             this.expandPath(keys.join(this.pathSeparator), attr, f);
34319         }else{
34320             this.root.select();
34321             if(callback){
34322                 callback(true, this.root);
34323             }
34324         }
34325     },
34326
34327     getTreeEl : function(){
34328         return this.el;
34329     },
34330
34331     /**
34332      * Trigger rendering of this TreePanel
34333      */
34334     render : function(){
34335         if (this.innerCt) {
34336             return this; // stop it rendering more than once!!
34337         }
34338         
34339         this.innerCt = this.el.createChild({tag:"ul",
34340                cls:"x-tree-root-ct " +
34341                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34342
34343         if(this.containerScroll){
34344             Roo.dd.ScrollManager.register(this.el);
34345         }
34346         if((this.enableDD || this.enableDrop) && !this.dropZone){
34347            /**
34348             * The dropZone used by this tree if drop is enabled
34349             * @type Roo.tree.TreeDropZone
34350             */
34351              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34352                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34353            });
34354         }
34355         if((this.enableDD || this.enableDrag) && !this.dragZone){
34356            /**
34357             * The dragZone used by this tree if drag is enabled
34358             * @type Roo.tree.TreeDragZone
34359             */
34360             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34361                ddGroup: this.ddGroup || "TreeDD",
34362                scroll: this.ddScroll
34363            });
34364         }
34365         this.getSelectionModel().init(this);
34366         if (!this.root) {
34367             Roo.log("ROOT not set in tree");
34368             return this;
34369         }
34370         this.root.render();
34371         if(!this.rootVisible){
34372             this.root.renderChildren();
34373         }
34374         return this;
34375     }
34376 });/*
34377  * Based on:
34378  * Ext JS Library 1.1.1
34379  * Copyright(c) 2006-2007, Ext JS, LLC.
34380  *
34381  * Originally Released Under LGPL - original licence link has changed is not relivant.
34382  *
34383  * Fork - LGPL
34384  * <script type="text/javascript">
34385  */
34386  
34387
34388 /**
34389  * @class Roo.tree.DefaultSelectionModel
34390  * @extends Roo.util.Observable
34391  * The default single selection for a TreePanel.
34392  * @param {Object} cfg Configuration
34393  */
34394 Roo.tree.DefaultSelectionModel = function(cfg){
34395    this.selNode = null;
34396    
34397    
34398    
34399    this.addEvents({
34400        /**
34401         * @event selectionchange
34402         * Fires when the selected node changes
34403         * @param {DefaultSelectionModel} this
34404         * @param {TreeNode} node the new selection
34405         */
34406        "selectionchange" : true,
34407
34408        /**
34409         * @event beforeselect
34410         * Fires before the selected node changes, return false to cancel the change
34411         * @param {DefaultSelectionModel} this
34412         * @param {TreeNode} node the new selection
34413         * @param {TreeNode} node the old selection
34414         */
34415        "beforeselect" : true
34416    });
34417    
34418     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34419 };
34420
34421 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34422     init : function(tree){
34423         this.tree = tree;
34424         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34425         tree.on("click", this.onNodeClick, this);
34426     },
34427     
34428     onNodeClick : function(node, e){
34429         if (e.ctrlKey && this.selNode == node)  {
34430             this.unselect(node);
34431             return;
34432         }
34433         this.select(node);
34434     },
34435     
34436     /**
34437      * Select a node.
34438      * @param {TreeNode} node The node to select
34439      * @return {TreeNode} The selected node
34440      */
34441     select : function(node){
34442         var last = this.selNode;
34443         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34444             if(last){
34445                 last.ui.onSelectedChange(false);
34446             }
34447             this.selNode = node;
34448             node.ui.onSelectedChange(true);
34449             this.fireEvent("selectionchange", this, node, last);
34450         }
34451         return node;
34452     },
34453     
34454     /**
34455      * Deselect a node.
34456      * @param {TreeNode} node The node to unselect
34457      */
34458     unselect : function(node){
34459         if(this.selNode == node){
34460             this.clearSelections();
34461         }    
34462     },
34463     
34464     /**
34465      * Clear all selections
34466      */
34467     clearSelections : function(){
34468         var n = this.selNode;
34469         if(n){
34470             n.ui.onSelectedChange(false);
34471             this.selNode = null;
34472             this.fireEvent("selectionchange", this, null);
34473         }
34474         return n;
34475     },
34476     
34477     /**
34478      * Get the selected node
34479      * @return {TreeNode} The selected node
34480      */
34481     getSelectedNode : function(){
34482         return this.selNode;    
34483     },
34484     
34485     /**
34486      * Returns true if the node is selected
34487      * @param {TreeNode} node The node to check
34488      * @return {Boolean}
34489      */
34490     isSelected : function(node){
34491         return this.selNode == node;  
34492     },
34493
34494     /**
34495      * Selects the node above the selected node in the tree, intelligently walking the nodes
34496      * @return TreeNode The new selection
34497      */
34498     selectPrevious : function(){
34499         var s = this.selNode || this.lastSelNode;
34500         if(!s){
34501             return null;
34502         }
34503         var ps = s.previousSibling;
34504         if(ps){
34505             if(!ps.isExpanded() || ps.childNodes.length < 1){
34506                 return this.select(ps);
34507             } else{
34508                 var lc = ps.lastChild;
34509                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34510                     lc = lc.lastChild;
34511                 }
34512                 return this.select(lc);
34513             }
34514         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34515             return this.select(s.parentNode);
34516         }
34517         return null;
34518     },
34519
34520     /**
34521      * Selects the node above the selected node in the tree, intelligently walking the nodes
34522      * @return TreeNode The new selection
34523      */
34524     selectNext : function(){
34525         var s = this.selNode || this.lastSelNode;
34526         if(!s){
34527             return null;
34528         }
34529         if(s.firstChild && s.isExpanded()){
34530              return this.select(s.firstChild);
34531          }else if(s.nextSibling){
34532              return this.select(s.nextSibling);
34533          }else if(s.parentNode){
34534             var newS = null;
34535             s.parentNode.bubble(function(){
34536                 if(this.nextSibling){
34537                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34538                     return false;
34539                 }
34540             });
34541             return newS;
34542          }
34543         return null;
34544     },
34545
34546     onKeyDown : function(e){
34547         var s = this.selNode || this.lastSelNode;
34548         // undesirable, but required
34549         var sm = this;
34550         if(!s){
34551             return;
34552         }
34553         var k = e.getKey();
34554         switch(k){
34555              case e.DOWN:
34556                  e.stopEvent();
34557                  this.selectNext();
34558              break;
34559              case e.UP:
34560                  e.stopEvent();
34561                  this.selectPrevious();
34562              break;
34563              case e.RIGHT:
34564                  e.preventDefault();
34565                  if(s.hasChildNodes()){
34566                      if(!s.isExpanded()){
34567                          s.expand();
34568                      }else if(s.firstChild){
34569                          this.select(s.firstChild, e);
34570                      }
34571                  }
34572              break;
34573              case e.LEFT:
34574                  e.preventDefault();
34575                  if(s.hasChildNodes() && s.isExpanded()){
34576                      s.collapse();
34577                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34578                      this.select(s.parentNode, e);
34579                  }
34580              break;
34581         };
34582     }
34583 });
34584
34585 /**
34586  * @class Roo.tree.MultiSelectionModel
34587  * @extends Roo.util.Observable
34588  * Multi selection for a TreePanel.
34589  * @param {Object} cfg Configuration
34590  */
34591 Roo.tree.MultiSelectionModel = function(){
34592    this.selNodes = [];
34593    this.selMap = {};
34594    this.addEvents({
34595        /**
34596         * @event selectionchange
34597         * Fires when the selected nodes change
34598         * @param {MultiSelectionModel} this
34599         * @param {Array} nodes Array of the selected nodes
34600         */
34601        "selectionchange" : true
34602    });
34603    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34604    
34605 };
34606
34607 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34608     init : function(tree){
34609         this.tree = tree;
34610         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34611         tree.on("click", this.onNodeClick, this);
34612     },
34613     
34614     onNodeClick : function(node, e){
34615         this.select(node, e, e.ctrlKey);
34616     },
34617     
34618     /**
34619      * Select a node.
34620      * @param {TreeNode} node The node to select
34621      * @param {EventObject} e (optional) An event associated with the selection
34622      * @param {Boolean} keepExisting True to retain existing selections
34623      * @return {TreeNode} The selected node
34624      */
34625     select : function(node, e, keepExisting){
34626         if(keepExisting !== true){
34627             this.clearSelections(true);
34628         }
34629         if(this.isSelected(node)){
34630             this.lastSelNode = node;
34631             return node;
34632         }
34633         this.selNodes.push(node);
34634         this.selMap[node.id] = node;
34635         this.lastSelNode = node;
34636         node.ui.onSelectedChange(true);
34637         this.fireEvent("selectionchange", this, this.selNodes);
34638         return node;
34639     },
34640     
34641     /**
34642      * Deselect a node.
34643      * @param {TreeNode} node The node to unselect
34644      */
34645     unselect : function(node){
34646         if(this.selMap[node.id]){
34647             node.ui.onSelectedChange(false);
34648             var sn = this.selNodes;
34649             var index = -1;
34650             if(sn.indexOf){
34651                 index = sn.indexOf(node);
34652             }else{
34653                 for(var i = 0, len = sn.length; i < len; i++){
34654                     if(sn[i] == node){
34655                         index = i;
34656                         break;
34657                     }
34658                 }
34659             }
34660             if(index != -1){
34661                 this.selNodes.splice(index, 1);
34662             }
34663             delete this.selMap[node.id];
34664             this.fireEvent("selectionchange", this, this.selNodes);
34665         }
34666     },
34667     
34668     /**
34669      * Clear all selections
34670      */
34671     clearSelections : function(suppressEvent){
34672         var sn = this.selNodes;
34673         if(sn.length > 0){
34674             for(var i = 0, len = sn.length; i < len; i++){
34675                 sn[i].ui.onSelectedChange(false);
34676             }
34677             this.selNodes = [];
34678             this.selMap = {};
34679             if(suppressEvent !== true){
34680                 this.fireEvent("selectionchange", this, this.selNodes);
34681             }
34682         }
34683     },
34684     
34685     /**
34686      * Returns true if the node is selected
34687      * @param {TreeNode} node The node to check
34688      * @return {Boolean}
34689      */
34690     isSelected : function(node){
34691         return this.selMap[node.id] ? true : false;  
34692     },
34693     
34694     /**
34695      * Returns an array of the selected nodes
34696      * @return {Array}
34697      */
34698     getSelectedNodes : function(){
34699         return this.selNodes;    
34700     },
34701
34702     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34703
34704     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34705
34706     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34707 });/*
34708  * Based on:
34709  * Ext JS Library 1.1.1
34710  * Copyright(c) 2006-2007, Ext JS, LLC.
34711  *
34712  * Originally Released Under LGPL - original licence link has changed is not relivant.
34713  *
34714  * Fork - LGPL
34715  * <script type="text/javascript">
34716  */
34717  
34718 /**
34719  * @class Roo.tree.TreeNode
34720  * @extends Roo.data.Node
34721  * @cfg {String} text The text for this node
34722  * @cfg {Boolean} expanded true to start the node expanded
34723  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34724  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34725  * @cfg {Boolean} disabled true to start the node disabled
34726  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34727  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
34728  * @cfg {String} cls A css class to be added to the node
34729  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34730  * @cfg {String} href URL of the link used for the node (defaults to #)
34731  * @cfg {String} hrefTarget target frame for the link
34732  * @cfg {String} qtip An Ext QuickTip for the node
34733  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34734  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34735  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34736  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34737  * (defaults to undefined with no checkbox rendered)
34738  * @constructor
34739  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34740  */
34741 Roo.tree.TreeNode = function(attributes){
34742     attributes = attributes || {};
34743     if(typeof attributes == "string"){
34744         attributes = {text: attributes};
34745     }
34746     this.childrenRendered = false;
34747     this.rendered = false;
34748     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34749     this.expanded = attributes.expanded === true;
34750     this.isTarget = attributes.isTarget !== false;
34751     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34752     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34753
34754     /**
34755      * Read-only. The text for this node. To change it use setText().
34756      * @type String
34757      */
34758     this.text = attributes.text;
34759     /**
34760      * True if this node is disabled.
34761      * @type Boolean
34762      */
34763     this.disabled = attributes.disabled === true;
34764
34765     this.addEvents({
34766         /**
34767         * @event textchange
34768         * Fires when the text for this node is changed
34769         * @param {Node} this This node
34770         * @param {String} text The new text
34771         * @param {String} oldText The old text
34772         */
34773         "textchange" : true,
34774         /**
34775         * @event beforeexpand
34776         * Fires before this node is expanded, return false to cancel.
34777         * @param {Node} this This node
34778         * @param {Boolean} deep
34779         * @param {Boolean} anim
34780         */
34781         "beforeexpand" : true,
34782         /**
34783         * @event beforecollapse
34784         * Fires before this node is collapsed, return false to cancel.
34785         * @param {Node} this This node
34786         * @param {Boolean} deep
34787         * @param {Boolean} anim
34788         */
34789         "beforecollapse" : true,
34790         /**
34791         * @event expand
34792         * Fires when this node is expanded
34793         * @param {Node} this This node
34794         */
34795         "expand" : true,
34796         /**
34797         * @event disabledchange
34798         * Fires when the disabled status of this node changes
34799         * @param {Node} this This node
34800         * @param {Boolean} disabled
34801         */
34802         "disabledchange" : true,
34803         /**
34804         * @event collapse
34805         * Fires when this node is collapsed
34806         * @param {Node} this This node
34807         */
34808         "collapse" : true,
34809         /**
34810         * @event beforeclick
34811         * Fires before click processing. Return false to cancel the default action.
34812         * @param {Node} this This node
34813         * @param {Roo.EventObject} e The event object
34814         */
34815         "beforeclick":true,
34816         /**
34817         * @event checkchange
34818         * Fires when a node with a checkbox's checked property changes
34819         * @param {Node} this This node
34820         * @param {Boolean} checked
34821         */
34822         "checkchange":true,
34823         /**
34824         * @event click
34825         * Fires when this node is clicked
34826         * @param {Node} this This node
34827         * @param {Roo.EventObject} e The event object
34828         */
34829         "click":true,
34830         /**
34831         * @event dblclick
34832         * Fires when this node is double clicked
34833         * @param {Node} this This node
34834         * @param {Roo.EventObject} e The event object
34835         */
34836         "dblclick":true,
34837         /**
34838         * @event contextmenu
34839         * Fires when this node is right clicked
34840         * @param {Node} this This node
34841         * @param {Roo.EventObject} e The event object
34842         */
34843         "contextmenu":true,
34844         /**
34845         * @event beforechildrenrendered
34846         * Fires right before the child nodes for this node are rendered
34847         * @param {Node} this This node
34848         */
34849         "beforechildrenrendered":true
34850     });
34851
34852     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
34853
34854     /**
34855      * Read-only. The UI for this node
34856      * @type TreeNodeUI
34857      */
34858     this.ui = new uiClass(this);
34859     
34860     // finally support items[]
34861     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
34862         return;
34863     }
34864     
34865     
34866     Roo.each(this.attributes.items, function(c) {
34867         this.appendChild(Roo.factory(c,Roo.Tree));
34868     }, this);
34869     delete this.attributes.items;
34870     
34871     
34872     
34873 };
34874 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
34875     preventHScroll: true,
34876     /**
34877      * Returns true if this node is expanded
34878      * @return {Boolean}
34879      */
34880     isExpanded : function(){
34881         return this.expanded;
34882     },
34883
34884     /**
34885      * Returns the UI object for this node
34886      * @return {TreeNodeUI}
34887      */
34888     getUI : function(){
34889         return this.ui;
34890     },
34891
34892     // private override
34893     setFirstChild : function(node){
34894         var of = this.firstChild;
34895         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
34896         if(this.childrenRendered && of && node != of){
34897             of.renderIndent(true, true);
34898         }
34899         if(this.rendered){
34900             this.renderIndent(true, true);
34901         }
34902     },
34903
34904     // private override
34905     setLastChild : function(node){
34906         var ol = this.lastChild;
34907         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
34908         if(this.childrenRendered && ol && node != ol){
34909             ol.renderIndent(true, true);
34910         }
34911         if(this.rendered){
34912             this.renderIndent(true, true);
34913         }
34914     },
34915
34916     // these methods are overridden to provide lazy rendering support
34917     // private override
34918     appendChild : function()
34919     {
34920         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
34921         if(node && this.childrenRendered){
34922             node.render();
34923         }
34924         this.ui.updateExpandIcon();
34925         return node;
34926     },
34927
34928     // private override
34929     removeChild : function(node){
34930         this.ownerTree.getSelectionModel().unselect(node);
34931         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
34932         // if it's been rendered remove dom node
34933         if(this.childrenRendered){
34934             node.ui.remove();
34935         }
34936         if(this.childNodes.length < 1){
34937             this.collapse(false, false);
34938         }else{
34939             this.ui.updateExpandIcon();
34940         }
34941         if(!this.firstChild) {
34942             this.childrenRendered = false;
34943         }
34944         return node;
34945     },
34946
34947     // private override
34948     insertBefore : function(node, refNode){
34949         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
34950         if(newNode && refNode && this.childrenRendered){
34951             node.render();
34952         }
34953         this.ui.updateExpandIcon();
34954         return newNode;
34955     },
34956
34957     /**
34958      * Sets the text for this node
34959      * @param {String} text
34960      */
34961     setText : function(text){
34962         var oldText = this.text;
34963         this.text = text;
34964         this.attributes.text = text;
34965         if(this.rendered){ // event without subscribing
34966             this.ui.onTextChange(this, text, oldText);
34967         }
34968         this.fireEvent("textchange", this, text, oldText);
34969     },
34970
34971     /**
34972      * Triggers selection of this node
34973      */
34974     select : function(){
34975         this.getOwnerTree().getSelectionModel().select(this);
34976     },
34977
34978     /**
34979      * Triggers deselection of this node
34980      */
34981     unselect : function(){
34982         this.getOwnerTree().getSelectionModel().unselect(this);
34983     },
34984
34985     /**
34986      * Returns true if this node is selected
34987      * @return {Boolean}
34988      */
34989     isSelected : function(){
34990         return this.getOwnerTree().getSelectionModel().isSelected(this);
34991     },
34992
34993     /**
34994      * Expand this node.
34995      * @param {Boolean} deep (optional) True to expand all children as well
34996      * @param {Boolean} anim (optional) false to cancel the default animation
34997      * @param {Function} callback (optional) A callback to be called when
34998      * expanding this node completes (does not wait for deep expand to complete).
34999      * Called with 1 parameter, this node.
35000      */
35001     expand : function(deep, anim, callback){
35002         if(!this.expanded){
35003             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35004                 return;
35005             }
35006             if(!this.childrenRendered){
35007                 this.renderChildren();
35008             }
35009             this.expanded = true;
35010             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
35011                 this.ui.animExpand(function(){
35012                     this.fireEvent("expand", this);
35013                     if(typeof callback == "function"){
35014                         callback(this);
35015                     }
35016                     if(deep === true){
35017                         this.expandChildNodes(true);
35018                     }
35019                 }.createDelegate(this));
35020                 return;
35021             }else{
35022                 this.ui.expand();
35023                 this.fireEvent("expand", this);
35024                 if(typeof callback == "function"){
35025                     callback(this);
35026                 }
35027             }
35028         }else{
35029            if(typeof callback == "function"){
35030                callback(this);
35031            }
35032         }
35033         if(deep === true){
35034             this.expandChildNodes(true);
35035         }
35036     },
35037
35038     isHiddenRoot : function(){
35039         return this.isRoot && !this.getOwnerTree().rootVisible;
35040     },
35041
35042     /**
35043      * Collapse this node.
35044      * @param {Boolean} deep (optional) True to collapse all children as well
35045      * @param {Boolean} anim (optional) false to cancel the default animation
35046      */
35047     collapse : function(deep, anim){
35048         if(this.expanded && !this.isHiddenRoot()){
35049             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35050                 return;
35051             }
35052             this.expanded = false;
35053             if((this.getOwnerTree().animate && anim !== false) || anim){
35054                 this.ui.animCollapse(function(){
35055                     this.fireEvent("collapse", this);
35056                     if(deep === true){
35057                         this.collapseChildNodes(true);
35058                     }
35059                 }.createDelegate(this));
35060                 return;
35061             }else{
35062                 this.ui.collapse();
35063                 this.fireEvent("collapse", this);
35064             }
35065         }
35066         if(deep === true){
35067             var cs = this.childNodes;
35068             for(var i = 0, len = cs.length; i < len; i++) {
35069                 cs[i].collapse(true, false);
35070             }
35071         }
35072     },
35073
35074     // private
35075     delayedExpand : function(delay){
35076         if(!this.expandProcId){
35077             this.expandProcId = this.expand.defer(delay, this);
35078         }
35079     },
35080
35081     // private
35082     cancelExpand : function(){
35083         if(this.expandProcId){
35084             clearTimeout(this.expandProcId);
35085         }
35086         this.expandProcId = false;
35087     },
35088
35089     /**
35090      * Toggles expanded/collapsed state of the node
35091      */
35092     toggle : function(){
35093         if(this.expanded){
35094             this.collapse();
35095         }else{
35096             this.expand();
35097         }
35098     },
35099
35100     /**
35101      * Ensures all parent nodes are expanded
35102      */
35103     ensureVisible : function(callback){
35104         var tree = this.getOwnerTree();
35105         tree.expandPath(this.parentNode.getPath(), false, function(){
35106             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35107             Roo.callback(callback);
35108         }.createDelegate(this));
35109     },
35110
35111     /**
35112      * Expand all child nodes
35113      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35114      */
35115     expandChildNodes : function(deep){
35116         var cs = this.childNodes;
35117         for(var i = 0, len = cs.length; i < len; i++) {
35118                 cs[i].expand(deep);
35119         }
35120     },
35121
35122     /**
35123      * Collapse all child nodes
35124      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35125      */
35126     collapseChildNodes : function(deep){
35127         var cs = this.childNodes;
35128         for(var i = 0, len = cs.length; i < len; i++) {
35129                 cs[i].collapse(deep);
35130         }
35131     },
35132
35133     /**
35134      * Disables this node
35135      */
35136     disable : function(){
35137         this.disabled = true;
35138         this.unselect();
35139         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35140             this.ui.onDisableChange(this, true);
35141         }
35142         this.fireEvent("disabledchange", this, true);
35143     },
35144
35145     /**
35146      * Enables this node
35147      */
35148     enable : function(){
35149         this.disabled = false;
35150         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35151             this.ui.onDisableChange(this, false);
35152         }
35153         this.fireEvent("disabledchange", this, false);
35154     },
35155
35156     // private
35157     renderChildren : function(suppressEvent){
35158         if(suppressEvent !== false){
35159             this.fireEvent("beforechildrenrendered", this);
35160         }
35161         var cs = this.childNodes;
35162         for(var i = 0, len = cs.length; i < len; i++){
35163             cs[i].render(true);
35164         }
35165         this.childrenRendered = true;
35166     },
35167
35168     // private
35169     sort : function(fn, scope){
35170         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35171         if(this.childrenRendered){
35172             var cs = this.childNodes;
35173             for(var i = 0, len = cs.length; i < len; i++){
35174                 cs[i].render(true);
35175             }
35176         }
35177     },
35178
35179     // private
35180     render : function(bulkRender){
35181         this.ui.render(bulkRender);
35182         if(!this.rendered){
35183             this.rendered = true;
35184             if(this.expanded){
35185                 this.expanded = false;
35186                 this.expand(false, false);
35187             }
35188         }
35189     },
35190
35191     // private
35192     renderIndent : function(deep, refresh){
35193         if(refresh){
35194             this.ui.childIndent = null;
35195         }
35196         this.ui.renderIndent();
35197         if(deep === true && this.childrenRendered){
35198             var cs = this.childNodes;
35199             for(var i = 0, len = cs.length; i < len; i++){
35200                 cs[i].renderIndent(true, refresh);
35201             }
35202         }
35203     }
35204 });/*
35205  * Based on:
35206  * Ext JS Library 1.1.1
35207  * Copyright(c) 2006-2007, Ext JS, LLC.
35208  *
35209  * Originally Released Under LGPL - original licence link has changed is not relivant.
35210  *
35211  * Fork - LGPL
35212  * <script type="text/javascript">
35213  */
35214  
35215 /**
35216  * @class Roo.tree.AsyncTreeNode
35217  * @extends Roo.tree.TreeNode
35218  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35219  * @constructor
35220  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35221  */
35222  Roo.tree.AsyncTreeNode = function(config){
35223     this.loaded = false;
35224     this.loading = false;
35225     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35226     /**
35227     * @event beforeload
35228     * Fires before this node is loaded, return false to cancel
35229     * @param {Node} this This node
35230     */
35231     this.addEvents({'beforeload':true, 'load': true});
35232     /**
35233     * @event load
35234     * Fires when this node is loaded
35235     * @param {Node} this This node
35236     */
35237     /**
35238      * The loader used by this node (defaults to using the tree's defined loader)
35239      * @type TreeLoader
35240      * @property loader
35241      */
35242 };
35243 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35244     expand : function(deep, anim, callback){
35245         if(this.loading){ // if an async load is already running, waiting til it's done
35246             var timer;
35247             var f = function(){
35248                 if(!this.loading){ // done loading
35249                     clearInterval(timer);
35250                     this.expand(deep, anim, callback);
35251                 }
35252             }.createDelegate(this);
35253             timer = setInterval(f, 200);
35254             return;
35255         }
35256         if(!this.loaded){
35257             if(this.fireEvent("beforeload", this) === false){
35258                 return;
35259             }
35260             this.loading = true;
35261             this.ui.beforeLoad(this);
35262             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35263             if(loader){
35264                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35265                 return;
35266             }
35267         }
35268         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35269     },
35270     
35271     /**
35272      * Returns true if this node is currently loading
35273      * @return {Boolean}
35274      */
35275     isLoading : function(){
35276         return this.loading;  
35277     },
35278     
35279     loadComplete : function(deep, anim, callback){
35280         this.loading = false;
35281         this.loaded = true;
35282         this.ui.afterLoad(this);
35283         this.fireEvent("load", this);
35284         this.expand(deep, anim, callback);
35285     },
35286     
35287     /**
35288      * Returns true if this node has been loaded
35289      * @return {Boolean}
35290      */
35291     isLoaded : function(){
35292         return this.loaded;
35293     },
35294     
35295     hasChildNodes : function(){
35296         if(!this.isLeaf() && !this.loaded){
35297             return true;
35298         }else{
35299             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35300         }
35301     },
35302
35303     /**
35304      * Trigger a reload for this node
35305      * @param {Function} callback
35306      */
35307     reload : function(callback){
35308         this.collapse(false, false);
35309         while(this.firstChild){
35310             this.removeChild(this.firstChild);
35311         }
35312         this.childrenRendered = false;
35313         this.loaded = false;
35314         if(this.isHiddenRoot()){
35315             this.expanded = false;
35316         }
35317         this.expand(false, false, callback);
35318     }
35319 });/*
35320  * Based on:
35321  * Ext JS Library 1.1.1
35322  * Copyright(c) 2006-2007, Ext JS, LLC.
35323  *
35324  * Originally Released Under LGPL - original licence link has changed is not relivant.
35325  *
35326  * Fork - LGPL
35327  * <script type="text/javascript">
35328  */
35329  
35330 /**
35331  * @class Roo.tree.TreeNodeUI
35332  * @constructor
35333  * @param {Object} node The node to render
35334  * The TreeNode UI implementation is separate from the
35335  * tree implementation. Unless you are customizing the tree UI,
35336  * you should never have to use this directly.
35337  */
35338 Roo.tree.TreeNodeUI = function(node){
35339     this.node = node;
35340     this.rendered = false;
35341     this.animating = false;
35342     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35343 };
35344
35345 Roo.tree.TreeNodeUI.prototype = {
35346     removeChild : function(node){
35347         if(this.rendered){
35348             this.ctNode.removeChild(node.ui.getEl());
35349         }
35350     },
35351
35352     beforeLoad : function(){
35353          this.addClass("x-tree-node-loading");
35354     },
35355
35356     afterLoad : function(){
35357          this.removeClass("x-tree-node-loading");
35358     },
35359
35360     onTextChange : function(node, text, oldText){
35361         if(this.rendered){
35362             this.textNode.innerHTML = text;
35363         }
35364     },
35365
35366     onDisableChange : function(node, state){
35367         this.disabled = state;
35368         if(state){
35369             this.addClass("x-tree-node-disabled");
35370         }else{
35371             this.removeClass("x-tree-node-disabled");
35372         }
35373     },
35374
35375     onSelectedChange : function(state){
35376         if(state){
35377             this.focus();
35378             this.addClass("x-tree-selected");
35379         }else{
35380             //this.blur();
35381             this.removeClass("x-tree-selected");
35382         }
35383     },
35384
35385     onMove : function(tree, node, oldParent, newParent, index, refNode){
35386         this.childIndent = null;
35387         if(this.rendered){
35388             var targetNode = newParent.ui.getContainer();
35389             if(!targetNode){//target not rendered
35390                 this.holder = document.createElement("div");
35391                 this.holder.appendChild(this.wrap);
35392                 return;
35393             }
35394             var insertBefore = refNode ? refNode.ui.getEl() : null;
35395             if(insertBefore){
35396                 targetNode.insertBefore(this.wrap, insertBefore);
35397             }else{
35398                 targetNode.appendChild(this.wrap);
35399             }
35400             this.node.renderIndent(true);
35401         }
35402     },
35403
35404     addClass : function(cls){
35405         if(this.elNode){
35406             Roo.fly(this.elNode).addClass(cls);
35407         }
35408     },
35409
35410     removeClass : function(cls){
35411         if(this.elNode){
35412             Roo.fly(this.elNode).removeClass(cls);
35413         }
35414     },
35415
35416     remove : function(){
35417         if(this.rendered){
35418             this.holder = document.createElement("div");
35419             this.holder.appendChild(this.wrap);
35420         }
35421     },
35422
35423     fireEvent : function(){
35424         return this.node.fireEvent.apply(this.node, arguments);
35425     },
35426
35427     initEvents : function(){
35428         this.node.on("move", this.onMove, this);
35429         var E = Roo.EventManager;
35430         var a = this.anchor;
35431
35432         var el = Roo.fly(a, '_treeui');
35433
35434         if(Roo.isOpera){ // opera render bug ignores the CSS
35435             el.setStyle("text-decoration", "none");
35436         }
35437
35438         el.on("click", this.onClick, this);
35439         el.on("dblclick", this.onDblClick, this);
35440
35441         if(this.checkbox){
35442             Roo.EventManager.on(this.checkbox,
35443                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35444         }
35445
35446         el.on("contextmenu", this.onContextMenu, this);
35447
35448         var icon = Roo.fly(this.iconNode);
35449         icon.on("click", this.onClick, this);
35450         icon.on("dblclick", this.onDblClick, this);
35451         icon.on("contextmenu", this.onContextMenu, this);
35452         E.on(this.ecNode, "click", this.ecClick, this, true);
35453
35454         if(this.node.disabled){
35455             this.addClass("x-tree-node-disabled");
35456         }
35457         if(this.node.hidden){
35458             this.addClass("x-tree-node-disabled");
35459         }
35460         var ot = this.node.getOwnerTree();
35461         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
35462         if(dd && (!this.node.isRoot || ot.rootVisible)){
35463             Roo.dd.Registry.register(this.elNode, {
35464                 node: this.node,
35465                 handles: this.getDDHandles(),
35466                 isHandle: false
35467             });
35468         }
35469     },
35470
35471     getDDHandles : function(){
35472         return [this.iconNode, this.textNode];
35473     },
35474
35475     hide : function(){
35476         if(this.rendered){
35477             this.wrap.style.display = "none";
35478         }
35479     },
35480
35481     show : function(){
35482         if(this.rendered){
35483             this.wrap.style.display = "";
35484         }
35485     },
35486
35487     onContextMenu : function(e){
35488         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35489             e.preventDefault();
35490             this.focus();
35491             this.fireEvent("contextmenu", this.node, e);
35492         }
35493     },
35494
35495     onClick : function(e){
35496         if(this.dropping){
35497             e.stopEvent();
35498             return;
35499         }
35500         if(this.fireEvent("beforeclick", this.node, e) !== false){
35501             if(!this.disabled && this.node.attributes.href){
35502                 this.fireEvent("click", this.node, e);
35503                 return;
35504             }
35505             e.preventDefault();
35506             if(this.disabled){
35507                 return;
35508             }
35509
35510             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35511                 this.node.toggle();
35512             }
35513
35514             this.fireEvent("click", this.node, e);
35515         }else{
35516             e.stopEvent();
35517         }
35518     },
35519
35520     onDblClick : function(e){
35521         e.preventDefault();
35522         if(this.disabled){
35523             return;
35524         }
35525         if(this.checkbox){
35526             this.toggleCheck();
35527         }
35528         if(!this.animating && this.node.hasChildNodes()){
35529             this.node.toggle();
35530         }
35531         this.fireEvent("dblclick", this.node, e);
35532     },
35533
35534     onCheckChange : function(){
35535         var checked = this.checkbox.checked;
35536         this.node.attributes.checked = checked;
35537         this.fireEvent('checkchange', this.node, checked);
35538     },
35539
35540     ecClick : function(e){
35541         if(!this.animating && this.node.hasChildNodes()){
35542             this.node.toggle();
35543         }
35544     },
35545
35546     startDrop : function(){
35547         this.dropping = true;
35548     },
35549
35550     // delayed drop so the click event doesn't get fired on a drop
35551     endDrop : function(){
35552        setTimeout(function(){
35553            this.dropping = false;
35554        }.createDelegate(this), 50);
35555     },
35556
35557     expand : function(){
35558         this.updateExpandIcon();
35559         this.ctNode.style.display = "";
35560     },
35561
35562     focus : function(){
35563         if(!this.node.preventHScroll){
35564             try{this.anchor.focus();
35565             }catch(e){}
35566         }else if(!Roo.isIE){
35567             try{
35568                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35569                 var l = noscroll.scrollLeft;
35570                 this.anchor.focus();
35571                 noscroll.scrollLeft = l;
35572             }catch(e){}
35573         }
35574     },
35575
35576     toggleCheck : function(value){
35577         var cb = this.checkbox;
35578         if(cb){
35579             cb.checked = (value === undefined ? !cb.checked : value);
35580         }
35581     },
35582
35583     blur : function(){
35584         try{
35585             this.anchor.blur();
35586         }catch(e){}
35587     },
35588
35589     animExpand : function(callback){
35590         var ct = Roo.get(this.ctNode);
35591         ct.stopFx();
35592         if(!this.node.hasChildNodes()){
35593             this.updateExpandIcon();
35594             this.ctNode.style.display = "";
35595             Roo.callback(callback);
35596             return;
35597         }
35598         this.animating = true;
35599         this.updateExpandIcon();
35600
35601         ct.slideIn('t', {
35602            callback : function(){
35603                this.animating = false;
35604                Roo.callback(callback);
35605             },
35606             scope: this,
35607             duration: this.node.ownerTree.duration || .25
35608         });
35609     },
35610
35611     highlight : function(){
35612         var tree = this.node.getOwnerTree();
35613         Roo.fly(this.wrap).highlight(
35614             tree.hlColor || "C3DAF9",
35615             {endColor: tree.hlBaseColor}
35616         );
35617     },
35618
35619     collapse : function(){
35620         this.updateExpandIcon();
35621         this.ctNode.style.display = "none";
35622     },
35623
35624     animCollapse : function(callback){
35625         var ct = Roo.get(this.ctNode);
35626         ct.enableDisplayMode('block');
35627         ct.stopFx();
35628
35629         this.animating = true;
35630         this.updateExpandIcon();
35631
35632         ct.slideOut('t', {
35633             callback : function(){
35634                this.animating = false;
35635                Roo.callback(callback);
35636             },
35637             scope: this,
35638             duration: this.node.ownerTree.duration || .25
35639         });
35640     },
35641
35642     getContainer : function(){
35643         return this.ctNode;
35644     },
35645
35646     getEl : function(){
35647         return this.wrap;
35648     },
35649
35650     appendDDGhost : function(ghostNode){
35651         ghostNode.appendChild(this.elNode.cloneNode(true));
35652     },
35653
35654     getDDRepairXY : function(){
35655         return Roo.lib.Dom.getXY(this.iconNode);
35656     },
35657
35658     onRender : function(){
35659         this.render();
35660     },
35661
35662     render : function(bulkRender){
35663         var n = this.node, a = n.attributes;
35664         var targetNode = n.parentNode ?
35665               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35666
35667         if(!this.rendered){
35668             this.rendered = true;
35669
35670             this.renderElements(n, a, targetNode, bulkRender);
35671
35672             if(a.qtip){
35673                if(this.textNode.setAttributeNS){
35674                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35675                    if(a.qtipTitle){
35676                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35677                    }
35678                }else{
35679                    this.textNode.setAttribute("ext:qtip", a.qtip);
35680                    if(a.qtipTitle){
35681                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35682                    }
35683                }
35684             }else if(a.qtipCfg){
35685                 a.qtipCfg.target = Roo.id(this.textNode);
35686                 Roo.QuickTips.register(a.qtipCfg);
35687             }
35688             this.initEvents();
35689             if(!this.node.expanded){
35690                 this.updateExpandIcon();
35691             }
35692         }else{
35693             if(bulkRender === true) {
35694                 targetNode.appendChild(this.wrap);
35695             }
35696         }
35697     },
35698
35699     renderElements : function(n, a, targetNode, bulkRender)
35700     {
35701         // add some indent caching, this helps performance when rendering a large tree
35702         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35703         var t = n.getOwnerTree();
35704         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35705         if (typeof(n.attributes.html) != 'undefined') {
35706             txt = n.attributes.html;
35707         }
35708         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
35709         var cb = typeof a.checked == 'boolean';
35710         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35711         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35712             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35713             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35714             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35715             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35716             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35717              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35718                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35719             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35720             "</li>"];
35721
35722         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35723             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35724                                 n.nextSibling.ui.getEl(), buf.join(""));
35725         }else{
35726             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35727         }
35728
35729         this.elNode = this.wrap.childNodes[0];
35730         this.ctNode = this.wrap.childNodes[1];
35731         var cs = this.elNode.childNodes;
35732         this.indentNode = cs[0];
35733         this.ecNode = cs[1];
35734         this.iconNode = cs[2];
35735         var index = 3;
35736         if(cb){
35737             this.checkbox = cs[3];
35738             index++;
35739         }
35740         this.anchor = cs[index];
35741         this.textNode = cs[index].firstChild;
35742     },
35743
35744     getAnchor : function(){
35745         return this.anchor;
35746     },
35747
35748     getTextEl : function(){
35749         return this.textNode;
35750     },
35751
35752     getIconEl : function(){
35753         return this.iconNode;
35754     },
35755
35756     isChecked : function(){
35757         return this.checkbox ? this.checkbox.checked : false;
35758     },
35759
35760     updateExpandIcon : function(){
35761         if(this.rendered){
35762             var n = this.node, c1, c2;
35763             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35764             var hasChild = n.hasChildNodes();
35765             if(hasChild){
35766                 if(n.expanded){
35767                     cls += "-minus";
35768                     c1 = "x-tree-node-collapsed";
35769                     c2 = "x-tree-node-expanded";
35770                 }else{
35771                     cls += "-plus";
35772                     c1 = "x-tree-node-expanded";
35773                     c2 = "x-tree-node-collapsed";
35774                 }
35775                 if(this.wasLeaf){
35776                     this.removeClass("x-tree-node-leaf");
35777                     this.wasLeaf = false;
35778                 }
35779                 if(this.c1 != c1 || this.c2 != c2){
35780                     Roo.fly(this.elNode).replaceClass(c1, c2);
35781                     this.c1 = c1; this.c2 = c2;
35782                 }
35783             }else{
35784                 // this changes non-leafs into leafs if they have no children.
35785                 // it's not very rational behaviour..
35786                 
35787                 if(!this.wasLeaf && this.node.leaf){
35788                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35789                     delete this.c1;
35790                     delete this.c2;
35791                     this.wasLeaf = true;
35792                 }
35793             }
35794             var ecc = "x-tree-ec-icon "+cls;
35795             if(this.ecc != ecc){
35796                 this.ecNode.className = ecc;
35797                 this.ecc = ecc;
35798             }
35799         }
35800     },
35801
35802     getChildIndent : function(){
35803         if(!this.childIndent){
35804             var buf = [];
35805             var p = this.node;
35806             while(p){
35807                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35808                     if(!p.isLast()) {
35809                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35810                     } else {
35811                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35812                     }
35813                 }
35814                 p = p.parentNode;
35815             }
35816             this.childIndent = buf.join("");
35817         }
35818         return this.childIndent;
35819     },
35820
35821     renderIndent : function(){
35822         if(this.rendered){
35823             var indent = "";
35824             var p = this.node.parentNode;
35825             if(p){
35826                 indent = p.ui.getChildIndent();
35827             }
35828             if(this.indentMarkup != indent){ // don't rerender if not required
35829                 this.indentNode.innerHTML = indent;
35830                 this.indentMarkup = indent;
35831             }
35832             this.updateExpandIcon();
35833         }
35834     }
35835 };
35836
35837 Roo.tree.RootTreeNodeUI = function(){
35838     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
35839 };
35840 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
35841     render : function(){
35842         if(!this.rendered){
35843             var targetNode = this.node.ownerTree.innerCt.dom;
35844             this.node.expanded = true;
35845             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
35846             this.wrap = this.ctNode = targetNode.firstChild;
35847         }
35848     },
35849     collapse : function(){
35850     },
35851     expand : function(){
35852     }
35853 });/*
35854  * Based on:
35855  * Ext JS Library 1.1.1
35856  * Copyright(c) 2006-2007, Ext JS, LLC.
35857  *
35858  * Originally Released Under LGPL - original licence link has changed is not relivant.
35859  *
35860  * Fork - LGPL
35861  * <script type="text/javascript">
35862  */
35863 /**
35864  * @class Roo.tree.TreeLoader
35865  * @extends Roo.util.Observable
35866  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
35867  * nodes from a specified URL. The response must be a javascript Array definition
35868  * who's elements are node definition objects. eg:
35869  * <pre><code>
35870 {  success : true,
35871    data :      [
35872    
35873     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
35874     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
35875     ]
35876 }
35877
35878
35879 </code></pre>
35880  * <br><br>
35881  * The old style respose with just an array is still supported, but not recommended.
35882  * <br><br>
35883  *
35884  * A server request is sent, and child nodes are loaded only when a node is expanded.
35885  * The loading node's id is passed to the server under the parameter name "node" to
35886  * enable the server to produce the correct child nodes.
35887  * <br><br>
35888  * To pass extra parameters, an event handler may be attached to the "beforeload"
35889  * event, and the parameters specified in the TreeLoader's baseParams property:
35890  * <pre><code>
35891     myTreeLoader.on("beforeload", function(treeLoader, node) {
35892         this.baseParams.category = node.attributes.category;
35893     }, this);
35894 </code></pre><
35895  * This would pass an HTTP parameter called "category" to the server containing
35896  * the value of the Node's "category" attribute.
35897  * @constructor
35898  * Creates a new Treeloader.
35899  * @param {Object} config A config object containing config properties.
35900  */
35901 Roo.tree.TreeLoader = function(config){
35902     this.baseParams = {};
35903     this.requestMethod = "POST";
35904     Roo.apply(this, config);
35905
35906     this.addEvents({
35907     
35908         /**
35909          * @event beforeload
35910          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
35911          * @param {Object} This TreeLoader object.
35912          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35913          * @param {Object} callback The callback function specified in the {@link #load} call.
35914          */
35915         beforeload : true,
35916         /**
35917          * @event load
35918          * Fires when the node has been successfuly loaded.
35919          * @param {Object} This TreeLoader object.
35920          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35921          * @param {Object} response The response object containing the data from the server.
35922          */
35923         load : true,
35924         /**
35925          * @event loadexception
35926          * Fires if the network request failed.
35927          * @param {Object} This TreeLoader object.
35928          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35929          * @param {Object} response The response object containing the data from the server.
35930          */
35931         loadexception : true,
35932         /**
35933          * @event create
35934          * Fires before a node is created, enabling you to return custom Node types 
35935          * @param {Object} This TreeLoader object.
35936          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
35937          */
35938         create : true
35939     });
35940
35941     Roo.tree.TreeLoader.superclass.constructor.call(this);
35942 };
35943
35944 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
35945     /**
35946     * @cfg {String} dataUrl The URL from which to request a Json string which
35947     * specifies an array of node definition object representing the child nodes
35948     * to be loaded.
35949     */
35950     /**
35951     * @cfg {String} requestMethod either GET or POST
35952     * defaults to POST (due to BC)
35953     * to be loaded.
35954     */
35955     /**
35956     * @cfg {Object} baseParams (optional) An object containing properties which
35957     * specify HTTP parameters to be passed to each request for child nodes.
35958     */
35959     /**
35960     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
35961     * created by this loader. If the attributes sent by the server have an attribute in this object,
35962     * they take priority.
35963     */
35964     /**
35965     * @cfg {Object} uiProviders (optional) An object containing properties which
35966     * 
35967     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
35968     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
35969     * <i>uiProvider</i> attribute of a returned child node is a string rather
35970     * than a reference to a TreeNodeUI implementation, this that string value
35971     * is used as a property name in the uiProviders object. You can define the provider named
35972     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
35973     */
35974     uiProviders : {},
35975
35976     /**
35977     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
35978     * child nodes before loading.
35979     */
35980     clearOnLoad : true,
35981
35982     /**
35983     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
35984     * property on loading, rather than expecting an array. (eg. more compatible to a standard
35985     * Grid query { data : [ .....] }
35986     */
35987     
35988     root : false,
35989      /**
35990     * @cfg {String} queryParam (optional) 
35991     * Name of the query as it will be passed on the querystring (defaults to 'node')
35992     * eg. the request will be ?node=[id]
35993     */
35994     
35995     
35996     queryParam: false,
35997     
35998     /**
35999      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36000      * This is called automatically when a node is expanded, but may be used to reload
36001      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36002      * @param {Roo.tree.TreeNode} node
36003      * @param {Function} callback
36004      */
36005     load : function(node, callback){
36006         if(this.clearOnLoad){
36007             while(node.firstChild){
36008                 node.removeChild(node.firstChild);
36009             }
36010         }
36011         if(node.attributes.children){ // preloaded json children
36012             var cs = node.attributes.children;
36013             for(var i = 0, len = cs.length; i < len; i++){
36014                 node.appendChild(this.createNode(cs[i]));
36015             }
36016             if(typeof callback == "function"){
36017                 callback();
36018             }
36019         }else if(this.dataUrl){
36020             this.requestData(node, callback);
36021         }
36022     },
36023
36024     getParams: function(node){
36025         var buf = [], bp = this.baseParams;
36026         for(var key in bp){
36027             if(typeof bp[key] != "function"){
36028                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36029             }
36030         }
36031         var n = this.queryParam === false ? 'node' : this.queryParam;
36032         buf.push(n + "=", encodeURIComponent(node.id));
36033         return buf.join("");
36034     },
36035
36036     requestData : function(node, callback){
36037         if(this.fireEvent("beforeload", this, node, callback) !== false){
36038             this.transId = Roo.Ajax.request({
36039                 method:this.requestMethod,
36040                 url: this.dataUrl||this.url,
36041                 success: this.handleResponse,
36042                 failure: this.handleFailure,
36043                 scope: this,
36044                 argument: {callback: callback, node: node},
36045                 params: this.getParams(node)
36046             });
36047         }else{
36048             // if the load is cancelled, make sure we notify
36049             // the node that we are done
36050             if(typeof callback == "function"){
36051                 callback();
36052             }
36053         }
36054     },
36055
36056     isLoading : function(){
36057         return this.transId ? true : false;
36058     },
36059
36060     abort : function(){
36061         if(this.isLoading()){
36062             Roo.Ajax.abort(this.transId);
36063         }
36064     },
36065
36066     // private
36067     createNode : function(attr)
36068     {
36069         // apply baseAttrs, nice idea Corey!
36070         if(this.baseAttrs){
36071             Roo.applyIf(attr, this.baseAttrs);
36072         }
36073         if(this.applyLoader !== false){
36074             attr.loader = this;
36075         }
36076         // uiProvider = depreciated..
36077         
36078         if(typeof(attr.uiProvider) == 'string'){
36079            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36080                 /**  eval:var:attr */ eval(attr.uiProvider);
36081         }
36082         if(typeof(this.uiProviders['default']) != 'undefined') {
36083             attr.uiProvider = this.uiProviders['default'];
36084         }
36085         
36086         this.fireEvent('create', this, attr);
36087         
36088         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36089         return(attr.leaf ?
36090                         new Roo.tree.TreeNode(attr) :
36091                         new Roo.tree.AsyncTreeNode(attr));
36092     },
36093
36094     processResponse : function(response, node, callback)
36095     {
36096         var json = response.responseText;
36097         try {
36098             
36099             var o = Roo.decode(json);
36100             
36101             if (this.root === false && typeof(o.success) != undefined) {
36102                 this.root = 'data'; // the default behaviour for list like data..
36103                 }
36104                 
36105             if (this.root !== false &&  !o.success) {
36106                 // it's a failure condition.
36107                 var a = response.argument;
36108                 this.fireEvent("loadexception", this, a.node, response);
36109                 Roo.log("Load failed - should have a handler really");
36110                 return;
36111             }
36112             
36113             
36114             
36115             if (this.root !== false) {
36116                  o = o[this.root];
36117             }
36118             
36119             for(var i = 0, len = o.length; i < len; i++){
36120                 var n = this.createNode(o[i]);
36121                 if(n){
36122                     node.appendChild(n);
36123                 }
36124             }
36125             if(typeof callback == "function"){
36126                 callback(this, node);
36127             }
36128         }catch(e){
36129             this.handleFailure(response);
36130         }
36131     },
36132
36133     handleResponse : function(response){
36134         this.transId = false;
36135         var a = response.argument;
36136         this.processResponse(response, a.node, a.callback);
36137         this.fireEvent("load", this, a.node, response);
36138     },
36139
36140     handleFailure : function(response)
36141     {
36142         // should handle failure better..
36143         this.transId = false;
36144         var a = response.argument;
36145         this.fireEvent("loadexception", this, a.node, response);
36146         if(typeof a.callback == "function"){
36147             a.callback(this, a.node);
36148         }
36149     }
36150 });/*
36151  * Based on:
36152  * Ext JS Library 1.1.1
36153  * Copyright(c) 2006-2007, Ext JS, LLC.
36154  *
36155  * Originally Released Under LGPL - original licence link has changed is not relivant.
36156  *
36157  * Fork - LGPL
36158  * <script type="text/javascript">
36159  */
36160
36161 /**
36162 * @class Roo.tree.TreeFilter
36163 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36164 * @param {TreePanel} tree
36165 * @param {Object} config (optional)
36166  */
36167 Roo.tree.TreeFilter = function(tree, config){
36168     this.tree = tree;
36169     this.filtered = {};
36170     Roo.apply(this, config);
36171 };
36172
36173 Roo.tree.TreeFilter.prototype = {
36174     clearBlank:false,
36175     reverse:false,
36176     autoClear:false,
36177     remove:false,
36178
36179      /**
36180      * Filter the data by a specific attribute.
36181      * @param {String/RegExp} value Either string that the attribute value
36182      * should start with or a RegExp to test against the attribute
36183      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36184      * @param {TreeNode} startNode (optional) The node to start the filter at.
36185      */
36186     filter : function(value, attr, startNode){
36187         attr = attr || "text";
36188         var f;
36189         if(typeof value == "string"){
36190             var vlen = value.length;
36191             // auto clear empty filter
36192             if(vlen == 0 && this.clearBlank){
36193                 this.clear();
36194                 return;
36195             }
36196             value = value.toLowerCase();
36197             f = function(n){
36198                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36199             };
36200         }else if(value.exec){ // regex?
36201             f = function(n){
36202                 return value.test(n.attributes[attr]);
36203             };
36204         }else{
36205             throw 'Illegal filter type, must be string or regex';
36206         }
36207         this.filterBy(f, null, startNode);
36208         },
36209
36210     /**
36211      * Filter by a function. The passed function will be called with each
36212      * node in the tree (or from the startNode). If the function returns true, the node is kept
36213      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36214      * @param {Function} fn The filter function
36215      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36216      */
36217     filterBy : function(fn, scope, startNode){
36218         startNode = startNode || this.tree.root;
36219         if(this.autoClear){
36220             this.clear();
36221         }
36222         var af = this.filtered, rv = this.reverse;
36223         var f = function(n){
36224             if(n == startNode){
36225                 return true;
36226             }
36227             if(af[n.id]){
36228                 return false;
36229             }
36230             var m = fn.call(scope || n, n);
36231             if(!m || rv){
36232                 af[n.id] = n;
36233                 n.ui.hide();
36234                 return false;
36235             }
36236             return true;
36237         };
36238         startNode.cascade(f);
36239         if(this.remove){
36240            for(var id in af){
36241                if(typeof id != "function"){
36242                    var n = af[id];
36243                    if(n && n.parentNode){
36244                        n.parentNode.removeChild(n);
36245                    }
36246                }
36247            }
36248         }
36249     },
36250
36251     /**
36252      * Clears the current filter. Note: with the "remove" option
36253      * set a filter cannot be cleared.
36254      */
36255     clear : function(){
36256         var t = this.tree;
36257         var af = this.filtered;
36258         for(var id in af){
36259             if(typeof id != "function"){
36260                 var n = af[id];
36261                 if(n){
36262                     n.ui.show();
36263                 }
36264             }
36265         }
36266         this.filtered = {};
36267     }
36268 };
36269 /*
36270  * Based on:
36271  * Ext JS Library 1.1.1
36272  * Copyright(c) 2006-2007, Ext JS, LLC.
36273  *
36274  * Originally Released Under LGPL - original licence link has changed is not relivant.
36275  *
36276  * Fork - LGPL
36277  * <script type="text/javascript">
36278  */
36279  
36280
36281 /**
36282  * @class Roo.tree.TreeSorter
36283  * Provides sorting of nodes in a TreePanel
36284  * 
36285  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36286  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36287  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36288  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36289  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36290  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36291  * @constructor
36292  * @param {TreePanel} tree
36293  * @param {Object} config
36294  */
36295 Roo.tree.TreeSorter = function(tree, config){
36296     Roo.apply(this, config);
36297     tree.on("beforechildrenrendered", this.doSort, this);
36298     tree.on("append", this.updateSort, this);
36299     tree.on("insert", this.updateSort, this);
36300     
36301     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36302     var p = this.property || "text";
36303     var sortType = this.sortType;
36304     var fs = this.folderSort;
36305     var cs = this.caseSensitive === true;
36306     var leafAttr = this.leafAttr || 'leaf';
36307
36308     this.sortFn = function(n1, n2){
36309         if(fs){
36310             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36311                 return 1;
36312             }
36313             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36314                 return -1;
36315             }
36316         }
36317         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36318         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36319         if(v1 < v2){
36320                         return dsc ? +1 : -1;
36321                 }else if(v1 > v2){
36322                         return dsc ? -1 : +1;
36323         }else{
36324                 return 0;
36325         }
36326     };
36327 };
36328
36329 Roo.tree.TreeSorter.prototype = {
36330     doSort : function(node){
36331         node.sort(this.sortFn);
36332     },
36333     
36334     compareNodes : function(n1, n2){
36335         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36336     },
36337     
36338     updateSort : function(tree, node){
36339         if(node.childrenRendered){
36340             this.doSort.defer(1, this, [node]);
36341         }
36342     }
36343 };/*
36344  * Based on:
36345  * Ext JS Library 1.1.1
36346  * Copyright(c) 2006-2007, Ext JS, LLC.
36347  *
36348  * Originally Released Under LGPL - original licence link has changed is not relivant.
36349  *
36350  * Fork - LGPL
36351  * <script type="text/javascript">
36352  */
36353
36354 if(Roo.dd.DropZone){
36355     
36356 Roo.tree.TreeDropZone = function(tree, config){
36357     this.allowParentInsert = false;
36358     this.allowContainerDrop = false;
36359     this.appendOnly = false;
36360     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36361     this.tree = tree;
36362     this.lastInsertClass = "x-tree-no-status";
36363     this.dragOverData = {};
36364 };
36365
36366 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36367     ddGroup : "TreeDD",
36368     scroll:  true,
36369     
36370     expandDelay : 1000,
36371     
36372     expandNode : function(node){
36373         if(node.hasChildNodes() && !node.isExpanded()){
36374             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36375         }
36376     },
36377     
36378     queueExpand : function(node){
36379         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36380     },
36381     
36382     cancelExpand : function(){
36383         if(this.expandProcId){
36384             clearTimeout(this.expandProcId);
36385             this.expandProcId = false;
36386         }
36387     },
36388     
36389     isValidDropPoint : function(n, pt, dd, e, data){
36390         if(!n || !data){ return false; }
36391         var targetNode = n.node;
36392         var dropNode = data.node;
36393         // default drop rules
36394         if(!(targetNode && targetNode.isTarget && pt)){
36395             return false;
36396         }
36397         if(pt == "append" && targetNode.allowChildren === false){
36398             return false;
36399         }
36400         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36401             return false;
36402         }
36403         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36404             return false;
36405         }
36406         // reuse the object
36407         var overEvent = this.dragOverData;
36408         overEvent.tree = this.tree;
36409         overEvent.target = targetNode;
36410         overEvent.data = data;
36411         overEvent.point = pt;
36412         overEvent.source = dd;
36413         overEvent.rawEvent = e;
36414         overEvent.dropNode = dropNode;
36415         overEvent.cancel = false;  
36416         var result = this.tree.fireEvent("nodedragover", overEvent);
36417         return overEvent.cancel === false && result !== false;
36418     },
36419     
36420     getDropPoint : function(e, n, dd)
36421     {
36422         var tn = n.node;
36423         if(tn.isRoot){
36424             return tn.allowChildren !== false ? "append" : false; // always append for root
36425         }
36426         var dragEl = n.ddel;
36427         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36428         var y = Roo.lib.Event.getPageY(e);
36429         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36430         
36431         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36432         var noAppend = tn.allowChildren === false;
36433         if(this.appendOnly || tn.parentNode.allowChildren === false){
36434             return noAppend ? false : "append";
36435         }
36436         var noBelow = false;
36437         if(!this.allowParentInsert){
36438             noBelow = tn.hasChildNodes() && tn.isExpanded();
36439         }
36440         var q = (b - t) / (noAppend ? 2 : 3);
36441         if(y >= t && y < (t + q)){
36442             return "above";
36443         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36444             return "below";
36445         }else{
36446             return "append";
36447         }
36448     },
36449     
36450     onNodeEnter : function(n, dd, e, data)
36451     {
36452         this.cancelExpand();
36453     },
36454     
36455     onNodeOver : function(n, dd, e, data)
36456     {
36457        
36458         var pt = this.getDropPoint(e, n, dd);
36459         var node = n.node;
36460         
36461         // auto node expand check
36462         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36463             this.queueExpand(node);
36464         }else if(pt != "append"){
36465             this.cancelExpand();
36466         }
36467         
36468         // set the insert point style on the target node
36469         var returnCls = this.dropNotAllowed;
36470         if(this.isValidDropPoint(n, pt, dd, e, data)){
36471            if(pt){
36472                var el = n.ddel;
36473                var cls;
36474                if(pt == "above"){
36475                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36476                    cls = "x-tree-drag-insert-above";
36477                }else if(pt == "below"){
36478                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36479                    cls = "x-tree-drag-insert-below";
36480                }else{
36481                    returnCls = "x-tree-drop-ok-append";
36482                    cls = "x-tree-drag-append";
36483                }
36484                if(this.lastInsertClass != cls){
36485                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36486                    this.lastInsertClass = cls;
36487                }
36488            }
36489        }
36490        return returnCls;
36491     },
36492     
36493     onNodeOut : function(n, dd, e, data){
36494         
36495         this.cancelExpand();
36496         this.removeDropIndicators(n);
36497     },
36498     
36499     onNodeDrop : function(n, dd, e, data){
36500         var point = this.getDropPoint(e, n, dd);
36501         var targetNode = n.node;
36502         targetNode.ui.startDrop();
36503         if(!this.isValidDropPoint(n, point, dd, e, data)){
36504             targetNode.ui.endDrop();
36505             return false;
36506         }
36507         // first try to find the drop node
36508         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36509         var dropEvent = {
36510             tree : this.tree,
36511             target: targetNode,
36512             data: data,
36513             point: point,
36514             source: dd,
36515             rawEvent: e,
36516             dropNode: dropNode,
36517             cancel: !dropNode   
36518         };
36519         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36520         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36521             targetNode.ui.endDrop();
36522             return false;
36523         }
36524         // allow target changing
36525         targetNode = dropEvent.target;
36526         if(point == "append" && !targetNode.isExpanded()){
36527             targetNode.expand(false, null, function(){
36528                 this.completeDrop(dropEvent);
36529             }.createDelegate(this));
36530         }else{
36531             this.completeDrop(dropEvent);
36532         }
36533         return true;
36534     },
36535     
36536     completeDrop : function(de){
36537         var ns = de.dropNode, p = de.point, t = de.target;
36538         if(!(ns instanceof Array)){
36539             ns = [ns];
36540         }
36541         var n;
36542         for(var i = 0, len = ns.length; i < len; i++){
36543             n = ns[i];
36544             if(p == "above"){
36545                 t.parentNode.insertBefore(n, t);
36546             }else if(p == "below"){
36547                 t.parentNode.insertBefore(n, t.nextSibling);
36548             }else{
36549                 t.appendChild(n);
36550             }
36551         }
36552         n.ui.focus();
36553         if(this.tree.hlDrop){
36554             n.ui.highlight();
36555         }
36556         t.ui.endDrop();
36557         this.tree.fireEvent("nodedrop", de);
36558     },
36559     
36560     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36561         if(this.tree.hlDrop){
36562             dropNode.ui.focus();
36563             dropNode.ui.highlight();
36564         }
36565         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36566     },
36567     
36568     getTree : function(){
36569         return this.tree;
36570     },
36571     
36572     removeDropIndicators : function(n){
36573         if(n && n.ddel){
36574             var el = n.ddel;
36575             Roo.fly(el).removeClass([
36576                     "x-tree-drag-insert-above",
36577                     "x-tree-drag-insert-below",
36578                     "x-tree-drag-append"]);
36579             this.lastInsertClass = "_noclass";
36580         }
36581     },
36582     
36583     beforeDragDrop : function(target, e, id){
36584         this.cancelExpand();
36585         return true;
36586     },
36587     
36588     afterRepair : function(data){
36589         if(data && Roo.enableFx){
36590             data.node.ui.highlight();
36591         }
36592         this.hideProxy();
36593     } 
36594     
36595 });
36596
36597 }
36598 /*
36599  * Based on:
36600  * Ext JS Library 1.1.1
36601  * Copyright(c) 2006-2007, Ext JS, LLC.
36602  *
36603  * Originally Released Under LGPL - original licence link has changed is not relivant.
36604  *
36605  * Fork - LGPL
36606  * <script type="text/javascript">
36607  */
36608  
36609
36610 if(Roo.dd.DragZone){
36611 Roo.tree.TreeDragZone = function(tree, config){
36612     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36613     this.tree = tree;
36614 };
36615
36616 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36617     ddGroup : "TreeDD",
36618    
36619     onBeforeDrag : function(data, e){
36620         var n = data.node;
36621         return n && n.draggable && !n.disabled;
36622     },
36623      
36624     
36625     onInitDrag : function(e){
36626         var data = this.dragData;
36627         this.tree.getSelectionModel().select(data.node);
36628         this.proxy.update("");
36629         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36630         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36631     },
36632     
36633     getRepairXY : function(e, data){
36634         return data.node.ui.getDDRepairXY();
36635     },
36636     
36637     onEndDrag : function(data, e){
36638         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36639         
36640         
36641     },
36642     
36643     onValidDrop : function(dd, e, id){
36644         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36645         this.hideProxy();
36646     },
36647     
36648     beforeInvalidDrop : function(e, id){
36649         // this scrolls the original position back into view
36650         var sm = this.tree.getSelectionModel();
36651         sm.clearSelections();
36652         sm.select(this.dragData.node);
36653     }
36654 });
36655 }/*
36656  * Based on:
36657  * Ext JS Library 1.1.1
36658  * Copyright(c) 2006-2007, Ext JS, LLC.
36659  *
36660  * Originally Released Under LGPL - original licence link has changed is not relivant.
36661  *
36662  * Fork - LGPL
36663  * <script type="text/javascript">
36664  */
36665 /**
36666  * @class Roo.tree.TreeEditor
36667  * @extends Roo.Editor
36668  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36669  * as the editor field.
36670  * @constructor
36671  * @param {Object} config (used to be the tree panel.)
36672  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36673  * 
36674  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36675  * @cfg {Roo.form.TextField|Object} field The field configuration
36676  *
36677  * 
36678  */
36679 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36680     var tree = config;
36681     var field;
36682     if (oldconfig) { // old style..
36683         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36684     } else {
36685         // new style..
36686         tree = config.tree;
36687         config.field = config.field  || {};
36688         config.field.xtype = 'TextField';
36689         field = Roo.factory(config.field, Roo.form);
36690     }
36691     config = config || {};
36692     
36693     
36694     this.addEvents({
36695         /**
36696          * @event beforenodeedit
36697          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36698          * false from the handler of this event.
36699          * @param {Editor} this
36700          * @param {Roo.tree.Node} node 
36701          */
36702         "beforenodeedit" : true
36703     });
36704     
36705     //Roo.log(config);
36706     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36707
36708     this.tree = tree;
36709
36710     tree.on('beforeclick', this.beforeNodeClick, this);
36711     tree.getTreeEl().on('mousedown', this.hide, this);
36712     this.on('complete', this.updateNode, this);
36713     this.on('beforestartedit', this.fitToTree, this);
36714     this.on('startedit', this.bindScroll, this, {delay:10});
36715     this.on('specialkey', this.onSpecialKey, this);
36716 };
36717
36718 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36719     /**
36720      * @cfg {String} alignment
36721      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36722      */
36723     alignment: "l-l",
36724     // inherit
36725     autoSize: false,
36726     /**
36727      * @cfg {Boolean} hideEl
36728      * True to hide the bound element while the editor is displayed (defaults to false)
36729      */
36730     hideEl : false,
36731     /**
36732      * @cfg {String} cls
36733      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36734      */
36735     cls: "x-small-editor x-tree-editor",
36736     /**
36737      * @cfg {Boolean} shim
36738      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36739      */
36740     shim:false,
36741     // inherit
36742     shadow:"frame",
36743     /**
36744      * @cfg {Number} maxWidth
36745      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36746      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36747      * scroll and client offsets into account prior to each edit.
36748      */
36749     maxWidth: 250,
36750
36751     editDelay : 350,
36752
36753     // private
36754     fitToTree : function(ed, el){
36755         var td = this.tree.getTreeEl().dom, nd = el.dom;
36756         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36757             td.scrollLeft = nd.offsetLeft;
36758         }
36759         var w = Math.min(
36760                 this.maxWidth,
36761                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36762         this.setSize(w, '');
36763         
36764         return this.fireEvent('beforenodeedit', this, this.editNode);
36765         
36766     },
36767
36768     // private
36769     triggerEdit : function(node){
36770         this.completeEdit();
36771         this.editNode = node;
36772         this.startEdit(node.ui.textNode, node.text);
36773     },
36774
36775     // private
36776     bindScroll : function(){
36777         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36778     },
36779
36780     // private
36781     beforeNodeClick : function(node, e){
36782         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36783         this.lastClick = new Date();
36784         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36785             e.stopEvent();
36786             this.triggerEdit(node);
36787             return false;
36788         }
36789         return true;
36790     },
36791
36792     // private
36793     updateNode : function(ed, value){
36794         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36795         this.editNode.setText(value);
36796     },
36797
36798     // private
36799     onHide : function(){
36800         Roo.tree.TreeEditor.superclass.onHide.call(this);
36801         if(this.editNode){
36802             this.editNode.ui.focus();
36803         }
36804     },
36805
36806     // private
36807     onSpecialKey : function(field, e){
36808         var k = e.getKey();
36809         if(k == e.ESC){
36810             e.stopEvent();
36811             this.cancelEdit();
36812         }else if(k == e.ENTER && !e.hasModifier()){
36813             e.stopEvent();
36814             this.completeEdit();
36815         }
36816     }
36817 });//<Script type="text/javascript">
36818 /*
36819  * Based on:
36820  * Ext JS Library 1.1.1
36821  * Copyright(c) 2006-2007, Ext JS, LLC.
36822  *
36823  * Originally Released Under LGPL - original licence link has changed is not relivant.
36824  *
36825  * Fork - LGPL
36826  * <script type="text/javascript">
36827  */
36828  
36829 /**
36830  * Not documented??? - probably should be...
36831  */
36832
36833 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
36834     //focus: Roo.emptyFn, // prevent odd scrolling behavior
36835     
36836     renderElements : function(n, a, targetNode, bulkRender){
36837         //consel.log("renderElements?");
36838         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36839
36840         var t = n.getOwnerTree();
36841         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
36842         
36843         var cols = t.columns;
36844         var bw = t.borderWidth;
36845         var c = cols[0];
36846         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36847          var cb = typeof a.checked == "boolean";
36848         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36849         var colcls = 'x-t-' + tid + '-c0';
36850         var buf = [
36851             '<li class="x-tree-node">',
36852             
36853                 
36854                 '<div class="x-tree-node-el ', a.cls,'">',
36855                     // extran...
36856                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
36857                 
36858                 
36859                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
36860                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
36861                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
36862                            (a.icon ? ' x-tree-node-inline-icon' : ''),
36863                            (a.iconCls ? ' '+a.iconCls : ''),
36864                            '" unselectable="on" />',
36865                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
36866                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
36867                              
36868                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36869                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
36870                             '<span unselectable="on" qtip="' + tx + '">',
36871                              tx,
36872                              '</span></a>' ,
36873                     '</div>',
36874                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36875                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
36876                  ];
36877         for(var i = 1, len = cols.length; i < len; i++){
36878             c = cols[i];
36879             colcls = 'x-t-' + tid + '-c' +i;
36880             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36881             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
36882                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
36883                       "</div>");
36884          }
36885          
36886          buf.push(
36887             '</a>',
36888             '<div class="x-clear"></div></div>',
36889             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36890             "</li>");
36891         
36892         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36893             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36894                                 n.nextSibling.ui.getEl(), buf.join(""));
36895         }else{
36896             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36897         }
36898         var el = this.wrap.firstChild;
36899         this.elRow = el;
36900         this.elNode = el.firstChild;
36901         this.ranchor = el.childNodes[1];
36902         this.ctNode = this.wrap.childNodes[1];
36903         var cs = el.firstChild.childNodes;
36904         this.indentNode = cs[0];
36905         this.ecNode = cs[1];
36906         this.iconNode = cs[2];
36907         var index = 3;
36908         if(cb){
36909             this.checkbox = cs[3];
36910             index++;
36911         }
36912         this.anchor = cs[index];
36913         
36914         this.textNode = cs[index].firstChild;
36915         
36916         //el.on("click", this.onClick, this);
36917         //el.on("dblclick", this.onDblClick, this);
36918         
36919         
36920        // console.log(this);
36921     },
36922     initEvents : function(){
36923         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
36924         
36925             
36926         var a = this.ranchor;
36927
36928         var el = Roo.get(a);
36929
36930         if(Roo.isOpera){ // opera render bug ignores the CSS
36931             el.setStyle("text-decoration", "none");
36932         }
36933
36934         el.on("click", this.onClick, this);
36935         el.on("dblclick", this.onDblClick, this);
36936         el.on("contextmenu", this.onContextMenu, this);
36937         
36938     },
36939     
36940     /*onSelectedChange : function(state){
36941         if(state){
36942             this.focus();
36943             this.addClass("x-tree-selected");
36944         }else{
36945             //this.blur();
36946             this.removeClass("x-tree-selected");
36947         }
36948     },*/
36949     addClass : function(cls){
36950         if(this.elRow){
36951             Roo.fly(this.elRow).addClass(cls);
36952         }
36953         
36954     },
36955     
36956     
36957     removeClass : function(cls){
36958         if(this.elRow){
36959             Roo.fly(this.elRow).removeClass(cls);
36960         }
36961     }
36962
36963     
36964     
36965 });//<Script type="text/javascript">
36966
36967 /*
36968  * Based on:
36969  * Ext JS Library 1.1.1
36970  * Copyright(c) 2006-2007, Ext JS, LLC.
36971  *
36972  * Originally Released Under LGPL - original licence link has changed is not relivant.
36973  *
36974  * Fork - LGPL
36975  * <script type="text/javascript">
36976  */
36977  
36978
36979 /**
36980  * @class Roo.tree.ColumnTree
36981  * @extends Roo.data.TreePanel
36982  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
36983  * @cfg {int} borderWidth  compined right/left border allowance
36984  * @constructor
36985  * @param {String/HTMLElement/Element} el The container element
36986  * @param {Object} config
36987  */
36988 Roo.tree.ColumnTree =  function(el, config)
36989 {
36990    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
36991    this.addEvents({
36992         /**
36993         * @event resize
36994         * Fire this event on a container when it resizes
36995         * @param {int} w Width
36996         * @param {int} h Height
36997         */
36998        "resize" : true
36999     });
37000     this.on('resize', this.onResize, this);
37001 };
37002
37003 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37004     //lines:false,
37005     
37006     
37007     borderWidth: Roo.isBorderBox ? 0 : 2, 
37008     headEls : false,
37009     
37010     render : function(){
37011         // add the header.....
37012        
37013         Roo.tree.ColumnTree.superclass.render.apply(this);
37014         
37015         this.el.addClass('x-column-tree');
37016         
37017         this.headers = this.el.createChild(
37018             {cls:'x-tree-headers'},this.innerCt.dom);
37019    
37020         var cols = this.columns, c;
37021         var totalWidth = 0;
37022         this.headEls = [];
37023         var  len = cols.length;
37024         for(var i = 0; i < len; i++){
37025              c = cols[i];
37026              totalWidth += c.width;
37027             this.headEls.push(this.headers.createChild({
37028                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37029                  cn: {
37030                      cls:'x-tree-hd-text',
37031                      html: c.header
37032                  },
37033                  style:'width:'+(c.width-this.borderWidth)+'px;'
37034              }));
37035         }
37036         this.headers.createChild({cls:'x-clear'});
37037         // prevent floats from wrapping when clipped
37038         this.headers.setWidth(totalWidth);
37039         //this.innerCt.setWidth(totalWidth);
37040         this.innerCt.setStyle({ overflow: 'auto' });
37041         this.onResize(this.width, this.height);
37042              
37043         
37044     },
37045     onResize : function(w,h)
37046     {
37047         this.height = h;
37048         this.width = w;
37049         // resize cols..
37050         this.innerCt.setWidth(this.width);
37051         this.innerCt.setHeight(this.height-20);
37052         
37053         // headers...
37054         var cols = this.columns, c;
37055         var totalWidth = 0;
37056         var expEl = false;
37057         var len = cols.length;
37058         for(var i = 0; i < len; i++){
37059             c = cols[i];
37060             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37061                 // it's the expander..
37062                 expEl  = this.headEls[i];
37063                 continue;
37064             }
37065             totalWidth += c.width;
37066             
37067         }
37068         if (expEl) {
37069             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37070         }
37071         this.headers.setWidth(w-20);
37072
37073         
37074         
37075         
37076     }
37077 });
37078 /*
37079  * Based on:
37080  * Ext JS Library 1.1.1
37081  * Copyright(c) 2006-2007, Ext JS, LLC.
37082  *
37083  * Originally Released Under LGPL - original licence link has changed is not relivant.
37084  *
37085  * Fork - LGPL
37086  * <script type="text/javascript">
37087  */
37088  
37089 /**
37090  * @class Roo.menu.Menu
37091  * @extends Roo.util.Observable
37092  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37093  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37094  * @constructor
37095  * Creates a new Menu
37096  * @param {Object} config Configuration options
37097  */
37098 Roo.menu.Menu = function(config){
37099     Roo.apply(this, config);
37100     this.id = this.id || Roo.id();
37101     this.addEvents({
37102         /**
37103          * @event beforeshow
37104          * Fires before this menu is displayed
37105          * @param {Roo.menu.Menu} this
37106          */
37107         beforeshow : true,
37108         /**
37109          * @event beforehide
37110          * Fires before this menu is hidden
37111          * @param {Roo.menu.Menu} this
37112          */
37113         beforehide : true,
37114         /**
37115          * @event show
37116          * Fires after this menu is displayed
37117          * @param {Roo.menu.Menu} this
37118          */
37119         show : true,
37120         /**
37121          * @event hide
37122          * Fires after this menu is hidden
37123          * @param {Roo.menu.Menu} this
37124          */
37125         hide : true,
37126         /**
37127          * @event click
37128          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37129          * @param {Roo.menu.Menu} this
37130          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37131          * @param {Roo.EventObject} e
37132          */
37133         click : true,
37134         /**
37135          * @event mouseover
37136          * Fires when the mouse is hovering over this menu
37137          * @param {Roo.menu.Menu} this
37138          * @param {Roo.EventObject} e
37139          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37140          */
37141         mouseover : true,
37142         /**
37143          * @event mouseout
37144          * Fires when the mouse exits this menu
37145          * @param {Roo.menu.Menu} this
37146          * @param {Roo.EventObject} e
37147          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37148          */
37149         mouseout : true,
37150         /**
37151          * @event itemclick
37152          * Fires when a menu item contained in this menu is clicked
37153          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37154          * @param {Roo.EventObject} e
37155          */
37156         itemclick: true
37157     });
37158     if (this.registerMenu) {
37159         Roo.menu.MenuMgr.register(this);
37160     }
37161     
37162     var mis = this.items;
37163     this.items = new Roo.util.MixedCollection();
37164     if(mis){
37165         this.add.apply(this, mis);
37166     }
37167 };
37168
37169 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37170     /**
37171      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37172      */
37173     minWidth : 120,
37174     /**
37175      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37176      * for bottom-right shadow (defaults to "sides")
37177      */
37178     shadow : "sides",
37179     /**
37180      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37181      * this menu (defaults to "tl-tr?")
37182      */
37183     subMenuAlign : "tl-tr?",
37184     /**
37185      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37186      * relative to its element of origin (defaults to "tl-bl?")
37187      */
37188     defaultAlign : "tl-bl?",
37189     /**
37190      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37191      */
37192     allowOtherMenus : false,
37193     /**
37194      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37195      */
37196     registerMenu : true,
37197
37198     hidden:true,
37199
37200     // private
37201     render : function(){
37202         if(this.el){
37203             return;
37204         }
37205         var el = this.el = new Roo.Layer({
37206             cls: "x-menu",
37207             shadow:this.shadow,
37208             constrain: false,
37209             parentEl: this.parentEl || document.body,
37210             zindex:15000
37211         });
37212
37213         this.keyNav = new Roo.menu.MenuNav(this);
37214
37215         if(this.plain){
37216             el.addClass("x-menu-plain");
37217         }
37218         if(this.cls){
37219             el.addClass(this.cls);
37220         }
37221         // generic focus element
37222         this.focusEl = el.createChild({
37223             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37224         });
37225         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37226         //disabling touch- as it's causing issues ..
37227         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37228         ul.on('click'   , this.onClick, this);
37229         
37230         
37231         ul.on("mouseover", this.onMouseOver, this);
37232         ul.on("mouseout", this.onMouseOut, this);
37233         this.items.each(function(item){
37234             if (item.hidden) {
37235                 return;
37236             }
37237             
37238             var li = document.createElement("li");
37239             li.className = "x-menu-list-item";
37240             ul.dom.appendChild(li);
37241             item.render(li, this);
37242         }, this);
37243         this.ul = ul;
37244         this.autoWidth();
37245     },
37246
37247     // private
37248     autoWidth : function(){
37249         var el = this.el, ul = this.ul;
37250         if(!el){
37251             return;
37252         }
37253         var w = this.width;
37254         if(w){
37255             el.setWidth(w);
37256         }else if(Roo.isIE){
37257             el.setWidth(this.minWidth);
37258             var t = el.dom.offsetWidth; // force recalc
37259             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37260         }
37261     },
37262
37263     // private
37264     delayAutoWidth : function(){
37265         if(this.rendered){
37266             if(!this.awTask){
37267                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37268             }
37269             this.awTask.delay(20);
37270         }
37271     },
37272
37273     // private
37274     findTargetItem : function(e){
37275         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37276         if(t && t.menuItemId){
37277             return this.items.get(t.menuItemId);
37278         }
37279     },
37280
37281     // private
37282     onClick : function(e){
37283         Roo.log("menu.onClick");
37284         var t = this.findTargetItem(e);
37285         if(!t){
37286             return;
37287         }
37288         Roo.log(e);
37289         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37290             if(t == this.activeItem && t.shouldDeactivate(e)){
37291                 this.activeItem.deactivate();
37292                 delete this.activeItem;
37293                 return;
37294             }
37295             if(t.canActivate){
37296                 this.setActiveItem(t, true);
37297             }
37298             return;
37299             
37300             
37301         }
37302         
37303         t.onClick(e);
37304         this.fireEvent("click", this, t, e);
37305     },
37306
37307     // private
37308     setActiveItem : function(item, autoExpand){
37309         if(item != this.activeItem){
37310             if(this.activeItem){
37311                 this.activeItem.deactivate();
37312             }
37313             this.activeItem = item;
37314             item.activate(autoExpand);
37315         }else if(autoExpand){
37316             item.expandMenu();
37317         }
37318     },
37319
37320     // private
37321     tryActivate : function(start, step){
37322         var items = this.items;
37323         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37324             var item = items.get(i);
37325             if(!item.disabled && item.canActivate){
37326                 this.setActiveItem(item, false);
37327                 return item;
37328             }
37329         }
37330         return false;
37331     },
37332
37333     // private
37334     onMouseOver : function(e){
37335         var t;
37336         if(t = this.findTargetItem(e)){
37337             if(t.canActivate && !t.disabled){
37338                 this.setActiveItem(t, true);
37339             }
37340         }
37341         this.fireEvent("mouseover", this, e, t);
37342     },
37343
37344     // private
37345     onMouseOut : function(e){
37346         var t;
37347         if(t = this.findTargetItem(e)){
37348             if(t == this.activeItem && t.shouldDeactivate(e)){
37349                 this.activeItem.deactivate();
37350                 delete this.activeItem;
37351             }
37352         }
37353         this.fireEvent("mouseout", this, e, t);
37354     },
37355
37356     /**
37357      * Read-only.  Returns true if the menu is currently displayed, else false.
37358      * @type Boolean
37359      */
37360     isVisible : function(){
37361         return this.el && !this.hidden;
37362     },
37363
37364     /**
37365      * Displays this menu relative to another element
37366      * @param {String/HTMLElement/Roo.Element} element The element to align to
37367      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37368      * the element (defaults to this.defaultAlign)
37369      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37370      */
37371     show : function(el, pos, parentMenu){
37372         this.parentMenu = parentMenu;
37373         if(!this.el){
37374             this.render();
37375         }
37376         this.fireEvent("beforeshow", this);
37377         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37378     },
37379
37380     /**
37381      * Displays this menu at a specific xy position
37382      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37383      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37384      */
37385     showAt : function(xy, parentMenu, /* private: */_e){
37386         this.parentMenu = parentMenu;
37387         if(!this.el){
37388             this.render();
37389         }
37390         if(_e !== false){
37391             this.fireEvent("beforeshow", this);
37392             xy = this.el.adjustForConstraints(xy);
37393         }
37394         this.el.setXY(xy);
37395         this.el.show();
37396         this.hidden = false;
37397         this.focus();
37398         this.fireEvent("show", this);
37399     },
37400
37401     focus : function(){
37402         if(!this.hidden){
37403             this.doFocus.defer(50, this);
37404         }
37405     },
37406
37407     doFocus : function(){
37408         if(!this.hidden){
37409             this.focusEl.focus();
37410         }
37411     },
37412
37413     /**
37414      * Hides this menu and optionally all parent menus
37415      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37416      */
37417     hide : function(deep){
37418         if(this.el && this.isVisible()){
37419             this.fireEvent("beforehide", this);
37420             if(this.activeItem){
37421                 this.activeItem.deactivate();
37422                 this.activeItem = null;
37423             }
37424             this.el.hide();
37425             this.hidden = true;
37426             this.fireEvent("hide", this);
37427         }
37428         if(deep === true && this.parentMenu){
37429             this.parentMenu.hide(true);
37430         }
37431     },
37432
37433     /**
37434      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37435      * Any of the following are valid:
37436      * <ul>
37437      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37438      * <li>An HTMLElement object which will be converted to a menu item</li>
37439      * <li>A menu item config object that will be created as a new menu item</li>
37440      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37441      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37442      * </ul>
37443      * Usage:
37444      * <pre><code>
37445 // Create the menu
37446 var menu = new Roo.menu.Menu();
37447
37448 // Create a menu item to add by reference
37449 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37450
37451 // Add a bunch of items at once using different methods.
37452 // Only the last item added will be returned.
37453 var item = menu.add(
37454     menuItem,                // add existing item by ref
37455     'Dynamic Item',          // new TextItem
37456     '-',                     // new separator
37457     { text: 'Config Item' }  // new item by config
37458 );
37459 </code></pre>
37460      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37461      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37462      */
37463     add : function(){
37464         var a = arguments, l = a.length, item;
37465         for(var i = 0; i < l; i++){
37466             var el = a[i];
37467             if ((typeof(el) == "object") && el.xtype && el.xns) {
37468                 el = Roo.factory(el, Roo.menu);
37469             }
37470             
37471             if(el.render){ // some kind of Item
37472                 item = this.addItem(el);
37473             }else if(typeof el == "string"){ // string
37474                 if(el == "separator" || el == "-"){
37475                     item = this.addSeparator();
37476                 }else{
37477                     item = this.addText(el);
37478                 }
37479             }else if(el.tagName || el.el){ // element
37480                 item = this.addElement(el);
37481             }else if(typeof el == "object"){ // must be menu item config?
37482                 item = this.addMenuItem(el);
37483             }
37484         }
37485         return item;
37486     },
37487
37488     /**
37489      * Returns this menu's underlying {@link Roo.Element} object
37490      * @return {Roo.Element} The element
37491      */
37492     getEl : function(){
37493         if(!this.el){
37494             this.render();
37495         }
37496         return this.el;
37497     },
37498
37499     /**
37500      * Adds a separator bar to the menu
37501      * @return {Roo.menu.Item} The menu item that was added
37502      */
37503     addSeparator : function(){
37504         return this.addItem(new Roo.menu.Separator());
37505     },
37506
37507     /**
37508      * Adds an {@link Roo.Element} object to the menu
37509      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37510      * @return {Roo.menu.Item} The menu item that was added
37511      */
37512     addElement : function(el){
37513         return this.addItem(new Roo.menu.BaseItem(el));
37514     },
37515
37516     /**
37517      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37518      * @param {Roo.menu.Item} item The menu item to add
37519      * @return {Roo.menu.Item} The menu item that was added
37520      */
37521     addItem : function(item){
37522         this.items.add(item);
37523         if(this.ul){
37524             var li = document.createElement("li");
37525             li.className = "x-menu-list-item";
37526             this.ul.dom.appendChild(li);
37527             item.render(li, this);
37528             this.delayAutoWidth();
37529         }
37530         return item;
37531     },
37532
37533     /**
37534      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37535      * @param {Object} config A MenuItem config object
37536      * @return {Roo.menu.Item} The menu item that was added
37537      */
37538     addMenuItem : function(config){
37539         if(!(config instanceof Roo.menu.Item)){
37540             if(typeof config.checked == "boolean"){ // must be check menu item config?
37541                 config = new Roo.menu.CheckItem(config);
37542             }else{
37543                 config = new Roo.menu.Item(config);
37544             }
37545         }
37546         return this.addItem(config);
37547     },
37548
37549     /**
37550      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37551      * @param {String} text The text to display in the menu item
37552      * @return {Roo.menu.Item} The menu item that was added
37553      */
37554     addText : function(text){
37555         return this.addItem(new Roo.menu.TextItem({ text : text }));
37556     },
37557
37558     /**
37559      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37560      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37561      * @param {Roo.menu.Item} item The menu item to add
37562      * @return {Roo.menu.Item} The menu item that was added
37563      */
37564     insert : function(index, item){
37565         this.items.insert(index, item);
37566         if(this.ul){
37567             var li = document.createElement("li");
37568             li.className = "x-menu-list-item";
37569             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37570             item.render(li, this);
37571             this.delayAutoWidth();
37572         }
37573         return item;
37574     },
37575
37576     /**
37577      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37578      * @param {Roo.menu.Item} item The menu item to remove
37579      */
37580     remove : function(item){
37581         this.items.removeKey(item.id);
37582         item.destroy();
37583     },
37584
37585     /**
37586      * Removes and destroys all items in the menu
37587      */
37588     removeAll : function(){
37589         var f;
37590         while(f = this.items.first()){
37591             this.remove(f);
37592         }
37593     }
37594 });
37595
37596 // MenuNav is a private utility class used internally by the Menu
37597 Roo.menu.MenuNav = function(menu){
37598     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37599     this.scope = this.menu = menu;
37600 };
37601
37602 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37603     doRelay : function(e, h){
37604         var k = e.getKey();
37605         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37606             this.menu.tryActivate(0, 1);
37607             return false;
37608         }
37609         return h.call(this.scope || this, e, this.menu);
37610     },
37611
37612     up : function(e, m){
37613         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37614             m.tryActivate(m.items.length-1, -1);
37615         }
37616     },
37617
37618     down : function(e, m){
37619         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37620             m.tryActivate(0, 1);
37621         }
37622     },
37623
37624     right : function(e, m){
37625         if(m.activeItem){
37626             m.activeItem.expandMenu(true);
37627         }
37628     },
37629
37630     left : function(e, m){
37631         m.hide();
37632         if(m.parentMenu && m.parentMenu.activeItem){
37633             m.parentMenu.activeItem.activate();
37634         }
37635     },
37636
37637     enter : function(e, m){
37638         if(m.activeItem){
37639             e.stopPropagation();
37640             m.activeItem.onClick(e);
37641             m.fireEvent("click", this, m.activeItem);
37642             return true;
37643         }
37644     }
37645 });/*
37646  * Based on:
37647  * Ext JS Library 1.1.1
37648  * Copyright(c) 2006-2007, Ext JS, LLC.
37649  *
37650  * Originally Released Under LGPL - original licence link has changed is not relivant.
37651  *
37652  * Fork - LGPL
37653  * <script type="text/javascript">
37654  */
37655  
37656 /**
37657  * @class Roo.menu.MenuMgr
37658  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37659  * @singleton
37660  */
37661 Roo.menu.MenuMgr = function(){
37662    var menus, active, groups = {}, attached = false, lastShow = new Date();
37663
37664    // private - called when first menu is created
37665    function init(){
37666        menus = {};
37667        active = new Roo.util.MixedCollection();
37668        Roo.get(document).addKeyListener(27, function(){
37669            if(active.length > 0){
37670                hideAll();
37671            }
37672        });
37673    }
37674
37675    // private
37676    function hideAll(){
37677        if(active && active.length > 0){
37678            var c = active.clone();
37679            c.each(function(m){
37680                m.hide();
37681            });
37682        }
37683    }
37684
37685    // private
37686    function onHide(m){
37687        active.remove(m);
37688        if(active.length < 1){
37689            Roo.get(document).un("mousedown", onMouseDown);
37690            attached = false;
37691        }
37692    }
37693
37694    // private
37695    function onShow(m){
37696        var last = active.last();
37697        lastShow = new Date();
37698        active.add(m);
37699        if(!attached){
37700            Roo.get(document).on("mousedown", onMouseDown);
37701            attached = true;
37702        }
37703        if(m.parentMenu){
37704           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37705           m.parentMenu.activeChild = m;
37706        }else if(last && last.isVisible()){
37707           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37708        }
37709    }
37710
37711    // private
37712    function onBeforeHide(m){
37713        if(m.activeChild){
37714            m.activeChild.hide();
37715        }
37716        if(m.autoHideTimer){
37717            clearTimeout(m.autoHideTimer);
37718            delete m.autoHideTimer;
37719        }
37720    }
37721
37722    // private
37723    function onBeforeShow(m){
37724        var pm = m.parentMenu;
37725        if(!pm && !m.allowOtherMenus){
37726            hideAll();
37727        }else if(pm && pm.activeChild && active != m){
37728            pm.activeChild.hide();
37729        }
37730    }
37731
37732    // private
37733    function onMouseDown(e){
37734        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37735            hideAll();
37736        }
37737    }
37738
37739    // private
37740    function onBeforeCheck(mi, state){
37741        if(state){
37742            var g = groups[mi.group];
37743            for(var i = 0, l = g.length; i < l; i++){
37744                if(g[i] != mi){
37745                    g[i].setChecked(false);
37746                }
37747            }
37748        }
37749    }
37750
37751    return {
37752
37753        /**
37754         * Hides all menus that are currently visible
37755         */
37756        hideAll : function(){
37757             hideAll();  
37758        },
37759
37760        // private
37761        register : function(menu){
37762            if(!menus){
37763                init();
37764            }
37765            menus[menu.id] = menu;
37766            menu.on("beforehide", onBeforeHide);
37767            menu.on("hide", onHide);
37768            menu.on("beforeshow", onBeforeShow);
37769            menu.on("show", onShow);
37770            var g = menu.group;
37771            if(g && menu.events["checkchange"]){
37772                if(!groups[g]){
37773                    groups[g] = [];
37774                }
37775                groups[g].push(menu);
37776                menu.on("checkchange", onCheck);
37777            }
37778        },
37779
37780         /**
37781          * Returns a {@link Roo.menu.Menu} object
37782          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37783          * be used to generate and return a new Menu instance.
37784          */
37785        get : function(menu){
37786            if(typeof menu == "string"){ // menu id
37787                return menus[menu];
37788            }else if(menu.events){  // menu instance
37789                return menu;
37790            }else if(typeof menu.length == 'number'){ // array of menu items?
37791                return new Roo.menu.Menu({items:menu});
37792            }else{ // otherwise, must be a config
37793                return new Roo.menu.Menu(menu);
37794            }
37795        },
37796
37797        // private
37798        unregister : function(menu){
37799            delete menus[menu.id];
37800            menu.un("beforehide", onBeforeHide);
37801            menu.un("hide", onHide);
37802            menu.un("beforeshow", onBeforeShow);
37803            menu.un("show", onShow);
37804            var g = menu.group;
37805            if(g && menu.events["checkchange"]){
37806                groups[g].remove(menu);
37807                menu.un("checkchange", onCheck);
37808            }
37809        },
37810
37811        // private
37812        registerCheckable : function(menuItem){
37813            var g = menuItem.group;
37814            if(g){
37815                if(!groups[g]){
37816                    groups[g] = [];
37817                }
37818                groups[g].push(menuItem);
37819                menuItem.on("beforecheckchange", onBeforeCheck);
37820            }
37821        },
37822
37823        // private
37824        unregisterCheckable : function(menuItem){
37825            var g = menuItem.group;
37826            if(g){
37827                groups[g].remove(menuItem);
37828                menuItem.un("beforecheckchange", onBeforeCheck);
37829            }
37830        }
37831    };
37832 }();/*
37833  * Based on:
37834  * Ext JS Library 1.1.1
37835  * Copyright(c) 2006-2007, Ext JS, LLC.
37836  *
37837  * Originally Released Under LGPL - original licence link has changed is not relivant.
37838  *
37839  * Fork - LGPL
37840  * <script type="text/javascript">
37841  */
37842  
37843
37844 /**
37845  * @class Roo.menu.BaseItem
37846  * @extends Roo.Component
37847  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
37848  * management and base configuration options shared by all menu components.
37849  * @constructor
37850  * Creates a new BaseItem
37851  * @param {Object} config Configuration options
37852  */
37853 Roo.menu.BaseItem = function(config){
37854     Roo.menu.BaseItem.superclass.constructor.call(this, config);
37855
37856     this.addEvents({
37857         /**
37858          * @event click
37859          * Fires when this item is clicked
37860          * @param {Roo.menu.BaseItem} this
37861          * @param {Roo.EventObject} e
37862          */
37863         click: true,
37864         /**
37865          * @event activate
37866          * Fires when this item is activated
37867          * @param {Roo.menu.BaseItem} this
37868          */
37869         activate : true,
37870         /**
37871          * @event deactivate
37872          * Fires when this item is deactivated
37873          * @param {Roo.menu.BaseItem} this
37874          */
37875         deactivate : true
37876     });
37877
37878     if(this.handler){
37879         this.on("click", this.handler, this.scope, true);
37880     }
37881 };
37882
37883 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
37884     /**
37885      * @cfg {Function} handler
37886      * A function that will handle the click event of this menu item (defaults to undefined)
37887      */
37888     /**
37889      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
37890      */
37891     canActivate : false,
37892     
37893      /**
37894      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
37895      */
37896     hidden: false,
37897     
37898     /**
37899      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
37900      */
37901     activeClass : "x-menu-item-active",
37902     /**
37903      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
37904      */
37905     hideOnClick : true,
37906     /**
37907      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
37908      */
37909     hideDelay : 100,
37910
37911     // private
37912     ctype: "Roo.menu.BaseItem",
37913
37914     // private
37915     actionMode : "container",
37916
37917     // private
37918     render : function(container, parentMenu){
37919         this.parentMenu = parentMenu;
37920         Roo.menu.BaseItem.superclass.render.call(this, container);
37921         this.container.menuItemId = this.id;
37922     },
37923
37924     // private
37925     onRender : function(container, position){
37926         this.el = Roo.get(this.el);
37927         container.dom.appendChild(this.el.dom);
37928     },
37929
37930     // private
37931     onClick : function(e){
37932         if(!this.disabled && this.fireEvent("click", this, e) !== false
37933                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
37934             this.handleClick(e);
37935         }else{
37936             e.stopEvent();
37937         }
37938     },
37939
37940     // private
37941     activate : function(){
37942         if(this.disabled){
37943             return false;
37944         }
37945         var li = this.container;
37946         li.addClass(this.activeClass);
37947         this.region = li.getRegion().adjust(2, 2, -2, -2);
37948         this.fireEvent("activate", this);
37949         return true;
37950     },
37951
37952     // private
37953     deactivate : function(){
37954         this.container.removeClass(this.activeClass);
37955         this.fireEvent("deactivate", this);
37956     },
37957
37958     // private
37959     shouldDeactivate : function(e){
37960         return !this.region || !this.region.contains(e.getPoint());
37961     },
37962
37963     // private
37964     handleClick : function(e){
37965         if(this.hideOnClick){
37966             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
37967         }
37968     },
37969
37970     // private
37971     expandMenu : function(autoActivate){
37972         // do nothing
37973     },
37974
37975     // private
37976     hideMenu : function(){
37977         // do nothing
37978     }
37979 });/*
37980  * Based on:
37981  * Ext JS Library 1.1.1
37982  * Copyright(c) 2006-2007, Ext JS, LLC.
37983  *
37984  * Originally Released Under LGPL - original licence link has changed is not relivant.
37985  *
37986  * Fork - LGPL
37987  * <script type="text/javascript">
37988  */
37989  
37990 /**
37991  * @class Roo.menu.Adapter
37992  * @extends Roo.menu.BaseItem
37993  * 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.
37994  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
37995  * @constructor
37996  * Creates a new Adapter
37997  * @param {Object} config Configuration options
37998  */
37999 Roo.menu.Adapter = function(component, config){
38000     Roo.menu.Adapter.superclass.constructor.call(this, config);
38001     this.component = component;
38002 };
38003 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38004     // private
38005     canActivate : true,
38006
38007     // private
38008     onRender : function(container, position){
38009         this.component.render(container);
38010         this.el = this.component.getEl();
38011     },
38012
38013     // private
38014     activate : function(){
38015         if(this.disabled){
38016             return false;
38017         }
38018         this.component.focus();
38019         this.fireEvent("activate", this);
38020         return true;
38021     },
38022
38023     // private
38024     deactivate : function(){
38025         this.fireEvent("deactivate", this);
38026     },
38027
38028     // private
38029     disable : function(){
38030         this.component.disable();
38031         Roo.menu.Adapter.superclass.disable.call(this);
38032     },
38033
38034     // private
38035     enable : function(){
38036         this.component.enable();
38037         Roo.menu.Adapter.superclass.enable.call(this);
38038     }
38039 });/*
38040  * Based on:
38041  * Ext JS Library 1.1.1
38042  * Copyright(c) 2006-2007, Ext JS, LLC.
38043  *
38044  * Originally Released Under LGPL - original licence link has changed is not relivant.
38045  *
38046  * Fork - LGPL
38047  * <script type="text/javascript">
38048  */
38049
38050 /**
38051  * @class Roo.menu.TextItem
38052  * @extends Roo.menu.BaseItem
38053  * Adds a static text string to a menu, usually used as either a heading or group separator.
38054  * Note: old style constructor with text is still supported.
38055  * 
38056  * @constructor
38057  * Creates a new TextItem
38058  * @param {Object} cfg Configuration
38059  */
38060 Roo.menu.TextItem = function(cfg){
38061     if (typeof(cfg) == 'string') {
38062         this.text = cfg;
38063     } else {
38064         Roo.apply(this,cfg);
38065     }
38066     
38067     Roo.menu.TextItem.superclass.constructor.call(this);
38068 };
38069
38070 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38071     /**
38072      * @cfg {Boolean} text Text to show on item.
38073      */
38074     text : '',
38075     
38076     /**
38077      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38078      */
38079     hideOnClick : false,
38080     /**
38081      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38082      */
38083     itemCls : "x-menu-text",
38084
38085     // private
38086     onRender : function(){
38087         var s = document.createElement("span");
38088         s.className = this.itemCls;
38089         s.innerHTML = this.text;
38090         this.el = s;
38091         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38092     }
38093 });/*
38094  * Based on:
38095  * Ext JS Library 1.1.1
38096  * Copyright(c) 2006-2007, Ext JS, LLC.
38097  *
38098  * Originally Released Under LGPL - original licence link has changed is not relivant.
38099  *
38100  * Fork - LGPL
38101  * <script type="text/javascript">
38102  */
38103
38104 /**
38105  * @class Roo.menu.Separator
38106  * @extends Roo.menu.BaseItem
38107  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38108  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38109  * @constructor
38110  * @param {Object} config Configuration options
38111  */
38112 Roo.menu.Separator = function(config){
38113     Roo.menu.Separator.superclass.constructor.call(this, config);
38114 };
38115
38116 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38117     /**
38118      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38119      */
38120     itemCls : "x-menu-sep",
38121     /**
38122      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38123      */
38124     hideOnClick : false,
38125
38126     // private
38127     onRender : function(li){
38128         var s = document.createElement("span");
38129         s.className = this.itemCls;
38130         s.innerHTML = "&#160;";
38131         this.el = s;
38132         li.addClass("x-menu-sep-li");
38133         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38134     }
38135 });/*
38136  * Based on:
38137  * Ext JS Library 1.1.1
38138  * Copyright(c) 2006-2007, Ext JS, LLC.
38139  *
38140  * Originally Released Under LGPL - original licence link has changed is not relivant.
38141  *
38142  * Fork - LGPL
38143  * <script type="text/javascript">
38144  */
38145 /**
38146  * @class Roo.menu.Item
38147  * @extends Roo.menu.BaseItem
38148  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38149  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38150  * activation and click handling.
38151  * @constructor
38152  * Creates a new Item
38153  * @param {Object} config Configuration options
38154  */
38155 Roo.menu.Item = function(config){
38156     Roo.menu.Item.superclass.constructor.call(this, config);
38157     if(this.menu){
38158         this.menu = Roo.menu.MenuMgr.get(this.menu);
38159     }
38160 };
38161 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38162     
38163     /**
38164      * @cfg {String} text
38165      * The text to show on the menu item.
38166      */
38167     text: '',
38168      /**
38169      * @cfg {String} HTML to render in menu
38170      * The text to show on the menu item (HTML version).
38171      */
38172     html: '',
38173     /**
38174      * @cfg {String} icon
38175      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38176      */
38177     icon: undefined,
38178     /**
38179      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38180      */
38181     itemCls : "x-menu-item",
38182     /**
38183      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38184      */
38185     canActivate : true,
38186     /**
38187      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38188      */
38189     showDelay: 200,
38190     // doc'd in BaseItem
38191     hideDelay: 200,
38192
38193     // private
38194     ctype: "Roo.menu.Item",
38195     
38196     // private
38197     onRender : function(container, position){
38198         var el = document.createElement("a");
38199         el.hideFocus = true;
38200         el.unselectable = "on";
38201         el.href = this.href || "#";
38202         if(this.hrefTarget){
38203             el.target = this.hrefTarget;
38204         }
38205         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38206         
38207         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38208         
38209         el.innerHTML = String.format(
38210                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38211                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38212         this.el = el;
38213         Roo.menu.Item.superclass.onRender.call(this, container, position);
38214     },
38215
38216     /**
38217      * Sets the text to display in this menu item
38218      * @param {String} text The text to display
38219      * @param {Boolean} isHTML true to indicate text is pure html.
38220      */
38221     setText : function(text, isHTML){
38222         if (isHTML) {
38223             this.html = text;
38224         } else {
38225             this.text = text;
38226             this.html = '';
38227         }
38228         if(this.rendered){
38229             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38230      
38231             this.el.update(String.format(
38232                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38233                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38234             this.parentMenu.autoWidth();
38235         }
38236     },
38237
38238     // private
38239     handleClick : function(e){
38240         if(!this.href){ // if no link defined, stop the event automatically
38241             e.stopEvent();
38242         }
38243         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38244     },
38245
38246     // private
38247     activate : function(autoExpand){
38248         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38249             this.focus();
38250             if(autoExpand){
38251                 this.expandMenu();
38252             }
38253         }
38254         return true;
38255     },
38256
38257     // private
38258     shouldDeactivate : function(e){
38259         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38260             if(this.menu && this.menu.isVisible()){
38261                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38262             }
38263             return true;
38264         }
38265         return false;
38266     },
38267
38268     // private
38269     deactivate : function(){
38270         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38271         this.hideMenu();
38272     },
38273
38274     // private
38275     expandMenu : function(autoActivate){
38276         if(!this.disabled && this.menu){
38277             clearTimeout(this.hideTimer);
38278             delete this.hideTimer;
38279             if(!this.menu.isVisible() && !this.showTimer){
38280                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38281             }else if (this.menu.isVisible() && autoActivate){
38282                 this.menu.tryActivate(0, 1);
38283             }
38284         }
38285     },
38286
38287     // private
38288     deferExpand : function(autoActivate){
38289         delete this.showTimer;
38290         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38291         if(autoActivate){
38292             this.menu.tryActivate(0, 1);
38293         }
38294     },
38295
38296     // private
38297     hideMenu : function(){
38298         clearTimeout(this.showTimer);
38299         delete this.showTimer;
38300         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38301             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38302         }
38303     },
38304
38305     // private
38306     deferHide : function(){
38307         delete this.hideTimer;
38308         this.menu.hide();
38309     }
38310 });/*
38311  * Based on:
38312  * Ext JS Library 1.1.1
38313  * Copyright(c) 2006-2007, Ext JS, LLC.
38314  *
38315  * Originally Released Under LGPL - original licence link has changed is not relivant.
38316  *
38317  * Fork - LGPL
38318  * <script type="text/javascript">
38319  */
38320  
38321 /**
38322  * @class Roo.menu.CheckItem
38323  * @extends Roo.menu.Item
38324  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38325  * @constructor
38326  * Creates a new CheckItem
38327  * @param {Object} config Configuration options
38328  */
38329 Roo.menu.CheckItem = function(config){
38330     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38331     this.addEvents({
38332         /**
38333          * @event beforecheckchange
38334          * Fires before the checked value is set, providing an opportunity to cancel if needed
38335          * @param {Roo.menu.CheckItem} this
38336          * @param {Boolean} checked The new checked value that will be set
38337          */
38338         "beforecheckchange" : true,
38339         /**
38340          * @event checkchange
38341          * Fires after the checked value has been set
38342          * @param {Roo.menu.CheckItem} this
38343          * @param {Boolean} checked The checked value that was set
38344          */
38345         "checkchange" : true
38346     });
38347     if(this.checkHandler){
38348         this.on('checkchange', this.checkHandler, this.scope);
38349     }
38350 };
38351 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38352     /**
38353      * @cfg {String} group
38354      * All check items with the same group name will automatically be grouped into a single-select
38355      * radio button group (defaults to '')
38356      */
38357     /**
38358      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38359      */
38360     itemCls : "x-menu-item x-menu-check-item",
38361     /**
38362      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38363      */
38364     groupClass : "x-menu-group-item",
38365
38366     /**
38367      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38368      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38369      * initialized with checked = true will be rendered as checked.
38370      */
38371     checked: false,
38372
38373     // private
38374     ctype: "Roo.menu.CheckItem",
38375
38376     // private
38377     onRender : function(c){
38378         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38379         if(this.group){
38380             this.el.addClass(this.groupClass);
38381         }
38382         Roo.menu.MenuMgr.registerCheckable(this);
38383         if(this.checked){
38384             this.checked = false;
38385             this.setChecked(true, true);
38386         }
38387     },
38388
38389     // private
38390     destroy : function(){
38391         if(this.rendered){
38392             Roo.menu.MenuMgr.unregisterCheckable(this);
38393         }
38394         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38395     },
38396
38397     /**
38398      * Set the checked state of this item
38399      * @param {Boolean} checked The new checked value
38400      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38401      */
38402     setChecked : function(state, suppressEvent){
38403         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38404             if(this.container){
38405                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38406             }
38407             this.checked = state;
38408             if(suppressEvent !== true){
38409                 this.fireEvent("checkchange", this, state);
38410             }
38411         }
38412     },
38413
38414     // private
38415     handleClick : function(e){
38416        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38417            this.setChecked(!this.checked);
38418        }
38419        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38420     }
38421 });/*
38422  * Based on:
38423  * Ext JS Library 1.1.1
38424  * Copyright(c) 2006-2007, Ext JS, LLC.
38425  *
38426  * Originally Released Under LGPL - original licence link has changed is not relivant.
38427  *
38428  * Fork - LGPL
38429  * <script type="text/javascript">
38430  */
38431  
38432 /**
38433  * @class Roo.menu.DateItem
38434  * @extends Roo.menu.Adapter
38435  * A menu item that wraps the {@link Roo.DatPicker} component.
38436  * @constructor
38437  * Creates a new DateItem
38438  * @param {Object} config Configuration options
38439  */
38440 Roo.menu.DateItem = function(config){
38441     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38442     /** The Roo.DatePicker object @type Roo.DatePicker */
38443     this.picker = this.component;
38444     this.addEvents({select: true});
38445     
38446     this.picker.on("render", function(picker){
38447         picker.getEl().swallowEvent("click");
38448         picker.container.addClass("x-menu-date-item");
38449     });
38450
38451     this.picker.on("select", this.onSelect, this);
38452 };
38453
38454 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38455     // private
38456     onSelect : function(picker, date){
38457         this.fireEvent("select", this, date, picker);
38458         Roo.menu.DateItem.superclass.handleClick.call(this);
38459     }
38460 });/*
38461  * Based on:
38462  * Ext JS Library 1.1.1
38463  * Copyright(c) 2006-2007, Ext JS, LLC.
38464  *
38465  * Originally Released Under LGPL - original licence link has changed is not relivant.
38466  *
38467  * Fork - LGPL
38468  * <script type="text/javascript">
38469  */
38470  
38471 /**
38472  * @class Roo.menu.ColorItem
38473  * @extends Roo.menu.Adapter
38474  * A menu item that wraps the {@link Roo.ColorPalette} component.
38475  * @constructor
38476  * Creates a new ColorItem
38477  * @param {Object} config Configuration options
38478  */
38479 Roo.menu.ColorItem = function(config){
38480     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38481     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38482     this.palette = this.component;
38483     this.relayEvents(this.palette, ["select"]);
38484     if(this.selectHandler){
38485         this.on('select', this.selectHandler, this.scope);
38486     }
38487 };
38488 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38489  * Based on:
38490  * Ext JS Library 1.1.1
38491  * Copyright(c) 2006-2007, Ext JS, LLC.
38492  *
38493  * Originally Released Under LGPL - original licence link has changed is not relivant.
38494  *
38495  * Fork - LGPL
38496  * <script type="text/javascript">
38497  */
38498  
38499
38500 /**
38501  * @class Roo.menu.DateMenu
38502  * @extends Roo.menu.Menu
38503  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38504  * @constructor
38505  * Creates a new DateMenu
38506  * @param {Object} config Configuration options
38507  */
38508 Roo.menu.DateMenu = function(config){
38509     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38510     this.plain = true;
38511     var di = new Roo.menu.DateItem(config);
38512     this.add(di);
38513     /**
38514      * The {@link Roo.DatePicker} instance for this DateMenu
38515      * @type DatePicker
38516      */
38517     this.picker = di.picker;
38518     /**
38519      * @event select
38520      * @param {DatePicker} picker
38521      * @param {Date} date
38522      */
38523     this.relayEvents(di, ["select"]);
38524     this.on('beforeshow', function(){
38525         if(this.picker){
38526             this.picker.hideMonthPicker(false);
38527         }
38528     }, this);
38529 };
38530 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38531     cls:'x-date-menu'
38532 });/*
38533  * Based on:
38534  * Ext JS Library 1.1.1
38535  * Copyright(c) 2006-2007, Ext JS, LLC.
38536  *
38537  * Originally Released Under LGPL - original licence link has changed is not relivant.
38538  *
38539  * Fork - LGPL
38540  * <script type="text/javascript">
38541  */
38542  
38543
38544 /**
38545  * @class Roo.menu.ColorMenu
38546  * @extends Roo.menu.Menu
38547  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38548  * @constructor
38549  * Creates a new ColorMenu
38550  * @param {Object} config Configuration options
38551  */
38552 Roo.menu.ColorMenu = function(config){
38553     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38554     this.plain = true;
38555     var ci = new Roo.menu.ColorItem(config);
38556     this.add(ci);
38557     /**
38558      * The {@link Roo.ColorPalette} instance for this ColorMenu
38559      * @type ColorPalette
38560      */
38561     this.palette = ci.palette;
38562     /**
38563      * @event select
38564      * @param {ColorPalette} palette
38565      * @param {String} color
38566      */
38567     this.relayEvents(ci, ["select"]);
38568 };
38569 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
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  * @class Roo.form.Field
38582  * @extends Roo.BoxComponent
38583  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38584  * @constructor
38585  * Creates a new Field
38586  * @param {Object} config Configuration options
38587  */
38588 Roo.form.Field = function(config){
38589     Roo.form.Field.superclass.constructor.call(this, config);
38590 };
38591
38592 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38593     /**
38594      * @cfg {String} fieldLabel Label to use when rendering a form.
38595      */
38596        /**
38597      * @cfg {String} qtip Mouse over tip
38598      */
38599      
38600     /**
38601      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38602      */
38603     invalidClass : "x-form-invalid",
38604     /**
38605      * @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")
38606      */
38607     invalidText : "The value in this field is invalid",
38608     /**
38609      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38610      */
38611     focusClass : "x-form-focus",
38612     /**
38613      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38614       automatic validation (defaults to "keyup").
38615      */
38616     validationEvent : "keyup",
38617     /**
38618      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38619      */
38620     validateOnBlur : true,
38621     /**
38622      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38623      */
38624     validationDelay : 250,
38625     /**
38626      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38627      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38628      */
38629     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38630     /**
38631      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38632      */
38633     fieldClass : "x-form-field",
38634     /**
38635      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38636      *<pre>
38637 Value         Description
38638 -----------   ----------------------------------------------------------------------
38639 qtip          Display a quick tip when the user hovers over the field
38640 title         Display a default browser title attribute popup
38641 under         Add a block div beneath the field containing the error text
38642 side          Add an error icon to the right of the field with a popup on hover
38643 [element id]  Add the error text directly to the innerHTML of the specified element
38644 </pre>
38645      */
38646     msgTarget : 'qtip',
38647     /**
38648      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38649      */
38650     msgFx : 'normal',
38651
38652     /**
38653      * @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.
38654      */
38655     readOnly : false,
38656
38657     /**
38658      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38659      */
38660     disabled : false,
38661
38662     /**
38663      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38664      */
38665     inputType : undefined,
38666     
38667     /**
38668      * @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).
38669          */
38670         tabIndex : undefined,
38671         
38672     // private
38673     isFormField : true,
38674
38675     // private
38676     hasFocus : false,
38677     /**
38678      * @property {Roo.Element} fieldEl
38679      * Element Containing the rendered Field (with label etc.)
38680      */
38681     /**
38682      * @cfg {Mixed} value A value to initialize this field with.
38683      */
38684     value : undefined,
38685
38686     /**
38687      * @cfg {String} name The field's HTML name attribute.
38688      */
38689     /**
38690      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38691      */
38692     // private
38693     loadedValue : false,
38694      
38695      
38696         // private ??
38697         initComponent : function(){
38698         Roo.form.Field.superclass.initComponent.call(this);
38699         this.addEvents({
38700             /**
38701              * @event focus
38702              * Fires when this field receives input focus.
38703              * @param {Roo.form.Field} this
38704              */
38705             focus : true,
38706             /**
38707              * @event blur
38708              * Fires when this field loses input focus.
38709              * @param {Roo.form.Field} this
38710              */
38711             blur : true,
38712             /**
38713              * @event specialkey
38714              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38715              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38716              * @param {Roo.form.Field} this
38717              * @param {Roo.EventObject} e The event object
38718              */
38719             specialkey : true,
38720             /**
38721              * @event change
38722              * Fires just before the field blurs if the field value has changed.
38723              * @param {Roo.form.Field} this
38724              * @param {Mixed} newValue The new value
38725              * @param {Mixed} oldValue The original value
38726              */
38727             change : true,
38728             /**
38729              * @event invalid
38730              * Fires after the field has been marked as invalid.
38731              * @param {Roo.form.Field} this
38732              * @param {String} msg The validation message
38733              */
38734             invalid : true,
38735             /**
38736              * @event valid
38737              * Fires after the field has been validated with no errors.
38738              * @param {Roo.form.Field} this
38739              */
38740             valid : true,
38741              /**
38742              * @event keyup
38743              * Fires after the key up
38744              * @param {Roo.form.Field} this
38745              * @param {Roo.EventObject}  e The event Object
38746              */
38747             keyup : true
38748         });
38749     },
38750
38751     /**
38752      * Returns the name attribute of the field if available
38753      * @return {String} name The field name
38754      */
38755     getName: function(){
38756          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
38757     },
38758
38759     // private
38760     onRender : function(ct, position){
38761         Roo.form.Field.superclass.onRender.call(this, ct, position);
38762         if(!this.el){
38763             var cfg = this.getAutoCreate();
38764             if(!cfg.name){
38765                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38766             }
38767             if (!cfg.name.length) {
38768                 delete cfg.name;
38769             }
38770             if(this.inputType){
38771                 cfg.type = this.inputType;
38772             }
38773             this.el = ct.createChild(cfg, position);
38774         }
38775         var type = this.el.dom.type;
38776         if(type){
38777             if(type == 'password'){
38778                 type = 'text';
38779             }
38780             this.el.addClass('x-form-'+type);
38781         }
38782         if(this.readOnly){
38783             this.el.dom.readOnly = true;
38784         }
38785         if(this.tabIndex !== undefined){
38786             this.el.dom.setAttribute('tabIndex', this.tabIndex);
38787         }
38788
38789         this.el.addClass([this.fieldClass, this.cls]);
38790         this.initValue();
38791     },
38792
38793     /**
38794      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
38795      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
38796      * @return {Roo.form.Field} this
38797      */
38798     applyTo : function(target){
38799         this.allowDomMove = false;
38800         this.el = Roo.get(target);
38801         this.render(this.el.dom.parentNode);
38802         return this;
38803     },
38804
38805     // private
38806     initValue : function(){
38807         if(this.value !== undefined){
38808             this.setValue(this.value);
38809         }else if(this.el.dom.value.length > 0){
38810             this.setValue(this.el.dom.value);
38811         }
38812     },
38813
38814     /**
38815      * Returns true if this field has been changed since it was originally loaded and is not disabled.
38816      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
38817      */
38818     isDirty : function() {
38819         if(this.disabled) {
38820             return false;
38821         }
38822         return String(this.getValue()) !== String(this.originalValue);
38823     },
38824
38825     /**
38826      * stores the current value in loadedValue
38827      */
38828     resetHasChanged : function()
38829     {
38830         this.loadedValue = String(this.getValue());
38831     },
38832     /**
38833      * checks the current value against the 'loaded' value.
38834      * Note - will return false if 'resetHasChanged' has not been called first.
38835      */
38836     hasChanged : function()
38837     {
38838         if(this.disabled || this.readOnly) {
38839             return false;
38840         }
38841         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
38842     },
38843     
38844     
38845     
38846     // private
38847     afterRender : function(){
38848         Roo.form.Field.superclass.afterRender.call(this);
38849         this.initEvents();
38850     },
38851
38852     // private
38853     fireKey : function(e){
38854         //Roo.log('field ' + e.getKey());
38855         if(e.isNavKeyPress()){
38856             this.fireEvent("specialkey", this, e);
38857         }
38858     },
38859
38860     /**
38861      * Resets the current field value to the originally loaded value and clears any validation messages
38862      */
38863     reset : function(){
38864         this.setValue(this.resetValue);
38865         this.clearInvalid();
38866     },
38867
38868     // private
38869     initEvents : function(){
38870         // safari killled keypress - so keydown is now used..
38871         this.el.on("keydown" , this.fireKey,  this);
38872         this.el.on("focus", this.onFocus,  this);
38873         this.el.on("blur", this.onBlur,  this);
38874         this.el.relayEvent('keyup', this);
38875
38876         // reference to original value for reset
38877         this.originalValue = this.getValue();
38878         this.resetValue =  this.getValue();
38879     },
38880
38881     // private
38882     onFocus : function(){
38883         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38884             this.el.addClass(this.focusClass);
38885         }
38886         if(!this.hasFocus){
38887             this.hasFocus = true;
38888             this.startValue = this.getValue();
38889             this.fireEvent("focus", this);
38890         }
38891     },
38892
38893     beforeBlur : Roo.emptyFn,
38894
38895     // private
38896     onBlur : function(){
38897         this.beforeBlur();
38898         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38899             this.el.removeClass(this.focusClass);
38900         }
38901         this.hasFocus = false;
38902         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
38903             this.validate();
38904         }
38905         var v = this.getValue();
38906         if(String(v) !== String(this.startValue)){
38907             this.fireEvent('change', this, v, this.startValue);
38908         }
38909         this.fireEvent("blur", this);
38910     },
38911
38912     /**
38913      * Returns whether or not the field value is currently valid
38914      * @param {Boolean} preventMark True to disable marking the field invalid
38915      * @return {Boolean} True if the value is valid, else false
38916      */
38917     isValid : function(preventMark){
38918         if(this.disabled){
38919             return true;
38920         }
38921         var restore = this.preventMark;
38922         this.preventMark = preventMark === true;
38923         var v = this.validateValue(this.processValue(this.getRawValue()));
38924         this.preventMark = restore;
38925         return v;
38926     },
38927
38928     /**
38929      * Validates the field value
38930      * @return {Boolean} True if the value is valid, else false
38931      */
38932     validate : function(){
38933         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
38934             this.clearInvalid();
38935             return true;
38936         }
38937         return false;
38938     },
38939
38940     processValue : function(value){
38941         return value;
38942     },
38943
38944     // private
38945     // Subclasses should provide the validation implementation by overriding this
38946     validateValue : function(value){
38947         return true;
38948     },
38949
38950     /**
38951      * Mark this field as invalid
38952      * @param {String} msg The validation message
38953      */
38954     markInvalid : function(msg){
38955         if(!this.rendered || this.preventMark){ // not rendered
38956             return;
38957         }
38958         
38959         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
38960         
38961         obj.el.addClass(this.invalidClass);
38962         msg = msg || this.invalidText;
38963         switch(this.msgTarget){
38964             case 'qtip':
38965                 obj.el.dom.qtip = msg;
38966                 obj.el.dom.qclass = 'x-form-invalid-tip';
38967                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
38968                     Roo.QuickTips.enable();
38969                 }
38970                 break;
38971             case 'title':
38972                 this.el.dom.title = msg;
38973                 break;
38974             case 'under':
38975                 if(!this.errorEl){
38976                     var elp = this.el.findParent('.x-form-element', 5, true);
38977                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
38978                     this.errorEl.setWidth(elp.getWidth(true)-20);
38979                 }
38980                 this.errorEl.update(msg);
38981                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
38982                 break;
38983             case 'side':
38984                 if(!this.errorIcon){
38985                     var elp = this.el.findParent('.x-form-element', 5, true);
38986                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
38987                 }
38988                 this.alignErrorIcon();
38989                 this.errorIcon.dom.qtip = msg;
38990                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
38991                 this.errorIcon.show();
38992                 this.on('resize', this.alignErrorIcon, this);
38993                 break;
38994             default:
38995                 var t = Roo.getDom(this.msgTarget);
38996                 t.innerHTML = msg;
38997                 t.style.display = this.msgDisplay;
38998                 break;
38999         }
39000         this.fireEvent('invalid', this, msg);
39001     },
39002
39003     // private
39004     alignErrorIcon : function(){
39005         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39006     },
39007
39008     /**
39009      * Clear any invalid styles/messages for this field
39010      */
39011     clearInvalid : function(){
39012         if(!this.rendered || this.preventMark){ // not rendered
39013             return;
39014         }
39015         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39016         
39017         obj.el.removeClass(this.invalidClass);
39018         switch(this.msgTarget){
39019             case 'qtip':
39020                 obj.el.dom.qtip = '';
39021                 break;
39022             case 'title':
39023                 this.el.dom.title = '';
39024                 break;
39025             case 'under':
39026                 if(this.errorEl){
39027                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39028                 }
39029                 break;
39030             case 'side':
39031                 if(this.errorIcon){
39032                     this.errorIcon.dom.qtip = '';
39033                     this.errorIcon.hide();
39034                     this.un('resize', this.alignErrorIcon, this);
39035                 }
39036                 break;
39037             default:
39038                 var t = Roo.getDom(this.msgTarget);
39039                 t.innerHTML = '';
39040                 t.style.display = 'none';
39041                 break;
39042         }
39043         this.fireEvent('valid', this);
39044     },
39045
39046     /**
39047      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39048      * @return {Mixed} value The field value
39049      */
39050     getRawValue : function(){
39051         var v = this.el.getValue();
39052         
39053         return v;
39054     },
39055
39056     /**
39057      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39058      * @return {Mixed} value The field value
39059      */
39060     getValue : function(){
39061         var v = this.el.getValue();
39062          
39063         return v;
39064     },
39065
39066     /**
39067      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39068      * @param {Mixed} value The value to set
39069      */
39070     setRawValue : function(v){
39071         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39072     },
39073
39074     /**
39075      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39076      * @param {Mixed} value The value to set
39077      */
39078     setValue : function(v){
39079         this.value = v;
39080         if(this.rendered){
39081             this.el.dom.value = (v === null || v === undefined ? '' : v);
39082              this.validate();
39083         }
39084     },
39085
39086     adjustSize : function(w, h){
39087         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39088         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39089         return s;
39090     },
39091
39092     adjustWidth : function(tag, w){
39093         tag = tag.toLowerCase();
39094         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39095             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39096                 if(tag == 'input'){
39097                     return w + 2;
39098                 }
39099                 if(tag == 'textarea'){
39100                     return w-2;
39101                 }
39102             }else if(Roo.isOpera){
39103                 if(tag == 'input'){
39104                     return w + 2;
39105                 }
39106                 if(tag == 'textarea'){
39107                     return w-2;
39108                 }
39109             }
39110         }
39111         return w;
39112     }
39113 });
39114
39115
39116 // anything other than normal should be considered experimental
39117 Roo.form.Field.msgFx = {
39118     normal : {
39119         show: function(msgEl, f){
39120             msgEl.setDisplayed('block');
39121         },
39122
39123         hide : function(msgEl, f){
39124             msgEl.setDisplayed(false).update('');
39125         }
39126     },
39127
39128     slide : {
39129         show: function(msgEl, f){
39130             msgEl.slideIn('t', {stopFx:true});
39131         },
39132
39133         hide : function(msgEl, f){
39134             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39135         }
39136     },
39137
39138     slideRight : {
39139         show: function(msgEl, f){
39140             msgEl.fixDisplay();
39141             msgEl.alignTo(f.el, 'tl-tr');
39142             msgEl.slideIn('l', {stopFx:true});
39143         },
39144
39145         hide : function(msgEl, f){
39146             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39147         }
39148     }
39149 };/*
39150  * Based on:
39151  * Ext JS Library 1.1.1
39152  * Copyright(c) 2006-2007, Ext JS, LLC.
39153  *
39154  * Originally Released Under LGPL - original licence link has changed is not relivant.
39155  *
39156  * Fork - LGPL
39157  * <script type="text/javascript">
39158  */
39159  
39160
39161 /**
39162  * @class Roo.form.TextField
39163  * @extends Roo.form.Field
39164  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39165  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39166  * @constructor
39167  * Creates a new TextField
39168  * @param {Object} config Configuration options
39169  */
39170 Roo.form.TextField = function(config){
39171     Roo.form.TextField.superclass.constructor.call(this, config);
39172     this.addEvents({
39173         /**
39174          * @event autosize
39175          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39176          * according to the default logic, but this event provides a hook for the developer to apply additional
39177          * logic at runtime to resize the field if needed.
39178              * @param {Roo.form.Field} this This text field
39179              * @param {Number} width The new field width
39180              */
39181         autosize : true
39182     });
39183 };
39184
39185 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39186     /**
39187      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39188      */
39189     grow : false,
39190     /**
39191      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39192      */
39193     growMin : 30,
39194     /**
39195      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39196      */
39197     growMax : 800,
39198     /**
39199      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39200      */
39201     vtype : null,
39202     /**
39203      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39204      */
39205     maskRe : null,
39206     /**
39207      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39208      */
39209     disableKeyFilter : false,
39210     /**
39211      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39212      */
39213     allowBlank : true,
39214     /**
39215      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39216      */
39217     minLength : 0,
39218     /**
39219      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39220      */
39221     maxLength : Number.MAX_VALUE,
39222     /**
39223      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39224      */
39225     minLengthText : "The minimum length for this field is {0}",
39226     /**
39227      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39228      */
39229     maxLengthText : "The maximum length for this field is {0}",
39230     /**
39231      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39232      */
39233     selectOnFocus : false,
39234     /**
39235      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39236      */
39237     blankText : "This field is required",
39238     /**
39239      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39240      * If available, this function will be called only after the basic validators all return true, and will be passed the
39241      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39242      */
39243     validator : null,
39244     /**
39245      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39246      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39247      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39248      */
39249     regex : null,
39250     /**
39251      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39252      */
39253     regexText : "",
39254     /**
39255      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39256      */
39257     emptyText : null,
39258    
39259
39260     // private
39261     initEvents : function()
39262     {
39263         if (this.emptyText) {
39264             this.el.attr('placeholder', this.emptyText);
39265         }
39266         
39267         Roo.form.TextField.superclass.initEvents.call(this);
39268         if(this.validationEvent == 'keyup'){
39269             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39270             this.el.on('keyup', this.filterValidation, this);
39271         }
39272         else if(this.validationEvent !== false){
39273             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39274         }
39275         
39276         if(this.selectOnFocus){
39277             this.on("focus", this.preFocus, this);
39278             
39279         }
39280         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39281             this.el.on("keypress", this.filterKeys, this);
39282         }
39283         if(this.grow){
39284             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39285             this.el.on("click", this.autoSize,  this);
39286         }
39287         if(this.el.is('input[type=password]') && Roo.isSafari){
39288             this.el.on('keydown', this.SafariOnKeyDown, this);
39289         }
39290     },
39291
39292     processValue : function(value){
39293         if(this.stripCharsRe){
39294             var newValue = value.replace(this.stripCharsRe, '');
39295             if(newValue !== value){
39296                 this.setRawValue(newValue);
39297                 return newValue;
39298             }
39299         }
39300         return value;
39301     },
39302
39303     filterValidation : function(e){
39304         if(!e.isNavKeyPress()){
39305             this.validationTask.delay(this.validationDelay);
39306         }
39307     },
39308
39309     // private
39310     onKeyUp : function(e){
39311         if(!e.isNavKeyPress()){
39312             this.autoSize();
39313         }
39314     },
39315
39316     /**
39317      * Resets the current field value to the originally-loaded value and clears any validation messages.
39318      *  
39319      */
39320     reset : function(){
39321         Roo.form.TextField.superclass.reset.call(this);
39322        
39323     },
39324
39325     
39326     // private
39327     preFocus : function(){
39328         
39329         if(this.selectOnFocus){
39330             this.el.dom.select();
39331         }
39332     },
39333
39334     
39335     // private
39336     filterKeys : function(e){
39337         var k = e.getKey();
39338         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39339             return;
39340         }
39341         var c = e.getCharCode(), cc = String.fromCharCode(c);
39342         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39343             return;
39344         }
39345         if(!this.maskRe.test(cc)){
39346             e.stopEvent();
39347         }
39348     },
39349
39350     setValue : function(v){
39351         
39352         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39353         
39354         this.autoSize();
39355     },
39356
39357     /**
39358      * Validates a value according to the field's validation rules and marks the field as invalid
39359      * if the validation fails
39360      * @param {Mixed} value The value to validate
39361      * @return {Boolean} True if the value is valid, else false
39362      */
39363     validateValue : function(value){
39364         if(value.length < 1)  { // if it's blank
39365              if(this.allowBlank){
39366                 this.clearInvalid();
39367                 return true;
39368              }else{
39369                 this.markInvalid(this.blankText);
39370                 return false;
39371              }
39372         }
39373         if(value.length < this.minLength){
39374             this.markInvalid(String.format(this.minLengthText, this.minLength));
39375             return false;
39376         }
39377         if(value.length > this.maxLength){
39378             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39379             return false;
39380         }
39381         if(this.vtype){
39382             var vt = Roo.form.VTypes;
39383             if(!vt[this.vtype](value, this)){
39384                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39385                 return false;
39386             }
39387         }
39388         if(typeof this.validator == "function"){
39389             var msg = this.validator(value);
39390             if(msg !== true){
39391                 this.markInvalid(msg);
39392                 return false;
39393             }
39394         }
39395         if(this.regex && !this.regex.test(value)){
39396             this.markInvalid(this.regexText);
39397             return false;
39398         }
39399         return true;
39400     },
39401
39402     /**
39403      * Selects text in this field
39404      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39405      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39406      */
39407     selectText : function(start, end){
39408         var v = this.getRawValue();
39409         if(v.length > 0){
39410             start = start === undefined ? 0 : start;
39411             end = end === undefined ? v.length : end;
39412             var d = this.el.dom;
39413             if(d.setSelectionRange){
39414                 d.setSelectionRange(start, end);
39415             }else if(d.createTextRange){
39416                 var range = d.createTextRange();
39417                 range.moveStart("character", start);
39418                 range.moveEnd("character", v.length-end);
39419                 range.select();
39420             }
39421         }
39422     },
39423
39424     /**
39425      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39426      * This only takes effect if grow = true, and fires the autosize event.
39427      */
39428     autoSize : function(){
39429         if(!this.grow || !this.rendered){
39430             return;
39431         }
39432         if(!this.metrics){
39433             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39434         }
39435         var el = this.el;
39436         var v = el.dom.value;
39437         var d = document.createElement('div');
39438         d.appendChild(document.createTextNode(v));
39439         v = d.innerHTML;
39440         d = null;
39441         v += "&#160;";
39442         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39443         this.el.setWidth(w);
39444         this.fireEvent("autosize", this, w);
39445     },
39446     
39447     // private
39448     SafariOnKeyDown : function(event)
39449     {
39450         // this is a workaround for a password hang bug on chrome/ webkit.
39451         
39452         var isSelectAll = false;
39453         
39454         if(this.el.dom.selectionEnd > 0){
39455             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39456         }
39457         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39458             event.preventDefault();
39459             this.setValue('');
39460             return;
39461         }
39462         
39463         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39464             
39465             event.preventDefault();
39466             // this is very hacky as keydown always get's upper case.
39467             
39468             var cc = String.fromCharCode(event.getCharCode());
39469             
39470             
39471             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39472             
39473         }
39474         
39475         
39476     }
39477 });/*
39478  * Based on:
39479  * Ext JS Library 1.1.1
39480  * Copyright(c) 2006-2007, Ext JS, LLC.
39481  *
39482  * Originally Released Under LGPL - original licence link has changed is not relivant.
39483  *
39484  * Fork - LGPL
39485  * <script type="text/javascript">
39486  */
39487  
39488 /**
39489  * @class Roo.form.Hidden
39490  * @extends Roo.form.TextField
39491  * Simple Hidden element used on forms 
39492  * 
39493  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39494  * 
39495  * @constructor
39496  * Creates a new Hidden form element.
39497  * @param {Object} config Configuration options
39498  */
39499
39500
39501
39502 // easy hidden field...
39503 Roo.form.Hidden = function(config){
39504     Roo.form.Hidden.superclass.constructor.call(this, config);
39505 };
39506   
39507 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39508     fieldLabel:      '',
39509     inputType:      'hidden',
39510     width:          50,
39511     allowBlank:     true,
39512     labelSeparator: '',
39513     hidden:         true,
39514     itemCls :       'x-form-item-display-none'
39515
39516
39517 });
39518
39519
39520 /*
39521  * Based on:
39522  * Ext JS Library 1.1.1
39523  * Copyright(c) 2006-2007, Ext JS, LLC.
39524  *
39525  * Originally Released Under LGPL - original licence link has changed is not relivant.
39526  *
39527  * Fork - LGPL
39528  * <script type="text/javascript">
39529  */
39530  
39531 /**
39532  * @class Roo.form.TriggerField
39533  * @extends Roo.form.TextField
39534  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39535  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39536  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39537  * for which you can provide a custom implementation.  For example:
39538  * <pre><code>
39539 var trigger = new Roo.form.TriggerField();
39540 trigger.onTriggerClick = myTriggerFn;
39541 trigger.applyTo('my-field');
39542 </code></pre>
39543  *
39544  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39545  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39546  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39547  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39548  * @constructor
39549  * Create a new TriggerField.
39550  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39551  * to the base TextField)
39552  */
39553 Roo.form.TriggerField = function(config){
39554     this.mimicing = false;
39555     Roo.form.TriggerField.superclass.constructor.call(this, config);
39556 };
39557
39558 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39559     /**
39560      * @cfg {String} triggerClass A CSS class to apply to the trigger
39561      */
39562     /**
39563      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39564      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39565      */
39566     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39567     /**
39568      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39569      */
39570     hideTrigger:false,
39571
39572     /** @cfg {Boolean} grow @hide */
39573     /** @cfg {Number} growMin @hide */
39574     /** @cfg {Number} growMax @hide */
39575
39576     /**
39577      * @hide 
39578      * @method
39579      */
39580     autoSize: Roo.emptyFn,
39581     // private
39582     monitorTab : true,
39583     // private
39584     deferHeight : true,
39585
39586     
39587     actionMode : 'wrap',
39588     // private
39589     onResize : function(w, h){
39590         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39591         if(typeof w == 'number'){
39592             var x = w - this.trigger.getWidth();
39593             this.el.setWidth(this.adjustWidth('input', x));
39594             this.trigger.setStyle('left', x+'px');
39595         }
39596     },
39597
39598     // private
39599     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39600
39601     // private
39602     getResizeEl : function(){
39603         return this.wrap;
39604     },
39605
39606     // private
39607     getPositionEl : function(){
39608         return this.wrap;
39609     },
39610
39611     // private
39612     alignErrorIcon : function(){
39613         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39614     },
39615
39616     // private
39617     onRender : function(ct, position){
39618         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39619         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39620         this.trigger = this.wrap.createChild(this.triggerConfig ||
39621                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39622         if(this.hideTrigger){
39623             this.trigger.setDisplayed(false);
39624         }
39625         this.initTrigger();
39626         if(!this.width){
39627             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39628         }
39629     },
39630
39631     // private
39632     initTrigger : function(){
39633         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39634         this.trigger.addClassOnOver('x-form-trigger-over');
39635         this.trigger.addClassOnClick('x-form-trigger-click');
39636     },
39637
39638     // private
39639     onDestroy : function(){
39640         if(this.trigger){
39641             this.trigger.removeAllListeners();
39642             this.trigger.remove();
39643         }
39644         if(this.wrap){
39645             this.wrap.remove();
39646         }
39647         Roo.form.TriggerField.superclass.onDestroy.call(this);
39648     },
39649
39650     // private
39651     onFocus : function(){
39652         Roo.form.TriggerField.superclass.onFocus.call(this);
39653         if(!this.mimicing){
39654             this.wrap.addClass('x-trigger-wrap-focus');
39655             this.mimicing = true;
39656             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39657             if(this.monitorTab){
39658                 this.el.on("keydown", this.checkTab, this);
39659             }
39660         }
39661     },
39662
39663     // private
39664     checkTab : function(e){
39665         if(e.getKey() == e.TAB){
39666             this.triggerBlur();
39667         }
39668     },
39669
39670     // private
39671     onBlur : function(){
39672         // do nothing
39673     },
39674
39675     // private
39676     mimicBlur : function(e, t){
39677         if(!this.wrap.contains(t) && this.validateBlur()){
39678             this.triggerBlur();
39679         }
39680     },
39681
39682     // private
39683     triggerBlur : function(){
39684         this.mimicing = false;
39685         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39686         if(this.monitorTab){
39687             this.el.un("keydown", this.checkTab, this);
39688         }
39689         this.wrap.removeClass('x-trigger-wrap-focus');
39690         Roo.form.TriggerField.superclass.onBlur.call(this);
39691     },
39692
39693     // private
39694     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39695     validateBlur : function(e, t){
39696         return true;
39697     },
39698
39699     // private
39700     onDisable : function(){
39701         Roo.form.TriggerField.superclass.onDisable.call(this);
39702         if(this.wrap){
39703             this.wrap.addClass('x-item-disabled');
39704         }
39705     },
39706
39707     // private
39708     onEnable : function(){
39709         Roo.form.TriggerField.superclass.onEnable.call(this);
39710         if(this.wrap){
39711             this.wrap.removeClass('x-item-disabled');
39712         }
39713     },
39714
39715     // private
39716     onShow : function(){
39717         var ae = this.getActionEl();
39718         
39719         if(ae){
39720             ae.dom.style.display = '';
39721             ae.dom.style.visibility = 'visible';
39722         }
39723     },
39724
39725     // private
39726     
39727     onHide : function(){
39728         var ae = this.getActionEl();
39729         ae.dom.style.display = 'none';
39730     },
39731
39732     /**
39733      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39734      * by an implementing function.
39735      * @method
39736      * @param {EventObject} e
39737      */
39738     onTriggerClick : Roo.emptyFn
39739 });
39740
39741 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
39742 // to be extended by an implementing class.  For an example of implementing this class, see the custom
39743 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
39744 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
39745     initComponent : function(){
39746         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
39747
39748         this.triggerConfig = {
39749             tag:'span', cls:'x-form-twin-triggers', cn:[
39750             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
39751             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
39752         ]};
39753     },
39754
39755     getTrigger : function(index){
39756         return this.triggers[index];
39757     },
39758
39759     initTrigger : function(){
39760         var ts = this.trigger.select('.x-form-trigger', true);
39761         this.wrap.setStyle('overflow', 'hidden');
39762         var triggerField = this;
39763         ts.each(function(t, all, index){
39764             t.hide = function(){
39765                 var w = triggerField.wrap.getWidth();
39766                 this.dom.style.display = 'none';
39767                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39768             };
39769             t.show = function(){
39770                 var w = triggerField.wrap.getWidth();
39771                 this.dom.style.display = '';
39772                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39773             };
39774             var triggerIndex = 'Trigger'+(index+1);
39775
39776             if(this['hide'+triggerIndex]){
39777                 t.dom.style.display = 'none';
39778             }
39779             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
39780             t.addClassOnOver('x-form-trigger-over');
39781             t.addClassOnClick('x-form-trigger-click');
39782         }, this);
39783         this.triggers = ts.elements;
39784     },
39785
39786     onTrigger1Click : Roo.emptyFn,
39787     onTrigger2Click : Roo.emptyFn
39788 });/*
39789  * Based on:
39790  * Ext JS Library 1.1.1
39791  * Copyright(c) 2006-2007, Ext JS, LLC.
39792  *
39793  * Originally Released Under LGPL - original licence link has changed is not relivant.
39794  *
39795  * Fork - LGPL
39796  * <script type="text/javascript">
39797  */
39798  
39799 /**
39800  * @class Roo.form.TextArea
39801  * @extends Roo.form.TextField
39802  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
39803  * support for auto-sizing.
39804  * @constructor
39805  * Creates a new TextArea
39806  * @param {Object} config Configuration options
39807  */
39808 Roo.form.TextArea = function(config){
39809     Roo.form.TextArea.superclass.constructor.call(this, config);
39810     // these are provided exchanges for backwards compat
39811     // minHeight/maxHeight were replaced by growMin/growMax to be
39812     // compatible with TextField growing config values
39813     if(this.minHeight !== undefined){
39814         this.growMin = this.minHeight;
39815     }
39816     if(this.maxHeight !== undefined){
39817         this.growMax = this.maxHeight;
39818     }
39819 };
39820
39821 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
39822     /**
39823      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
39824      */
39825     growMin : 60,
39826     /**
39827      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
39828      */
39829     growMax: 1000,
39830     /**
39831      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
39832      * in the field (equivalent to setting overflow: hidden, defaults to false)
39833      */
39834     preventScrollbars: false,
39835     /**
39836      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39837      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
39838      */
39839
39840     // private
39841     onRender : function(ct, position){
39842         if(!this.el){
39843             this.defaultAutoCreate = {
39844                 tag: "textarea",
39845                 style:"width:300px;height:60px;",
39846                 autocomplete: "new-password"
39847             };
39848         }
39849         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
39850         if(this.grow){
39851             this.textSizeEl = Roo.DomHelper.append(document.body, {
39852                 tag: "pre", cls: "x-form-grow-sizer"
39853             });
39854             if(this.preventScrollbars){
39855                 this.el.setStyle("overflow", "hidden");
39856             }
39857             this.el.setHeight(this.growMin);
39858         }
39859     },
39860
39861     onDestroy : function(){
39862         if(this.textSizeEl){
39863             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
39864         }
39865         Roo.form.TextArea.superclass.onDestroy.call(this);
39866     },
39867
39868     // private
39869     onKeyUp : function(e){
39870         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
39871             this.autoSize();
39872         }
39873     },
39874
39875     /**
39876      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
39877      * This only takes effect if grow = true, and fires the autosize event if the height changes.
39878      */
39879     autoSize : function(){
39880         if(!this.grow || !this.textSizeEl){
39881             return;
39882         }
39883         var el = this.el;
39884         var v = el.dom.value;
39885         var ts = this.textSizeEl;
39886
39887         ts.innerHTML = '';
39888         ts.appendChild(document.createTextNode(v));
39889         v = ts.innerHTML;
39890
39891         Roo.fly(ts).setWidth(this.el.getWidth());
39892         if(v.length < 1){
39893             v = "&#160;&#160;";
39894         }else{
39895             if(Roo.isIE){
39896                 v = v.replace(/\n/g, '<p>&#160;</p>');
39897             }
39898             v += "&#160;\n&#160;";
39899         }
39900         ts.innerHTML = v;
39901         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
39902         if(h != this.lastHeight){
39903             this.lastHeight = h;
39904             this.el.setHeight(h);
39905             this.fireEvent("autosize", this, h);
39906         }
39907     }
39908 });/*
39909  * Based on:
39910  * Ext JS Library 1.1.1
39911  * Copyright(c) 2006-2007, Ext JS, LLC.
39912  *
39913  * Originally Released Under LGPL - original licence link has changed is not relivant.
39914  *
39915  * Fork - LGPL
39916  * <script type="text/javascript">
39917  */
39918  
39919
39920 /**
39921  * @class Roo.form.NumberField
39922  * @extends Roo.form.TextField
39923  * Numeric text field that provides automatic keystroke filtering and numeric validation.
39924  * @constructor
39925  * Creates a new NumberField
39926  * @param {Object} config Configuration options
39927  */
39928 Roo.form.NumberField = function(config){
39929     Roo.form.NumberField.superclass.constructor.call(this, config);
39930 };
39931
39932 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
39933     /**
39934      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
39935      */
39936     fieldClass: "x-form-field x-form-num-field",
39937     /**
39938      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39939      */
39940     allowDecimals : true,
39941     /**
39942      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39943      */
39944     decimalSeparator : ".",
39945     /**
39946      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39947      */
39948     decimalPrecision : 2,
39949     /**
39950      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39951      */
39952     allowNegative : true,
39953     /**
39954      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39955      */
39956     minValue : Number.NEGATIVE_INFINITY,
39957     /**
39958      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39959      */
39960     maxValue : Number.MAX_VALUE,
39961     /**
39962      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39963      */
39964     minText : "The minimum value for this field is {0}",
39965     /**
39966      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39967      */
39968     maxText : "The maximum value for this field is {0}",
39969     /**
39970      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39971      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39972      */
39973     nanText : "{0} is not a valid number",
39974
39975     // private
39976     initEvents : function(){
39977         Roo.form.NumberField.superclass.initEvents.call(this);
39978         var allowed = "0123456789";
39979         if(this.allowDecimals){
39980             allowed += this.decimalSeparator;
39981         }
39982         if(this.allowNegative){
39983             allowed += "-";
39984         }
39985         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39986         var keyPress = function(e){
39987             var k = e.getKey();
39988             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39989                 return;
39990             }
39991             var c = e.getCharCode();
39992             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39993                 e.stopEvent();
39994             }
39995         };
39996         this.el.on("keypress", keyPress, this);
39997     },
39998
39999     // private
40000     validateValue : function(value){
40001         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40002             return false;
40003         }
40004         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40005              return true;
40006         }
40007         var num = this.parseValue(value);
40008         if(isNaN(num)){
40009             this.markInvalid(String.format(this.nanText, value));
40010             return false;
40011         }
40012         if(num < this.minValue){
40013             this.markInvalid(String.format(this.minText, this.minValue));
40014             return false;
40015         }
40016         if(num > this.maxValue){
40017             this.markInvalid(String.format(this.maxText, this.maxValue));
40018             return false;
40019         }
40020         return true;
40021     },
40022
40023     getValue : function(){
40024         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40025     },
40026
40027     // private
40028     parseValue : function(value){
40029         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40030         return isNaN(value) ? '' : value;
40031     },
40032
40033     // private
40034     fixPrecision : function(value){
40035         var nan = isNaN(value);
40036         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40037             return nan ? '' : value;
40038         }
40039         return parseFloat(value).toFixed(this.decimalPrecision);
40040     },
40041
40042     setValue : function(v){
40043         v = this.fixPrecision(v);
40044         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40045     },
40046
40047     // private
40048     decimalPrecisionFcn : function(v){
40049         return Math.floor(v);
40050     },
40051
40052     beforeBlur : function(){
40053         var v = this.parseValue(this.getRawValue());
40054         if(v){
40055             this.setValue(v);
40056         }
40057     }
40058 });/*
40059  * Based on:
40060  * Ext JS Library 1.1.1
40061  * Copyright(c) 2006-2007, Ext JS, LLC.
40062  *
40063  * Originally Released Under LGPL - original licence link has changed is not relivant.
40064  *
40065  * Fork - LGPL
40066  * <script type="text/javascript">
40067  */
40068  
40069 /**
40070  * @class Roo.form.DateField
40071  * @extends Roo.form.TriggerField
40072  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40073 * @constructor
40074 * Create a new DateField
40075 * @param {Object} config
40076  */
40077 Roo.form.DateField = function(config){
40078     Roo.form.DateField.superclass.constructor.call(this, config);
40079     
40080       this.addEvents({
40081          
40082         /**
40083          * @event select
40084          * Fires when a date is selected
40085              * @param {Roo.form.DateField} combo This combo box
40086              * @param {Date} date The date selected
40087              */
40088         'select' : true
40089          
40090     });
40091     
40092     
40093     if(typeof this.minValue == "string") {
40094         this.minValue = this.parseDate(this.minValue);
40095     }
40096     if(typeof this.maxValue == "string") {
40097         this.maxValue = this.parseDate(this.maxValue);
40098     }
40099     this.ddMatch = null;
40100     if(this.disabledDates){
40101         var dd = this.disabledDates;
40102         var re = "(?:";
40103         for(var i = 0; i < dd.length; i++){
40104             re += dd[i];
40105             if(i != dd.length-1) {
40106                 re += "|";
40107             }
40108         }
40109         this.ddMatch = new RegExp(re + ")");
40110     }
40111 };
40112
40113 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40114     /**
40115      * @cfg {String} format
40116      * The default date format string which can be overriden for localization support.  The format must be
40117      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40118      */
40119     format : "m/d/y",
40120     /**
40121      * @cfg {String} altFormats
40122      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40123      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40124      */
40125     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40126     /**
40127      * @cfg {Array} disabledDays
40128      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40129      */
40130     disabledDays : null,
40131     /**
40132      * @cfg {String} disabledDaysText
40133      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40134      */
40135     disabledDaysText : "Disabled",
40136     /**
40137      * @cfg {Array} disabledDates
40138      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40139      * expression so they are very powerful. Some examples:
40140      * <ul>
40141      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40142      * <li>["03/08", "09/16"] would disable those days for every year</li>
40143      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40144      * <li>["03/../2006"] would disable every day in March 2006</li>
40145      * <li>["^03"] would disable every day in every March</li>
40146      * </ul>
40147      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40148      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40149      */
40150     disabledDates : null,
40151     /**
40152      * @cfg {String} disabledDatesText
40153      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40154      */
40155     disabledDatesText : "Disabled",
40156     /**
40157      * @cfg {Date/String} minValue
40158      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40159      * valid format (defaults to null).
40160      */
40161     minValue : null,
40162     /**
40163      * @cfg {Date/String} maxValue
40164      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40165      * valid format (defaults to null).
40166      */
40167     maxValue : null,
40168     /**
40169      * @cfg {String} minText
40170      * The error text to display when the date in the cell is before minValue (defaults to
40171      * 'The date in this field must be after {minValue}').
40172      */
40173     minText : "The date in this field must be equal to or after {0}",
40174     /**
40175      * @cfg {String} maxText
40176      * The error text to display when the date in the cell is after maxValue (defaults to
40177      * 'The date in this field must be before {maxValue}').
40178      */
40179     maxText : "The date in this field must be equal to or before {0}",
40180     /**
40181      * @cfg {String} invalidText
40182      * The error text to display when the date in the field is invalid (defaults to
40183      * '{value} is not a valid date - it must be in the format {format}').
40184      */
40185     invalidText : "{0} is not a valid date - it must be in the format {1}",
40186     /**
40187      * @cfg {String} triggerClass
40188      * An additional CSS class used to style the trigger button.  The trigger will always get the
40189      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40190      * which displays a calendar icon).
40191      */
40192     triggerClass : 'x-form-date-trigger',
40193     
40194
40195     /**
40196      * @cfg {Boolean} useIso
40197      * if enabled, then the date field will use a hidden field to store the 
40198      * real value as iso formated date. default (false)
40199      */ 
40200     useIso : false,
40201     /**
40202      * @cfg {String/Object} autoCreate
40203      * A DomHelper element spec, or true for a default element spec (defaults to
40204      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40205      */ 
40206     // private
40207     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40208     
40209     // private
40210     hiddenField: false,
40211     
40212     onRender : function(ct, position)
40213     {
40214         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40215         if (this.useIso) {
40216             //this.el.dom.removeAttribute('name'); 
40217             Roo.log("Changing name?");
40218             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40219             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40220                     'before', true);
40221             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40222             // prevent input submission
40223             this.hiddenName = this.name;
40224         }
40225             
40226             
40227     },
40228     
40229     // private
40230     validateValue : function(value)
40231     {
40232         value = this.formatDate(value);
40233         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40234             Roo.log('super failed');
40235             return false;
40236         }
40237         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40238              return true;
40239         }
40240         var svalue = value;
40241         value = this.parseDate(value);
40242         if(!value){
40243             Roo.log('parse date failed' + svalue);
40244             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40245             return false;
40246         }
40247         var time = value.getTime();
40248         if(this.minValue && time < this.minValue.getTime()){
40249             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40250             return false;
40251         }
40252         if(this.maxValue && time > this.maxValue.getTime()){
40253             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40254             return false;
40255         }
40256         if(this.disabledDays){
40257             var day = value.getDay();
40258             for(var i = 0; i < this.disabledDays.length; i++) {
40259                 if(day === this.disabledDays[i]){
40260                     this.markInvalid(this.disabledDaysText);
40261                     return false;
40262                 }
40263             }
40264         }
40265         var fvalue = this.formatDate(value);
40266         if(this.ddMatch && this.ddMatch.test(fvalue)){
40267             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40268             return false;
40269         }
40270         return true;
40271     },
40272
40273     // private
40274     // Provides logic to override the default TriggerField.validateBlur which just returns true
40275     validateBlur : function(){
40276         return !this.menu || !this.menu.isVisible();
40277     },
40278     
40279     getName: function()
40280     {
40281         // returns hidden if it's set..
40282         if (!this.rendered) {return ''};
40283         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40284         
40285     },
40286
40287     /**
40288      * Returns the current date value of the date field.
40289      * @return {Date} The date value
40290      */
40291     getValue : function(){
40292         
40293         return  this.hiddenField ?
40294                 this.hiddenField.value :
40295                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40296     },
40297
40298     /**
40299      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40300      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40301      * (the default format used is "m/d/y").
40302      * <br />Usage:
40303      * <pre><code>
40304 //All of these calls set the same date value (May 4, 2006)
40305
40306 //Pass a date object:
40307 var dt = new Date('5/4/06');
40308 dateField.setValue(dt);
40309
40310 //Pass a date string (default format):
40311 dateField.setValue('5/4/06');
40312
40313 //Pass a date string (custom format):
40314 dateField.format = 'Y-m-d';
40315 dateField.setValue('2006-5-4');
40316 </code></pre>
40317      * @param {String/Date} date The date or valid date string
40318      */
40319     setValue : function(date){
40320         if (this.hiddenField) {
40321             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40322         }
40323         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40324         // make sure the value field is always stored as a date..
40325         this.value = this.parseDate(date);
40326         
40327         
40328     },
40329
40330     // private
40331     parseDate : function(value){
40332         if(!value || value instanceof Date){
40333             return value;
40334         }
40335         var v = Date.parseDate(value, this.format);
40336          if (!v && this.useIso) {
40337             v = Date.parseDate(value, 'Y-m-d');
40338         }
40339         if(!v && this.altFormats){
40340             if(!this.altFormatsArray){
40341                 this.altFormatsArray = this.altFormats.split("|");
40342             }
40343             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40344                 v = Date.parseDate(value, this.altFormatsArray[i]);
40345             }
40346         }
40347         return v;
40348     },
40349
40350     // private
40351     formatDate : function(date, fmt){
40352         return (!date || !(date instanceof Date)) ?
40353                date : date.dateFormat(fmt || this.format);
40354     },
40355
40356     // private
40357     menuListeners : {
40358         select: function(m, d){
40359             
40360             this.setValue(d);
40361             this.fireEvent('select', this, d);
40362         },
40363         show : function(){ // retain focus styling
40364             this.onFocus();
40365         },
40366         hide : function(){
40367             this.focus.defer(10, this);
40368             var ml = this.menuListeners;
40369             this.menu.un("select", ml.select,  this);
40370             this.menu.un("show", ml.show,  this);
40371             this.menu.un("hide", ml.hide,  this);
40372         }
40373     },
40374
40375     // private
40376     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40377     onTriggerClick : function(){
40378         if(this.disabled){
40379             return;
40380         }
40381         if(this.menu == null){
40382             this.menu = new Roo.menu.DateMenu();
40383         }
40384         Roo.apply(this.menu.picker,  {
40385             showClear: this.allowBlank,
40386             minDate : this.minValue,
40387             maxDate : this.maxValue,
40388             disabledDatesRE : this.ddMatch,
40389             disabledDatesText : this.disabledDatesText,
40390             disabledDays : this.disabledDays,
40391             disabledDaysText : this.disabledDaysText,
40392             format : this.useIso ? 'Y-m-d' : this.format,
40393             minText : String.format(this.minText, this.formatDate(this.minValue)),
40394             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40395         });
40396         this.menu.on(Roo.apply({}, this.menuListeners, {
40397             scope:this
40398         }));
40399         this.menu.picker.setValue(this.getValue() || new Date());
40400         this.menu.show(this.el, "tl-bl?");
40401     },
40402
40403     beforeBlur : function(){
40404         var v = this.parseDate(this.getRawValue());
40405         if(v){
40406             this.setValue(v);
40407         }
40408     },
40409
40410     /*@
40411      * overide
40412      * 
40413      */
40414     isDirty : function() {
40415         if(this.disabled) {
40416             return false;
40417         }
40418         
40419         if(typeof(this.startValue) === 'undefined'){
40420             return false;
40421         }
40422         
40423         return String(this.getValue()) !== String(this.startValue);
40424         
40425     }
40426 });/*
40427  * Based on:
40428  * Ext JS Library 1.1.1
40429  * Copyright(c) 2006-2007, Ext JS, LLC.
40430  *
40431  * Originally Released Under LGPL - original licence link has changed is not relivant.
40432  *
40433  * Fork - LGPL
40434  * <script type="text/javascript">
40435  */
40436  
40437 /**
40438  * @class Roo.form.MonthField
40439  * @extends Roo.form.TriggerField
40440  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40441 * @constructor
40442 * Create a new MonthField
40443 * @param {Object} config
40444  */
40445 Roo.form.MonthField = function(config){
40446     
40447     Roo.form.MonthField.superclass.constructor.call(this, config);
40448     
40449       this.addEvents({
40450          
40451         /**
40452          * @event select
40453          * Fires when a date is selected
40454              * @param {Roo.form.MonthFieeld} combo This combo box
40455              * @param {Date} date The date selected
40456              */
40457         'select' : true
40458          
40459     });
40460     
40461     
40462     if(typeof this.minValue == "string") {
40463         this.minValue = this.parseDate(this.minValue);
40464     }
40465     if(typeof this.maxValue == "string") {
40466         this.maxValue = this.parseDate(this.maxValue);
40467     }
40468     this.ddMatch = null;
40469     if(this.disabledDates){
40470         var dd = this.disabledDates;
40471         var re = "(?:";
40472         for(var i = 0; i < dd.length; i++){
40473             re += dd[i];
40474             if(i != dd.length-1) {
40475                 re += "|";
40476             }
40477         }
40478         this.ddMatch = new RegExp(re + ")");
40479     }
40480 };
40481
40482 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40483     /**
40484      * @cfg {String} format
40485      * The default date format string which can be overriden for localization support.  The format must be
40486      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40487      */
40488     format : "M Y",
40489     /**
40490      * @cfg {String} altFormats
40491      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40492      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40493      */
40494     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40495     /**
40496      * @cfg {Array} disabledDays
40497      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40498      */
40499     disabledDays : [0,1,2,3,4,5,6],
40500     /**
40501      * @cfg {String} disabledDaysText
40502      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40503      */
40504     disabledDaysText : "Disabled",
40505     /**
40506      * @cfg {Array} disabledDates
40507      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40508      * expression so they are very powerful. Some examples:
40509      * <ul>
40510      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40511      * <li>["03/08", "09/16"] would disable those days for every year</li>
40512      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40513      * <li>["03/../2006"] would disable every day in March 2006</li>
40514      * <li>["^03"] would disable every day in every March</li>
40515      * </ul>
40516      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40517      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40518      */
40519     disabledDates : null,
40520     /**
40521      * @cfg {String} disabledDatesText
40522      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40523      */
40524     disabledDatesText : "Disabled",
40525     /**
40526      * @cfg {Date/String} minValue
40527      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40528      * valid format (defaults to null).
40529      */
40530     minValue : null,
40531     /**
40532      * @cfg {Date/String} maxValue
40533      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40534      * valid format (defaults to null).
40535      */
40536     maxValue : null,
40537     /**
40538      * @cfg {String} minText
40539      * The error text to display when the date in the cell is before minValue (defaults to
40540      * 'The date in this field must be after {minValue}').
40541      */
40542     minText : "The date in this field must be equal to or after {0}",
40543     /**
40544      * @cfg {String} maxTextf
40545      * The error text to display when the date in the cell is after maxValue (defaults to
40546      * 'The date in this field must be before {maxValue}').
40547      */
40548     maxText : "The date in this field must be equal to or before {0}",
40549     /**
40550      * @cfg {String} invalidText
40551      * The error text to display when the date in the field is invalid (defaults to
40552      * '{value} is not a valid date - it must be in the format {format}').
40553      */
40554     invalidText : "{0} is not a valid date - it must be in the format {1}",
40555     /**
40556      * @cfg {String} triggerClass
40557      * An additional CSS class used to style the trigger button.  The trigger will always get the
40558      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40559      * which displays a calendar icon).
40560      */
40561     triggerClass : 'x-form-date-trigger',
40562     
40563
40564     /**
40565      * @cfg {Boolean} useIso
40566      * if enabled, then the date field will use a hidden field to store the 
40567      * real value as iso formated date. default (true)
40568      */ 
40569     useIso : true,
40570     /**
40571      * @cfg {String/Object} autoCreate
40572      * A DomHelper element spec, or true for a default element spec (defaults to
40573      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40574      */ 
40575     // private
40576     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40577     
40578     // private
40579     hiddenField: false,
40580     
40581     hideMonthPicker : false,
40582     
40583     onRender : function(ct, position)
40584     {
40585         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40586         if (this.useIso) {
40587             this.el.dom.removeAttribute('name'); 
40588             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40589                     'before', true);
40590             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40591             // prevent input submission
40592             this.hiddenName = this.name;
40593         }
40594             
40595             
40596     },
40597     
40598     // private
40599     validateValue : function(value)
40600     {
40601         value = this.formatDate(value);
40602         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40603             return false;
40604         }
40605         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40606              return true;
40607         }
40608         var svalue = value;
40609         value = this.parseDate(value);
40610         if(!value){
40611             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40612             return false;
40613         }
40614         var time = value.getTime();
40615         if(this.minValue && time < this.minValue.getTime()){
40616             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40617             return false;
40618         }
40619         if(this.maxValue && time > this.maxValue.getTime()){
40620             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40621             return false;
40622         }
40623         /*if(this.disabledDays){
40624             var day = value.getDay();
40625             for(var i = 0; i < this.disabledDays.length; i++) {
40626                 if(day === this.disabledDays[i]){
40627                     this.markInvalid(this.disabledDaysText);
40628                     return false;
40629                 }
40630             }
40631         }
40632         */
40633         var fvalue = this.formatDate(value);
40634         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40635             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40636             return false;
40637         }
40638         */
40639         return true;
40640     },
40641
40642     // private
40643     // Provides logic to override the default TriggerField.validateBlur which just returns true
40644     validateBlur : function(){
40645         return !this.menu || !this.menu.isVisible();
40646     },
40647
40648     /**
40649      * Returns the current date value of the date field.
40650      * @return {Date} The date value
40651      */
40652     getValue : function(){
40653         
40654         
40655         
40656         return  this.hiddenField ?
40657                 this.hiddenField.value :
40658                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40659     },
40660
40661     /**
40662      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40663      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40664      * (the default format used is "m/d/y").
40665      * <br />Usage:
40666      * <pre><code>
40667 //All of these calls set the same date value (May 4, 2006)
40668
40669 //Pass a date object:
40670 var dt = new Date('5/4/06');
40671 monthField.setValue(dt);
40672
40673 //Pass a date string (default format):
40674 monthField.setValue('5/4/06');
40675
40676 //Pass a date string (custom format):
40677 monthField.format = 'Y-m-d';
40678 monthField.setValue('2006-5-4');
40679 </code></pre>
40680      * @param {String/Date} date The date or valid date string
40681      */
40682     setValue : function(date){
40683         Roo.log('month setValue' + date);
40684         // can only be first of month..
40685         
40686         var val = this.parseDate(date);
40687         
40688         if (this.hiddenField) {
40689             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40690         }
40691         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40692         this.value = this.parseDate(date);
40693     },
40694
40695     // private
40696     parseDate : function(value){
40697         if(!value || value instanceof Date){
40698             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40699             return value;
40700         }
40701         var v = Date.parseDate(value, this.format);
40702         if (!v && this.useIso) {
40703             v = Date.parseDate(value, 'Y-m-d');
40704         }
40705         if (v) {
40706             // 
40707             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40708         }
40709         
40710         
40711         if(!v && this.altFormats){
40712             if(!this.altFormatsArray){
40713                 this.altFormatsArray = this.altFormats.split("|");
40714             }
40715             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40716                 v = Date.parseDate(value, this.altFormatsArray[i]);
40717             }
40718         }
40719         return v;
40720     },
40721
40722     // private
40723     formatDate : function(date, fmt){
40724         return (!date || !(date instanceof Date)) ?
40725                date : date.dateFormat(fmt || this.format);
40726     },
40727
40728     // private
40729     menuListeners : {
40730         select: function(m, d){
40731             this.setValue(d);
40732             this.fireEvent('select', this, d);
40733         },
40734         show : function(){ // retain focus styling
40735             this.onFocus();
40736         },
40737         hide : function(){
40738             this.focus.defer(10, this);
40739             var ml = this.menuListeners;
40740             this.menu.un("select", ml.select,  this);
40741             this.menu.un("show", ml.show,  this);
40742             this.menu.un("hide", ml.hide,  this);
40743         }
40744     },
40745     // private
40746     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40747     onTriggerClick : function(){
40748         if(this.disabled){
40749             return;
40750         }
40751         if(this.menu == null){
40752             this.menu = new Roo.menu.DateMenu();
40753            
40754         }
40755         
40756         Roo.apply(this.menu.picker,  {
40757             
40758             showClear: this.allowBlank,
40759             minDate : this.minValue,
40760             maxDate : this.maxValue,
40761             disabledDatesRE : this.ddMatch,
40762             disabledDatesText : this.disabledDatesText,
40763             
40764             format : this.useIso ? 'Y-m-d' : this.format,
40765             minText : String.format(this.minText, this.formatDate(this.minValue)),
40766             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40767             
40768         });
40769          this.menu.on(Roo.apply({}, this.menuListeners, {
40770             scope:this
40771         }));
40772        
40773         
40774         var m = this.menu;
40775         var p = m.picker;
40776         
40777         // hide month picker get's called when we called by 'before hide';
40778         
40779         var ignorehide = true;
40780         p.hideMonthPicker  = function(disableAnim){
40781             if (ignorehide) {
40782                 return;
40783             }
40784              if(this.monthPicker){
40785                 Roo.log("hideMonthPicker called");
40786                 if(disableAnim === true){
40787                     this.monthPicker.hide();
40788                 }else{
40789                     this.monthPicker.slideOut('t', {duration:.2});
40790                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
40791                     p.fireEvent("select", this, this.value);
40792                     m.hide();
40793                 }
40794             }
40795         }
40796         
40797         Roo.log('picker set value');
40798         Roo.log(this.getValue());
40799         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
40800         m.show(this.el, 'tl-bl?');
40801         ignorehide  = false;
40802         // this will trigger hideMonthPicker..
40803         
40804         
40805         // hidden the day picker
40806         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
40807         
40808         
40809         
40810       
40811         
40812         p.showMonthPicker.defer(100, p);
40813     
40814         
40815        
40816     },
40817
40818     beforeBlur : function(){
40819         var v = this.parseDate(this.getRawValue());
40820         if(v){
40821             this.setValue(v);
40822         }
40823     }
40824
40825     /** @cfg {Boolean} grow @hide */
40826     /** @cfg {Number} growMin @hide */
40827     /** @cfg {Number} growMax @hide */
40828     /**
40829      * @hide
40830      * @method autoSize
40831      */
40832 });/*
40833  * Based on:
40834  * Ext JS Library 1.1.1
40835  * Copyright(c) 2006-2007, Ext JS, LLC.
40836  *
40837  * Originally Released Under LGPL - original licence link has changed is not relivant.
40838  *
40839  * Fork - LGPL
40840  * <script type="text/javascript">
40841  */
40842  
40843
40844 /**
40845  * @class Roo.form.ComboBox
40846  * @extends Roo.form.TriggerField
40847  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
40848  * @constructor
40849  * Create a new ComboBox.
40850  * @param {Object} config Configuration options
40851  */
40852 Roo.form.ComboBox = function(config){
40853     Roo.form.ComboBox.superclass.constructor.call(this, config);
40854     this.addEvents({
40855         /**
40856          * @event expand
40857          * Fires when the dropdown list is expanded
40858              * @param {Roo.form.ComboBox} combo This combo box
40859              */
40860         'expand' : true,
40861         /**
40862          * @event collapse
40863          * Fires when the dropdown list is collapsed
40864              * @param {Roo.form.ComboBox} combo This combo box
40865              */
40866         'collapse' : true,
40867         /**
40868          * @event beforeselect
40869          * Fires before a list item is selected. Return false to cancel the selection.
40870              * @param {Roo.form.ComboBox} combo This combo box
40871              * @param {Roo.data.Record} record The data record returned from the underlying store
40872              * @param {Number} index The index of the selected item in the dropdown list
40873              */
40874         'beforeselect' : true,
40875         /**
40876          * @event select
40877          * Fires when a list item is selected
40878              * @param {Roo.form.ComboBox} combo This combo box
40879              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
40880              * @param {Number} index The index of the selected item in the dropdown list
40881              */
40882         'select' : true,
40883         /**
40884          * @event beforequery
40885          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
40886          * The event object passed has these properties:
40887              * @param {Roo.form.ComboBox} combo This combo box
40888              * @param {String} query The query
40889              * @param {Boolean} forceAll true to force "all" query
40890              * @param {Boolean} cancel true to cancel the query
40891              * @param {Object} e The query event object
40892              */
40893         'beforequery': true,
40894          /**
40895          * @event add
40896          * Fires when the 'add' icon is pressed (add a listener to enable add button)
40897              * @param {Roo.form.ComboBox} combo This combo box
40898              */
40899         'add' : true,
40900         /**
40901          * @event edit
40902          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
40903              * @param {Roo.form.ComboBox} combo This combo box
40904              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
40905              */
40906         'edit' : true
40907         
40908         
40909     });
40910     if(this.transform){
40911         this.allowDomMove = false;
40912         var s = Roo.getDom(this.transform);
40913         if(!this.hiddenName){
40914             this.hiddenName = s.name;
40915         }
40916         if(!this.store){
40917             this.mode = 'local';
40918             var d = [], opts = s.options;
40919             for(var i = 0, len = opts.length;i < len; i++){
40920                 var o = opts[i];
40921                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
40922                 if(o.selected) {
40923                     this.value = value;
40924                 }
40925                 d.push([value, o.text]);
40926             }
40927             this.store = new Roo.data.SimpleStore({
40928                 'id': 0,
40929                 fields: ['value', 'text'],
40930                 data : d
40931             });
40932             this.valueField = 'value';
40933             this.displayField = 'text';
40934         }
40935         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
40936         if(!this.lazyRender){
40937             this.target = true;
40938             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
40939             s.parentNode.removeChild(s); // remove it
40940             this.render(this.el.parentNode);
40941         }else{
40942             s.parentNode.removeChild(s); // remove it
40943         }
40944
40945     }
40946     if (this.store) {
40947         this.store = Roo.factory(this.store, Roo.data);
40948     }
40949     
40950     this.selectedIndex = -1;
40951     if(this.mode == 'local'){
40952         if(config.queryDelay === undefined){
40953             this.queryDelay = 10;
40954         }
40955         if(config.minChars === undefined){
40956             this.minChars = 0;
40957         }
40958     }
40959 };
40960
40961 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
40962     /**
40963      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
40964      */
40965     /**
40966      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
40967      * rendering into an Roo.Editor, defaults to false)
40968      */
40969     /**
40970      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
40971      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
40972      */
40973     /**
40974      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
40975      */
40976     /**
40977      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
40978      * the dropdown list (defaults to undefined, with no header element)
40979      */
40980
40981      /**
40982      * @cfg {String/Roo.Template} tpl The template to use to render the output
40983      */
40984      
40985     // private
40986     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
40987     /**
40988      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
40989      */
40990     listWidth: undefined,
40991     /**
40992      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
40993      * mode = 'remote' or 'text' if mode = 'local')
40994      */
40995     displayField: undefined,
40996     /**
40997      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
40998      * mode = 'remote' or 'value' if mode = 'local'). 
40999      * Note: use of a valueField requires the user make a selection
41000      * in order for a value to be mapped.
41001      */
41002     valueField: undefined,
41003     
41004     
41005     /**
41006      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41007      * field's data value (defaults to the underlying DOM element's name)
41008      */
41009     hiddenName: undefined,
41010     /**
41011      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41012      */
41013     listClass: '',
41014     /**
41015      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41016      */
41017     selectedClass: 'x-combo-selected',
41018     /**
41019      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41020      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41021      * which displays a downward arrow icon).
41022      */
41023     triggerClass : 'x-form-arrow-trigger',
41024     /**
41025      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41026      */
41027     shadow:'sides',
41028     /**
41029      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41030      * anchor positions (defaults to 'tl-bl')
41031      */
41032     listAlign: 'tl-bl?',
41033     /**
41034      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41035      */
41036     maxHeight: 300,
41037     /**
41038      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41039      * query specified by the allQuery config option (defaults to 'query')
41040      */
41041     triggerAction: 'query',
41042     /**
41043      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41044      * (defaults to 4, does not apply if editable = false)
41045      */
41046     minChars : 4,
41047     /**
41048      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41049      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41050      */
41051     typeAhead: false,
41052     /**
41053      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41054      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41055      */
41056     queryDelay: 500,
41057     /**
41058      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41059      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41060      */
41061     pageSize: 0,
41062     /**
41063      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41064      * when editable = true (defaults to false)
41065      */
41066     selectOnFocus:false,
41067     /**
41068      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41069      */
41070     queryParam: 'query',
41071     /**
41072      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41073      * when mode = 'remote' (defaults to 'Loading...')
41074      */
41075     loadingText: 'Loading...',
41076     /**
41077      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41078      */
41079     resizable: false,
41080     /**
41081      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41082      */
41083     handleHeight : 8,
41084     /**
41085      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41086      * traditional select (defaults to true)
41087      */
41088     editable: true,
41089     /**
41090      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41091      */
41092     allQuery: '',
41093     /**
41094      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41095      */
41096     mode: 'remote',
41097     /**
41098      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41099      * listWidth has a higher value)
41100      */
41101     minListWidth : 70,
41102     /**
41103      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41104      * allow the user to set arbitrary text into the field (defaults to false)
41105      */
41106     forceSelection:false,
41107     /**
41108      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41109      * if typeAhead = true (defaults to 250)
41110      */
41111     typeAheadDelay : 250,
41112     /**
41113      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41114      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41115      */
41116     valueNotFoundText : undefined,
41117     /**
41118      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41119      */
41120     blockFocus : false,
41121     
41122     /**
41123      * @cfg {Boolean} disableClear Disable showing of clear button.
41124      */
41125     disableClear : false,
41126     /**
41127      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41128      */
41129     alwaysQuery : false,
41130     
41131     //private
41132     addicon : false,
41133     editicon: false,
41134     
41135     // element that contains real text value.. (when hidden is used..)
41136      
41137     // private
41138     onRender : function(ct, position){
41139         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41140         if(this.hiddenName){
41141             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41142                     'before', true);
41143             this.hiddenField.value =
41144                 this.hiddenValue !== undefined ? this.hiddenValue :
41145                 this.value !== undefined ? this.value : '';
41146
41147             // prevent input submission
41148             this.el.dom.removeAttribute('name');
41149              
41150              
41151         }
41152         if(Roo.isGecko){
41153             this.el.dom.setAttribute('autocomplete', 'off');
41154         }
41155
41156         var cls = 'x-combo-list';
41157
41158         this.list = new Roo.Layer({
41159             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41160         });
41161
41162         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41163         this.list.setWidth(lw);
41164         this.list.swallowEvent('mousewheel');
41165         this.assetHeight = 0;
41166
41167         if(this.title){
41168             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41169             this.assetHeight += this.header.getHeight();
41170         }
41171
41172         this.innerList = this.list.createChild({cls:cls+'-inner'});
41173         this.innerList.on('mouseover', this.onViewOver, this);
41174         this.innerList.on('mousemove', this.onViewMove, this);
41175         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41176         
41177         if(this.allowBlank && !this.pageSize && !this.disableClear){
41178             this.footer = this.list.createChild({cls:cls+'-ft'});
41179             this.pageTb = new Roo.Toolbar(this.footer);
41180            
41181         }
41182         if(this.pageSize){
41183             this.footer = this.list.createChild({cls:cls+'-ft'});
41184             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41185                     {pageSize: this.pageSize});
41186             
41187         }
41188         
41189         if (this.pageTb && this.allowBlank && !this.disableClear) {
41190             var _this = this;
41191             this.pageTb.add(new Roo.Toolbar.Fill(), {
41192                 cls: 'x-btn-icon x-btn-clear',
41193                 text: '&#160;',
41194                 handler: function()
41195                 {
41196                     _this.collapse();
41197                     _this.clearValue();
41198                     _this.onSelect(false, -1);
41199                 }
41200             });
41201         }
41202         if (this.footer) {
41203             this.assetHeight += this.footer.getHeight();
41204         }
41205         
41206
41207         if(!this.tpl){
41208             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41209         }
41210
41211         this.view = new Roo.View(this.innerList, this.tpl, {
41212             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41213         });
41214
41215         this.view.on('click', this.onViewClick, this);
41216
41217         this.store.on('beforeload', this.onBeforeLoad, this);
41218         this.store.on('load', this.onLoad, this);
41219         this.store.on('loadexception', this.onLoadException, this);
41220
41221         if(this.resizable){
41222             this.resizer = new Roo.Resizable(this.list,  {
41223                pinned:true, handles:'se'
41224             });
41225             this.resizer.on('resize', function(r, w, h){
41226                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41227                 this.listWidth = w;
41228                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41229                 this.restrictHeight();
41230             }, this);
41231             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41232         }
41233         if(!this.editable){
41234             this.editable = true;
41235             this.setEditable(false);
41236         }  
41237         
41238         
41239         if (typeof(this.events.add.listeners) != 'undefined') {
41240             
41241             this.addicon = this.wrap.createChild(
41242                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41243        
41244             this.addicon.on('click', function(e) {
41245                 this.fireEvent('add', this);
41246             }, this);
41247         }
41248         if (typeof(this.events.edit.listeners) != 'undefined') {
41249             
41250             this.editicon = this.wrap.createChild(
41251                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41252             if (this.addicon) {
41253                 this.editicon.setStyle('margin-left', '40px');
41254             }
41255             this.editicon.on('click', function(e) {
41256                 
41257                 // we fire even  if inothing is selected..
41258                 this.fireEvent('edit', this, this.lastData );
41259                 
41260             }, this);
41261         }
41262         
41263         
41264         
41265     },
41266
41267     // private
41268     initEvents : function(){
41269         Roo.form.ComboBox.superclass.initEvents.call(this);
41270
41271         this.keyNav = new Roo.KeyNav(this.el, {
41272             "up" : function(e){
41273                 this.inKeyMode = true;
41274                 this.selectPrev();
41275             },
41276
41277             "down" : function(e){
41278                 if(!this.isExpanded()){
41279                     this.onTriggerClick();
41280                 }else{
41281                     this.inKeyMode = true;
41282                     this.selectNext();
41283                 }
41284             },
41285
41286             "enter" : function(e){
41287                 this.onViewClick();
41288                 //return true;
41289             },
41290
41291             "esc" : function(e){
41292                 this.collapse();
41293             },
41294
41295             "tab" : function(e){
41296                 this.onViewClick(false);
41297                 this.fireEvent("specialkey", this, e);
41298                 return true;
41299             },
41300
41301             scope : this,
41302
41303             doRelay : function(foo, bar, hname){
41304                 if(hname == 'down' || this.scope.isExpanded()){
41305                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41306                 }
41307                 return true;
41308             },
41309
41310             forceKeyDown: true
41311         });
41312         this.queryDelay = Math.max(this.queryDelay || 10,
41313                 this.mode == 'local' ? 10 : 250);
41314         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41315         if(this.typeAhead){
41316             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41317         }
41318         if(this.editable !== false){
41319             this.el.on("keyup", this.onKeyUp, this);
41320         }
41321         if(this.forceSelection){
41322             this.on('blur', this.doForce, this);
41323         }
41324     },
41325
41326     onDestroy : function(){
41327         if(this.view){
41328             this.view.setStore(null);
41329             this.view.el.removeAllListeners();
41330             this.view.el.remove();
41331             this.view.purgeListeners();
41332         }
41333         if(this.list){
41334             this.list.destroy();
41335         }
41336         if(this.store){
41337             this.store.un('beforeload', this.onBeforeLoad, this);
41338             this.store.un('load', this.onLoad, this);
41339             this.store.un('loadexception', this.onLoadException, this);
41340         }
41341         Roo.form.ComboBox.superclass.onDestroy.call(this);
41342     },
41343
41344     // private
41345     fireKey : function(e){
41346         if(e.isNavKeyPress() && !this.list.isVisible()){
41347             this.fireEvent("specialkey", this, e);
41348         }
41349     },
41350
41351     // private
41352     onResize: function(w, h){
41353         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41354         
41355         if(typeof w != 'number'){
41356             // we do not handle it!?!?
41357             return;
41358         }
41359         var tw = this.trigger.getWidth();
41360         tw += this.addicon ? this.addicon.getWidth() : 0;
41361         tw += this.editicon ? this.editicon.getWidth() : 0;
41362         var x = w - tw;
41363         this.el.setWidth( this.adjustWidth('input', x));
41364             
41365         this.trigger.setStyle('left', x+'px');
41366         
41367         if(this.list && this.listWidth === undefined){
41368             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41369             this.list.setWidth(lw);
41370             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41371         }
41372         
41373     
41374         
41375     },
41376
41377     /**
41378      * Allow or prevent the user from directly editing the field text.  If false is passed,
41379      * the user will only be able to select from the items defined in the dropdown list.  This method
41380      * is the runtime equivalent of setting the 'editable' config option at config time.
41381      * @param {Boolean} value True to allow the user to directly edit the field text
41382      */
41383     setEditable : function(value){
41384         if(value == this.editable){
41385             return;
41386         }
41387         this.editable = value;
41388         if(!value){
41389             this.el.dom.setAttribute('readOnly', true);
41390             this.el.on('mousedown', this.onTriggerClick,  this);
41391             this.el.addClass('x-combo-noedit');
41392         }else{
41393             this.el.dom.setAttribute('readOnly', false);
41394             this.el.un('mousedown', this.onTriggerClick,  this);
41395             this.el.removeClass('x-combo-noedit');
41396         }
41397     },
41398
41399     // private
41400     onBeforeLoad : function(){
41401         if(!this.hasFocus){
41402             return;
41403         }
41404         this.innerList.update(this.loadingText ?
41405                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41406         this.restrictHeight();
41407         this.selectedIndex = -1;
41408     },
41409
41410     // private
41411     onLoad : function(){
41412         if(!this.hasFocus){
41413             return;
41414         }
41415         if(this.store.getCount() > 0){
41416             this.expand();
41417             this.restrictHeight();
41418             if(this.lastQuery == this.allQuery){
41419                 if(this.editable){
41420                     this.el.dom.select();
41421                 }
41422                 if(!this.selectByValue(this.value, true)){
41423                     this.select(0, true);
41424                 }
41425             }else{
41426                 this.selectNext();
41427                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41428                     this.taTask.delay(this.typeAheadDelay);
41429                 }
41430             }
41431         }else{
41432             this.onEmptyResults();
41433         }
41434         //this.el.focus();
41435     },
41436     // private
41437     onLoadException : function()
41438     {
41439         this.collapse();
41440         Roo.log(this.store.reader.jsonData);
41441         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41442             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41443         }
41444         
41445         
41446     },
41447     // private
41448     onTypeAhead : function(){
41449         if(this.store.getCount() > 0){
41450             var r = this.store.getAt(0);
41451             var newValue = r.data[this.displayField];
41452             var len = newValue.length;
41453             var selStart = this.getRawValue().length;
41454             if(selStart != len){
41455                 this.setRawValue(newValue);
41456                 this.selectText(selStart, newValue.length);
41457             }
41458         }
41459     },
41460
41461     // private
41462     onSelect : function(record, index){
41463         if(this.fireEvent('beforeselect', this, record, index) !== false){
41464             this.setFromData(index > -1 ? record.data : false);
41465             this.collapse();
41466             this.fireEvent('select', this, record, index);
41467         }
41468     },
41469
41470     /**
41471      * Returns the currently selected field value or empty string if no value is set.
41472      * @return {String} value The selected value
41473      */
41474     getValue : function(){
41475         if(this.valueField){
41476             return typeof this.value != 'undefined' ? this.value : '';
41477         }
41478         return Roo.form.ComboBox.superclass.getValue.call(this);
41479     },
41480
41481     /**
41482      * Clears any text/value currently set in the field
41483      */
41484     clearValue : function(){
41485         if(this.hiddenField){
41486             this.hiddenField.value = '';
41487         }
41488         this.value = '';
41489         this.setRawValue('');
41490         this.lastSelectionText = '';
41491         
41492     },
41493
41494     /**
41495      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41496      * will be displayed in the field.  If the value does not match the data value of an existing item,
41497      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41498      * Otherwise the field will be blank (although the value will still be set).
41499      * @param {String} value The value to match
41500      */
41501     setValue : function(v){
41502         var text = v;
41503         if(this.valueField){
41504             var r = this.findRecord(this.valueField, v);
41505             if(r){
41506                 text = r.data[this.displayField];
41507             }else if(this.valueNotFoundText !== undefined){
41508                 text = this.valueNotFoundText;
41509             }
41510         }
41511         this.lastSelectionText = text;
41512         if(this.hiddenField){
41513             this.hiddenField.value = v;
41514         }
41515         Roo.form.ComboBox.superclass.setValue.call(this, text);
41516         this.value = v;
41517     },
41518     /**
41519      * @property {Object} the last set data for the element
41520      */
41521     
41522     lastData : false,
41523     /**
41524      * Sets the value of the field based on a object which is related to the record format for the store.
41525      * @param {Object} value the value to set as. or false on reset?
41526      */
41527     setFromData : function(o){
41528         var dv = ''; // display value
41529         var vv = ''; // value value..
41530         this.lastData = o;
41531         if (this.displayField) {
41532             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41533         } else {
41534             // this is an error condition!!!
41535             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41536         }
41537         
41538         if(this.valueField){
41539             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41540         }
41541         if(this.hiddenField){
41542             this.hiddenField.value = vv;
41543             
41544             this.lastSelectionText = dv;
41545             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41546             this.value = vv;
41547             return;
41548         }
41549         // no hidden field.. - we store the value in 'value', but still display
41550         // display field!!!!
41551         this.lastSelectionText = dv;
41552         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41553         this.value = vv;
41554         
41555         
41556     },
41557     // private
41558     reset : function(){
41559         // overridden so that last data is reset..
41560         this.setValue(this.resetValue);
41561         this.clearInvalid();
41562         this.lastData = false;
41563         if (this.view) {
41564             this.view.clearSelections();
41565         }
41566     },
41567     // private
41568     findRecord : function(prop, value){
41569         var record;
41570         if(this.store.getCount() > 0){
41571             this.store.each(function(r){
41572                 if(r.data[prop] == value){
41573                     record = r;
41574                     return false;
41575                 }
41576                 return true;
41577             });
41578         }
41579         return record;
41580     },
41581     
41582     getName: function()
41583     {
41584         // returns hidden if it's set..
41585         if (!this.rendered) {return ''};
41586         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41587         
41588     },
41589     // private
41590     onViewMove : function(e, t){
41591         this.inKeyMode = false;
41592     },
41593
41594     // private
41595     onViewOver : function(e, t){
41596         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41597             return;
41598         }
41599         var item = this.view.findItemFromChild(t);
41600         if(item){
41601             var index = this.view.indexOf(item);
41602             this.select(index, false);
41603         }
41604     },
41605
41606     // private
41607     onViewClick : function(doFocus)
41608     {
41609         var index = this.view.getSelectedIndexes()[0];
41610         var r = this.store.getAt(index);
41611         if(r){
41612             this.onSelect(r, index);
41613         }
41614         if(doFocus !== false && !this.blockFocus){
41615             this.el.focus();
41616         }
41617     },
41618
41619     // private
41620     restrictHeight : function(){
41621         this.innerList.dom.style.height = '';
41622         var inner = this.innerList.dom;
41623         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41624         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41625         this.list.beginUpdate();
41626         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41627         this.list.alignTo(this.el, this.listAlign);
41628         this.list.endUpdate();
41629     },
41630
41631     // private
41632     onEmptyResults : function(){
41633         this.collapse();
41634     },
41635
41636     /**
41637      * Returns true if the dropdown list is expanded, else false.
41638      */
41639     isExpanded : function(){
41640         return this.list.isVisible();
41641     },
41642
41643     /**
41644      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41645      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41646      * @param {String} value The data value of the item to select
41647      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41648      * selected item if it is not currently in view (defaults to true)
41649      * @return {Boolean} True if the value matched an item in the list, else false
41650      */
41651     selectByValue : function(v, scrollIntoView){
41652         if(v !== undefined && v !== null){
41653             var r = this.findRecord(this.valueField || this.displayField, v);
41654             if(r){
41655                 this.select(this.store.indexOf(r), scrollIntoView);
41656                 return true;
41657             }
41658         }
41659         return false;
41660     },
41661
41662     /**
41663      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41664      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41665      * @param {Number} index The zero-based index of the list item to select
41666      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41667      * selected item if it is not currently in view (defaults to true)
41668      */
41669     select : function(index, scrollIntoView){
41670         this.selectedIndex = index;
41671         this.view.select(index);
41672         if(scrollIntoView !== false){
41673             var el = this.view.getNode(index);
41674             if(el){
41675                 this.innerList.scrollChildIntoView(el, false);
41676             }
41677         }
41678     },
41679
41680     // private
41681     selectNext : function(){
41682         var ct = this.store.getCount();
41683         if(ct > 0){
41684             if(this.selectedIndex == -1){
41685                 this.select(0);
41686             }else if(this.selectedIndex < ct-1){
41687                 this.select(this.selectedIndex+1);
41688             }
41689         }
41690     },
41691
41692     // private
41693     selectPrev : function(){
41694         var ct = this.store.getCount();
41695         if(ct > 0){
41696             if(this.selectedIndex == -1){
41697                 this.select(0);
41698             }else if(this.selectedIndex != 0){
41699                 this.select(this.selectedIndex-1);
41700             }
41701         }
41702     },
41703
41704     // private
41705     onKeyUp : function(e){
41706         if(this.editable !== false && !e.isSpecialKey()){
41707             this.lastKey = e.getKey();
41708             this.dqTask.delay(this.queryDelay);
41709         }
41710     },
41711
41712     // private
41713     validateBlur : function(){
41714         return !this.list || !this.list.isVisible();   
41715     },
41716
41717     // private
41718     initQuery : function(){
41719         this.doQuery(this.getRawValue());
41720     },
41721
41722     // private
41723     doForce : function(){
41724         if(this.el.dom.value.length > 0){
41725             this.el.dom.value =
41726                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
41727              
41728         }
41729     },
41730
41731     /**
41732      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
41733      * query allowing the query action to be canceled if needed.
41734      * @param {String} query The SQL query to execute
41735      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
41736      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
41737      * saved in the current store (defaults to false)
41738      */
41739     doQuery : function(q, forceAll){
41740         if(q === undefined || q === null){
41741             q = '';
41742         }
41743         var qe = {
41744             query: q,
41745             forceAll: forceAll,
41746             combo: this,
41747             cancel:false
41748         };
41749         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
41750             return false;
41751         }
41752         q = qe.query;
41753         forceAll = qe.forceAll;
41754         if(forceAll === true || (q.length >= this.minChars)){
41755             if(this.lastQuery != q || this.alwaysQuery){
41756                 this.lastQuery = q;
41757                 if(this.mode == 'local'){
41758                     this.selectedIndex = -1;
41759                     if(forceAll){
41760                         this.store.clearFilter();
41761                     }else{
41762                         this.store.filter(this.displayField, q);
41763                     }
41764                     this.onLoad();
41765                 }else{
41766                     this.store.baseParams[this.queryParam] = q;
41767                     this.store.load({
41768                         params: this.getParams(q)
41769                     });
41770                     this.expand();
41771                 }
41772             }else{
41773                 this.selectedIndex = -1;
41774                 this.onLoad();   
41775             }
41776         }
41777     },
41778
41779     // private
41780     getParams : function(q){
41781         var p = {};
41782         //p[this.queryParam] = q;
41783         if(this.pageSize){
41784             p.start = 0;
41785             p.limit = this.pageSize;
41786         }
41787         return p;
41788     },
41789
41790     /**
41791      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
41792      */
41793     collapse : function(){
41794         if(!this.isExpanded()){
41795             return;
41796         }
41797         this.list.hide();
41798         Roo.get(document).un('mousedown', this.collapseIf, this);
41799         Roo.get(document).un('mousewheel', this.collapseIf, this);
41800         if (!this.editable) {
41801             Roo.get(document).un('keydown', this.listKeyPress, this);
41802         }
41803         this.fireEvent('collapse', this);
41804     },
41805
41806     // private
41807     collapseIf : function(e){
41808         if(!e.within(this.wrap) && !e.within(this.list)){
41809             this.collapse();
41810         }
41811     },
41812
41813     /**
41814      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
41815      */
41816     expand : function(){
41817         if(this.isExpanded() || !this.hasFocus){
41818             return;
41819         }
41820         this.list.alignTo(this.el, this.listAlign);
41821         this.list.show();
41822         Roo.get(document).on('mousedown', this.collapseIf, this);
41823         Roo.get(document).on('mousewheel', this.collapseIf, this);
41824         if (!this.editable) {
41825             Roo.get(document).on('keydown', this.listKeyPress, this);
41826         }
41827         
41828         this.fireEvent('expand', this);
41829     },
41830
41831     // private
41832     // Implements the default empty TriggerField.onTriggerClick function
41833     onTriggerClick : function(){
41834         if(this.disabled){
41835             return;
41836         }
41837         if(this.isExpanded()){
41838             this.collapse();
41839             if (!this.blockFocus) {
41840                 this.el.focus();
41841             }
41842             
41843         }else {
41844             this.hasFocus = true;
41845             if(this.triggerAction == 'all') {
41846                 this.doQuery(this.allQuery, true);
41847             } else {
41848                 this.doQuery(this.getRawValue());
41849             }
41850             if (!this.blockFocus) {
41851                 this.el.focus();
41852             }
41853         }
41854     },
41855     listKeyPress : function(e)
41856     {
41857         //Roo.log('listkeypress');
41858         // scroll to first matching element based on key pres..
41859         if (e.isSpecialKey()) {
41860             return false;
41861         }
41862         var k = String.fromCharCode(e.getKey()).toUpperCase();
41863         //Roo.log(k);
41864         var match  = false;
41865         var csel = this.view.getSelectedNodes();
41866         var cselitem = false;
41867         if (csel.length) {
41868             var ix = this.view.indexOf(csel[0]);
41869             cselitem  = this.store.getAt(ix);
41870             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
41871                 cselitem = false;
41872             }
41873             
41874         }
41875         
41876         this.store.each(function(v) { 
41877             if (cselitem) {
41878                 // start at existing selection.
41879                 if (cselitem.id == v.id) {
41880                     cselitem = false;
41881                 }
41882                 return;
41883             }
41884                 
41885             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
41886                 match = this.store.indexOf(v);
41887                 return false;
41888             }
41889         }, this);
41890         
41891         if (match === false) {
41892             return true; // no more action?
41893         }
41894         // scroll to?
41895         this.view.select(match);
41896         var sn = Roo.get(this.view.getSelectedNodes()[0]);
41897         sn.scrollIntoView(sn.dom.parentNode, false);
41898     }
41899
41900     /** 
41901     * @cfg {Boolean} grow 
41902     * @hide 
41903     */
41904     /** 
41905     * @cfg {Number} growMin 
41906     * @hide 
41907     */
41908     /** 
41909     * @cfg {Number} growMax 
41910     * @hide 
41911     */
41912     /**
41913      * @hide
41914      * @method autoSize
41915      */
41916 });/*
41917  * Copyright(c) 2010-2012, Roo J Solutions Limited
41918  *
41919  * Licence LGPL
41920  *
41921  */
41922
41923 /**
41924  * @class Roo.form.ComboBoxArray
41925  * @extends Roo.form.TextField
41926  * A facebook style adder... for lists of email / people / countries  etc...
41927  * pick multiple items from a combo box, and shows each one.
41928  *
41929  *  Fred [x]  Brian [x]  [Pick another |v]
41930  *
41931  *
41932  *  For this to work: it needs various extra information
41933  *    - normal combo problay has
41934  *      name, hiddenName
41935  *    + displayField, valueField
41936  *
41937  *    For our purpose...
41938  *
41939  *
41940  *   If we change from 'extends' to wrapping...
41941  *   
41942  *  
41943  *
41944  
41945  
41946  * @constructor
41947  * Create a new ComboBoxArray.
41948  * @param {Object} config Configuration options
41949  */
41950  
41951
41952 Roo.form.ComboBoxArray = function(config)
41953 {
41954     this.addEvents({
41955         /**
41956          * @event beforeremove
41957          * Fires before remove the value from the list
41958              * @param {Roo.form.ComboBoxArray} _self This combo box array
41959              * @param {Roo.form.ComboBoxArray.Item} item removed item
41960              */
41961         'beforeremove' : true,
41962         /**
41963          * @event remove
41964          * Fires when remove the value from the list
41965              * @param {Roo.form.ComboBoxArray} _self This combo box array
41966              * @param {Roo.form.ComboBoxArray.Item} item removed item
41967              */
41968         'remove' : true
41969         
41970         
41971     });
41972     
41973     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
41974     
41975     this.items = new Roo.util.MixedCollection(false);
41976     
41977     // construct the child combo...
41978     
41979     
41980     
41981     
41982    
41983     
41984 }
41985
41986  
41987 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
41988
41989     /**
41990      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
41991      */
41992     
41993     lastData : false,
41994     
41995     // behavies liek a hiddne field
41996     inputType:      'hidden',
41997     /**
41998      * @cfg {Number} width The width of the box that displays the selected element
41999      */ 
42000     width:          300,
42001
42002     
42003     
42004     /**
42005      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42006      */
42007     name : false,
42008     /**
42009      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42010      */
42011     hiddenName : false,
42012     
42013     
42014     // private the array of items that are displayed..
42015     items  : false,
42016     // private - the hidden field el.
42017     hiddenEl : false,
42018     // private - the filed el..
42019     el : false,
42020     
42021     //validateValue : function() { return true; }, // all values are ok!
42022     //onAddClick: function() { },
42023     
42024     onRender : function(ct, position) 
42025     {
42026         
42027         // create the standard hidden element
42028         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42029         
42030         
42031         // give fake names to child combo;
42032         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42033         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
42034         
42035         this.combo = Roo.factory(this.combo, Roo.form);
42036         this.combo.onRender(ct, position);
42037         if (typeof(this.combo.width) != 'undefined') {
42038             this.combo.onResize(this.combo.width,0);
42039         }
42040         
42041         this.combo.initEvents();
42042         
42043         // assigned so form know we need to do this..
42044         this.store          = this.combo.store;
42045         this.valueField     = this.combo.valueField;
42046         this.displayField   = this.combo.displayField ;
42047         
42048         
42049         this.combo.wrap.addClass('x-cbarray-grp');
42050         
42051         var cbwrap = this.combo.wrap.createChild(
42052             {tag: 'div', cls: 'x-cbarray-cb'},
42053             this.combo.el.dom
42054         );
42055         
42056              
42057         this.hiddenEl = this.combo.wrap.createChild({
42058             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42059         });
42060         this.el = this.combo.wrap.createChild({
42061             tag: 'input',  type:'hidden' , name: this.name, value : ''
42062         });
42063          //   this.el.dom.removeAttribute("name");
42064         
42065         
42066         this.outerWrap = this.combo.wrap;
42067         this.wrap = cbwrap;
42068         
42069         this.outerWrap.setWidth(this.width);
42070         this.outerWrap.dom.removeChild(this.el.dom);
42071         
42072         this.wrap.dom.appendChild(this.el.dom);
42073         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42074         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42075         
42076         this.combo.trigger.setStyle('position','relative');
42077         this.combo.trigger.setStyle('left', '0px');
42078         this.combo.trigger.setStyle('top', '2px');
42079         
42080         this.combo.el.setStyle('vertical-align', 'text-bottom');
42081         
42082         //this.trigger.setStyle('vertical-align', 'top');
42083         
42084         // this should use the code from combo really... on('add' ....)
42085         if (this.adder) {
42086             
42087         
42088             this.adder = this.outerWrap.createChild(
42089                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42090             var _t = this;
42091             this.adder.on('click', function(e) {
42092                 _t.fireEvent('adderclick', this, e);
42093             }, _t);
42094         }
42095         //var _t = this;
42096         //this.adder.on('click', this.onAddClick, _t);
42097         
42098         
42099         this.combo.on('select', function(cb, rec, ix) {
42100             this.addItem(rec.data);
42101             
42102             cb.setValue('');
42103             cb.el.dom.value = '';
42104             //cb.lastData = rec.data;
42105             // add to list
42106             
42107         }, this);
42108         
42109         
42110     },
42111     
42112     
42113     getName: function()
42114     {
42115         // returns hidden if it's set..
42116         if (!this.rendered) {return ''};
42117         return  this.hiddenName ? this.hiddenName : this.name;
42118         
42119     },
42120     
42121     
42122     onResize: function(w, h){
42123         
42124         return;
42125         // not sure if this is needed..
42126         //this.combo.onResize(w,h);
42127         
42128         if(typeof w != 'number'){
42129             // we do not handle it!?!?
42130             return;
42131         }
42132         var tw = this.combo.trigger.getWidth();
42133         tw += this.addicon ? this.addicon.getWidth() : 0;
42134         tw += this.editicon ? this.editicon.getWidth() : 0;
42135         var x = w - tw;
42136         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42137             
42138         this.combo.trigger.setStyle('left', '0px');
42139         
42140         if(this.list && this.listWidth === undefined){
42141             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42142             this.list.setWidth(lw);
42143             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42144         }
42145         
42146     
42147         
42148     },
42149     
42150     addItem: function(rec)
42151     {
42152         var valueField = this.combo.valueField;
42153         var displayField = this.combo.displayField;
42154         if (this.items.indexOfKey(rec[valueField]) > -1) {
42155             //console.log("GOT " + rec.data.id);
42156             return;
42157         }
42158         
42159         var x = new Roo.form.ComboBoxArray.Item({
42160             //id : rec[this.idField],
42161             data : rec,
42162             displayField : displayField ,
42163             tipField : displayField ,
42164             cb : this
42165         });
42166         // use the 
42167         this.items.add(rec[valueField],x);
42168         // add it before the element..
42169         this.updateHiddenEl();
42170         x.render(this.outerWrap, this.wrap.dom);
42171         // add the image handler..
42172     },
42173     
42174     updateHiddenEl : function()
42175     {
42176         this.validate();
42177         if (!this.hiddenEl) {
42178             return;
42179         }
42180         var ar = [];
42181         var idField = this.combo.valueField;
42182         
42183         this.items.each(function(f) {
42184             ar.push(f.data[idField]);
42185            
42186         });
42187         this.hiddenEl.dom.value = ar.join(',');
42188         this.validate();
42189     },
42190     
42191     reset : function()
42192     {
42193         this.items.clear();
42194         
42195         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42196            el.remove();
42197         });
42198         
42199         this.el.dom.value = '';
42200         if (this.hiddenEl) {
42201             this.hiddenEl.dom.value = '';
42202         }
42203         
42204     },
42205     getValue: function()
42206     {
42207         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42208     },
42209     setValue: function(v) // not a valid action - must use addItems..
42210     {
42211          
42212         this.reset();
42213         
42214         
42215         
42216         if (this.store.isLocal && (typeof(v) == 'string')) {
42217             // then we can use the store to find the values..
42218             // comma seperated at present.. this needs to allow JSON based encoding..
42219             this.hiddenEl.value  = v;
42220             var v_ar = [];
42221             Roo.each(v.split(','), function(k) {
42222                 Roo.log("CHECK " + this.valueField + ',' + k);
42223                 var li = this.store.query(this.valueField, k);
42224                 if (!li.length) {
42225                     return;
42226                 }
42227                 var add = {};
42228                 add[this.valueField] = k;
42229                 add[this.displayField] = li.item(0).data[this.displayField];
42230                 
42231                 this.addItem(add);
42232             }, this) 
42233              
42234         }
42235         if (typeof(v) == 'object' ) {
42236             // then let's assume it's an array of objects..
42237             Roo.each(v, function(l) {
42238                 this.addItem(l);
42239             }, this);
42240              
42241         }
42242         
42243         
42244     },
42245     setFromData: function(v)
42246     {
42247         // this recieves an object, if setValues is called.
42248         this.reset();
42249         this.el.dom.value = v[this.displayField];
42250         this.hiddenEl.dom.value = v[this.valueField];
42251         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42252             return;
42253         }
42254         var kv = v[this.valueField];
42255         var dv = v[this.displayField];
42256         kv = typeof(kv) != 'string' ? '' : kv;
42257         dv = typeof(dv) != 'string' ? '' : dv;
42258         
42259         
42260         var keys = kv.split(',');
42261         var display = dv.split(',');
42262         for (var i = 0 ; i < keys.length; i++) {
42263             
42264             add = {};
42265             add[this.valueField] = keys[i];
42266             add[this.displayField] = display[i];
42267             this.addItem(add);
42268         }
42269       
42270         
42271     },
42272     
42273     /**
42274      * Validates the combox array value
42275      * @return {Boolean} True if the value is valid, else false
42276      */
42277     validate : function(){
42278         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42279             this.clearInvalid();
42280             return true;
42281         }
42282         return false;
42283     },
42284     
42285     validateValue : function(value){
42286         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42287         
42288     },
42289     
42290     /*@
42291      * overide
42292      * 
42293      */
42294     isDirty : function() {
42295         if(this.disabled) {
42296             return false;
42297         }
42298         
42299         try {
42300             var d = Roo.decode(String(this.originalValue));
42301         } catch (e) {
42302             return String(this.getValue()) !== String(this.originalValue);
42303         }
42304         
42305         var originalValue = [];
42306         
42307         for (var i = 0; i < d.length; i++){
42308             originalValue.push(d[i][this.valueField]);
42309         }
42310         
42311         return String(this.getValue()) !== String(originalValue.join(','));
42312         
42313     }
42314     
42315 });
42316
42317
42318
42319 /**
42320  * @class Roo.form.ComboBoxArray.Item
42321  * @extends Roo.BoxComponent
42322  * A selected item in the list
42323  *  Fred [x]  Brian [x]  [Pick another |v]
42324  * 
42325  * @constructor
42326  * Create a new item.
42327  * @param {Object} config Configuration options
42328  */
42329  
42330 Roo.form.ComboBoxArray.Item = function(config) {
42331     config.id = Roo.id();
42332     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42333 }
42334
42335 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42336     data : {},
42337     cb: false,
42338     displayField : false,
42339     tipField : false,
42340     
42341     
42342     defaultAutoCreate : {
42343         tag: 'div',
42344         cls: 'x-cbarray-item',
42345         cn : [ 
42346             { tag: 'div' },
42347             {
42348                 tag: 'img',
42349                 width:16,
42350                 height : 16,
42351                 src : Roo.BLANK_IMAGE_URL ,
42352                 align: 'center'
42353             }
42354         ]
42355         
42356     },
42357     
42358  
42359     onRender : function(ct, position)
42360     {
42361         Roo.form.Field.superclass.onRender.call(this, ct, position);
42362         
42363         if(!this.el){
42364             var cfg = this.getAutoCreate();
42365             this.el = ct.createChild(cfg, position);
42366         }
42367         
42368         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42369         
42370         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42371             this.cb.renderer(this.data) :
42372             String.format('{0}',this.data[this.displayField]);
42373         
42374             
42375         this.el.child('div').dom.setAttribute('qtip',
42376                         String.format('{0}',this.data[this.tipField])
42377         );
42378         
42379         this.el.child('img').on('click', this.remove, this);
42380         
42381     },
42382    
42383     remove : function()
42384     {
42385         if(this.cb.disabled){
42386             return;
42387         }
42388         
42389         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42390             this.cb.items.remove(this);
42391             this.el.child('img').un('click', this.remove, this);
42392             this.el.remove();
42393             this.cb.updateHiddenEl();
42394
42395             this.cb.fireEvent('remove', this.cb, this);
42396         }
42397         
42398     }
42399 });/*
42400  * Based on:
42401  * Ext JS Library 1.1.1
42402  * Copyright(c) 2006-2007, Ext JS, LLC.
42403  *
42404  * Originally Released Under LGPL - original licence link has changed is not relivant.
42405  *
42406  * Fork - LGPL
42407  * <script type="text/javascript">
42408  */
42409 /**
42410  * @class Roo.form.Checkbox
42411  * @extends Roo.form.Field
42412  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
42413  * @constructor
42414  * Creates a new Checkbox
42415  * @param {Object} config Configuration options
42416  */
42417 Roo.form.Checkbox = function(config){
42418     Roo.form.Checkbox.superclass.constructor.call(this, config);
42419     this.addEvents({
42420         /**
42421          * @event check
42422          * Fires when the checkbox is checked or unchecked.
42423              * @param {Roo.form.Checkbox} this This checkbox
42424              * @param {Boolean} checked The new checked value
42425              */
42426         check : true
42427     });
42428 };
42429
42430 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
42431     /**
42432      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42433      */
42434     focusClass : undefined,
42435     /**
42436      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42437      */
42438     fieldClass: "x-form-field",
42439     /**
42440      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
42441      */
42442     checked: false,
42443     /**
42444      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42445      * {tag: "input", type: "checkbox", autocomplete: "off"})
42446      */
42447     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42448     /**
42449      * @cfg {String} boxLabel The text that appears beside the checkbox
42450      */
42451     boxLabel : "",
42452     /**
42453      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
42454      */  
42455     inputValue : '1',
42456     /**
42457      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
42458      */
42459      valueOff: '0', // value when not checked..
42460
42461     actionMode : 'viewEl', 
42462     //
42463     // private
42464     itemCls : 'x-menu-check-item x-form-item',
42465     groupClass : 'x-menu-group-item',
42466     inputType : 'hidden',
42467     
42468     
42469     inSetChecked: false, // check that we are not calling self...
42470     
42471     inputElement: false, // real input element?
42472     basedOn: false, // ????
42473     
42474     isFormField: true, // not sure where this is needed!!!!
42475
42476     onResize : function(){
42477         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42478         if(!this.boxLabel){
42479             this.el.alignTo(this.wrap, 'c-c');
42480         }
42481     },
42482
42483     initEvents : function(){
42484         Roo.form.Checkbox.superclass.initEvents.call(this);
42485         this.el.on("click", this.onClick,  this);
42486         this.el.on("change", this.onClick,  this);
42487     },
42488
42489
42490     getResizeEl : function(){
42491         return this.wrap;
42492     },
42493
42494     getPositionEl : function(){
42495         return this.wrap;
42496     },
42497
42498     // private
42499     onRender : function(ct, position){
42500         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42501         /*
42502         if(this.inputValue !== undefined){
42503             this.el.dom.value = this.inputValue;
42504         }
42505         */
42506         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42507         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42508         var viewEl = this.wrap.createChild({ 
42509             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42510         this.viewEl = viewEl;   
42511         this.wrap.on('click', this.onClick,  this); 
42512         
42513         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42514         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42515         
42516         
42517         
42518         if(this.boxLabel){
42519             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42520         //    viewEl.on('click', this.onClick,  this); 
42521         }
42522         //if(this.checked){
42523             this.setChecked(this.checked);
42524         //}else{
42525             //this.checked = this.el.dom;
42526         //}
42527
42528     },
42529
42530     // private
42531     initValue : Roo.emptyFn,
42532
42533     /**
42534      * Returns the checked state of the checkbox.
42535      * @return {Boolean} True if checked, else false
42536      */
42537     getValue : function(){
42538         if(this.el){
42539             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
42540         }
42541         return this.valueOff;
42542         
42543     },
42544
42545         // private
42546     onClick : function(){ 
42547         if (this.disabled) {
42548             return;
42549         }
42550         this.setChecked(!this.checked);
42551
42552         //if(this.el.dom.checked != this.checked){
42553         //    this.setValue(this.el.dom.checked);
42554        // }
42555     },
42556
42557     /**
42558      * Sets the checked state of the checkbox.
42559      * On is always based on a string comparison between inputValue and the param.
42560      * @param {Boolean/String} value - the value to set 
42561      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42562      */
42563     setValue : function(v,suppressEvent){
42564         
42565         
42566         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
42567         //if(this.el && this.el.dom){
42568         //    this.el.dom.checked = this.checked;
42569         //    this.el.dom.defaultChecked = this.checked;
42570         //}
42571         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
42572         //this.fireEvent("check", this, this.checked);
42573     },
42574     // private..
42575     setChecked : function(state,suppressEvent)
42576     {
42577         if (this.inSetChecked) {
42578             this.checked = state;
42579             return;
42580         }
42581         
42582     
42583         if(this.wrap){
42584             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
42585         }
42586         this.checked = state;
42587         if(suppressEvent !== true){
42588             this.fireEvent('check', this, state);
42589         }
42590         this.inSetChecked = true;
42591         this.el.dom.value = state ? this.inputValue : this.valueOff;
42592         this.inSetChecked = false;
42593         
42594     },
42595     // handle setting of hidden value by some other method!!?!?
42596     setFromHidden: function()
42597     {
42598         if(!this.el){
42599             return;
42600         }
42601         //console.log("SET FROM HIDDEN");
42602         //alert('setFrom hidden');
42603         this.setValue(this.el.dom.value);
42604     },
42605     
42606     onDestroy : function()
42607     {
42608         if(this.viewEl){
42609             Roo.get(this.viewEl).remove();
42610         }
42611          
42612         Roo.form.Checkbox.superclass.onDestroy.call(this);
42613     },
42614     
42615     setBoxLabel : function(str)
42616     {
42617         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
42618     }
42619
42620 });/*
42621  * Based on:
42622  * Ext JS Library 1.1.1
42623  * Copyright(c) 2006-2007, Ext JS, LLC.
42624  *
42625  * Originally Released Under LGPL - original licence link has changed is not relivant.
42626  *
42627  * Fork - LGPL
42628  * <script type="text/javascript">
42629  */
42630  
42631 /**
42632  * @class Roo.form.Radio
42633  * @extends Roo.form.Checkbox
42634  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
42635  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
42636  * @constructor
42637  * Creates a new Radio
42638  * @param {Object} config Configuration options
42639  */
42640 Roo.form.Radio = function(){
42641     Roo.form.Radio.superclass.constructor.apply(this, arguments);
42642 };
42643 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
42644     inputType: 'radio',
42645
42646     /**
42647      * If this radio is part of a group, it will return the selected value
42648      * @return {String}
42649      */
42650     getGroupValue : function(){
42651         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
42652     },
42653     
42654     
42655     onRender : function(ct, position){
42656         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42657         
42658         if(this.inputValue !== undefined){
42659             this.el.dom.value = this.inputValue;
42660         }
42661          
42662         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42663         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42664         //var viewEl = this.wrap.createChild({ 
42665         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42666         //this.viewEl = viewEl;   
42667         //this.wrap.on('click', this.onClick,  this); 
42668         
42669         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42670         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
42671         
42672         
42673         
42674         if(this.boxLabel){
42675             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42676         //    viewEl.on('click', this.onClick,  this); 
42677         }
42678          if(this.checked){
42679             this.el.dom.checked =   'checked' ;
42680         }
42681          
42682     } 
42683     
42684     
42685 });//<script type="text/javascript">
42686
42687 /*
42688  * Based  Ext JS Library 1.1.1
42689  * Copyright(c) 2006-2007, Ext JS, LLC.
42690  * LGPL
42691  *
42692  */
42693  
42694 /**
42695  * @class Roo.HtmlEditorCore
42696  * @extends Roo.Component
42697  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
42698  *
42699  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42700  */
42701
42702 Roo.HtmlEditorCore = function(config){
42703     
42704     
42705     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
42706     
42707     
42708     this.addEvents({
42709         /**
42710          * @event initialize
42711          * Fires when the editor is fully initialized (including the iframe)
42712          * @param {Roo.HtmlEditorCore} this
42713          */
42714         initialize: true,
42715         /**
42716          * @event activate
42717          * Fires when the editor is first receives the focus. Any insertion must wait
42718          * until after this event.
42719          * @param {Roo.HtmlEditorCore} this
42720          */
42721         activate: true,
42722          /**
42723          * @event beforesync
42724          * Fires before the textarea is updated with content from the editor iframe. Return false
42725          * to cancel the sync.
42726          * @param {Roo.HtmlEditorCore} this
42727          * @param {String} html
42728          */
42729         beforesync: true,
42730          /**
42731          * @event beforepush
42732          * Fires before the iframe editor is updated with content from the textarea. Return false
42733          * to cancel the push.
42734          * @param {Roo.HtmlEditorCore} this
42735          * @param {String} html
42736          */
42737         beforepush: true,
42738          /**
42739          * @event sync
42740          * Fires when the textarea is updated with content from the editor iframe.
42741          * @param {Roo.HtmlEditorCore} this
42742          * @param {String} html
42743          */
42744         sync: true,
42745          /**
42746          * @event push
42747          * Fires when the iframe editor is updated with content from the textarea.
42748          * @param {Roo.HtmlEditorCore} this
42749          * @param {String} html
42750          */
42751         push: true,
42752         
42753         /**
42754          * @event editorevent
42755          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42756          * @param {Roo.HtmlEditorCore} this
42757          */
42758         editorevent: true
42759         
42760     });
42761     
42762     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
42763     
42764     // defaults : white / black...
42765     this.applyBlacklists();
42766     
42767     
42768     
42769 };
42770
42771
42772 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
42773
42774
42775      /**
42776      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
42777      */
42778     
42779     owner : false,
42780     
42781      /**
42782      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42783      *                        Roo.resizable.
42784      */
42785     resizable : false,
42786      /**
42787      * @cfg {Number} height (in pixels)
42788      */   
42789     height: 300,
42790    /**
42791      * @cfg {Number} width (in pixels)
42792      */   
42793     width: 500,
42794     
42795     /**
42796      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42797      * 
42798      */
42799     stylesheets: false,
42800     
42801     // id of frame..
42802     frameId: false,
42803     
42804     // private properties
42805     validationEvent : false,
42806     deferHeight: true,
42807     initialized : false,
42808     activated : false,
42809     sourceEditMode : false,
42810     onFocus : Roo.emptyFn,
42811     iframePad:3,
42812     hideMode:'offsets',
42813     
42814     clearUp: true,
42815     
42816     // blacklist + whitelisted elements..
42817     black: false,
42818     white: false,
42819      
42820     bodyCls : '',
42821
42822     /**
42823      * Protected method that will not generally be called directly. It
42824      * is called when the editor initializes the iframe with HTML contents. Override this method if you
42825      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
42826      */
42827     getDocMarkup : function(){
42828         // body styles..
42829         var st = '';
42830         
42831         // inherit styels from page...?? 
42832         if (this.stylesheets === false) {
42833             
42834             Roo.get(document.head).select('style').each(function(node) {
42835                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42836             });
42837             
42838             Roo.get(document.head).select('link').each(function(node) { 
42839                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42840             });
42841             
42842         } else if (!this.stylesheets.length) {
42843                 // simple..
42844                 st = '<style type="text/css">' +
42845                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42846                    '</style>';
42847         } else { 
42848             st = '<style type="text/css">' +
42849                     this.stylesheets +
42850                 '</style>';
42851         }
42852         
42853         st +=  '<style type="text/css">' +
42854             'IMG { cursor: pointer } ' +
42855         '</style>';
42856
42857         var cls = 'roo-htmleditor-body';
42858         
42859         if(this.bodyCls.length){
42860             cls += ' ' + this.bodyCls;
42861         }
42862         
42863         return '<html><head>' + st  +
42864             //<style type="text/css">' +
42865             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42866             //'</style>' +
42867             ' </head><body class="' +  cls + '"></body></html>';
42868     },
42869
42870     // private
42871     onRender : function(ct, position)
42872     {
42873         var _t = this;
42874         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
42875         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
42876         
42877         
42878         this.el.dom.style.border = '0 none';
42879         this.el.dom.setAttribute('tabIndex', -1);
42880         this.el.addClass('x-hidden hide');
42881         
42882         
42883         
42884         if(Roo.isIE){ // fix IE 1px bogus margin
42885             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
42886         }
42887        
42888         
42889         this.frameId = Roo.id();
42890         
42891          
42892         
42893         var iframe = this.owner.wrap.createChild({
42894             tag: 'iframe',
42895             cls: 'form-control', // bootstrap..
42896             id: this.frameId,
42897             name: this.frameId,
42898             frameBorder : 'no',
42899             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
42900         }, this.el
42901         );
42902         
42903         
42904         this.iframe = iframe.dom;
42905
42906          this.assignDocWin();
42907         
42908         this.doc.designMode = 'on';
42909        
42910         this.doc.open();
42911         this.doc.write(this.getDocMarkup());
42912         this.doc.close();
42913
42914         
42915         var task = { // must defer to wait for browser to be ready
42916             run : function(){
42917                 //console.log("run task?" + this.doc.readyState);
42918                 this.assignDocWin();
42919                 if(this.doc.body || this.doc.readyState == 'complete'){
42920                     try {
42921                         this.doc.designMode="on";
42922                     } catch (e) {
42923                         return;
42924                     }
42925                     Roo.TaskMgr.stop(task);
42926                     this.initEditor.defer(10, this);
42927                 }
42928             },
42929             interval : 10,
42930             duration: 10000,
42931             scope: this
42932         };
42933         Roo.TaskMgr.start(task);
42934
42935     },
42936
42937     // private
42938     onResize : function(w, h)
42939     {
42940          Roo.log('resize: ' +w + ',' + h );
42941         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
42942         if(!this.iframe){
42943             return;
42944         }
42945         if(typeof w == 'number'){
42946             
42947             this.iframe.style.width = w + 'px';
42948         }
42949         if(typeof h == 'number'){
42950             
42951             this.iframe.style.height = h + 'px';
42952             if(this.doc){
42953                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
42954             }
42955         }
42956         
42957     },
42958
42959     /**
42960      * Toggles the editor between standard and source edit mode.
42961      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42962      */
42963     toggleSourceEdit : function(sourceEditMode){
42964         
42965         this.sourceEditMode = sourceEditMode === true;
42966         
42967         if(this.sourceEditMode){
42968  
42969             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
42970             
42971         }else{
42972             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
42973             //this.iframe.className = '';
42974             this.deferFocus();
42975         }
42976         //this.setSize(this.owner.wrap.getSize());
42977         //this.fireEvent('editmodechange', this, this.sourceEditMode);
42978     },
42979
42980     
42981   
42982
42983     /**
42984      * Protected method that will not generally be called directly. If you need/want
42985      * custom HTML cleanup, this is the method you should override.
42986      * @param {String} html The HTML to be cleaned
42987      * return {String} The cleaned HTML
42988      */
42989     cleanHtml : function(html){
42990         html = String(html);
42991         if(html.length > 5){
42992             if(Roo.isSafari){ // strip safari nonsense
42993                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
42994             }
42995         }
42996         if(html == '&nbsp;'){
42997             html = '';
42998         }
42999         return html;
43000     },
43001
43002     /**
43003      * HTML Editor -> Textarea
43004      * Protected method that will not generally be called directly. Syncs the contents
43005      * of the editor iframe with the textarea.
43006      */
43007     syncValue : function(){
43008         if(this.initialized){
43009             var bd = (this.doc.body || this.doc.documentElement);
43010             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43011             var html = bd.innerHTML;
43012             if(Roo.isSafari){
43013                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43014                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43015                 if(m && m[1]){
43016                     html = '<div style="'+m[0]+'">' + html + '</div>';
43017                 }
43018             }
43019             html = this.cleanHtml(html);
43020             // fix up the special chars.. normaly like back quotes in word...
43021             // however we do not want to do this with chinese..
43022             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
43023                 var cc = b.charCodeAt();
43024                 if (
43025                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43026                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43027                     (cc >= 0xf900 && cc < 0xfb00 )
43028                 ) {
43029                         return b;
43030                 }
43031                 return "&#"+cc+";" 
43032             });
43033             if(this.owner.fireEvent('beforesync', this, html) !== false){
43034                 this.el.dom.value = html;
43035                 this.owner.fireEvent('sync', this, html);
43036             }
43037         }
43038     },
43039
43040     /**
43041      * Protected method that will not generally be called directly. Pushes the value of the textarea
43042      * into the iframe editor.
43043      */
43044     pushValue : function(){
43045         if(this.initialized){
43046             var v = this.el.dom.value.trim();
43047             
43048 //            if(v.length < 1){
43049 //                v = '&#160;';
43050 //            }
43051             
43052             if(this.owner.fireEvent('beforepush', this, v) !== false){
43053                 var d = (this.doc.body || this.doc.documentElement);
43054                 d.innerHTML = v;
43055                 this.cleanUpPaste();
43056                 this.el.dom.value = d.innerHTML;
43057                 this.owner.fireEvent('push', this, v);
43058             }
43059         }
43060     },
43061
43062     // private
43063     deferFocus : function(){
43064         this.focus.defer(10, this);
43065     },
43066
43067     // doc'ed in Field
43068     focus : function(){
43069         if(this.win && !this.sourceEditMode){
43070             this.win.focus();
43071         }else{
43072             this.el.focus();
43073         }
43074     },
43075     
43076     assignDocWin: function()
43077     {
43078         var iframe = this.iframe;
43079         
43080          if(Roo.isIE){
43081             this.doc = iframe.contentWindow.document;
43082             this.win = iframe.contentWindow;
43083         } else {
43084 //            if (!Roo.get(this.frameId)) {
43085 //                return;
43086 //            }
43087 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43088 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43089             
43090             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43091                 return;
43092             }
43093             
43094             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43095             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43096         }
43097     },
43098     
43099     // private
43100     initEditor : function(){
43101         //console.log("INIT EDITOR");
43102         this.assignDocWin();
43103         
43104         
43105         
43106         this.doc.designMode="on";
43107         this.doc.open();
43108         this.doc.write(this.getDocMarkup());
43109         this.doc.close();
43110         
43111         var dbody = (this.doc.body || this.doc.documentElement);
43112         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43113         // this copies styles from the containing element into thsi one..
43114         // not sure why we need all of this..
43115         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43116         
43117         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43118         //ss['background-attachment'] = 'fixed'; // w3c
43119         dbody.bgProperties = 'fixed'; // ie
43120         //Roo.DomHelper.applyStyles(dbody, ss);
43121         Roo.EventManager.on(this.doc, {
43122             //'mousedown': this.onEditorEvent,
43123             'mouseup': this.onEditorEvent,
43124             'dblclick': this.onEditorEvent,
43125             'click': this.onEditorEvent,
43126             'keyup': this.onEditorEvent,
43127             buffer:100,
43128             scope: this
43129         });
43130         if(Roo.isGecko){
43131             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43132         }
43133         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43134             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43135         }
43136         this.initialized = true;
43137
43138         this.owner.fireEvent('initialize', this);
43139         this.pushValue();
43140     },
43141
43142     // private
43143     onDestroy : function(){
43144         
43145         
43146         
43147         if(this.rendered){
43148             
43149             //for (var i =0; i < this.toolbars.length;i++) {
43150             //    // fixme - ask toolbars for heights?
43151             //    this.toolbars[i].onDestroy();
43152            // }
43153             
43154             //this.wrap.dom.innerHTML = '';
43155             //this.wrap.remove();
43156         }
43157     },
43158
43159     // private
43160     onFirstFocus : function(){
43161         
43162         this.assignDocWin();
43163         
43164         
43165         this.activated = true;
43166          
43167     
43168         if(Roo.isGecko){ // prevent silly gecko errors
43169             this.win.focus();
43170             var s = this.win.getSelection();
43171             if(!s.focusNode || s.focusNode.nodeType != 3){
43172                 var r = s.getRangeAt(0);
43173                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43174                 r.collapse(true);
43175                 this.deferFocus();
43176             }
43177             try{
43178                 this.execCmd('useCSS', true);
43179                 this.execCmd('styleWithCSS', false);
43180             }catch(e){}
43181         }
43182         this.owner.fireEvent('activate', this);
43183     },
43184
43185     // private
43186     adjustFont: function(btn){
43187         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43188         //if(Roo.isSafari){ // safari
43189         //    adjust *= 2;
43190        // }
43191         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43192         if(Roo.isSafari){ // safari
43193             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43194             v =  (v < 10) ? 10 : v;
43195             v =  (v > 48) ? 48 : v;
43196             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43197             
43198         }
43199         
43200         
43201         v = Math.max(1, v+adjust);
43202         
43203         this.execCmd('FontSize', v  );
43204     },
43205
43206     onEditorEvent : function(e)
43207     {
43208         this.owner.fireEvent('editorevent', this, e);
43209       //  this.updateToolbar();
43210         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43211     },
43212
43213     insertTag : function(tg)
43214     {
43215         // could be a bit smarter... -> wrap the current selected tRoo..
43216         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
43217             
43218             range = this.createRange(this.getSelection());
43219             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43220             wrappingNode.appendChild(range.extractContents());
43221             range.insertNode(wrappingNode);
43222
43223             return;
43224             
43225             
43226             
43227         }
43228         this.execCmd("formatblock",   tg);
43229         
43230     },
43231     
43232     insertText : function(txt)
43233     {
43234         
43235         
43236         var range = this.createRange();
43237         range.deleteContents();
43238                //alert(Sender.getAttribute('label'));
43239                
43240         range.insertNode(this.doc.createTextNode(txt));
43241     } ,
43242     
43243      
43244
43245     /**
43246      * Executes a Midas editor command on the editor document and performs necessary focus and
43247      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43248      * @param {String} cmd The Midas command
43249      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43250      */
43251     relayCmd : function(cmd, value){
43252         this.win.focus();
43253         this.execCmd(cmd, value);
43254         this.owner.fireEvent('editorevent', this);
43255         //this.updateToolbar();
43256         this.owner.deferFocus();
43257     },
43258
43259     /**
43260      * Executes a Midas editor command directly on the editor document.
43261      * For visual commands, you should use {@link #relayCmd} instead.
43262      * <b>This should only be called after the editor is initialized.</b>
43263      * @param {String} cmd The Midas command
43264      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43265      */
43266     execCmd : function(cmd, value){
43267         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43268         this.syncValue();
43269     },
43270  
43271  
43272    
43273     /**
43274      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43275      * to insert tRoo.
43276      * @param {String} text | dom node.. 
43277      */
43278     insertAtCursor : function(text)
43279     {
43280         
43281         if(!this.activated){
43282             return;
43283         }
43284         /*
43285         if(Roo.isIE){
43286             this.win.focus();
43287             var r = this.doc.selection.createRange();
43288             if(r){
43289                 r.collapse(true);
43290                 r.pasteHTML(text);
43291                 this.syncValue();
43292                 this.deferFocus();
43293             
43294             }
43295             return;
43296         }
43297         */
43298         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43299             this.win.focus();
43300             
43301             
43302             // from jquery ui (MIT licenced)
43303             var range, node;
43304             var win = this.win;
43305             
43306             if (win.getSelection && win.getSelection().getRangeAt) {
43307                 range = win.getSelection().getRangeAt(0);
43308                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43309                 range.insertNode(node);
43310             } else if (win.document.selection && win.document.selection.createRange) {
43311                 // no firefox support
43312                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43313                 win.document.selection.createRange().pasteHTML(txt);
43314             } else {
43315                 // no firefox support
43316                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43317                 this.execCmd('InsertHTML', txt);
43318             } 
43319             
43320             this.syncValue();
43321             
43322             this.deferFocus();
43323         }
43324     },
43325  // private
43326     mozKeyPress : function(e){
43327         if(e.ctrlKey){
43328             var c = e.getCharCode(), cmd;
43329           
43330             if(c > 0){
43331                 c = String.fromCharCode(c).toLowerCase();
43332                 switch(c){
43333                     case 'b':
43334                         cmd = 'bold';
43335                         break;
43336                     case 'i':
43337                         cmd = 'italic';
43338                         break;
43339                     
43340                     case 'u':
43341                         cmd = 'underline';
43342                         break;
43343                     
43344                     case 'v':
43345                         this.cleanUpPaste.defer(100, this);
43346                         return;
43347                         
43348                 }
43349                 if(cmd){
43350                     this.win.focus();
43351                     this.execCmd(cmd);
43352                     this.deferFocus();
43353                     e.preventDefault();
43354                 }
43355                 
43356             }
43357         }
43358     },
43359
43360     // private
43361     fixKeys : function(){ // load time branching for fastest keydown performance
43362         if(Roo.isIE){
43363             return function(e){
43364                 var k = e.getKey(), r;
43365                 if(k == e.TAB){
43366                     e.stopEvent();
43367                     r = this.doc.selection.createRange();
43368                     if(r){
43369                         r.collapse(true);
43370                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43371                         this.deferFocus();
43372                     }
43373                     return;
43374                 }
43375                 
43376                 if(k == e.ENTER){
43377                     r = this.doc.selection.createRange();
43378                     if(r){
43379                         var target = r.parentElement();
43380                         if(!target || target.tagName.toLowerCase() != 'li'){
43381                             e.stopEvent();
43382                             r.pasteHTML('<br />');
43383                             r.collapse(false);
43384                             r.select();
43385                         }
43386                     }
43387                 }
43388                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43389                     this.cleanUpPaste.defer(100, this);
43390                     return;
43391                 }
43392                 
43393                 
43394             };
43395         }else if(Roo.isOpera){
43396             return function(e){
43397                 var k = e.getKey();
43398                 if(k == e.TAB){
43399                     e.stopEvent();
43400                     this.win.focus();
43401                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
43402                     this.deferFocus();
43403                 }
43404                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43405                     this.cleanUpPaste.defer(100, this);
43406                     return;
43407                 }
43408                 
43409             };
43410         }else if(Roo.isSafari){
43411             return function(e){
43412                 var k = e.getKey();
43413                 
43414                 if(k == e.TAB){
43415                     e.stopEvent();
43416                     this.execCmd('InsertText','\t');
43417                     this.deferFocus();
43418                     return;
43419                 }
43420                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43421                     this.cleanUpPaste.defer(100, this);
43422                     return;
43423                 }
43424                 
43425              };
43426         }
43427     }(),
43428     
43429     getAllAncestors: function()
43430     {
43431         var p = this.getSelectedNode();
43432         var a = [];
43433         if (!p) {
43434             a.push(p); // push blank onto stack..
43435             p = this.getParentElement();
43436         }
43437         
43438         
43439         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
43440             a.push(p);
43441             p = p.parentNode;
43442         }
43443         a.push(this.doc.body);
43444         return a;
43445     },
43446     lastSel : false,
43447     lastSelNode : false,
43448     
43449     
43450     getSelection : function() 
43451     {
43452         this.assignDocWin();
43453         return Roo.isIE ? this.doc.selection : this.win.getSelection();
43454     },
43455     
43456     getSelectedNode: function() 
43457     {
43458         // this may only work on Gecko!!!
43459         
43460         // should we cache this!!!!
43461         
43462         
43463         
43464          
43465         var range = this.createRange(this.getSelection()).cloneRange();
43466         
43467         if (Roo.isIE) {
43468             var parent = range.parentElement();
43469             while (true) {
43470                 var testRange = range.duplicate();
43471                 testRange.moveToElementText(parent);
43472                 if (testRange.inRange(range)) {
43473                     break;
43474                 }
43475                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
43476                     break;
43477                 }
43478                 parent = parent.parentElement;
43479             }
43480             return parent;
43481         }
43482         
43483         // is ancestor a text element.
43484         var ac =  range.commonAncestorContainer;
43485         if (ac.nodeType == 3) {
43486             ac = ac.parentNode;
43487         }
43488         
43489         var ar = ac.childNodes;
43490          
43491         var nodes = [];
43492         var other_nodes = [];
43493         var has_other_nodes = false;
43494         for (var i=0;i<ar.length;i++) {
43495             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
43496                 continue;
43497             }
43498             // fullly contained node.
43499             
43500             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
43501                 nodes.push(ar[i]);
43502                 continue;
43503             }
43504             
43505             // probably selected..
43506             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
43507                 other_nodes.push(ar[i]);
43508                 continue;
43509             }
43510             // outer..
43511             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
43512                 continue;
43513             }
43514             
43515             
43516             has_other_nodes = true;
43517         }
43518         if (!nodes.length && other_nodes.length) {
43519             nodes= other_nodes;
43520         }
43521         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
43522             return false;
43523         }
43524         
43525         return nodes[0];
43526     },
43527     createRange: function(sel)
43528     {
43529         // this has strange effects when using with 
43530         // top toolbar - not sure if it's a great idea.
43531         //this.editor.contentWindow.focus();
43532         if (typeof sel != "undefined") {
43533             try {
43534                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
43535             } catch(e) {
43536                 return this.doc.createRange();
43537             }
43538         } else {
43539             return this.doc.createRange();
43540         }
43541     },
43542     getParentElement: function()
43543     {
43544         
43545         this.assignDocWin();
43546         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
43547         
43548         var range = this.createRange(sel);
43549          
43550         try {
43551             var p = range.commonAncestorContainer;
43552             while (p.nodeType == 3) { // text node
43553                 p = p.parentNode;
43554             }
43555             return p;
43556         } catch (e) {
43557             return null;
43558         }
43559     
43560     },
43561     /***
43562      *
43563      * Range intersection.. the hard stuff...
43564      *  '-1' = before
43565      *  '0' = hits..
43566      *  '1' = after.
43567      *         [ -- selected range --- ]
43568      *   [fail]                        [fail]
43569      *
43570      *    basically..
43571      *      if end is before start or  hits it. fail.
43572      *      if start is after end or hits it fail.
43573      *
43574      *   if either hits (but other is outside. - then it's not 
43575      *   
43576      *    
43577      **/
43578     
43579     
43580     // @see http://www.thismuchiknow.co.uk/?p=64.
43581     rangeIntersectsNode : function(range, node)
43582     {
43583         var nodeRange = node.ownerDocument.createRange();
43584         try {
43585             nodeRange.selectNode(node);
43586         } catch (e) {
43587             nodeRange.selectNodeContents(node);
43588         }
43589     
43590         var rangeStartRange = range.cloneRange();
43591         rangeStartRange.collapse(true);
43592     
43593         var rangeEndRange = range.cloneRange();
43594         rangeEndRange.collapse(false);
43595     
43596         var nodeStartRange = nodeRange.cloneRange();
43597         nodeStartRange.collapse(true);
43598     
43599         var nodeEndRange = nodeRange.cloneRange();
43600         nodeEndRange.collapse(false);
43601     
43602         return rangeStartRange.compareBoundaryPoints(
43603                  Range.START_TO_START, nodeEndRange) == -1 &&
43604                rangeEndRange.compareBoundaryPoints(
43605                  Range.START_TO_START, nodeStartRange) == 1;
43606         
43607          
43608     },
43609     rangeCompareNode : function(range, node)
43610     {
43611         var nodeRange = node.ownerDocument.createRange();
43612         try {
43613             nodeRange.selectNode(node);
43614         } catch (e) {
43615             nodeRange.selectNodeContents(node);
43616         }
43617         
43618         
43619         range.collapse(true);
43620     
43621         nodeRange.collapse(true);
43622      
43623         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
43624         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
43625          
43626         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
43627         
43628         var nodeIsBefore   =  ss == 1;
43629         var nodeIsAfter    = ee == -1;
43630         
43631         if (nodeIsBefore && nodeIsAfter) {
43632             return 0; // outer
43633         }
43634         if (!nodeIsBefore && nodeIsAfter) {
43635             return 1; //right trailed.
43636         }
43637         
43638         if (nodeIsBefore && !nodeIsAfter) {
43639             return 2;  // left trailed.
43640         }
43641         // fully contined.
43642         return 3;
43643     },
43644
43645     // private? - in a new class?
43646     cleanUpPaste :  function()
43647     {
43648         // cleans up the whole document..
43649         Roo.log('cleanuppaste');
43650         
43651         this.cleanUpChildren(this.doc.body);
43652         var clean = this.cleanWordChars(this.doc.body.innerHTML);
43653         if (clean != this.doc.body.innerHTML) {
43654             this.doc.body.innerHTML = clean;
43655         }
43656         
43657     },
43658     
43659     cleanWordChars : function(input) {// change the chars to hex code
43660         var he = Roo.HtmlEditorCore;
43661         
43662         var output = input;
43663         Roo.each(he.swapCodes, function(sw) { 
43664             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
43665             
43666             output = output.replace(swapper, sw[1]);
43667         });
43668         
43669         return output;
43670     },
43671     
43672     
43673     cleanUpChildren : function (n)
43674     {
43675         if (!n.childNodes.length) {
43676             return;
43677         }
43678         for (var i = n.childNodes.length-1; i > -1 ; i--) {
43679            this.cleanUpChild(n.childNodes[i]);
43680         }
43681     },
43682     
43683     
43684         
43685     
43686     cleanUpChild : function (node)
43687     {
43688         var ed = this;
43689         //console.log(node);
43690         if (node.nodeName == "#text") {
43691             // clean up silly Windows -- stuff?
43692             return; 
43693         }
43694         if (node.nodeName == "#comment") {
43695             node.parentNode.removeChild(node);
43696             // clean up silly Windows -- stuff?
43697             return; 
43698         }
43699         var lcname = node.tagName.toLowerCase();
43700         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
43701         // whitelist of tags..
43702         
43703         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
43704             // remove node.
43705             node.parentNode.removeChild(node);
43706             return;
43707             
43708         }
43709         
43710         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
43711         
43712         // remove <a name=....> as rendering on yahoo mailer is borked with this.
43713         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
43714         
43715         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
43716         //    remove_keep_children = true;
43717         //}
43718         
43719         if (remove_keep_children) {
43720             this.cleanUpChildren(node);
43721             // inserts everything just before this node...
43722             while (node.childNodes.length) {
43723                 var cn = node.childNodes[0];
43724                 node.removeChild(cn);
43725                 node.parentNode.insertBefore(cn, node);
43726             }
43727             node.parentNode.removeChild(node);
43728             return;
43729         }
43730         
43731         if (!node.attributes || !node.attributes.length) {
43732             this.cleanUpChildren(node);
43733             return;
43734         }
43735         
43736         function cleanAttr(n,v)
43737         {
43738             
43739             if (v.match(/^\./) || v.match(/^\//)) {
43740                 return;
43741             }
43742             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
43743                 return;
43744             }
43745             if (v.match(/^#/)) {
43746                 return;
43747             }
43748 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
43749             node.removeAttribute(n);
43750             
43751         }
43752         
43753         var cwhite = this.cwhite;
43754         var cblack = this.cblack;
43755             
43756         function cleanStyle(n,v)
43757         {
43758             if (v.match(/expression/)) { //XSS?? should we even bother..
43759                 node.removeAttribute(n);
43760                 return;
43761             }
43762             
43763             var parts = v.split(/;/);
43764             var clean = [];
43765             
43766             Roo.each(parts, function(p) {
43767                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
43768                 if (!p.length) {
43769                     return true;
43770                 }
43771                 var l = p.split(':').shift().replace(/\s+/g,'');
43772                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
43773                 
43774                 if ( cwhite.length && cblack.indexOf(l) > -1) {
43775 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43776                     //node.removeAttribute(n);
43777                     return true;
43778                 }
43779                 //Roo.log()
43780                 // only allow 'c whitelisted system attributes'
43781                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
43782 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43783                     //node.removeAttribute(n);
43784                     return true;
43785                 }
43786                 
43787                 
43788                  
43789                 
43790                 clean.push(p);
43791                 return true;
43792             });
43793             if (clean.length) { 
43794                 node.setAttribute(n, clean.join(';'));
43795             } else {
43796                 node.removeAttribute(n);
43797             }
43798             
43799         }
43800         
43801         
43802         for (var i = node.attributes.length-1; i > -1 ; i--) {
43803             var a = node.attributes[i];
43804             //console.log(a);
43805             
43806             if (a.name.toLowerCase().substr(0,2)=='on')  {
43807                 node.removeAttribute(a.name);
43808                 continue;
43809             }
43810             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
43811                 node.removeAttribute(a.name);
43812                 continue;
43813             }
43814             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
43815                 cleanAttr(a.name,a.value); // fixme..
43816                 continue;
43817             }
43818             if (a.name == 'style') {
43819                 cleanStyle(a.name,a.value);
43820                 continue;
43821             }
43822             /// clean up MS crap..
43823             // tecnically this should be a list of valid class'es..
43824             
43825             
43826             if (a.name == 'class') {
43827                 if (a.value.match(/^Mso/)) {
43828                     node.className = '';
43829                 }
43830                 
43831                 if (a.value.match(/^body$/)) {
43832                     node.className = '';
43833                 }
43834                 continue;
43835             }
43836             
43837             // style cleanup!?
43838             // class cleanup?
43839             
43840         }
43841         
43842         
43843         this.cleanUpChildren(node);
43844         
43845         
43846     },
43847     
43848     /**
43849      * Clean up MS wordisms...
43850      */
43851     cleanWord : function(node)
43852     {
43853         
43854         
43855         if (!node) {
43856             this.cleanWord(this.doc.body);
43857             return;
43858         }
43859         if (node.nodeName == "#text") {
43860             // clean up silly Windows -- stuff?
43861             return; 
43862         }
43863         if (node.nodeName == "#comment") {
43864             node.parentNode.removeChild(node);
43865             // clean up silly Windows -- stuff?
43866             return; 
43867         }
43868         
43869         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
43870             node.parentNode.removeChild(node);
43871             return;
43872         }
43873         
43874         // remove - but keep children..
43875         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
43876             while (node.childNodes.length) {
43877                 var cn = node.childNodes[0];
43878                 node.removeChild(cn);
43879                 node.parentNode.insertBefore(cn, node);
43880             }
43881             node.parentNode.removeChild(node);
43882             this.iterateChildren(node, this.cleanWord);
43883             return;
43884         }
43885         // clean styles
43886         if (node.className.length) {
43887             
43888             var cn = node.className.split(/\W+/);
43889             var cna = [];
43890             Roo.each(cn, function(cls) {
43891                 if (cls.match(/Mso[a-zA-Z]+/)) {
43892                     return;
43893                 }
43894                 cna.push(cls);
43895             });
43896             node.className = cna.length ? cna.join(' ') : '';
43897             if (!cna.length) {
43898                 node.removeAttribute("class");
43899             }
43900         }
43901         
43902         if (node.hasAttribute("lang")) {
43903             node.removeAttribute("lang");
43904         }
43905         
43906         if (node.hasAttribute("style")) {
43907             
43908             var styles = node.getAttribute("style").split(";");
43909             var nstyle = [];
43910             Roo.each(styles, function(s) {
43911                 if (!s.match(/:/)) {
43912                     return;
43913                 }
43914                 var kv = s.split(":");
43915                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
43916                     return;
43917                 }
43918                 // what ever is left... we allow.
43919                 nstyle.push(s);
43920             });
43921             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43922             if (!nstyle.length) {
43923                 node.removeAttribute('style');
43924             }
43925         }
43926         this.iterateChildren(node, this.cleanWord);
43927         
43928         
43929         
43930     },
43931     /**
43932      * iterateChildren of a Node, calling fn each time, using this as the scole..
43933      * @param {DomNode} node node to iterate children of.
43934      * @param {Function} fn method of this class to call on each item.
43935      */
43936     iterateChildren : function(node, fn)
43937     {
43938         if (!node.childNodes.length) {
43939                 return;
43940         }
43941         for (var i = node.childNodes.length-1; i > -1 ; i--) {
43942            fn.call(this, node.childNodes[i])
43943         }
43944     },
43945     
43946     
43947     /**
43948      * cleanTableWidths.
43949      *
43950      * Quite often pasting from word etc.. results in tables with column and widths.
43951      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
43952      *
43953      */
43954     cleanTableWidths : function(node)
43955     {
43956          
43957          
43958         if (!node) {
43959             this.cleanTableWidths(this.doc.body);
43960             return;
43961         }
43962         
43963         // ignore list...
43964         if (node.nodeName == "#text" || node.nodeName == "#comment") {
43965             return; 
43966         }
43967         Roo.log(node.tagName);
43968         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
43969             this.iterateChildren(node, this.cleanTableWidths);
43970             return;
43971         }
43972         if (node.hasAttribute('width')) {
43973             node.removeAttribute('width');
43974         }
43975         
43976          
43977         if (node.hasAttribute("style")) {
43978             // pretty basic...
43979             
43980             var styles = node.getAttribute("style").split(";");
43981             var nstyle = [];
43982             Roo.each(styles, function(s) {
43983                 if (!s.match(/:/)) {
43984                     return;
43985                 }
43986                 var kv = s.split(":");
43987                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
43988                     return;
43989                 }
43990                 // what ever is left... we allow.
43991                 nstyle.push(s);
43992             });
43993             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43994             if (!nstyle.length) {
43995                 node.removeAttribute('style');
43996             }
43997         }
43998         
43999         this.iterateChildren(node, this.cleanTableWidths);
44000         
44001         
44002     },
44003     
44004     
44005     
44006     
44007     domToHTML : function(currentElement, depth, nopadtext) {
44008         
44009         depth = depth || 0;
44010         nopadtext = nopadtext || false;
44011     
44012         if (!currentElement) {
44013             return this.domToHTML(this.doc.body);
44014         }
44015         
44016         //Roo.log(currentElement);
44017         var j;
44018         var allText = false;
44019         var nodeName = currentElement.nodeName;
44020         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44021         
44022         if  (nodeName == '#text') {
44023             
44024             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44025         }
44026         
44027         
44028         var ret = '';
44029         if (nodeName != 'BODY') {
44030              
44031             var i = 0;
44032             // Prints the node tagName, such as <A>, <IMG>, etc
44033             if (tagName) {
44034                 var attr = [];
44035                 for(i = 0; i < currentElement.attributes.length;i++) {
44036                     // quoting?
44037                     var aname = currentElement.attributes.item(i).name;
44038                     if (!currentElement.attributes.item(i).value.length) {
44039                         continue;
44040                     }
44041                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44042                 }
44043                 
44044                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44045             } 
44046             else {
44047                 
44048                 // eack
44049             }
44050         } else {
44051             tagName = false;
44052         }
44053         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44054             return ret;
44055         }
44056         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44057             nopadtext = true;
44058         }
44059         
44060         
44061         // Traverse the tree
44062         i = 0;
44063         var currentElementChild = currentElement.childNodes.item(i);
44064         var allText = true;
44065         var innerHTML  = '';
44066         lastnode = '';
44067         while (currentElementChild) {
44068             // Formatting code (indent the tree so it looks nice on the screen)
44069             var nopad = nopadtext;
44070             if (lastnode == 'SPAN') {
44071                 nopad  = true;
44072             }
44073             // text
44074             if  (currentElementChild.nodeName == '#text') {
44075                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44076                 toadd = nopadtext ? toadd : toadd.trim();
44077                 if (!nopad && toadd.length > 80) {
44078                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44079                 }
44080                 innerHTML  += toadd;
44081                 
44082                 i++;
44083                 currentElementChild = currentElement.childNodes.item(i);
44084                 lastNode = '';
44085                 continue;
44086             }
44087             allText = false;
44088             
44089             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44090                 
44091             // Recursively traverse the tree structure of the child node
44092             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44093             lastnode = currentElementChild.nodeName;
44094             i++;
44095             currentElementChild=currentElement.childNodes.item(i);
44096         }
44097         
44098         ret += innerHTML;
44099         
44100         if (!allText) {
44101                 // The remaining code is mostly for formatting the tree
44102             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44103         }
44104         
44105         
44106         if (tagName) {
44107             ret+= "</"+tagName+">";
44108         }
44109         return ret;
44110         
44111     },
44112         
44113     applyBlacklists : function()
44114     {
44115         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44116         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44117         
44118         this.white = [];
44119         this.black = [];
44120         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44121             if (b.indexOf(tag) > -1) {
44122                 return;
44123             }
44124             this.white.push(tag);
44125             
44126         }, this);
44127         
44128         Roo.each(w, function(tag) {
44129             if (b.indexOf(tag) > -1) {
44130                 return;
44131             }
44132             if (this.white.indexOf(tag) > -1) {
44133                 return;
44134             }
44135             this.white.push(tag);
44136             
44137         }, this);
44138         
44139         
44140         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44141             if (w.indexOf(tag) > -1) {
44142                 return;
44143             }
44144             this.black.push(tag);
44145             
44146         }, this);
44147         
44148         Roo.each(b, function(tag) {
44149             if (w.indexOf(tag) > -1) {
44150                 return;
44151             }
44152             if (this.black.indexOf(tag) > -1) {
44153                 return;
44154             }
44155             this.black.push(tag);
44156             
44157         }, this);
44158         
44159         
44160         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44161         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44162         
44163         this.cwhite = [];
44164         this.cblack = [];
44165         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44166             if (b.indexOf(tag) > -1) {
44167                 return;
44168             }
44169             this.cwhite.push(tag);
44170             
44171         }, this);
44172         
44173         Roo.each(w, function(tag) {
44174             if (b.indexOf(tag) > -1) {
44175                 return;
44176             }
44177             if (this.cwhite.indexOf(tag) > -1) {
44178                 return;
44179             }
44180             this.cwhite.push(tag);
44181             
44182         }, this);
44183         
44184         
44185         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44186             if (w.indexOf(tag) > -1) {
44187                 return;
44188             }
44189             this.cblack.push(tag);
44190             
44191         }, this);
44192         
44193         Roo.each(b, function(tag) {
44194             if (w.indexOf(tag) > -1) {
44195                 return;
44196             }
44197             if (this.cblack.indexOf(tag) > -1) {
44198                 return;
44199             }
44200             this.cblack.push(tag);
44201             
44202         }, this);
44203     },
44204     
44205     setStylesheets : function(stylesheets)
44206     {
44207         if(typeof(stylesheets) == 'string'){
44208             Roo.get(this.iframe.contentDocument.head).createChild({
44209                 tag : 'link',
44210                 rel : 'stylesheet',
44211                 type : 'text/css',
44212                 href : stylesheets
44213             });
44214             
44215             return;
44216         }
44217         var _this = this;
44218      
44219         Roo.each(stylesheets, function(s) {
44220             if(!s.length){
44221                 return;
44222             }
44223             
44224             Roo.get(_this.iframe.contentDocument.head).createChild({
44225                 tag : 'link',
44226                 rel : 'stylesheet',
44227                 type : 'text/css',
44228                 href : s
44229             });
44230         });
44231
44232         
44233     },
44234     
44235     removeStylesheets : function()
44236     {
44237         var _this = this;
44238         
44239         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44240             s.remove();
44241         });
44242     },
44243     
44244     setStyle : function(style)
44245     {
44246         Roo.get(this.iframe.contentDocument.head).createChild({
44247             tag : 'style',
44248             type : 'text/css',
44249             html : style
44250         });
44251
44252         return;
44253     }
44254     
44255     // hide stuff that is not compatible
44256     /**
44257      * @event blur
44258      * @hide
44259      */
44260     /**
44261      * @event change
44262      * @hide
44263      */
44264     /**
44265      * @event focus
44266      * @hide
44267      */
44268     /**
44269      * @event specialkey
44270      * @hide
44271      */
44272     /**
44273      * @cfg {String} fieldClass @hide
44274      */
44275     /**
44276      * @cfg {String} focusClass @hide
44277      */
44278     /**
44279      * @cfg {String} autoCreate @hide
44280      */
44281     /**
44282      * @cfg {String} inputType @hide
44283      */
44284     /**
44285      * @cfg {String} invalidClass @hide
44286      */
44287     /**
44288      * @cfg {String} invalidText @hide
44289      */
44290     /**
44291      * @cfg {String} msgFx @hide
44292      */
44293     /**
44294      * @cfg {String} validateOnBlur @hide
44295      */
44296 });
44297
44298 Roo.HtmlEditorCore.white = [
44299         'area', 'br', 'img', 'input', 'hr', 'wbr',
44300         
44301        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44302        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44303        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44304        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44305        'table',   'ul',         'xmp', 
44306        
44307        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44308       'thead',   'tr', 
44309      
44310       'dir', 'menu', 'ol', 'ul', 'dl',
44311        
44312       'embed',  'object'
44313 ];
44314
44315
44316 Roo.HtmlEditorCore.black = [
44317     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44318         'applet', // 
44319         'base',   'basefont', 'bgsound', 'blink',  'body', 
44320         'frame',  'frameset', 'head',    'html',   'ilayer', 
44321         'iframe', 'layer',  'link',     'meta',    'object',   
44322         'script', 'style' ,'title',  'xml' // clean later..
44323 ];
44324 Roo.HtmlEditorCore.clean = [
44325     'script', 'style', 'title', 'xml'
44326 ];
44327 Roo.HtmlEditorCore.remove = [
44328     'font'
44329 ];
44330 // attributes..
44331
44332 Roo.HtmlEditorCore.ablack = [
44333     'on'
44334 ];
44335     
44336 Roo.HtmlEditorCore.aclean = [ 
44337     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44338 ];
44339
44340 // protocols..
44341 Roo.HtmlEditorCore.pwhite= [
44342         'http',  'https',  'mailto'
44343 ];
44344
44345 // white listed style attributes.
44346 Roo.HtmlEditorCore.cwhite= [
44347       //  'text-align', /// default is to allow most things..
44348       
44349          
44350 //        'font-size'//??
44351 ];
44352
44353 // black listed style attributes.
44354 Roo.HtmlEditorCore.cblack= [
44355       //  'font-size' -- this can be set by the project 
44356 ];
44357
44358
44359 Roo.HtmlEditorCore.swapCodes   =[ 
44360     [    8211, "--" ], 
44361     [    8212, "--" ], 
44362     [    8216,  "'" ],  
44363     [    8217, "'" ],  
44364     [    8220, '"' ],  
44365     [    8221, '"' ],  
44366     [    8226, "*" ],  
44367     [    8230, "..." ]
44368 ]; 
44369
44370     //<script type="text/javascript">
44371
44372 /*
44373  * Ext JS Library 1.1.1
44374  * Copyright(c) 2006-2007, Ext JS, LLC.
44375  * Licence LGPL
44376  * 
44377  */
44378  
44379  
44380 Roo.form.HtmlEditor = function(config){
44381     
44382     
44383     
44384     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
44385     
44386     if (!this.toolbars) {
44387         this.toolbars = [];
44388     }
44389     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
44390     
44391     
44392 };
44393
44394 /**
44395  * @class Roo.form.HtmlEditor
44396  * @extends Roo.form.Field
44397  * Provides a lightweight HTML Editor component.
44398  *
44399  * This has been tested on Fireforx / Chrome.. IE may not be so great..
44400  * 
44401  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
44402  * supported by this editor.</b><br/><br/>
44403  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
44404  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44405  */
44406 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
44407     /**
44408      * @cfg {Boolean} clearUp
44409      */
44410     clearUp : true,
44411       /**
44412      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
44413      */
44414     toolbars : false,
44415    
44416      /**
44417      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44418      *                        Roo.resizable.
44419      */
44420     resizable : false,
44421      /**
44422      * @cfg {Number} height (in pixels)
44423      */   
44424     height: 300,
44425    /**
44426      * @cfg {Number} width (in pixels)
44427      */   
44428     width: 500,
44429     
44430     /**
44431      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44432      * 
44433      */
44434     stylesheets: false,
44435     
44436     
44437      /**
44438      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
44439      * 
44440      */
44441     cblack: false,
44442     /**
44443      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
44444      * 
44445      */
44446     cwhite: false,
44447     
44448      /**
44449      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
44450      * 
44451      */
44452     black: false,
44453     /**
44454      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
44455      * 
44456      */
44457     white: false,
44458     
44459     // id of frame..
44460     frameId: false,
44461     
44462     // private properties
44463     validationEvent : false,
44464     deferHeight: true,
44465     initialized : false,
44466     activated : false,
44467     
44468     onFocus : Roo.emptyFn,
44469     iframePad:3,
44470     hideMode:'offsets',
44471     
44472     actionMode : 'container', // defaults to hiding it...
44473     
44474     defaultAutoCreate : { // modified by initCompnoent..
44475         tag: "textarea",
44476         style:"width:500px;height:300px;",
44477         autocomplete: "new-password"
44478     },
44479
44480     // private
44481     initComponent : function(){
44482         this.addEvents({
44483             /**
44484              * @event initialize
44485              * Fires when the editor is fully initialized (including the iframe)
44486              * @param {HtmlEditor} this
44487              */
44488             initialize: true,
44489             /**
44490              * @event activate
44491              * Fires when the editor is first receives the focus. Any insertion must wait
44492              * until after this event.
44493              * @param {HtmlEditor} this
44494              */
44495             activate: true,
44496              /**
44497              * @event beforesync
44498              * Fires before the textarea is updated with content from the editor iframe. Return false
44499              * to cancel the sync.
44500              * @param {HtmlEditor} this
44501              * @param {String} html
44502              */
44503             beforesync: true,
44504              /**
44505              * @event beforepush
44506              * Fires before the iframe editor is updated with content from the textarea. Return false
44507              * to cancel the push.
44508              * @param {HtmlEditor} this
44509              * @param {String} html
44510              */
44511             beforepush: true,
44512              /**
44513              * @event sync
44514              * Fires when the textarea is updated with content from the editor iframe.
44515              * @param {HtmlEditor} this
44516              * @param {String} html
44517              */
44518             sync: true,
44519              /**
44520              * @event push
44521              * Fires when the iframe editor is updated with content from the textarea.
44522              * @param {HtmlEditor} this
44523              * @param {String} html
44524              */
44525             push: true,
44526              /**
44527              * @event editmodechange
44528              * Fires when the editor switches edit modes
44529              * @param {HtmlEditor} this
44530              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
44531              */
44532             editmodechange: true,
44533             /**
44534              * @event editorevent
44535              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44536              * @param {HtmlEditor} this
44537              */
44538             editorevent: true,
44539             /**
44540              * @event firstfocus
44541              * Fires when on first focus - needed by toolbars..
44542              * @param {HtmlEditor} this
44543              */
44544             firstfocus: true,
44545             /**
44546              * @event autosave
44547              * Auto save the htmlEditor value as a file into Events
44548              * @param {HtmlEditor} this
44549              */
44550             autosave: true,
44551             /**
44552              * @event savedpreview
44553              * preview the saved version of htmlEditor
44554              * @param {HtmlEditor} this
44555              */
44556             savedpreview: true,
44557             
44558             /**
44559             * @event stylesheetsclick
44560             * Fires when press the Sytlesheets button
44561             * @param {Roo.HtmlEditorCore} this
44562             */
44563             stylesheetsclick: true
44564         });
44565         this.defaultAutoCreate =  {
44566             tag: "textarea",
44567             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
44568             autocomplete: "new-password"
44569         };
44570     },
44571
44572     /**
44573      * Protected method that will not generally be called directly. It
44574      * is called when the editor creates its toolbar. Override this method if you need to
44575      * add custom toolbar buttons.
44576      * @param {HtmlEditor} editor
44577      */
44578     createToolbar : function(editor){
44579         Roo.log("create toolbars");
44580         if (!editor.toolbars || !editor.toolbars.length) {
44581             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
44582         }
44583         
44584         for (var i =0 ; i < editor.toolbars.length;i++) {
44585             editor.toolbars[i] = Roo.factory(
44586                     typeof(editor.toolbars[i]) == 'string' ?
44587                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
44588                 Roo.form.HtmlEditor);
44589             editor.toolbars[i].init(editor);
44590         }
44591          
44592         
44593     },
44594
44595      
44596     // private
44597     onRender : function(ct, position)
44598     {
44599         var _t = this;
44600         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
44601         
44602         this.wrap = this.el.wrap({
44603             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
44604         });
44605         
44606         this.editorcore.onRender(ct, position);
44607          
44608         if (this.resizable) {
44609             this.resizeEl = new Roo.Resizable(this.wrap, {
44610                 pinned : true,
44611                 wrap: true,
44612                 dynamic : true,
44613                 minHeight : this.height,
44614                 height: this.height,
44615                 handles : this.resizable,
44616                 width: this.width,
44617                 listeners : {
44618                     resize : function(r, w, h) {
44619                         _t.onResize(w,h); // -something
44620                     }
44621                 }
44622             });
44623             
44624         }
44625         this.createToolbar(this);
44626        
44627         
44628         if(!this.width){
44629             this.setSize(this.wrap.getSize());
44630         }
44631         if (this.resizeEl) {
44632             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
44633             // should trigger onReize..
44634         }
44635         
44636         this.keyNav = new Roo.KeyNav(this.el, {
44637             
44638             "tab" : function(e){
44639                 e.preventDefault();
44640                 
44641                 var value = this.getValue();
44642                 
44643                 var start = this.el.dom.selectionStart;
44644                 var end = this.el.dom.selectionEnd;
44645                 
44646                 if(!e.shiftKey){
44647                     
44648                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
44649                     this.el.dom.setSelectionRange(end + 1, end + 1);
44650                     return;
44651                 }
44652                 
44653                 var f = value.substring(0, start).split("\t");
44654                 
44655                 if(f.pop().length != 0){
44656                     return;
44657                 }
44658                 
44659                 this.setValue(f.join("\t") + value.substring(end));
44660                 this.el.dom.setSelectionRange(start - 1, start - 1);
44661                 
44662             },
44663             
44664             "home" : function(e){
44665                 e.preventDefault();
44666                 
44667                 var curr = this.el.dom.selectionStart;
44668                 var lines = this.getValue().split("\n");
44669                 
44670                 if(!lines.length){
44671                     return;
44672                 }
44673                 
44674                 if(e.ctrlKey){
44675                     this.el.dom.setSelectionRange(0, 0);
44676                     return;
44677                 }
44678                 
44679                 var pos = 0;
44680                 
44681                 for (var i = 0; i < lines.length;i++) {
44682                     pos += lines[i].length;
44683                     
44684                     if(i != 0){
44685                         pos += 1;
44686                     }
44687                     
44688                     if(pos < curr){
44689                         continue;
44690                     }
44691                     
44692                     pos -= lines[i].length;
44693                     
44694                     break;
44695                 }
44696                 
44697                 if(!e.shiftKey){
44698                     this.el.dom.setSelectionRange(pos, pos);
44699                     return;
44700                 }
44701                 
44702                 this.el.dom.selectionStart = pos;
44703                 this.el.dom.selectionEnd = curr;
44704             },
44705             
44706             "end" : function(e){
44707                 e.preventDefault();
44708                 
44709                 var curr = this.el.dom.selectionStart;
44710                 var lines = this.getValue().split("\n");
44711                 
44712                 if(!lines.length){
44713                     return;
44714                 }
44715                 
44716                 if(e.ctrlKey){
44717                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
44718                     return;
44719                 }
44720                 
44721                 var pos = 0;
44722                 
44723                 for (var i = 0; i < lines.length;i++) {
44724                     
44725                     pos += lines[i].length;
44726                     
44727                     if(i != 0){
44728                         pos += 1;
44729                     }
44730                     
44731                     if(pos < curr){
44732                         continue;
44733                     }
44734                     
44735                     break;
44736                 }
44737                 
44738                 if(!e.shiftKey){
44739                     this.el.dom.setSelectionRange(pos, pos);
44740                     return;
44741                 }
44742                 
44743                 this.el.dom.selectionStart = curr;
44744                 this.el.dom.selectionEnd = pos;
44745             },
44746
44747             scope : this,
44748
44749             doRelay : function(foo, bar, hname){
44750                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44751             },
44752
44753             forceKeyDown: true
44754         });
44755         
44756 //        if(this.autosave && this.w){
44757 //            this.autoSaveFn = setInterval(this.autosave, 1000);
44758 //        }
44759     },
44760
44761     // private
44762     onResize : function(w, h)
44763     {
44764         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
44765         var ew = false;
44766         var eh = false;
44767         
44768         if(this.el ){
44769             if(typeof w == 'number'){
44770                 var aw = w - this.wrap.getFrameWidth('lr');
44771                 this.el.setWidth(this.adjustWidth('textarea', aw));
44772                 ew = aw;
44773             }
44774             if(typeof h == 'number'){
44775                 var tbh = 0;
44776                 for (var i =0; i < this.toolbars.length;i++) {
44777                     // fixme - ask toolbars for heights?
44778                     tbh += this.toolbars[i].tb.el.getHeight();
44779                     if (this.toolbars[i].footer) {
44780                         tbh += this.toolbars[i].footer.el.getHeight();
44781                     }
44782                 }
44783                 
44784                 
44785                 
44786                 
44787                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
44788                 ah -= 5; // knock a few pixes off for look..
44789 //                Roo.log(ah);
44790                 this.el.setHeight(this.adjustWidth('textarea', ah));
44791                 var eh = ah;
44792             }
44793         }
44794         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
44795         this.editorcore.onResize(ew,eh);
44796         
44797     },
44798
44799     /**
44800      * Toggles the editor between standard and source edit mode.
44801      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
44802      */
44803     toggleSourceEdit : function(sourceEditMode)
44804     {
44805         this.editorcore.toggleSourceEdit(sourceEditMode);
44806         
44807         if(this.editorcore.sourceEditMode){
44808             Roo.log('editor - showing textarea');
44809             
44810 //            Roo.log('in');
44811 //            Roo.log(this.syncValue());
44812             this.editorcore.syncValue();
44813             this.el.removeClass('x-hidden');
44814             this.el.dom.removeAttribute('tabIndex');
44815             this.el.focus();
44816             
44817             for (var i = 0; i < this.toolbars.length; i++) {
44818                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44819                     this.toolbars[i].tb.hide();
44820                     this.toolbars[i].footer.hide();
44821                 }
44822             }
44823             
44824         }else{
44825             Roo.log('editor - hiding textarea');
44826 //            Roo.log('out')
44827 //            Roo.log(this.pushValue()); 
44828             this.editorcore.pushValue();
44829             
44830             this.el.addClass('x-hidden');
44831             this.el.dom.setAttribute('tabIndex', -1);
44832             
44833             for (var i = 0; i < this.toolbars.length; i++) {
44834                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44835                     this.toolbars[i].tb.show();
44836                     this.toolbars[i].footer.show();
44837                 }
44838             }
44839             
44840             //this.deferFocus();
44841         }
44842         
44843         this.setSize(this.wrap.getSize());
44844         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
44845         
44846         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
44847     },
44848  
44849     // private (for BoxComponent)
44850     adjustSize : Roo.BoxComponent.prototype.adjustSize,
44851
44852     // private (for BoxComponent)
44853     getResizeEl : function(){
44854         return this.wrap;
44855     },
44856
44857     // private (for BoxComponent)
44858     getPositionEl : function(){
44859         return this.wrap;
44860     },
44861
44862     // private
44863     initEvents : function(){
44864         this.originalValue = this.getValue();
44865     },
44866
44867     /**
44868      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44869      * @method
44870      */
44871     markInvalid : Roo.emptyFn,
44872     /**
44873      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44874      * @method
44875      */
44876     clearInvalid : Roo.emptyFn,
44877
44878     setValue : function(v){
44879         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
44880         this.editorcore.pushValue();
44881     },
44882
44883      
44884     // private
44885     deferFocus : function(){
44886         this.focus.defer(10, this);
44887     },
44888
44889     // doc'ed in Field
44890     focus : function(){
44891         this.editorcore.focus();
44892         
44893     },
44894       
44895
44896     // private
44897     onDestroy : function(){
44898         
44899         
44900         
44901         if(this.rendered){
44902             
44903             for (var i =0; i < this.toolbars.length;i++) {
44904                 // fixme - ask toolbars for heights?
44905                 this.toolbars[i].onDestroy();
44906             }
44907             
44908             this.wrap.dom.innerHTML = '';
44909             this.wrap.remove();
44910         }
44911     },
44912
44913     // private
44914     onFirstFocus : function(){
44915         //Roo.log("onFirstFocus");
44916         this.editorcore.onFirstFocus();
44917          for (var i =0; i < this.toolbars.length;i++) {
44918             this.toolbars[i].onFirstFocus();
44919         }
44920         
44921     },
44922     
44923     // private
44924     syncValue : function()
44925     {
44926         this.editorcore.syncValue();
44927     },
44928     
44929     pushValue : function()
44930     {
44931         this.editorcore.pushValue();
44932     },
44933     
44934     setStylesheets : function(stylesheets)
44935     {
44936         this.editorcore.setStylesheets(stylesheets);
44937     },
44938     
44939     removeStylesheets : function()
44940     {
44941         this.editorcore.removeStylesheets();
44942     }
44943      
44944     
44945     // hide stuff that is not compatible
44946     /**
44947      * @event blur
44948      * @hide
44949      */
44950     /**
44951      * @event change
44952      * @hide
44953      */
44954     /**
44955      * @event focus
44956      * @hide
44957      */
44958     /**
44959      * @event specialkey
44960      * @hide
44961      */
44962     /**
44963      * @cfg {String} fieldClass @hide
44964      */
44965     /**
44966      * @cfg {String} focusClass @hide
44967      */
44968     /**
44969      * @cfg {String} autoCreate @hide
44970      */
44971     /**
44972      * @cfg {String} inputType @hide
44973      */
44974     /**
44975      * @cfg {String} invalidClass @hide
44976      */
44977     /**
44978      * @cfg {String} invalidText @hide
44979      */
44980     /**
44981      * @cfg {String} msgFx @hide
44982      */
44983     /**
44984      * @cfg {String} validateOnBlur @hide
44985      */
44986 });
44987  
44988     // <script type="text/javascript">
44989 /*
44990  * Based on
44991  * Ext JS Library 1.1.1
44992  * Copyright(c) 2006-2007, Ext JS, LLC.
44993  *  
44994  
44995  */
44996
44997 /**
44998  * @class Roo.form.HtmlEditorToolbar1
44999  * Basic Toolbar
45000  * 
45001  * Usage:
45002  *
45003  new Roo.form.HtmlEditor({
45004     ....
45005     toolbars : [
45006         new Roo.form.HtmlEditorToolbar1({
45007             disable : { fonts: 1 , format: 1, ..., ... , ...],
45008             btns : [ .... ]
45009         })
45010     }
45011      
45012  * 
45013  * @cfg {Object} disable List of elements to disable..
45014  * @cfg {Array} btns List of additional buttons.
45015  * 
45016  * 
45017  * NEEDS Extra CSS? 
45018  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45019  */
45020  
45021 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45022 {
45023     
45024     Roo.apply(this, config);
45025     
45026     // default disabled, based on 'good practice'..
45027     this.disable = this.disable || {};
45028     Roo.applyIf(this.disable, {
45029         fontSize : true,
45030         colors : true,
45031         specialElements : true
45032     });
45033     
45034     
45035     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45036     // dont call parent... till later.
45037 }
45038
45039 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45040     
45041     tb: false,
45042     
45043     rendered: false,
45044     
45045     editor : false,
45046     editorcore : false,
45047     /**
45048      * @cfg {Object} disable  List of toolbar elements to disable
45049          
45050      */
45051     disable : false,
45052     
45053     
45054      /**
45055      * @cfg {String} createLinkText The default text for the create link prompt
45056      */
45057     createLinkText : 'Please enter the URL for the link:',
45058     /**
45059      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45060      */
45061     defaultLinkValue : 'http:/'+'/',
45062    
45063     
45064       /**
45065      * @cfg {Array} fontFamilies An array of available font families
45066      */
45067     fontFamilies : [
45068         'Arial',
45069         'Courier New',
45070         'Tahoma',
45071         'Times New Roman',
45072         'Verdana'
45073     ],
45074     
45075     specialChars : [
45076            "&#169;",
45077           "&#174;",     
45078           "&#8482;",    
45079           "&#163;" ,    
45080          // "&#8212;",    
45081           "&#8230;",    
45082           "&#247;" ,    
45083         //  "&#225;" ,     ?? a acute?
45084            "&#8364;"    , //Euro
45085        //   "&#8220;"    ,
45086         //  "&#8221;"    ,
45087         //  "&#8226;"    ,
45088           "&#176;"  //   , // degrees
45089
45090          // "&#233;"     , // e ecute
45091          // "&#250;"     , // u ecute?
45092     ],
45093     
45094     specialElements : [
45095         {
45096             text: "Insert Table",
45097             xtype: 'MenuItem',
45098             xns : Roo.Menu,
45099             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45100                 
45101         },
45102         {    
45103             text: "Insert Image",
45104             xtype: 'MenuItem',
45105             xns : Roo.Menu,
45106             ihtml : '<img src="about:blank"/>'
45107             
45108         }
45109         
45110          
45111     ],
45112     
45113     
45114     inputElements : [ 
45115             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45116             "input:submit", "input:button", "select", "textarea", "label" ],
45117     formats : [
45118         ["p"] ,  
45119         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45120         ["pre"],[ "code"], 
45121         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45122         ['div'],['span']
45123     ],
45124     
45125     cleanStyles : [
45126         "font-size"
45127     ],
45128      /**
45129      * @cfg {String} defaultFont default font to use.
45130      */
45131     defaultFont: 'tahoma',
45132    
45133     fontSelect : false,
45134     
45135     
45136     formatCombo : false,
45137     
45138     init : function(editor)
45139     {
45140         this.editor = editor;
45141         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45142         var editorcore = this.editorcore;
45143         
45144         var _t = this;
45145         
45146         var fid = editorcore.frameId;
45147         var etb = this;
45148         function btn(id, toggle, handler){
45149             var xid = fid + '-'+ id ;
45150             return {
45151                 id : xid,
45152                 cmd : id,
45153                 cls : 'x-btn-icon x-edit-'+id,
45154                 enableToggle:toggle !== false,
45155                 scope: _t, // was editor...
45156                 handler:handler||_t.relayBtnCmd,
45157                 clickEvent:'mousedown',
45158                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45159                 tabIndex:-1
45160             };
45161         }
45162         
45163         
45164         
45165         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45166         this.tb = tb;
45167          // stop form submits
45168         tb.el.on('click', function(e){
45169             e.preventDefault(); // what does this do?
45170         });
45171
45172         if(!this.disable.font) { // && !Roo.isSafari){
45173             /* why no safari for fonts 
45174             editor.fontSelect = tb.el.createChild({
45175                 tag:'select',
45176                 tabIndex: -1,
45177                 cls:'x-font-select',
45178                 html: this.createFontOptions()
45179             });
45180             
45181             editor.fontSelect.on('change', function(){
45182                 var font = editor.fontSelect.dom.value;
45183                 editor.relayCmd('fontname', font);
45184                 editor.deferFocus();
45185             }, editor);
45186             
45187             tb.add(
45188                 editor.fontSelect.dom,
45189                 '-'
45190             );
45191             */
45192             
45193         };
45194         if(!this.disable.formats){
45195             this.formatCombo = new Roo.form.ComboBox({
45196                 store: new Roo.data.SimpleStore({
45197                     id : 'tag',
45198                     fields: ['tag'],
45199                     data : this.formats // from states.js
45200                 }),
45201                 blockFocus : true,
45202                 name : '',
45203                 //autoCreate : {tag: "div",  size: "20"},
45204                 displayField:'tag',
45205                 typeAhead: false,
45206                 mode: 'local',
45207                 editable : false,
45208                 triggerAction: 'all',
45209                 emptyText:'Add tag',
45210                 selectOnFocus:true,
45211                 width:135,
45212                 listeners : {
45213                     'select': function(c, r, i) {
45214                         editorcore.insertTag(r.get('tag'));
45215                         editor.focus();
45216                     }
45217                 }
45218
45219             });
45220             tb.addField(this.formatCombo);
45221             
45222         }
45223         
45224         if(!this.disable.format){
45225             tb.add(
45226                 btn('bold'),
45227                 btn('italic'),
45228                 btn('underline'),
45229                 btn('strikethrough')
45230             );
45231         };
45232         if(!this.disable.fontSize){
45233             tb.add(
45234                 '-',
45235                 
45236                 
45237                 btn('increasefontsize', false, editorcore.adjustFont),
45238                 btn('decreasefontsize', false, editorcore.adjustFont)
45239             );
45240         };
45241         
45242         
45243         if(!this.disable.colors){
45244             tb.add(
45245                 '-', {
45246                     id:editorcore.frameId +'-forecolor',
45247                     cls:'x-btn-icon x-edit-forecolor',
45248                     clickEvent:'mousedown',
45249                     tooltip: this.buttonTips['forecolor'] || undefined,
45250                     tabIndex:-1,
45251                     menu : new Roo.menu.ColorMenu({
45252                         allowReselect: true,
45253                         focus: Roo.emptyFn,
45254                         value:'000000',
45255                         plain:true,
45256                         selectHandler: function(cp, color){
45257                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45258                             editor.deferFocus();
45259                         },
45260                         scope: editorcore,
45261                         clickEvent:'mousedown'
45262                     })
45263                 }, {
45264                     id:editorcore.frameId +'backcolor',
45265                     cls:'x-btn-icon x-edit-backcolor',
45266                     clickEvent:'mousedown',
45267                     tooltip: this.buttonTips['backcolor'] || undefined,
45268                     tabIndex:-1,
45269                     menu : new Roo.menu.ColorMenu({
45270                         focus: Roo.emptyFn,
45271                         value:'FFFFFF',
45272                         plain:true,
45273                         allowReselect: true,
45274                         selectHandler: function(cp, color){
45275                             if(Roo.isGecko){
45276                                 editorcore.execCmd('useCSS', false);
45277                                 editorcore.execCmd('hilitecolor', color);
45278                                 editorcore.execCmd('useCSS', true);
45279                                 editor.deferFocus();
45280                             }else{
45281                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45282                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45283                                 editor.deferFocus();
45284                             }
45285                         },
45286                         scope:editorcore,
45287                         clickEvent:'mousedown'
45288                     })
45289                 }
45290             );
45291         };
45292         // now add all the items...
45293         
45294
45295         if(!this.disable.alignments){
45296             tb.add(
45297                 '-',
45298                 btn('justifyleft'),
45299                 btn('justifycenter'),
45300                 btn('justifyright')
45301             );
45302         };
45303
45304         //if(!Roo.isSafari){
45305             if(!this.disable.links){
45306                 tb.add(
45307                     '-',
45308                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45309                 );
45310             };
45311
45312             if(!this.disable.lists){
45313                 tb.add(
45314                     '-',
45315                     btn('insertorderedlist'),
45316                     btn('insertunorderedlist')
45317                 );
45318             }
45319             if(!this.disable.sourceEdit){
45320                 tb.add(
45321                     '-',
45322                     btn('sourceedit', true, function(btn){
45323                         this.toggleSourceEdit(btn.pressed);
45324                     })
45325                 );
45326             }
45327         //}
45328         
45329         var smenu = { };
45330         // special menu.. - needs to be tidied up..
45331         if (!this.disable.special) {
45332             smenu = {
45333                 text: "&#169;",
45334                 cls: 'x-edit-none',
45335                 
45336                 menu : {
45337                     items : []
45338                 }
45339             };
45340             for (var i =0; i < this.specialChars.length; i++) {
45341                 smenu.menu.items.push({
45342                     
45343                     html: this.specialChars[i],
45344                     handler: function(a,b) {
45345                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
45346                         //editor.insertAtCursor(a.html);
45347                         
45348                     },
45349                     tabIndex:-1
45350                 });
45351             }
45352             
45353             
45354             tb.add(smenu);
45355             
45356             
45357         }
45358         
45359         var cmenu = { };
45360         if (!this.disable.cleanStyles) {
45361             cmenu = {
45362                 cls: 'x-btn-icon x-btn-clear',
45363                 
45364                 menu : {
45365                     items : []
45366                 }
45367             };
45368             for (var i =0; i < this.cleanStyles.length; i++) {
45369                 cmenu.menu.items.push({
45370                     actiontype : this.cleanStyles[i],
45371                     html: 'Remove ' + this.cleanStyles[i],
45372                     handler: function(a,b) {
45373 //                        Roo.log(a);
45374 //                        Roo.log(b);
45375                         var c = Roo.get(editorcore.doc.body);
45376                         c.select('[style]').each(function(s) {
45377                             s.dom.style.removeProperty(a.actiontype);
45378                         });
45379                         editorcore.syncValue();
45380                     },
45381                     tabIndex:-1
45382                 });
45383             }
45384              cmenu.menu.items.push({
45385                 actiontype : 'tablewidths',
45386                 html: 'Remove Table Widths',
45387                 handler: function(a,b) {
45388                     editorcore.cleanTableWidths();
45389                     editorcore.syncValue();
45390                 },
45391                 tabIndex:-1
45392             });
45393             cmenu.menu.items.push({
45394                 actiontype : 'word',
45395                 html: 'Remove MS Word Formating',
45396                 handler: function(a,b) {
45397                     editorcore.cleanWord();
45398                     editorcore.syncValue();
45399                 },
45400                 tabIndex:-1
45401             });
45402             
45403             cmenu.menu.items.push({
45404                 actiontype : 'all',
45405                 html: 'Remove All Styles',
45406                 handler: function(a,b) {
45407                     
45408                     var c = Roo.get(editorcore.doc.body);
45409                     c.select('[style]').each(function(s) {
45410                         s.dom.removeAttribute('style');
45411                     });
45412                     editorcore.syncValue();
45413                 },
45414                 tabIndex:-1
45415             });
45416             
45417             cmenu.menu.items.push({
45418                 actiontype : 'all',
45419                 html: 'Remove All CSS Classes',
45420                 handler: function(a,b) {
45421                     
45422                     var c = Roo.get(editorcore.doc.body);
45423                     c.select('[class]').each(function(s) {
45424                         s.dom.className = '';
45425                     });
45426                     editorcore.syncValue();
45427                 },
45428                 tabIndex:-1
45429             });
45430             
45431              cmenu.menu.items.push({
45432                 actiontype : 'tidy',
45433                 html: 'Tidy HTML Source',
45434                 handler: function(a,b) {
45435                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
45436                     editorcore.syncValue();
45437                 },
45438                 tabIndex:-1
45439             });
45440             
45441             
45442             tb.add(cmenu);
45443         }
45444          
45445         if (!this.disable.specialElements) {
45446             var semenu = {
45447                 text: "Other;",
45448                 cls: 'x-edit-none',
45449                 menu : {
45450                     items : []
45451                 }
45452             };
45453             for (var i =0; i < this.specialElements.length; i++) {
45454                 semenu.menu.items.push(
45455                     Roo.apply({ 
45456                         handler: function(a,b) {
45457                             editor.insertAtCursor(this.ihtml);
45458                         }
45459                     }, this.specialElements[i])
45460                 );
45461                     
45462             }
45463             
45464             tb.add(semenu);
45465             
45466             
45467         }
45468          
45469         
45470         if (this.btns) {
45471             for(var i =0; i< this.btns.length;i++) {
45472                 var b = Roo.factory(this.btns[i],Roo.form);
45473                 b.cls =  'x-edit-none';
45474                 
45475                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
45476                     b.cls += ' x-init-enable';
45477                 }
45478                 
45479                 b.scope = editorcore;
45480                 tb.add(b);
45481             }
45482         
45483         }
45484         
45485         
45486         
45487         // disable everything...
45488         
45489         this.tb.items.each(function(item){
45490             
45491            if(
45492                 item.id != editorcore.frameId+ '-sourceedit' && 
45493                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
45494             ){
45495                 
45496                 item.disable();
45497             }
45498         });
45499         this.rendered = true;
45500         
45501         // the all the btns;
45502         editor.on('editorevent', this.updateToolbar, this);
45503         // other toolbars need to implement this..
45504         //editor.on('editmodechange', this.updateToolbar, this);
45505     },
45506     
45507     
45508     relayBtnCmd : function(btn) {
45509         this.editorcore.relayCmd(btn.cmd);
45510     },
45511     // private used internally
45512     createLink : function(){
45513         Roo.log("create link?");
45514         var url = prompt(this.createLinkText, this.defaultLinkValue);
45515         if(url && url != 'http:/'+'/'){
45516             this.editorcore.relayCmd('createlink', url);
45517         }
45518     },
45519
45520     
45521     /**
45522      * Protected method that will not generally be called directly. It triggers
45523      * a toolbar update by reading the markup state of the current selection in the editor.
45524      */
45525     updateToolbar: function(){
45526
45527         if(!this.editorcore.activated){
45528             this.editor.onFirstFocus();
45529             return;
45530         }
45531
45532         var btns = this.tb.items.map, 
45533             doc = this.editorcore.doc,
45534             frameId = this.editorcore.frameId;
45535
45536         if(!this.disable.font && !Roo.isSafari){
45537             /*
45538             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
45539             if(name != this.fontSelect.dom.value){
45540                 this.fontSelect.dom.value = name;
45541             }
45542             */
45543         }
45544         if(!this.disable.format){
45545             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
45546             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
45547             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
45548             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
45549         }
45550         if(!this.disable.alignments){
45551             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
45552             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
45553             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
45554         }
45555         if(!Roo.isSafari && !this.disable.lists){
45556             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
45557             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
45558         }
45559         
45560         var ans = this.editorcore.getAllAncestors();
45561         if (this.formatCombo) {
45562             
45563             
45564             var store = this.formatCombo.store;
45565             this.formatCombo.setValue("");
45566             for (var i =0; i < ans.length;i++) {
45567                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
45568                     // select it..
45569                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
45570                     break;
45571                 }
45572             }
45573         }
45574         
45575         
45576         
45577         // hides menus... - so this cant be on a menu...
45578         Roo.menu.MenuMgr.hideAll();
45579
45580         //this.editorsyncValue();
45581     },
45582    
45583     
45584     createFontOptions : function(){
45585         var buf = [], fs = this.fontFamilies, ff, lc;
45586         
45587         
45588         
45589         for(var i = 0, len = fs.length; i< len; i++){
45590             ff = fs[i];
45591             lc = ff.toLowerCase();
45592             buf.push(
45593                 '<option value="',lc,'" style="font-family:',ff,';"',
45594                     (this.defaultFont == lc ? ' selected="true">' : '>'),
45595                     ff,
45596                 '</option>'
45597             );
45598         }
45599         return buf.join('');
45600     },
45601     
45602     toggleSourceEdit : function(sourceEditMode){
45603         
45604         Roo.log("toolbar toogle");
45605         if(sourceEditMode === undefined){
45606             sourceEditMode = !this.sourceEditMode;
45607         }
45608         this.sourceEditMode = sourceEditMode === true;
45609         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
45610         // just toggle the button?
45611         if(btn.pressed !== this.sourceEditMode){
45612             btn.toggle(this.sourceEditMode);
45613             return;
45614         }
45615         
45616         if(sourceEditMode){
45617             Roo.log("disabling buttons");
45618             this.tb.items.each(function(item){
45619                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
45620                     item.disable();
45621                 }
45622             });
45623           
45624         }else{
45625             Roo.log("enabling buttons");
45626             if(this.editorcore.initialized){
45627                 this.tb.items.each(function(item){
45628                     item.enable();
45629                 });
45630             }
45631             
45632         }
45633         Roo.log("calling toggole on editor");
45634         // tell the editor that it's been pressed..
45635         this.editor.toggleSourceEdit(sourceEditMode);
45636        
45637     },
45638      /**
45639      * Object collection of toolbar tooltips for the buttons in the editor. The key
45640      * is the command id associated with that button and the value is a valid QuickTips object.
45641      * For example:
45642 <pre><code>
45643 {
45644     bold : {
45645         title: 'Bold (Ctrl+B)',
45646         text: 'Make the selected text bold.',
45647         cls: 'x-html-editor-tip'
45648     },
45649     italic : {
45650         title: 'Italic (Ctrl+I)',
45651         text: 'Make the selected text italic.',
45652         cls: 'x-html-editor-tip'
45653     },
45654     ...
45655 </code></pre>
45656     * @type Object
45657      */
45658     buttonTips : {
45659         bold : {
45660             title: 'Bold (Ctrl+B)',
45661             text: 'Make the selected text bold.',
45662             cls: 'x-html-editor-tip'
45663         },
45664         italic : {
45665             title: 'Italic (Ctrl+I)',
45666             text: 'Make the selected text italic.',
45667             cls: 'x-html-editor-tip'
45668         },
45669         underline : {
45670             title: 'Underline (Ctrl+U)',
45671             text: 'Underline the selected text.',
45672             cls: 'x-html-editor-tip'
45673         },
45674         strikethrough : {
45675             title: 'Strikethrough',
45676             text: 'Strikethrough the selected text.',
45677             cls: 'x-html-editor-tip'
45678         },
45679         increasefontsize : {
45680             title: 'Grow Text',
45681             text: 'Increase the font size.',
45682             cls: 'x-html-editor-tip'
45683         },
45684         decreasefontsize : {
45685             title: 'Shrink Text',
45686             text: 'Decrease the font size.',
45687             cls: 'x-html-editor-tip'
45688         },
45689         backcolor : {
45690             title: 'Text Highlight Color',
45691             text: 'Change the background color of the selected text.',
45692             cls: 'x-html-editor-tip'
45693         },
45694         forecolor : {
45695             title: 'Font Color',
45696             text: 'Change the color of the selected text.',
45697             cls: 'x-html-editor-tip'
45698         },
45699         justifyleft : {
45700             title: 'Align Text Left',
45701             text: 'Align text to the left.',
45702             cls: 'x-html-editor-tip'
45703         },
45704         justifycenter : {
45705             title: 'Center Text',
45706             text: 'Center text in the editor.',
45707             cls: 'x-html-editor-tip'
45708         },
45709         justifyright : {
45710             title: 'Align Text Right',
45711             text: 'Align text to the right.',
45712             cls: 'x-html-editor-tip'
45713         },
45714         insertunorderedlist : {
45715             title: 'Bullet List',
45716             text: 'Start a bulleted list.',
45717             cls: 'x-html-editor-tip'
45718         },
45719         insertorderedlist : {
45720             title: 'Numbered List',
45721             text: 'Start a numbered list.',
45722             cls: 'x-html-editor-tip'
45723         },
45724         createlink : {
45725             title: 'Hyperlink',
45726             text: 'Make the selected text a hyperlink.',
45727             cls: 'x-html-editor-tip'
45728         },
45729         sourceedit : {
45730             title: 'Source Edit',
45731             text: 'Switch to source editing mode.',
45732             cls: 'x-html-editor-tip'
45733         }
45734     },
45735     // private
45736     onDestroy : function(){
45737         if(this.rendered){
45738             
45739             this.tb.items.each(function(item){
45740                 if(item.menu){
45741                     item.menu.removeAll();
45742                     if(item.menu.el){
45743                         item.menu.el.destroy();
45744                     }
45745                 }
45746                 item.destroy();
45747             });
45748              
45749         }
45750     },
45751     onFirstFocus: function() {
45752         this.tb.items.each(function(item){
45753            item.enable();
45754         });
45755     }
45756 });
45757
45758
45759
45760
45761 // <script type="text/javascript">
45762 /*
45763  * Based on
45764  * Ext JS Library 1.1.1
45765  * Copyright(c) 2006-2007, Ext JS, LLC.
45766  *  
45767  
45768  */
45769
45770  
45771 /**
45772  * @class Roo.form.HtmlEditor.ToolbarContext
45773  * Context Toolbar
45774  * 
45775  * Usage:
45776  *
45777  new Roo.form.HtmlEditor({
45778     ....
45779     toolbars : [
45780         { xtype: 'ToolbarStandard', styles : {} }
45781         { xtype: 'ToolbarContext', disable : {} }
45782     ]
45783 })
45784
45785      
45786  * 
45787  * @config : {Object} disable List of elements to disable.. (not done yet.)
45788  * @config : {Object} styles  Map of styles available.
45789  * 
45790  */
45791
45792 Roo.form.HtmlEditor.ToolbarContext = function(config)
45793 {
45794     
45795     Roo.apply(this, config);
45796     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45797     // dont call parent... till later.
45798     this.styles = this.styles || {};
45799 }
45800
45801  
45802
45803 Roo.form.HtmlEditor.ToolbarContext.types = {
45804     'IMG' : {
45805         width : {
45806             title: "Width",
45807             width: 40
45808         },
45809         height:  {
45810             title: "Height",
45811             width: 40
45812         },
45813         align: {
45814             title: "Align",
45815             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
45816             width : 80
45817             
45818         },
45819         border: {
45820             title: "Border",
45821             width: 40
45822         },
45823         alt: {
45824             title: "Alt",
45825             width: 120
45826         },
45827         src : {
45828             title: "Src",
45829             width: 220
45830         }
45831         
45832     },
45833     'A' : {
45834         name : {
45835             title: "Name",
45836             width: 50
45837         },
45838         target:  {
45839             title: "Target",
45840             width: 120
45841         },
45842         href:  {
45843             title: "Href",
45844             width: 220
45845         } // border?
45846         
45847     },
45848     'TABLE' : {
45849         rows : {
45850             title: "Rows",
45851             width: 20
45852         },
45853         cols : {
45854             title: "Cols",
45855             width: 20
45856         },
45857         width : {
45858             title: "Width",
45859             width: 40
45860         },
45861         height : {
45862             title: "Height",
45863             width: 40
45864         },
45865         border : {
45866             title: "Border",
45867             width: 20
45868         }
45869     },
45870     'TD' : {
45871         width : {
45872             title: "Width",
45873             width: 40
45874         },
45875         height : {
45876             title: "Height",
45877             width: 40
45878         },   
45879         align: {
45880             title: "Align",
45881             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
45882             width: 80
45883         },
45884         valign: {
45885             title: "Valign",
45886             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
45887             width: 80
45888         },
45889         colspan: {
45890             title: "Colspan",
45891             width: 20
45892             
45893         },
45894          'font-family'  : {
45895             title : "Font",
45896             style : 'fontFamily',
45897             displayField: 'display',
45898             optname : 'font-family',
45899             width: 140
45900         }
45901     },
45902     'INPUT' : {
45903         name : {
45904             title: "name",
45905             width: 120
45906         },
45907         value : {
45908             title: "Value",
45909             width: 120
45910         },
45911         width : {
45912             title: "Width",
45913             width: 40
45914         }
45915     },
45916     'LABEL' : {
45917         'for' : {
45918             title: "For",
45919             width: 120
45920         }
45921     },
45922     'TEXTAREA' : {
45923           name : {
45924             title: "name",
45925             width: 120
45926         },
45927         rows : {
45928             title: "Rows",
45929             width: 20
45930         },
45931         cols : {
45932             title: "Cols",
45933             width: 20
45934         }
45935     },
45936     'SELECT' : {
45937         name : {
45938             title: "name",
45939             width: 120
45940         },
45941         selectoptions : {
45942             title: "Options",
45943             width: 200
45944         }
45945     },
45946     
45947     // should we really allow this??
45948     // should this just be 
45949     'BODY' : {
45950         title : {
45951             title: "Title",
45952             width: 200,
45953             disabled : true
45954         }
45955     },
45956     'SPAN' : {
45957         'font-family'  : {
45958             title : "Font",
45959             style : 'fontFamily',
45960             displayField: 'display',
45961             optname : 'font-family',
45962             width: 140
45963         }
45964     },
45965     'DIV' : {
45966         'font-family'  : {
45967             title : "Font",
45968             style : 'fontFamily',
45969             displayField: 'display',
45970             optname : 'font-family',
45971             width: 140
45972         }
45973     },
45974      'P' : {
45975         'font-family'  : {
45976             title : "Font",
45977             style : 'fontFamily',
45978             displayField: 'display',
45979             optname : 'font-family',
45980             width: 140
45981         }
45982     },
45983     
45984     '*' : {
45985         // empty..
45986     }
45987
45988 };
45989
45990 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
45991 Roo.form.HtmlEditor.ToolbarContext.stores = false;
45992
45993 Roo.form.HtmlEditor.ToolbarContext.options = {
45994         'font-family'  : [ 
45995                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
45996                 [ 'Courier New', 'Courier New'],
45997                 [ 'Tahoma', 'Tahoma'],
45998                 [ 'Times New Roman,serif', 'Times'],
45999                 [ 'Verdana','Verdana' ]
46000         ]
46001 };
46002
46003 // fixme - these need to be configurable..
46004  
46005
46006 //Roo.form.HtmlEditor.ToolbarContext.types
46007
46008
46009 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
46010     
46011     tb: false,
46012     
46013     rendered: false,
46014     
46015     editor : false,
46016     editorcore : false,
46017     /**
46018      * @cfg {Object} disable  List of toolbar elements to disable
46019          
46020      */
46021     disable : false,
46022     /**
46023      * @cfg {Object} styles List of styles 
46024      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46025      *
46026      * These must be defined in the page, so they get rendered correctly..
46027      * .headline { }
46028      * TD.underline { }
46029      * 
46030      */
46031     styles : false,
46032     
46033     options: false,
46034     
46035     toolbars : false,
46036     
46037     init : function(editor)
46038     {
46039         this.editor = editor;
46040         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46041         var editorcore = this.editorcore;
46042         
46043         var fid = editorcore.frameId;
46044         var etb = this;
46045         function btn(id, toggle, handler){
46046             var xid = fid + '-'+ id ;
46047             return {
46048                 id : xid,
46049                 cmd : id,
46050                 cls : 'x-btn-icon x-edit-'+id,
46051                 enableToggle:toggle !== false,
46052                 scope: editorcore, // was editor...
46053                 handler:handler||editorcore.relayBtnCmd,
46054                 clickEvent:'mousedown',
46055                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46056                 tabIndex:-1
46057             };
46058         }
46059         // create a new element.
46060         var wdiv = editor.wrap.createChild({
46061                 tag: 'div'
46062             }, editor.wrap.dom.firstChild.nextSibling, true);
46063         
46064         // can we do this more than once??
46065         
46066          // stop form submits
46067       
46068  
46069         // disable everything...
46070         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46071         this.toolbars = {};
46072            
46073         for (var i in  ty) {
46074           
46075             this.toolbars[i] = this.buildToolbar(ty[i],i);
46076         }
46077         this.tb = this.toolbars.BODY;
46078         this.tb.el.show();
46079         this.buildFooter();
46080         this.footer.show();
46081         editor.on('hide', function( ) { this.footer.hide() }, this);
46082         editor.on('show', function( ) { this.footer.show() }, this);
46083         
46084          
46085         this.rendered = true;
46086         
46087         // the all the btns;
46088         editor.on('editorevent', this.updateToolbar, this);
46089         // other toolbars need to implement this..
46090         //editor.on('editmodechange', this.updateToolbar, this);
46091     },
46092     
46093     
46094     
46095     /**
46096      * Protected method that will not generally be called directly. It triggers
46097      * a toolbar update by reading the markup state of the current selection in the editor.
46098      *
46099      * Note you can force an update by calling on('editorevent', scope, false)
46100      */
46101     updateToolbar: function(editor,ev,sel){
46102
46103         //Roo.log(ev);
46104         // capture mouse up - this is handy for selecting images..
46105         // perhaps should go somewhere else...
46106         if(!this.editorcore.activated){
46107              this.editor.onFirstFocus();
46108             return;
46109         }
46110         
46111         
46112         
46113         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46114         // selectNode - might want to handle IE?
46115         if (ev &&
46116             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46117             ev.target && ev.target.tagName == 'IMG') {
46118             // they have click on an image...
46119             // let's see if we can change the selection...
46120             sel = ev.target;
46121          
46122               var nodeRange = sel.ownerDocument.createRange();
46123             try {
46124                 nodeRange.selectNode(sel);
46125             } catch (e) {
46126                 nodeRange.selectNodeContents(sel);
46127             }
46128             //nodeRange.collapse(true);
46129             var s = this.editorcore.win.getSelection();
46130             s.removeAllRanges();
46131             s.addRange(nodeRange);
46132         }  
46133         
46134       
46135         var updateFooter = sel ? false : true;
46136         
46137         
46138         var ans = this.editorcore.getAllAncestors();
46139         
46140         // pick
46141         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46142         
46143         if (!sel) { 
46144             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46145             sel = sel ? sel : this.editorcore.doc.body;
46146             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46147             
46148         }
46149         // pick a menu that exists..
46150         var tn = sel.tagName.toUpperCase();
46151         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46152         
46153         tn = sel.tagName.toUpperCase();
46154         
46155         var lastSel = this.tb.selectedNode;
46156         
46157         this.tb.selectedNode = sel;
46158         
46159         // if current menu does not match..
46160         
46161         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46162                 
46163             this.tb.el.hide();
46164             ///console.log("show: " + tn);
46165             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46166             this.tb.el.show();
46167             // update name
46168             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46169             
46170             
46171             // update attributes
46172             if (this.tb.fields) {
46173                 this.tb.fields.each(function(e) {
46174                     if (e.stylename) {
46175                         e.setValue(sel.style[e.stylename]);
46176                         return;
46177                     } 
46178                    e.setValue(sel.getAttribute(e.attrname));
46179                 });
46180             }
46181             
46182             var hasStyles = false;
46183             for(var i in this.styles) {
46184                 hasStyles = true;
46185                 break;
46186             }
46187             
46188             // update styles
46189             if (hasStyles) { 
46190                 var st = this.tb.fields.item(0);
46191                 
46192                 st.store.removeAll();
46193                
46194                 
46195                 var cn = sel.className.split(/\s+/);
46196                 
46197                 var avs = [];
46198                 if (this.styles['*']) {
46199                     
46200                     Roo.each(this.styles['*'], function(v) {
46201                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46202                     });
46203                 }
46204                 if (this.styles[tn]) { 
46205                     Roo.each(this.styles[tn], function(v) {
46206                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46207                     });
46208                 }
46209                 
46210                 st.store.loadData(avs);
46211                 st.collapse();
46212                 st.setValue(cn);
46213             }
46214             // flag our selected Node.
46215             this.tb.selectedNode = sel;
46216            
46217            
46218             Roo.menu.MenuMgr.hideAll();
46219
46220         }
46221         
46222         if (!updateFooter) {
46223             //this.footDisp.dom.innerHTML = ''; 
46224             return;
46225         }
46226         // update the footer
46227         //
46228         var html = '';
46229         
46230         this.footerEls = ans.reverse();
46231         Roo.each(this.footerEls, function(a,i) {
46232             if (!a) { return; }
46233             html += html.length ? ' &gt; '  :  '';
46234             
46235             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46236             
46237         });
46238        
46239         // 
46240         var sz = this.footDisp.up('td').getSize();
46241         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46242         this.footDisp.dom.style.marginLeft = '5px';
46243         
46244         this.footDisp.dom.style.overflow = 'hidden';
46245         
46246         this.footDisp.dom.innerHTML = html;
46247             
46248         //this.editorsyncValue();
46249     },
46250      
46251     
46252    
46253        
46254     // private
46255     onDestroy : function(){
46256         if(this.rendered){
46257             
46258             this.tb.items.each(function(item){
46259                 if(item.menu){
46260                     item.menu.removeAll();
46261                     if(item.menu.el){
46262                         item.menu.el.destroy();
46263                     }
46264                 }
46265                 item.destroy();
46266             });
46267              
46268         }
46269     },
46270     onFirstFocus: function() {
46271         // need to do this for all the toolbars..
46272         this.tb.items.each(function(item){
46273            item.enable();
46274         });
46275     },
46276     buildToolbar: function(tlist, nm)
46277     {
46278         var editor = this.editor;
46279         var editorcore = this.editorcore;
46280          // create a new element.
46281         var wdiv = editor.wrap.createChild({
46282                 tag: 'div'
46283             }, editor.wrap.dom.firstChild.nextSibling, true);
46284         
46285        
46286         var tb = new Roo.Toolbar(wdiv);
46287         // add the name..
46288         
46289         tb.add(nm+ ":&nbsp;");
46290         
46291         var styles = [];
46292         for(var i in this.styles) {
46293             styles.push(i);
46294         }
46295         
46296         // styles...
46297         if (styles && styles.length) {
46298             
46299             // this needs a multi-select checkbox...
46300             tb.addField( new Roo.form.ComboBox({
46301                 store: new Roo.data.SimpleStore({
46302                     id : 'val',
46303                     fields: ['val', 'selected'],
46304                     data : [] 
46305                 }),
46306                 name : '-roo-edit-className',
46307                 attrname : 'className',
46308                 displayField: 'val',
46309                 typeAhead: false,
46310                 mode: 'local',
46311                 editable : false,
46312                 triggerAction: 'all',
46313                 emptyText:'Select Style',
46314                 selectOnFocus:true,
46315                 width: 130,
46316                 listeners : {
46317                     'select': function(c, r, i) {
46318                         // initial support only for on class per el..
46319                         tb.selectedNode.className =  r ? r.get('val') : '';
46320                         editorcore.syncValue();
46321                     }
46322                 }
46323     
46324             }));
46325         }
46326         
46327         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46328         var tbops = tbc.options;
46329         
46330         for (var i in tlist) {
46331             
46332             var item = tlist[i];
46333             tb.add(item.title + ":&nbsp;");
46334             
46335             
46336             //optname == used so you can configure the options available..
46337             var opts = item.opts ? item.opts : false;
46338             if (item.optname) {
46339                 opts = tbops[item.optname];
46340            
46341             }
46342             
46343             if (opts) {
46344                 // opts == pulldown..
46345                 tb.addField( new Roo.form.ComboBox({
46346                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
46347                         id : 'val',
46348                         fields: ['val', 'display'],
46349                         data : opts  
46350                     }),
46351                     name : '-roo-edit-' + i,
46352                     attrname : i,
46353                     stylename : item.style ? item.style : false,
46354                     displayField: item.displayField ? item.displayField : 'val',
46355                     valueField :  'val',
46356                     typeAhead: false,
46357                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
46358                     editable : false,
46359                     triggerAction: 'all',
46360                     emptyText:'Select',
46361                     selectOnFocus:true,
46362                     width: item.width ? item.width  : 130,
46363                     listeners : {
46364                         'select': function(c, r, i) {
46365                             if (c.stylename) {
46366                                 tb.selectedNode.style[c.stylename] =  r.get('val');
46367                                 return;
46368                             }
46369                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
46370                         }
46371                     }
46372
46373                 }));
46374                 continue;
46375                     
46376                  
46377                 
46378                 tb.addField( new Roo.form.TextField({
46379                     name: i,
46380                     width: 100,
46381                     //allowBlank:false,
46382                     value: ''
46383                 }));
46384                 continue;
46385             }
46386             tb.addField( new Roo.form.TextField({
46387                 name: '-roo-edit-' + i,
46388                 attrname : i,
46389                 
46390                 width: item.width,
46391                 //allowBlank:true,
46392                 value: '',
46393                 listeners: {
46394                     'change' : function(f, nv, ov) {
46395                         tb.selectedNode.setAttribute(f.attrname, nv);
46396                         editorcore.syncValue();
46397                     }
46398                 }
46399             }));
46400              
46401         }
46402         
46403         var _this = this;
46404         
46405         if(nm == 'BODY'){
46406             tb.addSeparator();
46407         
46408             tb.addButton( {
46409                 text: 'Stylesheets',
46410
46411                 listeners : {
46412                     click : function ()
46413                     {
46414                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
46415                     }
46416                 }
46417             });
46418         }
46419         
46420         tb.addFill();
46421         tb.addButton( {
46422             text: 'Remove Tag',
46423     
46424             listeners : {
46425                 click : function ()
46426                 {
46427                     // remove
46428                     // undo does not work.
46429                      
46430                     var sn = tb.selectedNode;
46431                     
46432                     var pn = sn.parentNode;
46433                     
46434                     var stn =  sn.childNodes[0];
46435                     var en = sn.childNodes[sn.childNodes.length - 1 ];
46436                     while (sn.childNodes.length) {
46437                         var node = sn.childNodes[0];
46438                         sn.removeChild(node);
46439                         //Roo.log(node);
46440                         pn.insertBefore(node, sn);
46441                         
46442                     }
46443                     pn.removeChild(sn);
46444                     var range = editorcore.createRange();
46445         
46446                     range.setStart(stn,0);
46447                     range.setEnd(en,0); //????
46448                     //range.selectNode(sel);
46449                     
46450                     
46451                     var selection = editorcore.getSelection();
46452                     selection.removeAllRanges();
46453                     selection.addRange(range);
46454                     
46455                     
46456                     
46457                     //_this.updateToolbar(null, null, pn);
46458                     _this.updateToolbar(null, null, null);
46459                     _this.footDisp.dom.innerHTML = ''; 
46460                 }
46461             }
46462             
46463                     
46464                 
46465             
46466         });
46467         
46468         
46469         tb.el.on('click', function(e){
46470             e.preventDefault(); // what does this do?
46471         });
46472         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
46473         tb.el.hide();
46474         tb.name = nm;
46475         // dont need to disable them... as they will get hidden
46476         return tb;
46477          
46478         
46479     },
46480     buildFooter : function()
46481     {
46482         
46483         var fel = this.editor.wrap.createChild();
46484         this.footer = new Roo.Toolbar(fel);
46485         // toolbar has scrolly on left / right?
46486         var footDisp= new Roo.Toolbar.Fill();
46487         var _t = this;
46488         this.footer.add(
46489             {
46490                 text : '&lt;',
46491                 xtype: 'Button',
46492                 handler : function() {
46493                     _t.footDisp.scrollTo('left',0,true)
46494                 }
46495             }
46496         );
46497         this.footer.add( footDisp );
46498         this.footer.add( 
46499             {
46500                 text : '&gt;',
46501                 xtype: 'Button',
46502                 handler : function() {
46503                     // no animation..
46504                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
46505                 }
46506             }
46507         );
46508         var fel = Roo.get(footDisp.el);
46509         fel.addClass('x-editor-context');
46510         this.footDispWrap = fel; 
46511         this.footDispWrap.overflow  = 'hidden';
46512         
46513         this.footDisp = fel.createChild();
46514         this.footDispWrap.on('click', this.onContextClick, this)
46515         
46516         
46517     },
46518     onContextClick : function (ev,dom)
46519     {
46520         ev.preventDefault();
46521         var  cn = dom.className;
46522         //Roo.log(cn);
46523         if (!cn.match(/x-ed-loc-/)) {
46524             return;
46525         }
46526         var n = cn.split('-').pop();
46527         var ans = this.footerEls;
46528         var sel = ans[n];
46529         
46530          // pick
46531         var range = this.editorcore.createRange();
46532         
46533         range.selectNodeContents(sel);
46534         //range.selectNode(sel);
46535         
46536         
46537         var selection = this.editorcore.getSelection();
46538         selection.removeAllRanges();
46539         selection.addRange(range);
46540         
46541         
46542         
46543         this.updateToolbar(null, null, sel);
46544         
46545         
46546     }
46547     
46548     
46549     
46550     
46551     
46552 });
46553
46554
46555
46556
46557
46558 /*
46559  * Based on:
46560  * Ext JS Library 1.1.1
46561  * Copyright(c) 2006-2007, Ext JS, LLC.
46562  *
46563  * Originally Released Under LGPL - original licence link has changed is not relivant.
46564  *
46565  * Fork - LGPL
46566  * <script type="text/javascript">
46567  */
46568  
46569 /**
46570  * @class Roo.form.BasicForm
46571  * @extends Roo.util.Observable
46572  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
46573  * @constructor
46574  * @param {String/HTMLElement/Roo.Element} el The form element or its id
46575  * @param {Object} config Configuration options
46576  */
46577 Roo.form.BasicForm = function(el, config){
46578     this.allItems = [];
46579     this.childForms = [];
46580     Roo.apply(this, config);
46581     /*
46582      * The Roo.form.Field items in this form.
46583      * @type MixedCollection
46584      */
46585      
46586      
46587     this.items = new Roo.util.MixedCollection(false, function(o){
46588         return o.id || (o.id = Roo.id());
46589     });
46590     this.addEvents({
46591         /**
46592          * @event beforeaction
46593          * Fires before any action is performed. Return false to cancel the action.
46594          * @param {Form} this
46595          * @param {Action} action The action to be performed
46596          */
46597         beforeaction: true,
46598         /**
46599          * @event actionfailed
46600          * Fires when an action fails.
46601          * @param {Form} this
46602          * @param {Action} action The action that failed
46603          */
46604         actionfailed : true,
46605         /**
46606          * @event actioncomplete
46607          * Fires when an action is completed.
46608          * @param {Form} this
46609          * @param {Action} action The action that completed
46610          */
46611         actioncomplete : true
46612     });
46613     if(el){
46614         this.initEl(el);
46615     }
46616     Roo.form.BasicForm.superclass.constructor.call(this);
46617 };
46618
46619 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
46620     /**
46621      * @cfg {String} method
46622      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
46623      */
46624     /**
46625      * @cfg {DataReader} reader
46626      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
46627      * This is optional as there is built-in support for processing JSON.
46628      */
46629     /**
46630      * @cfg {DataReader} errorReader
46631      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
46632      * This is completely optional as there is built-in support for processing JSON.
46633      */
46634     /**
46635      * @cfg {String} url
46636      * The URL to use for form actions if one isn't supplied in the action options.
46637      */
46638     /**
46639      * @cfg {Boolean} fileUpload
46640      * Set to true if this form is a file upload.
46641      */
46642      
46643     /**
46644      * @cfg {Object} baseParams
46645      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
46646      */
46647      /**
46648      
46649     /**
46650      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
46651      */
46652     timeout: 30,
46653
46654     // private
46655     activeAction : null,
46656
46657     /**
46658      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
46659      * or setValues() data instead of when the form was first created.
46660      */
46661     trackResetOnLoad : false,
46662     
46663     
46664     /**
46665      * childForms - used for multi-tab forms
46666      * @type {Array}
46667      */
46668     childForms : false,
46669     
46670     /**
46671      * allItems - full list of fields.
46672      * @type {Array}
46673      */
46674     allItems : false,
46675     
46676     /**
46677      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
46678      * element by passing it or its id or mask the form itself by passing in true.
46679      * @type Mixed
46680      */
46681     waitMsgTarget : false,
46682
46683     // private
46684     initEl : function(el){
46685         this.el = Roo.get(el);
46686         this.id = this.el.id || Roo.id();
46687         this.el.on('submit', this.onSubmit, this);
46688         this.el.addClass('x-form');
46689     },
46690
46691     // private
46692     onSubmit : function(e){
46693         e.stopEvent();
46694     },
46695
46696     /**
46697      * Returns true if client-side validation on the form is successful.
46698      * @return Boolean
46699      */
46700     isValid : function(){
46701         var valid = true;
46702         this.items.each(function(f){
46703            if(!f.validate()){
46704                valid = false;
46705            }
46706         });
46707         return valid;
46708     },
46709
46710     /**
46711      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
46712      * @return Boolean
46713      */
46714     isDirty : function(){
46715         var dirty = false;
46716         this.items.each(function(f){
46717            if(f.isDirty()){
46718                dirty = true;
46719                return false;
46720            }
46721         });
46722         return dirty;
46723     },
46724     
46725     /**
46726      * Returns true if any fields in this form have changed since their original load. (New version)
46727      * @return Boolean
46728      */
46729     
46730     hasChanged : function()
46731     {
46732         var dirty = false;
46733         this.items.each(function(f){
46734            if(f.hasChanged()){
46735                dirty = true;
46736                return false;
46737            }
46738         });
46739         return dirty;
46740         
46741     },
46742     /**
46743      * Resets all hasChanged to 'false' -
46744      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
46745      * So hasChanged storage is only to be used for this purpose
46746      * @return Boolean
46747      */
46748     resetHasChanged : function()
46749     {
46750         this.items.each(function(f){
46751            f.resetHasChanged();
46752         });
46753         
46754     },
46755     
46756     
46757     /**
46758      * Performs a predefined action (submit or load) or custom actions you define on this form.
46759      * @param {String} actionName The name of the action type
46760      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
46761      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
46762      * accept other config options):
46763      * <pre>
46764 Property          Type             Description
46765 ----------------  ---------------  ----------------------------------------------------------------------------------
46766 url               String           The url for the action (defaults to the form's url)
46767 method            String           The form method to use (defaults to the form's method, or POST if not defined)
46768 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
46769 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
46770                                    validate the form on the client (defaults to false)
46771      * </pre>
46772      * @return {BasicForm} this
46773      */
46774     doAction : function(action, options){
46775         if(typeof action == 'string'){
46776             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
46777         }
46778         if(this.fireEvent('beforeaction', this, action) !== false){
46779             this.beforeAction(action);
46780             action.run.defer(100, action);
46781         }
46782         return this;
46783     },
46784
46785     /**
46786      * Shortcut to do a submit action.
46787      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46788      * @return {BasicForm} this
46789      */
46790     submit : function(options){
46791         this.doAction('submit', options);
46792         return this;
46793     },
46794
46795     /**
46796      * Shortcut to do a load action.
46797      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46798      * @return {BasicForm} this
46799      */
46800     load : function(options){
46801         this.doAction('load', options);
46802         return this;
46803     },
46804
46805     /**
46806      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
46807      * @param {Record} record The record to edit
46808      * @return {BasicForm} this
46809      */
46810     updateRecord : function(record){
46811         record.beginEdit();
46812         var fs = record.fields;
46813         fs.each(function(f){
46814             var field = this.findField(f.name);
46815             if(field){
46816                 record.set(f.name, field.getValue());
46817             }
46818         }, this);
46819         record.endEdit();
46820         return this;
46821     },
46822
46823     /**
46824      * Loads an Roo.data.Record into this form.
46825      * @param {Record} record The record to load
46826      * @return {BasicForm} this
46827      */
46828     loadRecord : function(record){
46829         this.setValues(record.data);
46830         return this;
46831     },
46832
46833     // private
46834     beforeAction : function(action){
46835         var o = action.options;
46836         
46837        
46838         if(this.waitMsgTarget === true){
46839             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
46840         }else if(this.waitMsgTarget){
46841             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
46842             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
46843         }else {
46844             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
46845         }
46846          
46847     },
46848
46849     // private
46850     afterAction : function(action, success){
46851         this.activeAction = null;
46852         var o = action.options;
46853         
46854         if(this.waitMsgTarget === true){
46855             this.el.unmask();
46856         }else if(this.waitMsgTarget){
46857             this.waitMsgTarget.unmask();
46858         }else{
46859             Roo.MessageBox.updateProgress(1);
46860             Roo.MessageBox.hide();
46861         }
46862          
46863         if(success){
46864             if(o.reset){
46865                 this.reset();
46866             }
46867             Roo.callback(o.success, o.scope, [this, action]);
46868             this.fireEvent('actioncomplete', this, action);
46869             
46870         }else{
46871             
46872             // failure condition..
46873             // we have a scenario where updates need confirming.
46874             // eg. if a locking scenario exists..
46875             // we look for { errors : { needs_confirm : true }} in the response.
46876             if (
46877                 (typeof(action.result) != 'undefined')  &&
46878                 (typeof(action.result.errors) != 'undefined')  &&
46879                 (typeof(action.result.errors.needs_confirm) != 'undefined')
46880            ){
46881                 var _t = this;
46882                 Roo.MessageBox.confirm(
46883                     "Change requires confirmation",
46884                     action.result.errorMsg,
46885                     function(r) {
46886                         if (r != 'yes') {
46887                             return;
46888                         }
46889                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
46890                     }
46891                     
46892                 );
46893                 
46894                 
46895                 
46896                 return;
46897             }
46898             
46899             Roo.callback(o.failure, o.scope, [this, action]);
46900             // show an error message if no failed handler is set..
46901             if (!this.hasListener('actionfailed')) {
46902                 Roo.MessageBox.alert("Error",
46903                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
46904                         action.result.errorMsg :
46905                         "Saving Failed, please check your entries or try again"
46906                 );
46907             }
46908             
46909             this.fireEvent('actionfailed', this, action);
46910         }
46911         
46912     },
46913
46914     /**
46915      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
46916      * @param {String} id The value to search for
46917      * @return Field
46918      */
46919     findField : function(id){
46920         var field = this.items.get(id);
46921         if(!field){
46922             this.items.each(function(f){
46923                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
46924                     field = f;
46925                     return false;
46926                 }
46927             });
46928         }
46929         return field || null;
46930     },
46931
46932     /**
46933      * Add a secondary form to this one, 
46934      * Used to provide tabbed forms. One form is primary, with hidden values 
46935      * which mirror the elements from the other forms.
46936      * 
46937      * @param {Roo.form.Form} form to add.
46938      * 
46939      */
46940     addForm : function(form)
46941     {
46942        
46943         if (this.childForms.indexOf(form) > -1) {
46944             // already added..
46945             return;
46946         }
46947         this.childForms.push(form);
46948         var n = '';
46949         Roo.each(form.allItems, function (fe) {
46950             
46951             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
46952             if (this.findField(n)) { // already added..
46953                 return;
46954             }
46955             var add = new Roo.form.Hidden({
46956                 name : n
46957             });
46958             add.render(this.el);
46959             
46960             this.add( add );
46961         }, this);
46962         
46963     },
46964     /**
46965      * Mark fields in this form invalid in bulk.
46966      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
46967      * @return {BasicForm} this
46968      */
46969     markInvalid : function(errors){
46970         if(errors instanceof Array){
46971             for(var i = 0, len = errors.length; i < len; i++){
46972                 var fieldError = errors[i];
46973                 var f = this.findField(fieldError.id);
46974                 if(f){
46975                     f.markInvalid(fieldError.msg);
46976                 }
46977             }
46978         }else{
46979             var field, id;
46980             for(id in errors){
46981                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
46982                     field.markInvalid(errors[id]);
46983                 }
46984             }
46985         }
46986         Roo.each(this.childForms || [], function (f) {
46987             f.markInvalid(errors);
46988         });
46989         
46990         return this;
46991     },
46992
46993     /**
46994      * Set values for fields in this form in bulk.
46995      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
46996      * @return {BasicForm} this
46997      */
46998     setValues : function(values){
46999         if(values instanceof Array){ // array of objects
47000             for(var i = 0, len = values.length; i < len; i++){
47001                 var v = values[i];
47002                 var f = this.findField(v.id);
47003                 if(f){
47004                     f.setValue(v.value);
47005                     if(this.trackResetOnLoad){
47006                         f.originalValue = f.getValue();
47007                     }
47008                 }
47009             }
47010         }else{ // object hash
47011             var field, id;
47012             for(id in values){
47013                 if(typeof values[id] != 'function' && (field = this.findField(id))){
47014                     
47015                     if (field.setFromData && 
47016                         field.valueField && 
47017                         field.displayField &&
47018                         // combos' with local stores can 
47019                         // be queried via setValue()
47020                         // to set their value..
47021                         (field.store && !field.store.isLocal)
47022                         ) {
47023                         // it's a combo
47024                         var sd = { };
47025                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
47026                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47027                         field.setFromData(sd);
47028                         
47029                     } else {
47030                         field.setValue(values[id]);
47031                     }
47032                     
47033                     
47034                     if(this.trackResetOnLoad){
47035                         field.originalValue = field.getValue();
47036                     }
47037                 }
47038             }
47039         }
47040         this.resetHasChanged();
47041         
47042         
47043         Roo.each(this.childForms || [], function (f) {
47044             f.setValues(values);
47045             f.resetHasChanged();
47046         });
47047                 
47048         return this;
47049     },
47050
47051     /**
47052      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47053      * they are returned as an array.
47054      * @param {Boolean} asString
47055      * @return {Object}
47056      */
47057     getValues : function(asString){
47058         if (this.childForms) {
47059             // copy values from the child forms
47060             Roo.each(this.childForms, function (f) {
47061                 this.setValues(f.getValues());
47062             }, this);
47063         }
47064         
47065         
47066         
47067         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47068         if(asString === true){
47069             return fs;
47070         }
47071         return Roo.urlDecode(fs);
47072     },
47073     
47074     /**
47075      * Returns the fields in this form as an object with key/value pairs. 
47076      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47077      * @return {Object}
47078      */
47079     getFieldValues : function(with_hidden)
47080     {
47081         if (this.childForms) {
47082             // copy values from the child forms
47083             // should this call getFieldValues - probably not as we do not currently copy
47084             // hidden fields when we generate..
47085             Roo.each(this.childForms, function (f) {
47086                 this.setValues(f.getValues());
47087             }, this);
47088         }
47089         
47090         var ret = {};
47091         this.items.each(function(f){
47092             if (!f.getName()) {
47093                 return;
47094             }
47095             var v = f.getValue();
47096             if (f.inputType =='radio') {
47097                 if (typeof(ret[f.getName()]) == 'undefined') {
47098                     ret[f.getName()] = ''; // empty..
47099                 }
47100                 
47101                 if (!f.el.dom.checked) {
47102                     return;
47103                     
47104                 }
47105                 v = f.el.dom.value;
47106                 
47107             }
47108             
47109             // not sure if this supported any more..
47110             if ((typeof(v) == 'object') && f.getRawValue) {
47111                 v = f.getRawValue() ; // dates..
47112             }
47113             // combo boxes where name != hiddenName...
47114             if (f.name != f.getName()) {
47115                 ret[f.name] = f.getRawValue();
47116             }
47117             ret[f.getName()] = v;
47118         });
47119         
47120         return ret;
47121     },
47122
47123     /**
47124      * Clears all invalid messages in this form.
47125      * @return {BasicForm} this
47126      */
47127     clearInvalid : function(){
47128         this.items.each(function(f){
47129            f.clearInvalid();
47130         });
47131         
47132         Roo.each(this.childForms || [], function (f) {
47133             f.clearInvalid();
47134         });
47135         
47136         
47137         return this;
47138     },
47139
47140     /**
47141      * Resets this form.
47142      * @return {BasicForm} this
47143      */
47144     reset : function(){
47145         this.items.each(function(f){
47146             f.reset();
47147         });
47148         
47149         Roo.each(this.childForms || [], function (f) {
47150             f.reset();
47151         });
47152         this.resetHasChanged();
47153         
47154         return this;
47155     },
47156
47157     /**
47158      * Add Roo.form components to this form.
47159      * @param {Field} field1
47160      * @param {Field} field2 (optional)
47161      * @param {Field} etc (optional)
47162      * @return {BasicForm} this
47163      */
47164     add : function(){
47165         this.items.addAll(Array.prototype.slice.call(arguments, 0));
47166         return this;
47167     },
47168
47169
47170     /**
47171      * Removes a field from the items collection (does NOT remove its markup).
47172      * @param {Field} field
47173      * @return {BasicForm} this
47174      */
47175     remove : function(field){
47176         this.items.remove(field);
47177         return this;
47178     },
47179
47180     /**
47181      * Looks at the fields in this form, checks them for an id attribute,
47182      * and calls applyTo on the existing dom element with that id.
47183      * @return {BasicForm} this
47184      */
47185     render : function(){
47186         this.items.each(function(f){
47187             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47188                 f.applyTo(f.id);
47189             }
47190         });
47191         return this;
47192     },
47193
47194     /**
47195      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47196      * @param {Object} values
47197      * @return {BasicForm} this
47198      */
47199     applyToFields : function(o){
47200         this.items.each(function(f){
47201            Roo.apply(f, o);
47202         });
47203         return this;
47204     },
47205
47206     /**
47207      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47208      * @param {Object} values
47209      * @return {BasicForm} this
47210      */
47211     applyIfToFields : function(o){
47212         this.items.each(function(f){
47213            Roo.applyIf(f, o);
47214         });
47215         return this;
47216     }
47217 });
47218
47219 // back compat
47220 Roo.BasicForm = Roo.form.BasicForm;/*
47221  * Based on:
47222  * Ext JS Library 1.1.1
47223  * Copyright(c) 2006-2007, Ext JS, LLC.
47224  *
47225  * Originally Released Under LGPL - original licence link has changed is not relivant.
47226  *
47227  * Fork - LGPL
47228  * <script type="text/javascript">
47229  */
47230
47231 /**
47232  * @class Roo.form.Form
47233  * @extends Roo.form.BasicForm
47234  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
47235  * @constructor
47236  * @param {Object} config Configuration options
47237  */
47238 Roo.form.Form = function(config){
47239     var xitems =  [];
47240     if (config.items) {
47241         xitems = config.items;
47242         delete config.items;
47243     }
47244    
47245     
47246     Roo.form.Form.superclass.constructor.call(this, null, config);
47247     this.url = this.url || this.action;
47248     if(!this.root){
47249         this.root = new Roo.form.Layout(Roo.applyIf({
47250             id: Roo.id()
47251         }, config));
47252     }
47253     this.active = this.root;
47254     /**
47255      * Array of all the buttons that have been added to this form via {@link addButton}
47256      * @type Array
47257      */
47258     this.buttons = [];
47259     this.allItems = [];
47260     this.addEvents({
47261         /**
47262          * @event clientvalidation
47263          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
47264          * @param {Form} this
47265          * @param {Boolean} valid true if the form has passed client-side validation
47266          */
47267         clientvalidation: true,
47268         /**
47269          * @event rendered
47270          * Fires when the form is rendered
47271          * @param {Roo.form.Form} form
47272          */
47273         rendered : true
47274     });
47275     
47276     if (this.progressUrl) {
47277             // push a hidden field onto the list of fields..
47278             this.addxtype( {
47279                     xns: Roo.form, 
47280                     xtype : 'Hidden', 
47281                     name : 'UPLOAD_IDENTIFIER' 
47282             });
47283         }
47284         
47285     
47286     Roo.each(xitems, this.addxtype, this);
47287     
47288     
47289     
47290 };
47291
47292 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
47293     /**
47294      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
47295      */
47296     /**
47297      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
47298      */
47299     /**
47300      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
47301      */
47302     buttonAlign:'center',
47303
47304     /**
47305      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
47306      */
47307     minButtonWidth:75,
47308
47309     /**
47310      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
47311      * This property cascades to child containers if not set.
47312      */
47313     labelAlign:'left',
47314
47315     /**
47316      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
47317      * fires a looping event with that state. This is required to bind buttons to the valid
47318      * state using the config value formBind:true on the button.
47319      */
47320     monitorValid : false,
47321
47322     /**
47323      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
47324      */
47325     monitorPoll : 200,
47326     
47327     /**
47328      * @cfg {String} progressUrl - Url to return progress data 
47329      */
47330     
47331     progressUrl : false,
47332   
47333     /**
47334      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
47335      * fields are added and the column is closed. If no fields are passed the column remains open
47336      * until end() is called.
47337      * @param {Object} config The config to pass to the column
47338      * @param {Field} field1 (optional)
47339      * @param {Field} field2 (optional)
47340      * @param {Field} etc (optional)
47341      * @return Column The column container object
47342      */
47343     column : function(c){
47344         var col = new Roo.form.Column(c);
47345         this.start(col);
47346         if(arguments.length > 1){ // duplicate code required because of Opera
47347             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47348             this.end();
47349         }
47350         return col;
47351     },
47352
47353     /**
47354      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
47355      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
47356      * until end() is called.
47357      * @param {Object} config The config to pass to the fieldset
47358      * @param {Field} field1 (optional)
47359      * @param {Field} field2 (optional)
47360      * @param {Field} etc (optional)
47361      * @return FieldSet The fieldset container object
47362      */
47363     fieldset : function(c){
47364         var fs = new Roo.form.FieldSet(c);
47365         this.start(fs);
47366         if(arguments.length > 1){ // duplicate code required because of Opera
47367             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47368             this.end();
47369         }
47370         return fs;
47371     },
47372
47373     /**
47374      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
47375      * fields are added and the container is closed. If no fields are passed the container remains open
47376      * until end() is called.
47377      * @param {Object} config The config to pass to the Layout
47378      * @param {Field} field1 (optional)
47379      * @param {Field} field2 (optional)
47380      * @param {Field} etc (optional)
47381      * @return Layout The container object
47382      */
47383     container : function(c){
47384         var l = new Roo.form.Layout(c);
47385         this.start(l);
47386         if(arguments.length > 1){ // duplicate code required because of Opera
47387             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47388             this.end();
47389         }
47390         return l;
47391     },
47392
47393     /**
47394      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
47395      * @param {Object} container A Roo.form.Layout or subclass of Layout
47396      * @return {Form} this
47397      */
47398     start : function(c){
47399         // cascade label info
47400         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
47401         this.active.stack.push(c);
47402         c.ownerCt = this.active;
47403         this.active = c;
47404         return this;
47405     },
47406
47407     /**
47408      * Closes the current open container
47409      * @return {Form} this
47410      */
47411     end : function(){
47412         if(this.active == this.root){
47413             return this;
47414         }
47415         this.active = this.active.ownerCt;
47416         return this;
47417     },
47418
47419     /**
47420      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
47421      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
47422      * as the label of the field.
47423      * @param {Field} field1
47424      * @param {Field} field2 (optional)
47425      * @param {Field} etc. (optional)
47426      * @return {Form} this
47427      */
47428     add : function(){
47429         this.active.stack.push.apply(this.active.stack, arguments);
47430         this.allItems.push.apply(this.allItems,arguments);
47431         var r = [];
47432         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
47433             if(a[i].isFormField){
47434                 r.push(a[i]);
47435             }
47436         }
47437         if(r.length > 0){
47438             Roo.form.Form.superclass.add.apply(this, r);
47439         }
47440         return this;
47441     },
47442     
47443
47444     
47445     
47446     
47447      /**
47448      * Find any element that has been added to a form, using it's ID or name
47449      * This can include framesets, columns etc. along with regular fields..
47450      * @param {String} id - id or name to find.
47451      
47452      * @return {Element} e - or false if nothing found.
47453      */
47454     findbyId : function(id)
47455     {
47456         var ret = false;
47457         if (!id) {
47458             return ret;
47459         }
47460         Roo.each(this.allItems, function(f){
47461             if (f.id == id || f.name == id ){
47462                 ret = f;
47463                 return false;
47464             }
47465         });
47466         return ret;
47467     },
47468
47469     
47470     
47471     /**
47472      * Render this form into the passed container. This should only be called once!
47473      * @param {String/HTMLElement/Element} container The element this component should be rendered into
47474      * @return {Form} this
47475      */
47476     render : function(ct)
47477     {
47478         
47479         
47480         
47481         ct = Roo.get(ct);
47482         var o = this.autoCreate || {
47483             tag: 'form',
47484             method : this.method || 'POST',
47485             id : this.id || Roo.id()
47486         };
47487         this.initEl(ct.createChild(o));
47488
47489         this.root.render(this.el);
47490         
47491        
47492              
47493         this.items.each(function(f){
47494             f.render('x-form-el-'+f.id);
47495         });
47496
47497         if(this.buttons.length > 0){
47498             // tables are required to maintain order and for correct IE layout
47499             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
47500                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
47501                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
47502             }}, null, true);
47503             var tr = tb.getElementsByTagName('tr')[0];
47504             for(var i = 0, len = this.buttons.length; i < len; i++) {
47505                 var b = this.buttons[i];
47506                 var td = document.createElement('td');
47507                 td.className = 'x-form-btn-td';
47508                 b.render(tr.appendChild(td));
47509             }
47510         }
47511         if(this.monitorValid){ // initialize after render
47512             this.startMonitoring();
47513         }
47514         this.fireEvent('rendered', this);
47515         return this;
47516     },
47517
47518     /**
47519      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
47520      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
47521      * object or a valid Roo.DomHelper element config
47522      * @param {Function} handler The function called when the button is clicked
47523      * @param {Object} scope (optional) The scope of the handler function
47524      * @return {Roo.Button}
47525      */
47526     addButton : function(config, handler, scope){
47527         var bc = {
47528             handler: handler,
47529             scope: scope,
47530             minWidth: this.minButtonWidth,
47531             hideParent:true
47532         };
47533         if(typeof config == "string"){
47534             bc.text = config;
47535         }else{
47536             Roo.apply(bc, config);
47537         }
47538         var btn = new Roo.Button(null, bc);
47539         this.buttons.push(btn);
47540         return btn;
47541     },
47542
47543      /**
47544      * Adds a series of form elements (using the xtype property as the factory method.
47545      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
47546      * @param {Object} config 
47547      */
47548     
47549     addxtype : function()
47550     {
47551         var ar = Array.prototype.slice.call(arguments, 0);
47552         var ret = false;
47553         for(var i = 0; i < ar.length; i++) {
47554             if (!ar[i]) {
47555                 continue; // skip -- if this happends something invalid got sent, we 
47556                 // should ignore it, as basically that interface element will not show up
47557                 // and that should be pretty obvious!!
47558             }
47559             
47560             if (Roo.form[ar[i].xtype]) {
47561                 ar[i].form = this;
47562                 var fe = Roo.factory(ar[i], Roo.form);
47563                 if (!ret) {
47564                     ret = fe;
47565                 }
47566                 fe.form = this;
47567                 if (fe.store) {
47568                     fe.store.form = this;
47569                 }
47570                 if (fe.isLayout) {  
47571                          
47572                     this.start(fe);
47573                     this.allItems.push(fe);
47574                     if (fe.items && fe.addxtype) {
47575                         fe.addxtype.apply(fe, fe.items);
47576                         delete fe.items;
47577                     }
47578                      this.end();
47579                     continue;
47580                 }
47581                 
47582                 
47583                  
47584                 this.add(fe);
47585               //  console.log('adding ' + ar[i].xtype);
47586             }
47587             if (ar[i].xtype == 'Button') {  
47588                 //console.log('adding button');
47589                 //console.log(ar[i]);
47590                 this.addButton(ar[i]);
47591                 this.allItems.push(fe);
47592                 continue;
47593             }
47594             
47595             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
47596                 alert('end is not supported on xtype any more, use items');
47597             //    this.end();
47598             //    //console.log('adding end');
47599             }
47600             
47601         }
47602         return ret;
47603     },
47604     
47605     /**
47606      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
47607      * option "monitorValid"
47608      */
47609     startMonitoring : function(){
47610         if(!this.bound){
47611             this.bound = true;
47612             Roo.TaskMgr.start({
47613                 run : this.bindHandler,
47614                 interval : this.monitorPoll || 200,
47615                 scope: this
47616             });
47617         }
47618     },
47619
47620     /**
47621      * Stops monitoring of the valid state of this form
47622      */
47623     stopMonitoring : function(){
47624         this.bound = false;
47625     },
47626
47627     // private
47628     bindHandler : function(){
47629         if(!this.bound){
47630             return false; // stops binding
47631         }
47632         var valid = true;
47633         this.items.each(function(f){
47634             if(!f.isValid(true)){
47635                 valid = false;
47636                 return false;
47637             }
47638         });
47639         for(var i = 0, len = this.buttons.length; i < len; i++){
47640             var btn = this.buttons[i];
47641             if(btn.formBind === true && btn.disabled === valid){
47642                 btn.setDisabled(!valid);
47643             }
47644         }
47645         this.fireEvent('clientvalidation', this, valid);
47646     }
47647     
47648     
47649     
47650     
47651     
47652     
47653     
47654     
47655 });
47656
47657
47658 // back compat
47659 Roo.Form = Roo.form.Form;
47660 /*
47661  * Based on:
47662  * Ext JS Library 1.1.1
47663  * Copyright(c) 2006-2007, Ext JS, LLC.
47664  *
47665  * Originally Released Under LGPL - original licence link has changed is not relivant.
47666  *
47667  * Fork - LGPL
47668  * <script type="text/javascript">
47669  */
47670
47671 // as we use this in bootstrap.
47672 Roo.namespace('Roo.form');
47673  /**
47674  * @class Roo.form.Action
47675  * Internal Class used to handle form actions
47676  * @constructor
47677  * @param {Roo.form.BasicForm} el The form element or its id
47678  * @param {Object} config Configuration options
47679  */
47680
47681  
47682  
47683 // define the action interface
47684 Roo.form.Action = function(form, options){
47685     this.form = form;
47686     this.options = options || {};
47687 };
47688 /**
47689  * Client Validation Failed
47690  * @const 
47691  */
47692 Roo.form.Action.CLIENT_INVALID = 'client';
47693 /**
47694  * Server Validation Failed
47695  * @const 
47696  */
47697 Roo.form.Action.SERVER_INVALID = 'server';
47698  /**
47699  * Connect to Server Failed
47700  * @const 
47701  */
47702 Roo.form.Action.CONNECT_FAILURE = 'connect';
47703 /**
47704  * Reading Data from Server Failed
47705  * @const 
47706  */
47707 Roo.form.Action.LOAD_FAILURE = 'load';
47708
47709 Roo.form.Action.prototype = {
47710     type : 'default',
47711     failureType : undefined,
47712     response : undefined,
47713     result : undefined,
47714
47715     // interface method
47716     run : function(options){
47717
47718     },
47719
47720     // interface method
47721     success : function(response){
47722
47723     },
47724
47725     // interface method
47726     handleResponse : function(response){
47727
47728     },
47729
47730     // default connection failure
47731     failure : function(response){
47732         
47733         this.response = response;
47734         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47735         this.form.afterAction(this, false);
47736     },
47737
47738     processResponse : function(response){
47739         this.response = response;
47740         if(!response.responseText){
47741             return true;
47742         }
47743         this.result = this.handleResponse(response);
47744         return this.result;
47745     },
47746
47747     // utility functions used internally
47748     getUrl : function(appendParams){
47749         var url = this.options.url || this.form.url || this.form.el.dom.action;
47750         if(appendParams){
47751             var p = this.getParams();
47752             if(p){
47753                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
47754             }
47755         }
47756         return url;
47757     },
47758
47759     getMethod : function(){
47760         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
47761     },
47762
47763     getParams : function(){
47764         var bp = this.form.baseParams;
47765         var p = this.options.params;
47766         if(p){
47767             if(typeof p == "object"){
47768                 p = Roo.urlEncode(Roo.applyIf(p, bp));
47769             }else if(typeof p == 'string' && bp){
47770                 p += '&' + Roo.urlEncode(bp);
47771             }
47772         }else if(bp){
47773             p = Roo.urlEncode(bp);
47774         }
47775         return p;
47776     },
47777
47778     createCallback : function(){
47779         return {
47780             success: this.success,
47781             failure: this.failure,
47782             scope: this,
47783             timeout: (this.form.timeout*1000),
47784             upload: this.form.fileUpload ? this.success : undefined
47785         };
47786     }
47787 };
47788
47789 Roo.form.Action.Submit = function(form, options){
47790     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
47791 };
47792
47793 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
47794     type : 'submit',
47795
47796     haveProgress : false,
47797     uploadComplete : false,
47798     
47799     // uploadProgress indicator.
47800     uploadProgress : function()
47801     {
47802         if (!this.form.progressUrl) {
47803             return;
47804         }
47805         
47806         if (!this.haveProgress) {
47807             Roo.MessageBox.progress("Uploading", "Uploading");
47808         }
47809         if (this.uploadComplete) {
47810            Roo.MessageBox.hide();
47811            return;
47812         }
47813         
47814         this.haveProgress = true;
47815    
47816         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
47817         
47818         var c = new Roo.data.Connection();
47819         c.request({
47820             url : this.form.progressUrl,
47821             params: {
47822                 id : uid
47823             },
47824             method: 'GET',
47825             success : function(req){
47826                //console.log(data);
47827                 var rdata = false;
47828                 var edata;
47829                 try  {
47830                    rdata = Roo.decode(req.responseText)
47831                 } catch (e) {
47832                     Roo.log("Invalid data from server..");
47833                     Roo.log(edata);
47834                     return;
47835                 }
47836                 if (!rdata || !rdata.success) {
47837                     Roo.log(rdata);
47838                     Roo.MessageBox.alert(Roo.encode(rdata));
47839                     return;
47840                 }
47841                 var data = rdata.data;
47842                 
47843                 if (this.uploadComplete) {
47844                    Roo.MessageBox.hide();
47845                    return;
47846                 }
47847                    
47848                 if (data){
47849                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
47850                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
47851                     );
47852                 }
47853                 this.uploadProgress.defer(2000,this);
47854             },
47855        
47856             failure: function(data) {
47857                 Roo.log('progress url failed ');
47858                 Roo.log(data);
47859             },
47860             scope : this
47861         });
47862            
47863     },
47864     
47865     
47866     run : function()
47867     {
47868         // run get Values on the form, so it syncs any secondary forms.
47869         this.form.getValues();
47870         
47871         var o = this.options;
47872         var method = this.getMethod();
47873         var isPost = method == 'POST';
47874         if(o.clientValidation === false || this.form.isValid()){
47875             
47876             if (this.form.progressUrl) {
47877                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
47878                     (new Date() * 1) + '' + Math.random());
47879                     
47880             } 
47881             
47882             
47883             Roo.Ajax.request(Roo.apply(this.createCallback(), {
47884                 form:this.form.el.dom,
47885                 url:this.getUrl(!isPost),
47886                 method: method,
47887                 params:isPost ? this.getParams() : null,
47888                 isUpload: this.form.fileUpload
47889             }));
47890             
47891             this.uploadProgress();
47892
47893         }else if (o.clientValidation !== false){ // client validation failed
47894             this.failureType = Roo.form.Action.CLIENT_INVALID;
47895             this.form.afterAction(this, false);
47896         }
47897     },
47898
47899     success : function(response)
47900     {
47901         this.uploadComplete= true;
47902         if (this.haveProgress) {
47903             Roo.MessageBox.hide();
47904         }
47905         
47906         
47907         var result = this.processResponse(response);
47908         if(result === true || result.success){
47909             this.form.afterAction(this, true);
47910             return;
47911         }
47912         if(result.errors){
47913             this.form.markInvalid(result.errors);
47914             this.failureType = Roo.form.Action.SERVER_INVALID;
47915         }
47916         this.form.afterAction(this, false);
47917     },
47918     failure : function(response)
47919     {
47920         this.uploadComplete= true;
47921         if (this.haveProgress) {
47922             Roo.MessageBox.hide();
47923         }
47924         
47925         this.response = response;
47926         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47927         this.form.afterAction(this, false);
47928     },
47929     
47930     handleResponse : function(response){
47931         if(this.form.errorReader){
47932             var rs = this.form.errorReader.read(response);
47933             var errors = [];
47934             if(rs.records){
47935                 for(var i = 0, len = rs.records.length; i < len; i++) {
47936                     var r = rs.records[i];
47937                     errors[i] = r.data;
47938                 }
47939             }
47940             if(errors.length < 1){
47941                 errors = null;
47942             }
47943             return {
47944                 success : rs.success,
47945                 errors : errors
47946             };
47947         }
47948         var ret = false;
47949         try {
47950             ret = Roo.decode(response.responseText);
47951         } catch (e) {
47952             ret = {
47953                 success: false,
47954                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
47955                 errors : []
47956             };
47957         }
47958         return ret;
47959         
47960     }
47961 });
47962
47963
47964 Roo.form.Action.Load = function(form, options){
47965     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
47966     this.reader = this.form.reader;
47967 };
47968
47969 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
47970     type : 'load',
47971
47972     run : function(){
47973         
47974         Roo.Ajax.request(Roo.apply(
47975                 this.createCallback(), {
47976                     method:this.getMethod(),
47977                     url:this.getUrl(false),
47978                     params:this.getParams()
47979         }));
47980     },
47981
47982     success : function(response){
47983         
47984         var result = this.processResponse(response);
47985         if(result === true || !result.success || !result.data){
47986             this.failureType = Roo.form.Action.LOAD_FAILURE;
47987             this.form.afterAction(this, false);
47988             return;
47989         }
47990         this.form.clearInvalid();
47991         this.form.setValues(result.data);
47992         this.form.afterAction(this, true);
47993     },
47994
47995     handleResponse : function(response){
47996         if(this.form.reader){
47997             var rs = this.form.reader.read(response);
47998             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
47999             return {
48000                 success : rs.success,
48001                 data : data
48002             };
48003         }
48004         return Roo.decode(response.responseText);
48005     }
48006 });
48007
48008 Roo.form.Action.ACTION_TYPES = {
48009     'load' : Roo.form.Action.Load,
48010     'submit' : Roo.form.Action.Submit
48011 };/*
48012  * Based on:
48013  * Ext JS Library 1.1.1
48014  * Copyright(c) 2006-2007, Ext JS, LLC.
48015  *
48016  * Originally Released Under LGPL - original licence link has changed is not relivant.
48017  *
48018  * Fork - LGPL
48019  * <script type="text/javascript">
48020  */
48021  
48022 /**
48023  * @class Roo.form.Layout
48024  * @extends Roo.Component
48025  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
48026  * @constructor
48027  * @param {Object} config Configuration options
48028  */
48029 Roo.form.Layout = function(config){
48030     var xitems = [];
48031     if (config.items) {
48032         xitems = config.items;
48033         delete config.items;
48034     }
48035     Roo.form.Layout.superclass.constructor.call(this, config);
48036     this.stack = [];
48037     Roo.each(xitems, this.addxtype, this);
48038      
48039 };
48040
48041 Roo.extend(Roo.form.Layout, Roo.Component, {
48042     /**
48043      * @cfg {String/Object} autoCreate
48044      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
48045      */
48046     /**
48047      * @cfg {String/Object/Function} style
48048      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
48049      * a function which returns such a specification.
48050      */
48051     /**
48052      * @cfg {String} labelAlign
48053      * Valid values are "left," "top" and "right" (defaults to "left")
48054      */
48055     /**
48056      * @cfg {Number} labelWidth
48057      * Fixed width in pixels of all field labels (defaults to undefined)
48058      */
48059     /**
48060      * @cfg {Boolean} clear
48061      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
48062      */
48063     clear : true,
48064     /**
48065      * @cfg {String} labelSeparator
48066      * The separator to use after field labels (defaults to ':')
48067      */
48068     labelSeparator : ':',
48069     /**
48070      * @cfg {Boolean} hideLabels
48071      * True to suppress the display of field labels in this layout (defaults to false)
48072      */
48073     hideLabels : false,
48074
48075     // private
48076     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
48077     
48078     isLayout : true,
48079     
48080     // private
48081     onRender : function(ct, position){
48082         if(this.el){ // from markup
48083             this.el = Roo.get(this.el);
48084         }else {  // generate
48085             var cfg = this.getAutoCreate();
48086             this.el = ct.createChild(cfg, position);
48087         }
48088         if(this.style){
48089             this.el.applyStyles(this.style);
48090         }
48091         if(this.labelAlign){
48092             this.el.addClass('x-form-label-'+this.labelAlign);
48093         }
48094         if(this.hideLabels){
48095             this.labelStyle = "display:none";
48096             this.elementStyle = "padding-left:0;";
48097         }else{
48098             if(typeof this.labelWidth == 'number'){
48099                 this.labelStyle = "width:"+this.labelWidth+"px;";
48100                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
48101             }
48102             if(this.labelAlign == 'top'){
48103                 this.labelStyle = "width:auto;";
48104                 this.elementStyle = "padding-left:0;";
48105             }
48106         }
48107         var stack = this.stack;
48108         var slen = stack.length;
48109         if(slen > 0){
48110             if(!this.fieldTpl){
48111                 var t = new Roo.Template(
48112                     '<div class="x-form-item {5}">',
48113                         '<label for="{0}" style="{2}">{1}{4}</label>',
48114                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48115                         '</div>',
48116                     '</div><div class="x-form-clear-left"></div>'
48117                 );
48118                 t.disableFormats = true;
48119                 t.compile();
48120                 Roo.form.Layout.prototype.fieldTpl = t;
48121             }
48122             for(var i = 0; i < slen; i++) {
48123                 if(stack[i].isFormField){
48124                     this.renderField(stack[i]);
48125                 }else{
48126                     this.renderComponent(stack[i]);
48127                 }
48128             }
48129         }
48130         if(this.clear){
48131             this.el.createChild({cls:'x-form-clear'});
48132         }
48133     },
48134
48135     // private
48136     renderField : function(f){
48137         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
48138                f.id, //0
48139                f.fieldLabel, //1
48140                f.labelStyle||this.labelStyle||'', //2
48141                this.elementStyle||'', //3
48142                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
48143                f.itemCls||this.itemCls||''  //5
48144        ], true).getPrevSibling());
48145     },
48146
48147     // private
48148     renderComponent : function(c){
48149         c.render(c.isLayout ? this.el : this.el.createChild());    
48150     },
48151     /**
48152      * Adds a object form elements (using the xtype property as the factory method.)
48153      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
48154      * @param {Object} config 
48155      */
48156     addxtype : function(o)
48157     {
48158         // create the lement.
48159         o.form = this.form;
48160         var fe = Roo.factory(o, Roo.form);
48161         this.form.allItems.push(fe);
48162         this.stack.push(fe);
48163         
48164         if (fe.isFormField) {
48165             this.form.items.add(fe);
48166         }
48167          
48168         return fe;
48169     }
48170 });
48171
48172 /**
48173  * @class Roo.form.Column
48174  * @extends Roo.form.Layout
48175  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
48176  * @constructor
48177  * @param {Object} config Configuration options
48178  */
48179 Roo.form.Column = function(config){
48180     Roo.form.Column.superclass.constructor.call(this, config);
48181 };
48182
48183 Roo.extend(Roo.form.Column, Roo.form.Layout, {
48184     /**
48185      * @cfg {Number/String} width
48186      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48187      */
48188     /**
48189      * @cfg {String/Object} autoCreate
48190      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
48191      */
48192
48193     // private
48194     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
48195
48196     // private
48197     onRender : function(ct, position){
48198         Roo.form.Column.superclass.onRender.call(this, ct, position);
48199         if(this.width){
48200             this.el.setWidth(this.width);
48201         }
48202     }
48203 });
48204
48205
48206 /**
48207  * @class Roo.form.Row
48208  * @extends Roo.form.Layout
48209  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
48210  * @constructor
48211  * @param {Object} config Configuration options
48212  */
48213
48214  
48215 Roo.form.Row = function(config){
48216     Roo.form.Row.superclass.constructor.call(this, config);
48217 };
48218  
48219 Roo.extend(Roo.form.Row, Roo.form.Layout, {
48220       /**
48221      * @cfg {Number/String} width
48222      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48223      */
48224     /**
48225      * @cfg {Number/String} height
48226      * The fixed height of the column in pixels or CSS value (defaults to "auto")
48227      */
48228     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
48229     
48230     padWidth : 20,
48231     // private
48232     onRender : function(ct, position){
48233         //console.log('row render');
48234         if(!this.rowTpl){
48235             var t = new Roo.Template(
48236                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
48237                     '<label for="{0}" style="{2}">{1}{4}</label>',
48238                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48239                     '</div>',
48240                 '</div>'
48241             );
48242             t.disableFormats = true;
48243             t.compile();
48244             Roo.form.Layout.prototype.rowTpl = t;
48245         }
48246         this.fieldTpl = this.rowTpl;
48247         
48248         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
48249         var labelWidth = 100;
48250         
48251         if ((this.labelAlign != 'top')) {
48252             if (typeof this.labelWidth == 'number') {
48253                 labelWidth = this.labelWidth
48254             }
48255             this.padWidth =  20 + labelWidth;
48256             
48257         }
48258         
48259         Roo.form.Column.superclass.onRender.call(this, ct, position);
48260         if(this.width){
48261             this.el.setWidth(this.width);
48262         }
48263         if(this.height){
48264             this.el.setHeight(this.height);
48265         }
48266     },
48267     
48268     // private
48269     renderField : function(f){
48270         f.fieldEl = this.fieldTpl.append(this.el, [
48271                f.id, f.fieldLabel,
48272                f.labelStyle||this.labelStyle||'',
48273                this.elementStyle||'',
48274                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
48275                f.itemCls||this.itemCls||'',
48276                f.width ? f.width + this.padWidth : 160 + this.padWidth
48277        ],true);
48278     }
48279 });
48280  
48281
48282 /**
48283  * @class Roo.form.FieldSet
48284  * @extends Roo.form.Layout
48285  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
48286  * @constructor
48287  * @param {Object} config Configuration options
48288  */
48289 Roo.form.FieldSet = function(config){
48290     Roo.form.FieldSet.superclass.constructor.call(this, config);
48291 };
48292
48293 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
48294     /**
48295      * @cfg {String} legend
48296      * The text to display as the legend for the FieldSet (defaults to '')
48297      */
48298     /**
48299      * @cfg {String/Object} autoCreate
48300      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
48301      */
48302
48303     // private
48304     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
48305
48306     // private
48307     onRender : function(ct, position){
48308         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
48309         if(this.legend){
48310             this.setLegend(this.legend);
48311         }
48312     },
48313
48314     // private
48315     setLegend : function(text){
48316         if(this.rendered){
48317             this.el.child('legend').update(text);
48318         }
48319     }
48320 });/*
48321  * Based on:
48322  * Ext JS Library 1.1.1
48323  * Copyright(c) 2006-2007, Ext JS, LLC.
48324  *
48325  * Originally Released Under LGPL - original licence link has changed is not relivant.
48326  *
48327  * Fork - LGPL
48328  * <script type="text/javascript">
48329  */
48330 /**
48331  * @class Roo.form.VTypes
48332  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
48333  * @singleton
48334  */
48335 Roo.form.VTypes = function(){
48336     // closure these in so they are only created once.
48337     var alpha = /^[a-zA-Z_]+$/;
48338     var alphanum = /^[a-zA-Z0-9_]+$/;
48339     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
48340     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
48341
48342     // All these messages and functions are configurable
48343     return {
48344         /**
48345          * The function used to validate email addresses
48346          * @param {String} value The email address
48347          */
48348         'email' : function(v){
48349             return email.test(v);
48350         },
48351         /**
48352          * The error text to display when the email validation function returns false
48353          * @type String
48354          */
48355         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
48356         /**
48357          * The keystroke filter mask to be applied on email input
48358          * @type RegExp
48359          */
48360         'emailMask' : /[a-z0-9_\.\-@]/i,
48361
48362         /**
48363          * The function used to validate URLs
48364          * @param {String} value The URL
48365          */
48366         'url' : function(v){
48367             return url.test(v);
48368         },
48369         /**
48370          * The error text to display when the url validation function returns false
48371          * @type String
48372          */
48373         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
48374         
48375         /**
48376          * The function used to validate alpha values
48377          * @param {String} value The value
48378          */
48379         'alpha' : function(v){
48380             return alpha.test(v);
48381         },
48382         /**
48383          * The error text to display when the alpha validation function returns false
48384          * @type String
48385          */
48386         'alphaText' : 'This field should only contain letters and _',
48387         /**
48388          * The keystroke filter mask to be applied on alpha input
48389          * @type RegExp
48390          */
48391         'alphaMask' : /[a-z_]/i,
48392
48393         /**
48394          * The function used to validate alphanumeric values
48395          * @param {String} value The value
48396          */
48397         'alphanum' : function(v){
48398             return alphanum.test(v);
48399         },
48400         /**
48401          * The error text to display when the alphanumeric validation function returns false
48402          * @type String
48403          */
48404         'alphanumText' : 'This field should only contain letters, numbers and _',
48405         /**
48406          * The keystroke filter mask to be applied on alphanumeric input
48407          * @type RegExp
48408          */
48409         'alphanumMask' : /[a-z0-9_]/i
48410     };
48411 }();//<script type="text/javascript">
48412
48413 /**
48414  * @class Roo.form.FCKeditor
48415  * @extends Roo.form.TextArea
48416  * Wrapper around the FCKEditor http://www.fckeditor.net
48417  * @constructor
48418  * Creates a new FCKeditor
48419  * @param {Object} config Configuration options
48420  */
48421 Roo.form.FCKeditor = function(config){
48422     Roo.form.FCKeditor.superclass.constructor.call(this, config);
48423     this.addEvents({
48424          /**
48425          * @event editorinit
48426          * Fired when the editor is initialized - you can add extra handlers here..
48427          * @param {FCKeditor} this
48428          * @param {Object} the FCK object.
48429          */
48430         editorinit : true
48431     });
48432     
48433     
48434 };
48435 Roo.form.FCKeditor.editors = { };
48436 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
48437 {
48438     //defaultAutoCreate : {
48439     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
48440     //},
48441     // private
48442     /**
48443      * @cfg {Object} fck options - see fck manual for details.
48444      */
48445     fckconfig : false,
48446     
48447     /**
48448      * @cfg {Object} fck toolbar set (Basic or Default)
48449      */
48450     toolbarSet : 'Basic',
48451     /**
48452      * @cfg {Object} fck BasePath
48453      */ 
48454     basePath : '/fckeditor/',
48455     
48456     
48457     frame : false,
48458     
48459     value : '',
48460     
48461    
48462     onRender : function(ct, position)
48463     {
48464         if(!this.el){
48465             this.defaultAutoCreate = {
48466                 tag: "textarea",
48467                 style:"width:300px;height:60px;",
48468                 autocomplete: "new-password"
48469             };
48470         }
48471         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
48472         /*
48473         if(this.grow){
48474             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
48475             if(this.preventScrollbars){
48476                 this.el.setStyle("overflow", "hidden");
48477             }
48478             this.el.setHeight(this.growMin);
48479         }
48480         */
48481         //console.log('onrender' + this.getId() );
48482         Roo.form.FCKeditor.editors[this.getId()] = this;
48483          
48484
48485         this.replaceTextarea() ;
48486         
48487     },
48488     
48489     getEditor : function() {
48490         return this.fckEditor;
48491     },
48492     /**
48493      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
48494      * @param {Mixed} value The value to set
48495      */
48496     
48497     
48498     setValue : function(value)
48499     {
48500         //console.log('setValue: ' + value);
48501         
48502         if(typeof(value) == 'undefined') { // not sure why this is happending...
48503             return;
48504         }
48505         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48506         
48507         //if(!this.el || !this.getEditor()) {
48508         //    this.value = value;
48509             //this.setValue.defer(100,this,[value]);    
48510         //    return;
48511         //} 
48512         
48513         if(!this.getEditor()) {
48514             return;
48515         }
48516         
48517         this.getEditor().SetData(value);
48518         
48519         //
48520
48521     },
48522
48523     /**
48524      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
48525      * @return {Mixed} value The field value
48526      */
48527     getValue : function()
48528     {
48529         
48530         if (this.frame && this.frame.dom.style.display == 'none') {
48531             return Roo.form.FCKeditor.superclass.getValue.call(this);
48532         }
48533         
48534         if(!this.el || !this.getEditor()) {
48535            
48536            // this.getValue.defer(100,this); 
48537             return this.value;
48538         }
48539        
48540         
48541         var value=this.getEditor().GetData();
48542         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48543         return Roo.form.FCKeditor.superclass.getValue.call(this);
48544         
48545
48546     },
48547
48548     /**
48549      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
48550      * @return {Mixed} value The field value
48551      */
48552     getRawValue : function()
48553     {
48554         if (this.frame && this.frame.dom.style.display == 'none') {
48555             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48556         }
48557         
48558         if(!this.el || !this.getEditor()) {
48559             //this.getRawValue.defer(100,this); 
48560             return this.value;
48561             return;
48562         }
48563         
48564         
48565         
48566         var value=this.getEditor().GetData();
48567         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
48568         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48569          
48570     },
48571     
48572     setSize : function(w,h) {
48573         
48574         
48575         
48576         //if (this.frame && this.frame.dom.style.display == 'none') {
48577         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48578         //    return;
48579         //}
48580         //if(!this.el || !this.getEditor()) {
48581         //    this.setSize.defer(100,this, [w,h]); 
48582         //    return;
48583         //}
48584         
48585         
48586         
48587         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48588         
48589         this.frame.dom.setAttribute('width', w);
48590         this.frame.dom.setAttribute('height', h);
48591         this.frame.setSize(w,h);
48592         
48593     },
48594     
48595     toggleSourceEdit : function(value) {
48596         
48597       
48598          
48599         this.el.dom.style.display = value ? '' : 'none';
48600         this.frame.dom.style.display = value ?  'none' : '';
48601         
48602     },
48603     
48604     
48605     focus: function(tag)
48606     {
48607         if (this.frame.dom.style.display == 'none') {
48608             return Roo.form.FCKeditor.superclass.focus.call(this);
48609         }
48610         if(!this.el || !this.getEditor()) {
48611             this.focus.defer(100,this, [tag]); 
48612             return;
48613         }
48614         
48615         
48616         
48617         
48618         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
48619         this.getEditor().Focus();
48620         if (tgs.length) {
48621             if (!this.getEditor().Selection.GetSelection()) {
48622                 this.focus.defer(100,this, [tag]); 
48623                 return;
48624             }
48625             
48626             
48627             var r = this.getEditor().EditorDocument.createRange();
48628             r.setStart(tgs[0],0);
48629             r.setEnd(tgs[0],0);
48630             this.getEditor().Selection.GetSelection().removeAllRanges();
48631             this.getEditor().Selection.GetSelection().addRange(r);
48632             this.getEditor().Focus();
48633         }
48634         
48635     },
48636     
48637     
48638     
48639     replaceTextarea : function()
48640     {
48641         if ( document.getElementById( this.getId() + '___Frame' ) ) {
48642             return ;
48643         }
48644         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
48645         //{
48646             // We must check the elements firstly using the Id and then the name.
48647         var oTextarea = document.getElementById( this.getId() );
48648         
48649         var colElementsByName = document.getElementsByName( this.getId() ) ;
48650          
48651         oTextarea.style.display = 'none' ;
48652
48653         if ( oTextarea.tabIndex ) {            
48654             this.TabIndex = oTextarea.tabIndex ;
48655         }
48656         
48657         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
48658         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
48659         this.frame = Roo.get(this.getId() + '___Frame')
48660     },
48661     
48662     _getConfigHtml : function()
48663     {
48664         var sConfig = '' ;
48665
48666         for ( var o in this.fckconfig ) {
48667             sConfig += sConfig.length > 0  ? '&amp;' : '';
48668             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
48669         }
48670
48671         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
48672     },
48673     
48674     
48675     _getIFrameHtml : function()
48676     {
48677         var sFile = 'fckeditor.html' ;
48678         /* no idea what this is about..
48679         try
48680         {
48681             if ( (/fcksource=true/i).test( window.top.location.search ) )
48682                 sFile = 'fckeditor.original.html' ;
48683         }
48684         catch (e) { 
48685         */
48686
48687         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
48688         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
48689         
48690         
48691         var html = '<iframe id="' + this.getId() +
48692             '___Frame" src="' + sLink +
48693             '" width="' + this.width +
48694             '" height="' + this.height + '"' +
48695             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
48696             ' frameborder="0" scrolling="no"></iframe>' ;
48697
48698         return html ;
48699     },
48700     
48701     _insertHtmlBefore : function( html, element )
48702     {
48703         if ( element.insertAdjacentHTML )       {
48704             // IE
48705             element.insertAdjacentHTML( 'beforeBegin', html ) ;
48706         } else { // Gecko
48707             var oRange = document.createRange() ;
48708             oRange.setStartBefore( element ) ;
48709             var oFragment = oRange.createContextualFragment( html );
48710             element.parentNode.insertBefore( oFragment, element ) ;
48711         }
48712     }
48713     
48714     
48715   
48716     
48717     
48718     
48719     
48720
48721 });
48722
48723 //Roo.reg('fckeditor', Roo.form.FCKeditor);
48724
48725 function FCKeditor_OnComplete(editorInstance){
48726     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
48727     f.fckEditor = editorInstance;
48728     //console.log("loaded");
48729     f.fireEvent('editorinit', f, editorInstance);
48730
48731   
48732
48733  
48734
48735
48736
48737
48738
48739
48740
48741
48742
48743
48744
48745
48746
48747
48748
48749 //<script type="text/javascript">
48750 /**
48751  * @class Roo.form.GridField
48752  * @extends Roo.form.Field
48753  * Embed a grid (or editable grid into a form)
48754  * STATUS ALPHA
48755  * 
48756  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
48757  * it needs 
48758  * xgrid.store = Roo.data.Store
48759  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
48760  * xgrid.store.reader = Roo.data.JsonReader 
48761  * 
48762  * 
48763  * @constructor
48764  * Creates a new GridField
48765  * @param {Object} config Configuration options
48766  */
48767 Roo.form.GridField = function(config){
48768     Roo.form.GridField.superclass.constructor.call(this, config);
48769      
48770 };
48771
48772 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
48773     /**
48774      * @cfg {Number} width  - used to restrict width of grid..
48775      */
48776     width : 100,
48777     /**
48778      * @cfg {Number} height - used to restrict height of grid..
48779      */
48780     height : 50,
48781      /**
48782      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
48783          * 
48784          *}
48785      */
48786     xgrid : false, 
48787     /**
48788      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48789      * {tag: "input", type: "checkbox", autocomplete: "off"})
48790      */
48791    // defaultAutoCreate : { tag: 'div' },
48792     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
48793     /**
48794      * @cfg {String} addTitle Text to include for adding a title.
48795      */
48796     addTitle : false,
48797     //
48798     onResize : function(){
48799         Roo.form.Field.superclass.onResize.apply(this, arguments);
48800     },
48801
48802     initEvents : function(){
48803         // Roo.form.Checkbox.superclass.initEvents.call(this);
48804         // has no events...
48805        
48806     },
48807
48808
48809     getResizeEl : function(){
48810         return this.wrap;
48811     },
48812
48813     getPositionEl : function(){
48814         return this.wrap;
48815     },
48816
48817     // private
48818     onRender : function(ct, position){
48819         
48820         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
48821         var style = this.style;
48822         delete this.style;
48823         
48824         Roo.form.GridField.superclass.onRender.call(this, ct, position);
48825         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
48826         this.viewEl = this.wrap.createChild({ tag: 'div' });
48827         if (style) {
48828             this.viewEl.applyStyles(style);
48829         }
48830         if (this.width) {
48831             this.viewEl.setWidth(this.width);
48832         }
48833         if (this.height) {
48834             this.viewEl.setHeight(this.height);
48835         }
48836         //if(this.inputValue !== undefined){
48837         //this.setValue(this.value);
48838         
48839         
48840         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
48841         
48842         
48843         this.grid.render();
48844         this.grid.getDataSource().on('remove', this.refreshValue, this);
48845         this.grid.getDataSource().on('update', this.refreshValue, this);
48846         this.grid.on('afteredit', this.refreshValue, this);
48847  
48848     },
48849      
48850     
48851     /**
48852      * Sets the value of the item. 
48853      * @param {String} either an object  or a string..
48854      */
48855     setValue : function(v){
48856         //this.value = v;
48857         v = v || []; // empty set..
48858         // this does not seem smart - it really only affects memoryproxy grids..
48859         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
48860             var ds = this.grid.getDataSource();
48861             // assumes a json reader..
48862             var data = {}
48863             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
48864             ds.loadData( data);
48865         }
48866         // clear selection so it does not get stale.
48867         if (this.grid.sm) { 
48868             this.grid.sm.clearSelections();
48869         }
48870         
48871         Roo.form.GridField.superclass.setValue.call(this, v);
48872         this.refreshValue();
48873         // should load data in the grid really....
48874     },
48875     
48876     // private
48877     refreshValue: function() {
48878          var val = [];
48879         this.grid.getDataSource().each(function(r) {
48880             val.push(r.data);
48881         });
48882         this.el.dom.value = Roo.encode(val);
48883     }
48884     
48885      
48886     
48887     
48888 });/*
48889  * Based on:
48890  * Ext JS Library 1.1.1
48891  * Copyright(c) 2006-2007, Ext JS, LLC.
48892  *
48893  * Originally Released Under LGPL - original licence link has changed is not relivant.
48894  *
48895  * Fork - LGPL
48896  * <script type="text/javascript">
48897  */
48898 /**
48899  * @class Roo.form.DisplayField
48900  * @extends Roo.form.Field
48901  * A generic Field to display non-editable data.
48902  * @cfg {Boolean} closable (true|false) default false
48903  * @constructor
48904  * Creates a new Display Field item.
48905  * @param {Object} config Configuration options
48906  */
48907 Roo.form.DisplayField = function(config){
48908     Roo.form.DisplayField.superclass.constructor.call(this, config);
48909     
48910     this.addEvents({
48911         /**
48912          * @event close
48913          * Fires after the click the close btn
48914              * @param {Roo.form.DisplayField} this
48915              */
48916         close : true
48917     });
48918 };
48919
48920 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
48921     inputType:      'hidden',
48922     allowBlank:     true,
48923     readOnly:         true,
48924     
48925  
48926     /**
48927      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
48928      */
48929     focusClass : undefined,
48930     /**
48931      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
48932      */
48933     fieldClass: 'x-form-field',
48934     
48935      /**
48936      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
48937      */
48938     valueRenderer: undefined,
48939     
48940     width: 100,
48941     /**
48942      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48943      * {tag: "input", type: "checkbox", autocomplete: "off"})
48944      */
48945      
48946  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
48947  
48948     closable : false,
48949     
48950     onResize : function(){
48951         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
48952         
48953     },
48954
48955     initEvents : function(){
48956         // Roo.form.Checkbox.superclass.initEvents.call(this);
48957         // has no events...
48958         
48959         if(this.closable){
48960             this.closeEl.on('click', this.onClose, this);
48961         }
48962        
48963     },
48964
48965
48966     getResizeEl : function(){
48967         return this.wrap;
48968     },
48969
48970     getPositionEl : function(){
48971         return this.wrap;
48972     },
48973
48974     // private
48975     onRender : function(ct, position){
48976         
48977         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
48978         //if(this.inputValue !== undefined){
48979         this.wrap = this.el.wrap();
48980         
48981         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
48982         
48983         if(this.closable){
48984             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
48985         }
48986         
48987         if (this.bodyStyle) {
48988             this.viewEl.applyStyles(this.bodyStyle);
48989         }
48990         //this.viewEl.setStyle('padding', '2px');
48991         
48992         this.setValue(this.value);
48993         
48994     },
48995 /*
48996     // private
48997     initValue : Roo.emptyFn,
48998
48999   */
49000
49001         // private
49002     onClick : function(){
49003         
49004     },
49005
49006     /**
49007      * Sets the checked state of the checkbox.
49008      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
49009      */
49010     setValue : function(v){
49011         this.value = v;
49012         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
49013         // this might be called before we have a dom element..
49014         if (!this.viewEl) {
49015             return;
49016         }
49017         this.viewEl.dom.innerHTML = html;
49018         Roo.form.DisplayField.superclass.setValue.call(this, v);
49019
49020     },
49021     
49022     onClose : function(e)
49023     {
49024         e.preventDefault();
49025         
49026         this.fireEvent('close', this);
49027     }
49028 });/*
49029  * 
49030  * Licence- LGPL
49031  * 
49032  */
49033
49034 /**
49035  * @class Roo.form.DayPicker
49036  * @extends Roo.form.Field
49037  * A Day picker show [M] [T] [W] ....
49038  * @constructor
49039  * Creates a new Day Picker
49040  * @param {Object} config Configuration options
49041  */
49042 Roo.form.DayPicker= function(config){
49043     Roo.form.DayPicker.superclass.constructor.call(this, config);
49044      
49045 };
49046
49047 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
49048     /**
49049      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49050      */
49051     focusClass : undefined,
49052     /**
49053      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49054      */
49055     fieldClass: "x-form-field",
49056    
49057     /**
49058      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49059      * {tag: "input", type: "checkbox", autocomplete: "off"})
49060      */
49061     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
49062     
49063    
49064     actionMode : 'viewEl', 
49065     //
49066     // private
49067  
49068     inputType : 'hidden',
49069     
49070      
49071     inputElement: false, // real input element?
49072     basedOn: false, // ????
49073     
49074     isFormField: true, // not sure where this is needed!!!!
49075
49076     onResize : function(){
49077         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
49078         if(!this.boxLabel){
49079             this.el.alignTo(this.wrap, 'c-c');
49080         }
49081     },
49082
49083     initEvents : function(){
49084         Roo.form.Checkbox.superclass.initEvents.call(this);
49085         this.el.on("click", this.onClick,  this);
49086         this.el.on("change", this.onClick,  this);
49087     },
49088
49089
49090     getResizeEl : function(){
49091         return this.wrap;
49092     },
49093
49094     getPositionEl : function(){
49095         return this.wrap;
49096     },
49097
49098     
49099     // private
49100     onRender : function(ct, position){
49101         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
49102        
49103         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
49104         
49105         var r1 = '<table><tr>';
49106         var r2 = '<tr class="x-form-daypick-icons">';
49107         for (var i=0; i < 7; i++) {
49108             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
49109             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
49110         }
49111         
49112         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
49113         viewEl.select('img').on('click', this.onClick, this);
49114         this.viewEl = viewEl;   
49115         
49116         
49117         // this will not work on Chrome!!!
49118         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
49119         this.el.on('propertychange', this.setFromHidden,  this);  //ie
49120         
49121         
49122           
49123
49124     },
49125
49126     // private
49127     initValue : Roo.emptyFn,
49128
49129     /**
49130      * Returns the checked state of the checkbox.
49131      * @return {Boolean} True if checked, else false
49132      */
49133     getValue : function(){
49134         return this.el.dom.value;
49135         
49136     },
49137
49138         // private
49139     onClick : function(e){ 
49140         //this.setChecked(!this.checked);
49141         Roo.get(e.target).toggleClass('x-menu-item-checked');
49142         this.refreshValue();
49143         //if(this.el.dom.checked != this.checked){
49144         //    this.setValue(this.el.dom.checked);
49145        // }
49146     },
49147     
49148     // private
49149     refreshValue : function()
49150     {
49151         var val = '';
49152         this.viewEl.select('img',true).each(function(e,i,n)  {
49153             val += e.is(".x-menu-item-checked") ? String(n) : '';
49154         });
49155         this.setValue(val, true);
49156     },
49157
49158     /**
49159      * Sets the checked state of the checkbox.
49160      * On is always based on a string comparison between inputValue and the param.
49161      * @param {Boolean/String} value - the value to set 
49162      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
49163      */
49164     setValue : function(v,suppressEvent){
49165         if (!this.el.dom) {
49166             return;
49167         }
49168         var old = this.el.dom.value ;
49169         this.el.dom.value = v;
49170         if (suppressEvent) {
49171             return ;
49172         }
49173          
49174         // update display..
49175         this.viewEl.select('img',true).each(function(e,i,n)  {
49176             
49177             var on = e.is(".x-menu-item-checked");
49178             var newv = v.indexOf(String(n)) > -1;
49179             if (on != newv) {
49180                 e.toggleClass('x-menu-item-checked');
49181             }
49182             
49183         });
49184         
49185         
49186         this.fireEvent('change', this, v, old);
49187         
49188         
49189     },
49190    
49191     // handle setting of hidden value by some other method!!?!?
49192     setFromHidden: function()
49193     {
49194         if(!this.el){
49195             return;
49196         }
49197         //console.log("SET FROM HIDDEN");
49198         //alert('setFrom hidden');
49199         this.setValue(this.el.dom.value);
49200     },
49201     
49202     onDestroy : function()
49203     {
49204         if(this.viewEl){
49205             Roo.get(this.viewEl).remove();
49206         }
49207          
49208         Roo.form.DayPicker.superclass.onDestroy.call(this);
49209     }
49210
49211 });/*
49212  * RooJS Library 1.1.1
49213  * Copyright(c) 2008-2011  Alan Knowles
49214  *
49215  * License - LGPL
49216  */
49217  
49218
49219 /**
49220  * @class Roo.form.ComboCheck
49221  * @extends Roo.form.ComboBox
49222  * A combobox for multiple select items.
49223  *
49224  * FIXME - could do with a reset button..
49225  * 
49226  * @constructor
49227  * Create a new ComboCheck
49228  * @param {Object} config Configuration options
49229  */
49230 Roo.form.ComboCheck = function(config){
49231     Roo.form.ComboCheck.superclass.constructor.call(this, config);
49232     // should verify some data...
49233     // like
49234     // hiddenName = required..
49235     // displayField = required
49236     // valudField == required
49237     var req= [ 'hiddenName', 'displayField', 'valueField' ];
49238     var _t = this;
49239     Roo.each(req, function(e) {
49240         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
49241             throw "Roo.form.ComboCheck : missing value for: " + e;
49242         }
49243     });
49244     
49245     
49246 };
49247
49248 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
49249      
49250      
49251     editable : false,
49252      
49253     selectedClass: 'x-menu-item-checked', 
49254     
49255     // private
49256     onRender : function(ct, position){
49257         var _t = this;
49258         
49259         
49260         
49261         if(!this.tpl){
49262             var cls = 'x-combo-list';
49263
49264             
49265             this.tpl =  new Roo.Template({
49266                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
49267                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
49268                    '<span>{' + this.displayField + '}</span>' +
49269                     '</div>' 
49270                 
49271             });
49272         }
49273  
49274         
49275         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
49276         this.view.singleSelect = false;
49277         this.view.multiSelect = true;
49278         this.view.toggleSelect = true;
49279         this.pageTb.add(new Roo.Toolbar.Fill(), {
49280             
49281             text: 'Done',
49282             handler: function()
49283             {
49284                 _t.collapse();
49285             }
49286         });
49287     },
49288     
49289     onViewOver : function(e, t){
49290         // do nothing...
49291         return;
49292         
49293     },
49294     
49295     onViewClick : function(doFocus,index){
49296         return;
49297         
49298     },
49299     select: function () {
49300         //Roo.log("SELECT CALLED");
49301     },
49302      
49303     selectByValue : function(xv, scrollIntoView){
49304         var ar = this.getValueArray();
49305         var sels = [];
49306         
49307         Roo.each(ar, function(v) {
49308             if(v === undefined || v === null){
49309                 return;
49310             }
49311             var r = this.findRecord(this.valueField, v);
49312             if(r){
49313                 sels.push(this.store.indexOf(r))
49314                 
49315             }
49316         },this);
49317         this.view.select(sels);
49318         return false;
49319     },
49320     
49321     
49322     
49323     onSelect : function(record, index){
49324        // Roo.log("onselect Called");
49325        // this is only called by the clear button now..
49326         this.view.clearSelections();
49327         this.setValue('[]');
49328         if (this.value != this.valueBefore) {
49329             this.fireEvent('change', this, this.value, this.valueBefore);
49330             this.valueBefore = this.value;
49331         }
49332     },
49333     getValueArray : function()
49334     {
49335         var ar = [] ;
49336         
49337         try {
49338             //Roo.log(this.value);
49339             if (typeof(this.value) == 'undefined') {
49340                 return [];
49341             }
49342             var ar = Roo.decode(this.value);
49343             return  ar instanceof Array ? ar : []; //?? valid?
49344             
49345         } catch(e) {
49346             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
49347             return [];
49348         }
49349          
49350     },
49351     expand : function ()
49352     {
49353         
49354         Roo.form.ComboCheck.superclass.expand.call(this);
49355         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
49356         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
49357         
49358
49359     },
49360     
49361     collapse : function(){
49362         Roo.form.ComboCheck.superclass.collapse.call(this);
49363         var sl = this.view.getSelectedIndexes();
49364         var st = this.store;
49365         var nv = [];
49366         var tv = [];
49367         var r;
49368         Roo.each(sl, function(i) {
49369             r = st.getAt(i);
49370             nv.push(r.get(this.valueField));
49371         },this);
49372         this.setValue(Roo.encode(nv));
49373         if (this.value != this.valueBefore) {
49374
49375             this.fireEvent('change', this, this.value, this.valueBefore);
49376             this.valueBefore = this.value;
49377         }
49378         
49379     },
49380     
49381     setValue : function(v){
49382         // Roo.log(v);
49383         this.value = v;
49384         
49385         var vals = this.getValueArray();
49386         var tv = [];
49387         Roo.each(vals, function(k) {
49388             var r = this.findRecord(this.valueField, k);
49389             if(r){
49390                 tv.push(r.data[this.displayField]);
49391             }else if(this.valueNotFoundText !== undefined){
49392                 tv.push( this.valueNotFoundText );
49393             }
49394         },this);
49395        // Roo.log(tv);
49396         
49397         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
49398         this.hiddenField.value = v;
49399         this.value = v;
49400     }
49401     
49402 });/*
49403  * Based on:
49404  * Ext JS Library 1.1.1
49405  * Copyright(c) 2006-2007, Ext JS, LLC.
49406  *
49407  * Originally Released Under LGPL - original licence link has changed is not relivant.
49408  *
49409  * Fork - LGPL
49410  * <script type="text/javascript">
49411  */
49412  
49413 /**
49414  * @class Roo.form.Signature
49415  * @extends Roo.form.Field
49416  * Signature field.  
49417  * @constructor
49418  * 
49419  * @param {Object} config Configuration options
49420  */
49421
49422 Roo.form.Signature = function(config){
49423     Roo.form.Signature.superclass.constructor.call(this, config);
49424     
49425     this.addEvents({// not in used??
49426          /**
49427          * @event confirm
49428          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
49429              * @param {Roo.form.Signature} combo This combo box
49430              */
49431         'confirm' : true,
49432         /**
49433          * @event reset
49434          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
49435              * @param {Roo.form.ComboBox} combo This combo box
49436              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
49437              */
49438         'reset' : true
49439     });
49440 };
49441
49442 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
49443     /**
49444      * @cfg {Object} labels Label to use when rendering a form.
49445      * defaults to 
49446      * labels : { 
49447      *      clear : "Clear",
49448      *      confirm : "Confirm"
49449      *  }
49450      */
49451     labels : { 
49452         clear : "Clear",
49453         confirm : "Confirm"
49454     },
49455     /**
49456      * @cfg {Number} width The signature panel width (defaults to 300)
49457      */
49458     width: 300,
49459     /**
49460      * @cfg {Number} height The signature panel height (defaults to 100)
49461      */
49462     height : 100,
49463     /**
49464      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
49465      */
49466     allowBlank : false,
49467     
49468     //private
49469     // {Object} signPanel The signature SVG panel element (defaults to {})
49470     signPanel : {},
49471     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
49472     isMouseDown : false,
49473     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
49474     isConfirmed : false,
49475     // {String} signatureTmp SVG mapping string (defaults to empty string)
49476     signatureTmp : '',
49477     
49478     
49479     defaultAutoCreate : { // modified by initCompnoent..
49480         tag: "input",
49481         type:"hidden"
49482     },
49483
49484     // private
49485     onRender : function(ct, position){
49486         
49487         Roo.form.Signature.superclass.onRender.call(this, ct, position);
49488         
49489         this.wrap = this.el.wrap({
49490             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
49491         });
49492         
49493         this.createToolbar(this);
49494         this.signPanel = this.wrap.createChild({
49495                 tag: 'div',
49496                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
49497             }, this.el
49498         );
49499             
49500         this.svgID = Roo.id();
49501         this.svgEl = this.signPanel.createChild({
49502               xmlns : 'http://www.w3.org/2000/svg',
49503               tag : 'svg',
49504               id : this.svgID + "-svg",
49505               width: this.width,
49506               height: this.height,
49507               viewBox: '0 0 '+this.width+' '+this.height,
49508               cn : [
49509                 {
49510                     tag: "rect",
49511                     id: this.svgID + "-svg-r",
49512                     width: this.width,
49513                     height: this.height,
49514                     fill: "#ffa"
49515                 },
49516                 {
49517                     tag: "line",
49518                     id: this.svgID + "-svg-l",
49519                     x1: "0", // start
49520                     y1: (this.height*0.8), // start set the line in 80% of height
49521                     x2: this.width, // end
49522                     y2: (this.height*0.8), // end set the line in 80% of height
49523                     'stroke': "#666",
49524                     'stroke-width': "1",
49525                     'stroke-dasharray': "3",
49526                     'shape-rendering': "crispEdges",
49527                     'pointer-events': "none"
49528                 },
49529                 {
49530                     tag: "path",
49531                     id: this.svgID + "-svg-p",
49532                     'stroke': "navy",
49533                     'stroke-width': "3",
49534                     'fill': "none",
49535                     'pointer-events': 'none'
49536                 }
49537               ]
49538         });
49539         this.createSVG();
49540         this.svgBox = this.svgEl.dom.getScreenCTM();
49541     },
49542     createSVG : function(){ 
49543         var svg = this.signPanel;
49544         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
49545         var t = this;
49546
49547         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
49548         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
49549         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
49550         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
49551         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
49552         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
49553         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
49554         
49555     },
49556     isTouchEvent : function(e){
49557         return e.type.match(/^touch/);
49558     },
49559     getCoords : function (e) {
49560         var pt    = this.svgEl.dom.createSVGPoint();
49561         pt.x = e.clientX; 
49562         pt.y = e.clientY;
49563         if (this.isTouchEvent(e)) {
49564             pt.x =  e.targetTouches[0].clientX;
49565             pt.y = e.targetTouches[0].clientY;
49566         }
49567         var a = this.svgEl.dom.getScreenCTM();
49568         var b = a.inverse();
49569         var mx = pt.matrixTransform(b);
49570         return mx.x + ',' + mx.y;
49571     },
49572     //mouse event headler 
49573     down : function (e) {
49574         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
49575         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
49576         
49577         this.isMouseDown = true;
49578         
49579         e.preventDefault();
49580     },
49581     move : function (e) {
49582         if (this.isMouseDown) {
49583             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
49584             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
49585         }
49586         
49587         e.preventDefault();
49588     },
49589     up : function (e) {
49590         this.isMouseDown = false;
49591         var sp = this.signatureTmp.split(' ');
49592         
49593         if(sp.length > 1){
49594             if(!sp[sp.length-2].match(/^L/)){
49595                 sp.pop();
49596                 sp.pop();
49597                 sp.push("");
49598                 this.signatureTmp = sp.join(" ");
49599             }
49600         }
49601         if(this.getValue() != this.signatureTmp){
49602             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49603             this.isConfirmed = false;
49604         }
49605         e.preventDefault();
49606     },
49607     
49608     /**
49609      * Protected method that will not generally be called directly. It
49610      * is called when the editor creates its toolbar. Override this method if you need to
49611      * add custom toolbar buttons.
49612      * @param {HtmlEditor} editor
49613      */
49614     createToolbar : function(editor){
49615          function btn(id, toggle, handler){
49616             var xid = fid + '-'+ id ;
49617             return {
49618                 id : xid,
49619                 cmd : id,
49620                 cls : 'x-btn-icon x-edit-'+id,
49621                 enableToggle:toggle !== false,
49622                 scope: editor, // was editor...
49623                 handler:handler||editor.relayBtnCmd,
49624                 clickEvent:'mousedown',
49625                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49626                 tabIndex:-1
49627             };
49628         }
49629         
49630         
49631         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
49632         this.tb = tb;
49633         this.tb.add(
49634            {
49635                 cls : ' x-signature-btn x-signature-'+id,
49636                 scope: editor, // was editor...
49637                 handler: this.reset,
49638                 clickEvent:'mousedown',
49639                 text: this.labels.clear
49640             },
49641             {
49642                  xtype : 'Fill',
49643                  xns: Roo.Toolbar
49644             }, 
49645             {
49646                 cls : '  x-signature-btn x-signature-'+id,
49647                 scope: editor, // was editor...
49648                 handler: this.confirmHandler,
49649                 clickEvent:'mousedown',
49650                 text: this.labels.confirm
49651             }
49652         );
49653     
49654     },
49655     //public
49656     /**
49657      * when user is clicked confirm then show this image.....
49658      * 
49659      * @return {String} Image Data URI
49660      */
49661     getImageDataURI : function(){
49662         var svg = this.svgEl.dom.parentNode.innerHTML;
49663         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
49664         return src; 
49665     },
49666     /**
49667      * 
49668      * @return {Boolean} this.isConfirmed
49669      */
49670     getConfirmed : function(){
49671         return this.isConfirmed;
49672     },
49673     /**
49674      * 
49675      * @return {Number} this.width
49676      */
49677     getWidth : function(){
49678         return this.width;
49679     },
49680     /**
49681      * 
49682      * @return {Number} this.height
49683      */
49684     getHeight : function(){
49685         return this.height;
49686     },
49687     // private
49688     getSignature : function(){
49689         return this.signatureTmp;
49690     },
49691     // private
49692     reset : function(){
49693         this.signatureTmp = '';
49694         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49695         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
49696         this.isConfirmed = false;
49697         Roo.form.Signature.superclass.reset.call(this);
49698     },
49699     setSignature : function(s){
49700         this.signatureTmp = s;
49701         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49702         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
49703         this.setValue(s);
49704         this.isConfirmed = false;
49705         Roo.form.Signature.superclass.reset.call(this);
49706     }, 
49707     test : function(){
49708 //        Roo.log(this.signPanel.dom.contentWindow.up())
49709     },
49710     //private
49711     setConfirmed : function(){
49712         
49713         
49714         
49715 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
49716     },
49717     // private
49718     confirmHandler : function(){
49719         if(!this.getSignature()){
49720             return;
49721         }
49722         
49723         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
49724         this.setValue(this.getSignature());
49725         this.isConfirmed = true;
49726         
49727         this.fireEvent('confirm', this);
49728     },
49729     // private
49730     // Subclasses should provide the validation implementation by overriding this
49731     validateValue : function(value){
49732         if(this.allowBlank){
49733             return true;
49734         }
49735         
49736         if(this.isConfirmed){
49737             return true;
49738         }
49739         return false;
49740     }
49741 });/*
49742  * Based on:
49743  * Ext JS Library 1.1.1
49744  * Copyright(c) 2006-2007, Ext JS, LLC.
49745  *
49746  * Originally Released Under LGPL - original licence link has changed is not relivant.
49747  *
49748  * Fork - LGPL
49749  * <script type="text/javascript">
49750  */
49751  
49752
49753 /**
49754  * @class Roo.form.ComboBox
49755  * @extends Roo.form.TriggerField
49756  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
49757  * @constructor
49758  * Create a new ComboBox.
49759  * @param {Object} config Configuration options
49760  */
49761 Roo.form.Select = function(config){
49762     Roo.form.Select.superclass.constructor.call(this, config);
49763      
49764 };
49765
49766 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
49767     /**
49768      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
49769      */
49770     /**
49771      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
49772      * rendering into an Roo.Editor, defaults to false)
49773      */
49774     /**
49775      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
49776      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
49777      */
49778     /**
49779      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
49780      */
49781     /**
49782      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
49783      * the dropdown list (defaults to undefined, with no header element)
49784      */
49785
49786      /**
49787      * @cfg {String/Roo.Template} tpl The template to use to render the output
49788      */
49789      
49790     // private
49791     defaultAutoCreate : {tag: "select"  },
49792     /**
49793      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
49794      */
49795     listWidth: undefined,
49796     /**
49797      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
49798      * mode = 'remote' or 'text' if mode = 'local')
49799      */
49800     displayField: undefined,
49801     /**
49802      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
49803      * mode = 'remote' or 'value' if mode = 'local'). 
49804      * Note: use of a valueField requires the user make a selection
49805      * in order for a value to be mapped.
49806      */
49807     valueField: undefined,
49808     
49809     
49810     /**
49811      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
49812      * field's data value (defaults to the underlying DOM element's name)
49813      */
49814     hiddenName: undefined,
49815     /**
49816      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
49817      */
49818     listClass: '',
49819     /**
49820      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
49821      */
49822     selectedClass: 'x-combo-selected',
49823     /**
49824      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
49825      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
49826      * which displays a downward arrow icon).
49827      */
49828     triggerClass : 'x-form-arrow-trigger',
49829     /**
49830      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
49831      */
49832     shadow:'sides',
49833     /**
49834      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
49835      * anchor positions (defaults to 'tl-bl')
49836      */
49837     listAlign: 'tl-bl?',
49838     /**
49839      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
49840      */
49841     maxHeight: 300,
49842     /**
49843      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
49844      * query specified by the allQuery config option (defaults to 'query')
49845      */
49846     triggerAction: 'query',
49847     /**
49848      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
49849      * (defaults to 4, does not apply if editable = false)
49850      */
49851     minChars : 4,
49852     /**
49853      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
49854      * delay (typeAheadDelay) if it matches a known value (defaults to false)
49855      */
49856     typeAhead: false,
49857     /**
49858      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
49859      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
49860      */
49861     queryDelay: 500,
49862     /**
49863      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
49864      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
49865      */
49866     pageSize: 0,
49867     /**
49868      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
49869      * when editable = true (defaults to false)
49870      */
49871     selectOnFocus:false,
49872     /**
49873      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
49874      */
49875     queryParam: 'query',
49876     /**
49877      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
49878      * when mode = 'remote' (defaults to 'Loading...')
49879      */
49880     loadingText: 'Loading...',
49881     /**
49882      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
49883      */
49884     resizable: false,
49885     /**
49886      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
49887      */
49888     handleHeight : 8,
49889     /**
49890      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
49891      * traditional select (defaults to true)
49892      */
49893     editable: true,
49894     /**
49895      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
49896      */
49897     allQuery: '',
49898     /**
49899      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
49900      */
49901     mode: 'remote',
49902     /**
49903      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
49904      * listWidth has a higher value)
49905      */
49906     minListWidth : 70,
49907     /**
49908      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
49909      * allow the user to set arbitrary text into the field (defaults to false)
49910      */
49911     forceSelection:false,
49912     /**
49913      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
49914      * if typeAhead = true (defaults to 250)
49915      */
49916     typeAheadDelay : 250,
49917     /**
49918      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
49919      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
49920      */
49921     valueNotFoundText : undefined,
49922     
49923     /**
49924      * @cfg {String} defaultValue The value displayed after loading the store.
49925      */
49926     defaultValue: '',
49927     
49928     /**
49929      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
49930      */
49931     blockFocus : false,
49932     
49933     /**
49934      * @cfg {Boolean} disableClear Disable showing of clear button.
49935      */
49936     disableClear : false,
49937     /**
49938      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
49939      */
49940     alwaysQuery : false,
49941     
49942     //private
49943     addicon : false,
49944     editicon: false,
49945     
49946     // element that contains real text value.. (when hidden is used..)
49947      
49948     // private
49949     onRender : function(ct, position){
49950         Roo.form.Field.prototype.onRender.call(this, ct, position);
49951         
49952         if(this.store){
49953             this.store.on('beforeload', this.onBeforeLoad, this);
49954             this.store.on('load', this.onLoad, this);
49955             this.store.on('loadexception', this.onLoadException, this);
49956             this.store.load({});
49957         }
49958         
49959         
49960         
49961     },
49962
49963     // private
49964     initEvents : function(){
49965         //Roo.form.ComboBox.superclass.initEvents.call(this);
49966  
49967     },
49968
49969     onDestroy : function(){
49970        
49971         if(this.store){
49972             this.store.un('beforeload', this.onBeforeLoad, this);
49973             this.store.un('load', this.onLoad, this);
49974             this.store.un('loadexception', this.onLoadException, this);
49975         }
49976         //Roo.form.ComboBox.superclass.onDestroy.call(this);
49977     },
49978
49979     // private
49980     fireKey : function(e){
49981         if(e.isNavKeyPress() && !this.list.isVisible()){
49982             this.fireEvent("specialkey", this, e);
49983         }
49984     },
49985
49986     // private
49987     onResize: function(w, h){
49988         
49989         return; 
49990     
49991         
49992     },
49993
49994     /**
49995      * Allow or prevent the user from directly editing the field text.  If false is passed,
49996      * the user will only be able to select from the items defined in the dropdown list.  This method
49997      * is the runtime equivalent of setting the 'editable' config option at config time.
49998      * @param {Boolean} value True to allow the user to directly edit the field text
49999      */
50000     setEditable : function(value){
50001          
50002     },
50003
50004     // private
50005     onBeforeLoad : function(){
50006         
50007         Roo.log("Select before load");
50008         return;
50009     
50010         this.innerList.update(this.loadingText ?
50011                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
50012         //this.restrictHeight();
50013         this.selectedIndex = -1;
50014     },
50015
50016     // private
50017     onLoad : function(){
50018
50019     
50020         var dom = this.el.dom;
50021         dom.innerHTML = '';
50022          var od = dom.ownerDocument;
50023          
50024         if (this.emptyText) {
50025             var op = od.createElement('option');
50026             op.setAttribute('value', '');
50027             op.innerHTML = String.format('{0}', this.emptyText);
50028             dom.appendChild(op);
50029         }
50030         if(this.store.getCount() > 0){
50031            
50032             var vf = this.valueField;
50033             var df = this.displayField;
50034             this.store.data.each(function(r) {
50035                 // which colmsn to use... testing - cdoe / title..
50036                 var op = od.createElement('option');
50037                 op.setAttribute('value', r.data[vf]);
50038                 op.innerHTML = String.format('{0}', r.data[df]);
50039                 dom.appendChild(op);
50040             });
50041             if (typeof(this.defaultValue != 'undefined')) {
50042                 this.setValue(this.defaultValue);
50043             }
50044             
50045              
50046         }else{
50047             //this.onEmptyResults();
50048         }
50049         //this.el.focus();
50050     },
50051     // private
50052     onLoadException : function()
50053     {
50054         dom.innerHTML = '';
50055             
50056         Roo.log("Select on load exception");
50057         return;
50058     
50059         this.collapse();
50060         Roo.log(this.store.reader.jsonData);
50061         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
50062             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
50063         }
50064         
50065         
50066     },
50067     // private
50068     onTypeAhead : function(){
50069          
50070     },
50071
50072     // private
50073     onSelect : function(record, index){
50074         Roo.log('on select?');
50075         return;
50076         if(this.fireEvent('beforeselect', this, record, index) !== false){
50077             this.setFromData(index > -1 ? record.data : false);
50078             this.collapse();
50079             this.fireEvent('select', this, record, index);
50080         }
50081     },
50082
50083     /**
50084      * Returns the currently selected field value or empty string if no value is set.
50085      * @return {String} value The selected value
50086      */
50087     getValue : function(){
50088         var dom = this.el.dom;
50089         this.value = dom.options[dom.selectedIndex].value;
50090         return this.value;
50091         
50092     },
50093
50094     /**
50095      * Clears any text/value currently set in the field
50096      */
50097     clearValue : function(){
50098         this.value = '';
50099         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
50100         
50101     },
50102
50103     /**
50104      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
50105      * will be displayed in the field.  If the value does not match the data value of an existing item,
50106      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
50107      * Otherwise the field will be blank (although the value will still be set).
50108      * @param {String} value The value to match
50109      */
50110     setValue : function(v){
50111         var d = this.el.dom;
50112         for (var i =0; i < d.options.length;i++) {
50113             if (v == d.options[i].value) {
50114                 d.selectedIndex = i;
50115                 this.value = v;
50116                 return;
50117             }
50118         }
50119         this.clearValue();
50120     },
50121     /**
50122      * @property {Object} the last set data for the element
50123      */
50124     
50125     lastData : false,
50126     /**
50127      * Sets the value of the field based on a object which is related to the record format for the store.
50128      * @param {Object} value the value to set as. or false on reset?
50129      */
50130     setFromData : function(o){
50131         Roo.log('setfrom data?');
50132          
50133         
50134         
50135     },
50136     // private
50137     reset : function(){
50138         this.clearValue();
50139     },
50140     // private
50141     findRecord : function(prop, value){
50142         
50143         return false;
50144     
50145         var record;
50146         if(this.store.getCount() > 0){
50147             this.store.each(function(r){
50148                 if(r.data[prop] == value){
50149                     record = r;
50150                     return false;
50151                 }
50152                 return true;
50153             });
50154         }
50155         return record;
50156     },
50157     
50158     getName: function()
50159     {
50160         // returns hidden if it's set..
50161         if (!this.rendered) {return ''};
50162         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
50163         
50164     },
50165      
50166
50167     
50168
50169     // private
50170     onEmptyResults : function(){
50171         Roo.log('empty results');
50172         //this.collapse();
50173     },
50174
50175     /**
50176      * Returns true if the dropdown list is expanded, else false.
50177      */
50178     isExpanded : function(){
50179         return false;
50180     },
50181
50182     /**
50183      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
50184      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50185      * @param {String} value The data value of the item to select
50186      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50187      * selected item if it is not currently in view (defaults to true)
50188      * @return {Boolean} True if the value matched an item in the list, else false
50189      */
50190     selectByValue : function(v, scrollIntoView){
50191         Roo.log('select By Value');
50192         return false;
50193     
50194         if(v !== undefined && v !== null){
50195             var r = this.findRecord(this.valueField || this.displayField, v);
50196             if(r){
50197                 this.select(this.store.indexOf(r), scrollIntoView);
50198                 return true;
50199             }
50200         }
50201         return false;
50202     },
50203
50204     /**
50205      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
50206      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50207      * @param {Number} index The zero-based index of the list item to select
50208      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50209      * selected item if it is not currently in view (defaults to true)
50210      */
50211     select : function(index, scrollIntoView){
50212         Roo.log('select ');
50213         return  ;
50214         
50215         this.selectedIndex = index;
50216         this.view.select(index);
50217         if(scrollIntoView !== false){
50218             var el = this.view.getNode(index);
50219             if(el){
50220                 this.innerList.scrollChildIntoView(el, false);
50221             }
50222         }
50223     },
50224
50225       
50226
50227     // private
50228     validateBlur : function(){
50229         
50230         return;
50231         
50232     },
50233
50234     // private
50235     initQuery : function(){
50236         this.doQuery(this.getRawValue());
50237     },
50238
50239     // private
50240     doForce : function(){
50241         if(this.el.dom.value.length > 0){
50242             this.el.dom.value =
50243                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
50244              
50245         }
50246     },
50247
50248     /**
50249      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
50250      * query allowing the query action to be canceled if needed.
50251      * @param {String} query The SQL query to execute
50252      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
50253      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
50254      * saved in the current store (defaults to false)
50255      */
50256     doQuery : function(q, forceAll){
50257         
50258         Roo.log('doQuery?');
50259         if(q === undefined || q === null){
50260             q = '';
50261         }
50262         var qe = {
50263             query: q,
50264             forceAll: forceAll,
50265             combo: this,
50266             cancel:false
50267         };
50268         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
50269             return false;
50270         }
50271         q = qe.query;
50272         forceAll = qe.forceAll;
50273         if(forceAll === true || (q.length >= this.minChars)){
50274             if(this.lastQuery != q || this.alwaysQuery){
50275                 this.lastQuery = q;
50276                 if(this.mode == 'local'){
50277                     this.selectedIndex = -1;
50278                     if(forceAll){
50279                         this.store.clearFilter();
50280                     }else{
50281                         this.store.filter(this.displayField, q);
50282                     }
50283                     this.onLoad();
50284                 }else{
50285                     this.store.baseParams[this.queryParam] = q;
50286                     this.store.load({
50287                         params: this.getParams(q)
50288                     });
50289                     this.expand();
50290                 }
50291             }else{
50292                 this.selectedIndex = -1;
50293                 this.onLoad();   
50294             }
50295         }
50296     },
50297
50298     // private
50299     getParams : function(q){
50300         var p = {};
50301         //p[this.queryParam] = q;
50302         if(this.pageSize){
50303             p.start = 0;
50304             p.limit = this.pageSize;
50305         }
50306         return p;
50307     },
50308
50309     /**
50310      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
50311      */
50312     collapse : function(){
50313         
50314     },
50315
50316     // private
50317     collapseIf : function(e){
50318         
50319     },
50320
50321     /**
50322      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
50323      */
50324     expand : function(){
50325         
50326     } ,
50327
50328     // private
50329      
50330
50331     /** 
50332     * @cfg {Boolean} grow 
50333     * @hide 
50334     */
50335     /** 
50336     * @cfg {Number} growMin 
50337     * @hide 
50338     */
50339     /** 
50340     * @cfg {Number} growMax 
50341     * @hide 
50342     */
50343     /**
50344      * @hide
50345      * @method autoSize
50346      */
50347     
50348     setWidth : function()
50349     {
50350         
50351     },
50352     getResizeEl : function(){
50353         return this.el;
50354     }
50355 });//<script type="text/javasscript">
50356  
50357
50358 /**
50359  * @class Roo.DDView
50360  * A DnD enabled version of Roo.View.
50361  * @param {Element/String} container The Element in which to create the View.
50362  * @param {String} tpl The template string used to create the markup for each element of the View
50363  * @param {Object} config The configuration properties. These include all the config options of
50364  * {@link Roo.View} plus some specific to this class.<br>
50365  * <p>
50366  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
50367  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
50368  * <p>
50369  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
50370 .x-view-drag-insert-above {
50371         border-top:1px dotted #3366cc;
50372 }
50373 .x-view-drag-insert-below {
50374         border-bottom:1px dotted #3366cc;
50375 }
50376 </code></pre>
50377  * 
50378  */
50379  
50380 Roo.DDView = function(container, tpl, config) {
50381     Roo.DDView.superclass.constructor.apply(this, arguments);
50382     this.getEl().setStyle("outline", "0px none");
50383     this.getEl().unselectable();
50384     if (this.dragGroup) {
50385                 this.setDraggable(this.dragGroup.split(","));
50386     }
50387     if (this.dropGroup) {
50388                 this.setDroppable(this.dropGroup.split(","));
50389     }
50390     if (this.deletable) {
50391         this.setDeletable();
50392     }
50393     this.isDirtyFlag = false;
50394         this.addEvents({
50395                 "drop" : true
50396         });
50397 };
50398
50399 Roo.extend(Roo.DDView, Roo.View, {
50400 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
50401 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
50402 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
50403 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
50404
50405         isFormField: true,
50406
50407         reset: Roo.emptyFn,
50408         
50409         clearInvalid: Roo.form.Field.prototype.clearInvalid,
50410
50411         validate: function() {
50412                 return true;
50413         },
50414         
50415         destroy: function() {
50416                 this.purgeListeners();
50417                 this.getEl.removeAllListeners();
50418                 this.getEl().remove();
50419                 if (this.dragZone) {
50420                         if (this.dragZone.destroy) {
50421                                 this.dragZone.destroy();
50422                         }
50423                 }
50424                 if (this.dropZone) {
50425                         if (this.dropZone.destroy) {
50426                                 this.dropZone.destroy();
50427                         }
50428                 }
50429         },
50430
50431 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
50432         getName: function() {
50433                 return this.name;
50434         },
50435
50436 /**     Loads the View from a JSON string representing the Records to put into the Store. */
50437         setValue: function(v) {
50438                 if (!this.store) {
50439                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
50440                 }
50441                 var data = {};
50442                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
50443                 this.store.proxy = new Roo.data.MemoryProxy(data);
50444                 this.store.load();
50445         },
50446
50447 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
50448         getValue: function() {
50449                 var result = '(';
50450                 this.store.each(function(rec) {
50451                         result += rec.id + ',';
50452                 });
50453                 return result.substr(0, result.length - 1) + ')';
50454         },
50455         
50456         getIds: function() {
50457                 var i = 0, result = new Array(this.store.getCount());
50458                 this.store.each(function(rec) {
50459                         result[i++] = rec.id;
50460                 });
50461                 return result;
50462         },
50463         
50464         isDirty: function() {
50465                 return this.isDirtyFlag;
50466         },
50467
50468 /**
50469  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
50470  *      whole Element becomes the target, and this causes the drop gesture to append.
50471  */
50472     getTargetFromEvent : function(e) {
50473                 var target = e.getTarget();
50474                 while ((target !== null) && (target.parentNode != this.el.dom)) {
50475                 target = target.parentNode;
50476                 }
50477                 if (!target) {
50478                         target = this.el.dom.lastChild || this.el.dom;
50479                 }
50480                 return target;
50481     },
50482
50483 /**
50484  *      Create the drag data which consists of an object which has the property "ddel" as
50485  *      the drag proxy element. 
50486  */
50487     getDragData : function(e) {
50488         var target = this.findItemFromChild(e.getTarget());
50489                 if(target) {
50490                         this.handleSelection(e);
50491                         var selNodes = this.getSelectedNodes();
50492             var dragData = {
50493                 source: this,
50494                 copy: this.copy || (this.allowCopy && e.ctrlKey),
50495                 nodes: selNodes,
50496                 records: []
50497                         };
50498                         var selectedIndices = this.getSelectedIndexes();
50499                         for (var i = 0; i < selectedIndices.length; i++) {
50500                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
50501                         }
50502                         if (selNodes.length == 1) {
50503                                 dragData.ddel = target.cloneNode(true); // the div element
50504                         } else {
50505                                 var div = document.createElement('div'); // create the multi element drag "ghost"
50506                                 div.className = 'multi-proxy';
50507                                 for (var i = 0, len = selNodes.length; i < len; i++) {
50508                                         div.appendChild(selNodes[i].cloneNode(true));
50509                                 }
50510                                 dragData.ddel = div;
50511                         }
50512             //console.log(dragData)
50513             //console.log(dragData.ddel.innerHTML)
50514                         return dragData;
50515                 }
50516         //console.log('nodragData')
50517                 return false;
50518     },
50519     
50520 /**     Specify to which ddGroup items in this DDView may be dragged. */
50521     setDraggable: function(ddGroup) {
50522         if (ddGroup instanceof Array) {
50523                 Roo.each(ddGroup, this.setDraggable, this);
50524                 return;
50525         }
50526         if (this.dragZone) {
50527                 this.dragZone.addToGroup(ddGroup);
50528         } else {
50529                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
50530                                 containerScroll: true,
50531                                 ddGroup: ddGroup 
50532
50533                         });
50534 //                      Draggability implies selection. DragZone's mousedown selects the element.
50535                         if (!this.multiSelect) { this.singleSelect = true; }
50536
50537 //                      Wire the DragZone's handlers up to methods in *this*
50538                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
50539                 }
50540     },
50541
50542 /**     Specify from which ddGroup this DDView accepts drops. */
50543     setDroppable: function(ddGroup) {
50544         if (ddGroup instanceof Array) {
50545                 Roo.each(ddGroup, this.setDroppable, this);
50546                 return;
50547         }
50548         if (this.dropZone) {
50549                 this.dropZone.addToGroup(ddGroup);
50550         } else {
50551                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
50552                                 containerScroll: true,
50553                                 ddGroup: ddGroup
50554                         });
50555
50556 //                      Wire the DropZone's handlers up to methods in *this*
50557                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
50558                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
50559                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
50560                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
50561                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
50562                 }
50563     },
50564
50565 /**     Decide whether to drop above or below a View node. */
50566     getDropPoint : function(e, n, dd){
50567         if (n == this.el.dom) { return "above"; }
50568                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
50569                 var c = t + (b - t) / 2;
50570                 var y = Roo.lib.Event.getPageY(e);
50571                 if(y <= c) {
50572                         return "above";
50573                 }else{
50574                         return "below";
50575                 }
50576     },
50577
50578     onNodeEnter : function(n, dd, e, data){
50579                 return false;
50580     },
50581     
50582     onNodeOver : function(n, dd, e, data){
50583                 var pt = this.getDropPoint(e, n, dd);
50584                 // set the insert point style on the target node
50585                 var dragElClass = this.dropNotAllowed;
50586                 if (pt) {
50587                         var targetElClass;
50588                         if (pt == "above"){
50589                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
50590                                 targetElClass = "x-view-drag-insert-above";
50591                         } else {
50592                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
50593                                 targetElClass = "x-view-drag-insert-below";
50594                         }
50595                         if (this.lastInsertClass != targetElClass){
50596                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
50597                                 this.lastInsertClass = targetElClass;
50598                         }
50599                 }
50600                 return dragElClass;
50601         },
50602
50603     onNodeOut : function(n, dd, e, data){
50604                 this.removeDropIndicators(n);
50605     },
50606
50607     onNodeDrop : function(n, dd, e, data){
50608         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
50609                 return false;
50610         }
50611         var pt = this.getDropPoint(e, n, dd);
50612                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
50613                 if (pt == "below") { insertAt++; }
50614                 for (var i = 0; i < data.records.length; i++) {
50615                         var r = data.records[i];
50616                         var dup = this.store.getById(r.id);
50617                         if (dup && (dd != this.dragZone)) {
50618                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
50619                         } else {
50620                                 if (data.copy) {
50621                                         this.store.insert(insertAt++, r.copy());
50622                                 } else {
50623                                         data.source.isDirtyFlag = true;
50624                                         r.store.remove(r);
50625                                         this.store.insert(insertAt++, r);
50626                                 }
50627                                 this.isDirtyFlag = true;
50628                         }
50629                 }
50630                 this.dragZone.cachedTarget = null;
50631                 return true;
50632     },
50633
50634     removeDropIndicators : function(n){
50635                 if(n){
50636                         Roo.fly(n).removeClass([
50637                                 "x-view-drag-insert-above",
50638                                 "x-view-drag-insert-below"]);
50639                         this.lastInsertClass = "_noclass";
50640                 }
50641     },
50642
50643 /**
50644  *      Utility method. Add a delete option to the DDView's context menu.
50645  *      @param {String} imageUrl The URL of the "delete" icon image.
50646  */
50647         setDeletable: function(imageUrl) {
50648                 if (!this.singleSelect && !this.multiSelect) {
50649                         this.singleSelect = true;
50650                 }
50651                 var c = this.getContextMenu();
50652                 this.contextMenu.on("itemclick", function(item) {
50653                         switch (item.id) {
50654                                 case "delete":
50655                                         this.remove(this.getSelectedIndexes());
50656                                         break;
50657                         }
50658                 }, this);
50659                 this.contextMenu.add({
50660                         icon: imageUrl,
50661                         id: "delete",
50662                         text: 'Delete'
50663                 });
50664         },
50665         
50666 /**     Return the context menu for this DDView. */
50667         getContextMenu: function() {
50668                 if (!this.contextMenu) {
50669 //                      Create the View's context menu
50670                         this.contextMenu = new Roo.menu.Menu({
50671                                 id: this.id + "-contextmenu"
50672                         });
50673                         this.el.on("contextmenu", this.showContextMenu, this);
50674                 }
50675                 return this.contextMenu;
50676         },
50677         
50678         disableContextMenu: function() {
50679                 if (this.contextMenu) {
50680                         this.el.un("contextmenu", this.showContextMenu, this);
50681                 }
50682         },
50683
50684         showContextMenu: function(e, item) {
50685         item = this.findItemFromChild(e.getTarget());
50686                 if (item) {
50687                         e.stopEvent();
50688                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
50689                         this.contextMenu.showAt(e.getXY());
50690             }
50691     },
50692
50693 /**
50694  *      Remove {@link Roo.data.Record}s at the specified indices.
50695  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
50696  */
50697     remove: function(selectedIndices) {
50698                 selectedIndices = [].concat(selectedIndices);
50699                 for (var i = 0; i < selectedIndices.length; i++) {
50700                         var rec = this.store.getAt(selectedIndices[i]);
50701                         this.store.remove(rec);
50702                 }
50703     },
50704
50705 /**
50706  *      Double click fires the event, but also, if this is draggable, and there is only one other
50707  *      related DropZone, it transfers the selected node.
50708  */
50709     onDblClick : function(e){
50710         var item = this.findItemFromChild(e.getTarget());
50711         if(item){
50712             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
50713                 return false;
50714             }
50715             if (this.dragGroup) {
50716                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
50717                     while (targets.indexOf(this.dropZone) > -1) {
50718                             targets.remove(this.dropZone);
50719                                 }
50720                     if (targets.length == 1) {
50721                                         this.dragZone.cachedTarget = null;
50722                         var el = Roo.get(targets[0].getEl());
50723                         var box = el.getBox(true);
50724                         targets[0].onNodeDrop(el.dom, {
50725                                 target: el.dom,
50726                                 xy: [box.x, box.y + box.height - 1]
50727                         }, null, this.getDragData(e));
50728                     }
50729                 }
50730         }
50731     },
50732     
50733     handleSelection: function(e) {
50734                 this.dragZone.cachedTarget = null;
50735         var item = this.findItemFromChild(e.getTarget());
50736         if (!item) {
50737                 this.clearSelections(true);
50738                 return;
50739         }
50740                 if (item && (this.multiSelect || this.singleSelect)){
50741                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
50742                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
50743                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
50744                                 this.unselect(item);
50745                         } else {
50746                                 this.select(item, this.multiSelect && e.ctrlKey);
50747                                 this.lastSelection = item;
50748                         }
50749                 }
50750     },
50751
50752     onItemClick : function(item, index, e){
50753                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
50754                         return false;
50755                 }
50756                 return true;
50757     },
50758
50759     unselect : function(nodeInfo, suppressEvent){
50760                 var node = this.getNode(nodeInfo);
50761                 if(node && this.isSelected(node)){
50762                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
50763                                 Roo.fly(node).removeClass(this.selectedClass);
50764                                 this.selections.remove(node);
50765                                 if(!suppressEvent){
50766                                         this.fireEvent("selectionchange", this, this.selections);
50767                                 }
50768                         }
50769                 }
50770     }
50771 });
50772 /*
50773  * Based on:
50774  * Ext JS Library 1.1.1
50775  * Copyright(c) 2006-2007, Ext JS, LLC.
50776  *
50777  * Originally Released Under LGPL - original licence link has changed is not relivant.
50778  *
50779  * Fork - LGPL
50780  * <script type="text/javascript">
50781  */
50782  
50783 /**
50784  * @class Roo.LayoutManager
50785  * @extends Roo.util.Observable
50786  * Base class for layout managers.
50787  */
50788 Roo.LayoutManager = function(container, config){
50789     Roo.LayoutManager.superclass.constructor.call(this);
50790     this.el = Roo.get(container);
50791     // ie scrollbar fix
50792     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
50793         document.body.scroll = "no";
50794     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
50795         this.el.position('relative');
50796     }
50797     this.id = this.el.id;
50798     this.el.addClass("x-layout-container");
50799     /** false to disable window resize monitoring @type Boolean */
50800     this.monitorWindowResize = true;
50801     this.regions = {};
50802     this.addEvents({
50803         /**
50804          * @event layout
50805          * Fires when a layout is performed. 
50806          * @param {Roo.LayoutManager} this
50807          */
50808         "layout" : true,
50809         /**
50810          * @event regionresized
50811          * Fires when the user resizes a region. 
50812          * @param {Roo.LayoutRegion} region The resized region
50813          * @param {Number} newSize The new size (width for east/west, height for north/south)
50814          */
50815         "regionresized" : true,
50816         /**
50817          * @event regioncollapsed
50818          * Fires when a region is collapsed. 
50819          * @param {Roo.LayoutRegion} region The collapsed region
50820          */
50821         "regioncollapsed" : true,
50822         /**
50823          * @event regionexpanded
50824          * Fires when a region is expanded.  
50825          * @param {Roo.LayoutRegion} region The expanded region
50826          */
50827         "regionexpanded" : true
50828     });
50829     this.updating = false;
50830     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
50831 };
50832
50833 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
50834     /**
50835      * Returns true if this layout is currently being updated
50836      * @return {Boolean}
50837      */
50838     isUpdating : function(){
50839         return this.updating; 
50840     },
50841     
50842     /**
50843      * Suspend the LayoutManager from doing auto-layouts while
50844      * making multiple add or remove calls
50845      */
50846     beginUpdate : function(){
50847         this.updating = true;    
50848     },
50849     
50850     /**
50851      * Restore auto-layouts and optionally disable the manager from performing a layout
50852      * @param {Boolean} noLayout true to disable a layout update 
50853      */
50854     endUpdate : function(noLayout){
50855         this.updating = false;
50856         if(!noLayout){
50857             this.layout();
50858         }    
50859     },
50860     
50861     layout: function(){
50862         
50863     },
50864     
50865     onRegionResized : function(region, newSize){
50866         this.fireEvent("regionresized", region, newSize);
50867         this.layout();
50868     },
50869     
50870     onRegionCollapsed : function(region){
50871         this.fireEvent("regioncollapsed", region);
50872     },
50873     
50874     onRegionExpanded : function(region){
50875         this.fireEvent("regionexpanded", region);
50876     },
50877         
50878     /**
50879      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
50880      * performs box-model adjustments.
50881      * @return {Object} The size as an object {width: (the width), height: (the height)}
50882      */
50883     getViewSize : function(){
50884         var size;
50885         if(this.el.dom != document.body){
50886             size = this.el.getSize();
50887         }else{
50888             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
50889         }
50890         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
50891         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
50892         return size;
50893     },
50894     
50895     /**
50896      * Returns the Element this layout is bound to.
50897      * @return {Roo.Element}
50898      */
50899     getEl : function(){
50900         return this.el;
50901     },
50902     
50903     /**
50904      * Returns the specified region.
50905      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
50906      * @return {Roo.LayoutRegion}
50907      */
50908     getRegion : function(target){
50909         return this.regions[target.toLowerCase()];
50910     },
50911     
50912     onWindowResize : function(){
50913         if(this.monitorWindowResize){
50914             this.layout();
50915         }
50916     }
50917 });/*
50918  * Based on:
50919  * Ext JS Library 1.1.1
50920  * Copyright(c) 2006-2007, Ext JS, LLC.
50921  *
50922  * Originally Released Under LGPL - original licence link has changed is not relivant.
50923  *
50924  * Fork - LGPL
50925  * <script type="text/javascript">
50926  */
50927 /**
50928  * @class Roo.BorderLayout
50929  * @extends Roo.LayoutManager
50930  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
50931  * please see: <br><br>
50932  * <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>
50933  * <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>
50934  * Example:
50935  <pre><code>
50936  var layout = new Roo.BorderLayout(document.body, {
50937     north: {
50938         initialSize: 25,
50939         titlebar: false
50940     },
50941     west: {
50942         split:true,
50943         initialSize: 200,
50944         minSize: 175,
50945         maxSize: 400,
50946         titlebar: true,
50947         collapsible: true
50948     },
50949     east: {
50950         split:true,
50951         initialSize: 202,
50952         minSize: 175,
50953         maxSize: 400,
50954         titlebar: true,
50955         collapsible: true
50956     },
50957     south: {
50958         split:true,
50959         initialSize: 100,
50960         minSize: 100,
50961         maxSize: 200,
50962         titlebar: true,
50963         collapsible: true
50964     },
50965     center: {
50966         titlebar: true,
50967         autoScroll:true,
50968         resizeTabs: true,
50969         minTabWidth: 50,
50970         preferredTabWidth: 150
50971     }
50972 });
50973
50974 // shorthand
50975 var CP = Roo.ContentPanel;
50976
50977 layout.beginUpdate();
50978 layout.add("north", new CP("north", "North"));
50979 layout.add("south", new CP("south", {title: "South", closable: true}));
50980 layout.add("west", new CP("west", {title: "West"}));
50981 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
50982 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
50983 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
50984 layout.getRegion("center").showPanel("center1");
50985 layout.endUpdate();
50986 </code></pre>
50987
50988 <b>The container the layout is rendered into can be either the body element or any other element.
50989 If it is not the body element, the container needs to either be an absolute positioned element,
50990 or you will need to add "position:relative" to the css of the container.  You will also need to specify
50991 the container size if it is not the body element.</b>
50992
50993 * @constructor
50994 * Create a new BorderLayout
50995 * @param {String/HTMLElement/Element} container The container this layout is bound to
50996 * @param {Object} config Configuration options
50997  */
50998 Roo.BorderLayout = function(container, config){
50999     config = config || {};
51000     Roo.BorderLayout.superclass.constructor.call(this, container, config);
51001     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
51002     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
51003         var target = this.factory.validRegions[i];
51004         if(config[target]){
51005             this.addRegion(target, config[target]);
51006         }
51007     }
51008 };
51009
51010 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
51011     /**
51012      * Creates and adds a new region if it doesn't already exist.
51013      * @param {String} target The target region key (north, south, east, west or center).
51014      * @param {Object} config The regions config object
51015      * @return {BorderLayoutRegion} The new region
51016      */
51017     addRegion : function(target, config){
51018         if(!this.regions[target]){
51019             var r = this.factory.create(target, this, config);
51020             this.bindRegion(target, r);
51021         }
51022         return this.regions[target];
51023     },
51024
51025     // private (kinda)
51026     bindRegion : function(name, r){
51027         this.regions[name] = r;
51028         r.on("visibilitychange", this.layout, this);
51029         r.on("paneladded", this.layout, this);
51030         r.on("panelremoved", this.layout, this);
51031         r.on("invalidated", this.layout, this);
51032         r.on("resized", this.onRegionResized, this);
51033         r.on("collapsed", this.onRegionCollapsed, this);
51034         r.on("expanded", this.onRegionExpanded, this);
51035     },
51036
51037     /**
51038      * Performs a layout update.
51039      */
51040     layout : function(){
51041         if(this.updating) {
51042             return;
51043         }
51044         var size = this.getViewSize();
51045         var w = size.width;
51046         var h = size.height;
51047         var centerW = w;
51048         var centerH = h;
51049         var centerY = 0;
51050         var centerX = 0;
51051         //var x = 0, y = 0;
51052
51053         var rs = this.regions;
51054         var north = rs["north"];
51055         var south = rs["south"]; 
51056         var west = rs["west"];
51057         var east = rs["east"];
51058         var center = rs["center"];
51059         //if(this.hideOnLayout){ // not supported anymore
51060             //c.el.setStyle("display", "none");
51061         //}
51062         if(north && north.isVisible()){
51063             var b = north.getBox();
51064             var m = north.getMargins();
51065             b.width = w - (m.left+m.right);
51066             b.x = m.left;
51067             b.y = m.top;
51068             centerY = b.height + b.y + m.bottom;
51069             centerH -= centerY;
51070             north.updateBox(this.safeBox(b));
51071         }
51072         if(south && south.isVisible()){
51073             var b = south.getBox();
51074             var m = south.getMargins();
51075             b.width = w - (m.left+m.right);
51076             b.x = m.left;
51077             var totalHeight = (b.height + m.top + m.bottom);
51078             b.y = h - totalHeight + m.top;
51079             centerH -= totalHeight;
51080             south.updateBox(this.safeBox(b));
51081         }
51082         if(west && west.isVisible()){
51083             var b = west.getBox();
51084             var m = west.getMargins();
51085             b.height = centerH - (m.top+m.bottom);
51086             b.x = m.left;
51087             b.y = centerY + m.top;
51088             var totalWidth = (b.width + m.left + m.right);
51089             centerX += totalWidth;
51090             centerW -= totalWidth;
51091             west.updateBox(this.safeBox(b));
51092         }
51093         if(east && east.isVisible()){
51094             var b = east.getBox();
51095             var m = east.getMargins();
51096             b.height = centerH - (m.top+m.bottom);
51097             var totalWidth = (b.width + m.left + m.right);
51098             b.x = w - totalWidth + m.left;
51099             b.y = centerY + m.top;
51100             centerW -= totalWidth;
51101             east.updateBox(this.safeBox(b));
51102         }
51103         if(center){
51104             var m = center.getMargins();
51105             var centerBox = {
51106                 x: centerX + m.left,
51107                 y: centerY + m.top,
51108                 width: centerW - (m.left+m.right),
51109                 height: centerH - (m.top+m.bottom)
51110             };
51111             //if(this.hideOnLayout){
51112                 //center.el.setStyle("display", "block");
51113             //}
51114             center.updateBox(this.safeBox(centerBox));
51115         }
51116         this.el.repaint();
51117         this.fireEvent("layout", this);
51118     },
51119
51120     // private
51121     safeBox : function(box){
51122         box.width = Math.max(0, box.width);
51123         box.height = Math.max(0, box.height);
51124         return box;
51125     },
51126
51127     /**
51128      * Adds a ContentPanel (or subclass) to this layout.
51129      * @param {String} target The target region key (north, south, east, west or center).
51130      * @param {Roo.ContentPanel} panel The panel to add
51131      * @return {Roo.ContentPanel} The added panel
51132      */
51133     add : function(target, panel){
51134          
51135         target = target.toLowerCase();
51136         return this.regions[target].add(panel);
51137     },
51138
51139     /**
51140      * Remove a ContentPanel (or subclass) to this layout.
51141      * @param {String} target The target region key (north, south, east, west or center).
51142      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
51143      * @return {Roo.ContentPanel} The removed panel
51144      */
51145     remove : function(target, panel){
51146         target = target.toLowerCase();
51147         return this.regions[target].remove(panel);
51148     },
51149
51150     /**
51151      * Searches all regions for a panel with the specified id
51152      * @param {String} panelId
51153      * @return {Roo.ContentPanel} The panel or null if it wasn't found
51154      */
51155     findPanel : function(panelId){
51156         var rs = this.regions;
51157         for(var target in rs){
51158             if(typeof rs[target] != "function"){
51159                 var p = rs[target].getPanel(panelId);
51160                 if(p){
51161                     return p;
51162                 }
51163             }
51164         }
51165         return null;
51166     },
51167
51168     /**
51169      * Searches all regions for a panel with the specified id and activates (shows) it.
51170      * @param {String/ContentPanel} panelId The panels id or the panel itself
51171      * @return {Roo.ContentPanel} The shown panel or null
51172      */
51173     showPanel : function(panelId) {
51174       var rs = this.regions;
51175       for(var target in rs){
51176          var r = rs[target];
51177          if(typeof r != "function"){
51178             if(r.hasPanel(panelId)){
51179                return r.showPanel(panelId);
51180             }
51181          }
51182       }
51183       return null;
51184    },
51185
51186    /**
51187      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
51188      * @param {Roo.state.Provider} provider (optional) An alternate state provider
51189      */
51190     restoreState : function(provider){
51191         if(!provider){
51192             provider = Roo.state.Manager;
51193         }
51194         var sm = new Roo.LayoutStateManager();
51195         sm.init(this, provider);
51196     },
51197
51198     /**
51199      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
51200      * object should contain properties for each region to add ContentPanels to, and each property's value should be
51201      * a valid ContentPanel config object.  Example:
51202      * <pre><code>
51203 // Create the main layout
51204 var layout = new Roo.BorderLayout('main-ct', {
51205     west: {
51206         split:true,
51207         minSize: 175,
51208         titlebar: true
51209     },
51210     center: {
51211         title:'Components'
51212     }
51213 }, 'main-ct');
51214
51215 // Create and add multiple ContentPanels at once via configs
51216 layout.batchAdd({
51217    west: {
51218        id: 'source-files',
51219        autoCreate:true,
51220        title:'Ext Source Files',
51221        autoScroll:true,
51222        fitToFrame:true
51223    },
51224    center : {
51225        el: cview,
51226        autoScroll:true,
51227        fitToFrame:true,
51228        toolbar: tb,
51229        resizeEl:'cbody'
51230    }
51231 });
51232 </code></pre>
51233      * @param {Object} regions An object containing ContentPanel configs by region name
51234      */
51235     batchAdd : function(regions){
51236         this.beginUpdate();
51237         for(var rname in regions){
51238             var lr = this.regions[rname];
51239             if(lr){
51240                 this.addTypedPanels(lr, regions[rname]);
51241             }
51242         }
51243         this.endUpdate();
51244     },
51245
51246     // private
51247     addTypedPanels : function(lr, ps){
51248         if(typeof ps == 'string'){
51249             lr.add(new Roo.ContentPanel(ps));
51250         }
51251         else if(ps instanceof Array){
51252             for(var i =0, len = ps.length; i < len; i++){
51253                 this.addTypedPanels(lr, ps[i]);
51254             }
51255         }
51256         else if(!ps.events){ // raw config?
51257             var el = ps.el;
51258             delete ps.el; // prevent conflict
51259             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
51260         }
51261         else {  // panel object assumed!
51262             lr.add(ps);
51263         }
51264     },
51265     /**
51266      * Adds a xtype elements to the layout.
51267      * <pre><code>
51268
51269 layout.addxtype({
51270        xtype : 'ContentPanel',
51271        region: 'west',
51272        items: [ .... ]
51273    }
51274 );
51275
51276 layout.addxtype({
51277         xtype : 'NestedLayoutPanel',
51278         region: 'west',
51279         layout: {
51280            center: { },
51281            west: { }   
51282         },
51283         items : [ ... list of content panels or nested layout panels.. ]
51284    }
51285 );
51286 </code></pre>
51287      * @param {Object} cfg Xtype definition of item to add.
51288      */
51289     addxtype : function(cfg)
51290     {
51291         // basically accepts a pannel...
51292         // can accept a layout region..!?!?
51293         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
51294         
51295         if (!cfg.xtype.match(/Panel$/)) {
51296             return false;
51297         }
51298         var ret = false;
51299         
51300         if (typeof(cfg.region) == 'undefined') {
51301             Roo.log("Failed to add Panel, region was not set");
51302             Roo.log(cfg);
51303             return false;
51304         }
51305         var region = cfg.region;
51306         delete cfg.region;
51307         
51308           
51309         var xitems = [];
51310         if (cfg.items) {
51311             xitems = cfg.items;
51312             delete cfg.items;
51313         }
51314         var nb = false;
51315         
51316         switch(cfg.xtype) 
51317         {
51318             case 'ContentPanel':  // ContentPanel (el, cfg)
51319             case 'ScrollPanel':  // ContentPanel (el, cfg)
51320             case 'ViewPanel': 
51321                 if(cfg.autoCreate) {
51322                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51323                 } else {
51324                     var el = this.el.createChild();
51325                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
51326                 }
51327                 
51328                 this.add(region, ret);
51329                 break;
51330             
51331             
51332             case 'TreePanel': // our new panel!
51333                 cfg.el = this.el.createChild();
51334                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51335                 this.add(region, ret);
51336                 break;
51337             
51338             case 'NestedLayoutPanel': 
51339                 // create a new Layout (which is  a Border Layout...
51340                 var el = this.el.createChild();
51341                 var clayout = cfg.layout;
51342                 delete cfg.layout;
51343                 clayout.items   = clayout.items  || [];
51344                 // replace this exitems with the clayout ones..
51345                 xitems = clayout.items;
51346                  
51347                 
51348                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
51349                     cfg.background = false;
51350                 }
51351                 var layout = new Roo.BorderLayout(el, clayout);
51352                 
51353                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
51354                 //console.log('adding nested layout panel '  + cfg.toSource());
51355                 this.add(region, ret);
51356                 nb = {}; /// find first...
51357                 break;
51358                 
51359             case 'GridPanel': 
51360             
51361                 // needs grid and region
51362                 
51363                 //var el = this.getRegion(region).el.createChild();
51364                 var el = this.el.createChild();
51365                 // create the grid first...
51366                 
51367                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
51368                 delete cfg.grid;
51369                 if (region == 'center' && this.active ) {
51370                     cfg.background = false;
51371                 }
51372                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
51373                 
51374                 this.add(region, ret);
51375                 if (cfg.background) {
51376                     ret.on('activate', function(gp) {
51377                         if (!gp.grid.rendered) {
51378                             gp.grid.render();
51379                         }
51380                     });
51381                 } else {
51382                     grid.render();
51383                 }
51384                 break;
51385            
51386            
51387            
51388                 
51389                 
51390                 
51391             default:
51392                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
51393                     
51394                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51395                     this.add(region, ret);
51396                 } else {
51397                 
51398                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
51399                     return null;
51400                 }
51401                 
51402              // GridPanel (grid, cfg)
51403             
51404         }
51405         this.beginUpdate();
51406         // add children..
51407         var region = '';
51408         var abn = {};
51409         Roo.each(xitems, function(i)  {
51410             region = nb && i.region ? i.region : false;
51411             
51412             var add = ret.addxtype(i);
51413            
51414             if (region) {
51415                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
51416                 if (!i.background) {
51417                     abn[region] = nb[region] ;
51418                 }
51419             }
51420             
51421         });
51422         this.endUpdate();
51423
51424         // make the last non-background panel active..
51425         //if (nb) { Roo.log(abn); }
51426         if (nb) {
51427             
51428             for(var r in abn) {
51429                 region = this.getRegion(r);
51430                 if (region) {
51431                     // tried using nb[r], but it does not work..
51432                      
51433                     region.showPanel(abn[r]);
51434                    
51435                 }
51436             }
51437         }
51438         return ret;
51439         
51440     }
51441 });
51442
51443 /**
51444  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
51445  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
51446  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
51447  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
51448  * <pre><code>
51449 // shorthand
51450 var CP = Roo.ContentPanel;
51451
51452 var layout = Roo.BorderLayout.create({
51453     north: {
51454         initialSize: 25,
51455         titlebar: false,
51456         panels: [new CP("north", "North")]
51457     },
51458     west: {
51459         split:true,
51460         initialSize: 200,
51461         minSize: 175,
51462         maxSize: 400,
51463         titlebar: true,
51464         collapsible: true,
51465         panels: [new CP("west", {title: "West"})]
51466     },
51467     east: {
51468         split:true,
51469         initialSize: 202,
51470         minSize: 175,
51471         maxSize: 400,
51472         titlebar: true,
51473         collapsible: true,
51474         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
51475     },
51476     south: {
51477         split:true,
51478         initialSize: 100,
51479         minSize: 100,
51480         maxSize: 200,
51481         titlebar: true,
51482         collapsible: true,
51483         panels: [new CP("south", {title: "South", closable: true})]
51484     },
51485     center: {
51486         titlebar: true,
51487         autoScroll:true,
51488         resizeTabs: true,
51489         minTabWidth: 50,
51490         preferredTabWidth: 150,
51491         panels: [
51492             new CP("center1", {title: "Close Me", closable: true}),
51493             new CP("center2", {title: "Center Panel", closable: false})
51494         ]
51495     }
51496 }, document.body);
51497
51498 layout.getRegion("center").showPanel("center1");
51499 </code></pre>
51500  * @param config
51501  * @param targetEl
51502  */
51503 Roo.BorderLayout.create = function(config, targetEl){
51504     var layout = new Roo.BorderLayout(targetEl || document.body, config);
51505     layout.beginUpdate();
51506     var regions = Roo.BorderLayout.RegionFactory.validRegions;
51507     for(var j = 0, jlen = regions.length; j < jlen; j++){
51508         var lr = regions[j];
51509         if(layout.regions[lr] && config[lr].panels){
51510             var r = layout.regions[lr];
51511             var ps = config[lr].panels;
51512             layout.addTypedPanels(r, ps);
51513         }
51514     }
51515     layout.endUpdate();
51516     return layout;
51517 };
51518
51519 // private
51520 Roo.BorderLayout.RegionFactory = {
51521     // private
51522     validRegions : ["north","south","east","west","center"],
51523
51524     // private
51525     create : function(target, mgr, config){
51526         target = target.toLowerCase();
51527         if(config.lightweight || config.basic){
51528             return new Roo.BasicLayoutRegion(mgr, config, target);
51529         }
51530         switch(target){
51531             case "north":
51532                 return new Roo.NorthLayoutRegion(mgr, config);
51533             case "south":
51534                 return new Roo.SouthLayoutRegion(mgr, config);
51535             case "east":
51536                 return new Roo.EastLayoutRegion(mgr, config);
51537             case "west":
51538                 return new Roo.WestLayoutRegion(mgr, config);
51539             case "center":
51540                 return new Roo.CenterLayoutRegion(mgr, config);
51541         }
51542         throw 'Layout region "'+target+'" not supported.';
51543     }
51544 };/*
51545  * Based on:
51546  * Ext JS Library 1.1.1
51547  * Copyright(c) 2006-2007, Ext JS, LLC.
51548  *
51549  * Originally Released Under LGPL - original licence link has changed is not relivant.
51550  *
51551  * Fork - LGPL
51552  * <script type="text/javascript">
51553  */
51554  
51555 /**
51556  * @class Roo.BasicLayoutRegion
51557  * @extends Roo.util.Observable
51558  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
51559  * and does not have a titlebar, tabs or any other features. All it does is size and position 
51560  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
51561  */
51562 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
51563     this.mgr = mgr;
51564     this.position  = pos;
51565     this.events = {
51566         /**
51567          * @scope Roo.BasicLayoutRegion
51568          */
51569         
51570         /**
51571          * @event beforeremove
51572          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
51573          * @param {Roo.LayoutRegion} this
51574          * @param {Roo.ContentPanel} panel The panel
51575          * @param {Object} e The cancel event object
51576          */
51577         "beforeremove" : true,
51578         /**
51579          * @event invalidated
51580          * Fires when the layout for this region is changed.
51581          * @param {Roo.LayoutRegion} this
51582          */
51583         "invalidated" : true,
51584         /**
51585          * @event visibilitychange
51586          * Fires when this region is shown or hidden 
51587          * @param {Roo.LayoutRegion} this
51588          * @param {Boolean} visibility true or false
51589          */
51590         "visibilitychange" : true,
51591         /**
51592          * @event paneladded
51593          * Fires when a panel is added. 
51594          * @param {Roo.LayoutRegion} this
51595          * @param {Roo.ContentPanel} panel The panel
51596          */
51597         "paneladded" : true,
51598         /**
51599          * @event panelremoved
51600          * Fires when a panel is removed. 
51601          * @param {Roo.LayoutRegion} this
51602          * @param {Roo.ContentPanel} panel The panel
51603          */
51604         "panelremoved" : true,
51605         /**
51606          * @event beforecollapse
51607          * Fires when this region before collapse.
51608          * @param {Roo.LayoutRegion} this
51609          */
51610         "beforecollapse" : true,
51611         /**
51612          * @event collapsed
51613          * Fires when this region is collapsed.
51614          * @param {Roo.LayoutRegion} this
51615          */
51616         "collapsed" : true,
51617         /**
51618          * @event expanded
51619          * Fires when this region is expanded.
51620          * @param {Roo.LayoutRegion} this
51621          */
51622         "expanded" : true,
51623         /**
51624          * @event slideshow
51625          * Fires when this region is slid into view.
51626          * @param {Roo.LayoutRegion} this
51627          */
51628         "slideshow" : true,
51629         /**
51630          * @event slidehide
51631          * Fires when this region slides out of view. 
51632          * @param {Roo.LayoutRegion} this
51633          */
51634         "slidehide" : true,
51635         /**
51636          * @event panelactivated
51637          * Fires when a panel is activated. 
51638          * @param {Roo.LayoutRegion} this
51639          * @param {Roo.ContentPanel} panel The activated panel
51640          */
51641         "panelactivated" : true,
51642         /**
51643          * @event resized
51644          * Fires when the user resizes this region. 
51645          * @param {Roo.LayoutRegion} this
51646          * @param {Number} newSize The new size (width for east/west, height for north/south)
51647          */
51648         "resized" : true
51649     };
51650     /** A collection of panels in this region. @type Roo.util.MixedCollection */
51651     this.panels = new Roo.util.MixedCollection();
51652     this.panels.getKey = this.getPanelId.createDelegate(this);
51653     this.box = null;
51654     this.activePanel = null;
51655     // ensure listeners are added...
51656     
51657     if (config.listeners || config.events) {
51658         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
51659             listeners : config.listeners || {},
51660             events : config.events || {}
51661         });
51662     }
51663     
51664     if(skipConfig !== true){
51665         this.applyConfig(config);
51666     }
51667 };
51668
51669 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
51670     getPanelId : function(p){
51671         return p.getId();
51672     },
51673     
51674     applyConfig : function(config){
51675         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51676         this.config = config;
51677         
51678     },
51679     
51680     /**
51681      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
51682      * the width, for horizontal (north, south) the height.
51683      * @param {Number} newSize The new width or height
51684      */
51685     resizeTo : function(newSize){
51686         var el = this.el ? this.el :
51687                  (this.activePanel ? this.activePanel.getEl() : null);
51688         if(el){
51689             switch(this.position){
51690                 case "east":
51691                 case "west":
51692                     el.setWidth(newSize);
51693                     this.fireEvent("resized", this, newSize);
51694                 break;
51695                 case "north":
51696                 case "south":
51697                     el.setHeight(newSize);
51698                     this.fireEvent("resized", this, newSize);
51699                 break;                
51700             }
51701         }
51702     },
51703     
51704     getBox : function(){
51705         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
51706     },
51707     
51708     getMargins : function(){
51709         return this.margins;
51710     },
51711     
51712     updateBox : function(box){
51713         this.box = box;
51714         var el = this.activePanel.getEl();
51715         el.dom.style.left = box.x + "px";
51716         el.dom.style.top = box.y + "px";
51717         this.activePanel.setSize(box.width, box.height);
51718     },
51719     
51720     /**
51721      * Returns the container element for this region.
51722      * @return {Roo.Element}
51723      */
51724     getEl : function(){
51725         return this.activePanel;
51726     },
51727     
51728     /**
51729      * Returns true if this region is currently visible.
51730      * @return {Boolean}
51731      */
51732     isVisible : function(){
51733         return this.activePanel ? true : false;
51734     },
51735     
51736     setActivePanel : function(panel){
51737         panel = this.getPanel(panel);
51738         if(this.activePanel && this.activePanel != panel){
51739             this.activePanel.setActiveState(false);
51740             this.activePanel.getEl().setLeftTop(-10000,-10000);
51741         }
51742         this.activePanel = panel;
51743         panel.setActiveState(true);
51744         if(this.box){
51745             panel.setSize(this.box.width, this.box.height);
51746         }
51747         this.fireEvent("panelactivated", this, panel);
51748         this.fireEvent("invalidated");
51749     },
51750     
51751     /**
51752      * Show the specified panel.
51753      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
51754      * @return {Roo.ContentPanel} The shown panel or null
51755      */
51756     showPanel : function(panel){
51757         if(panel = this.getPanel(panel)){
51758             this.setActivePanel(panel);
51759         }
51760         return panel;
51761     },
51762     
51763     /**
51764      * Get the active panel for this region.
51765      * @return {Roo.ContentPanel} The active panel or null
51766      */
51767     getActivePanel : function(){
51768         return this.activePanel;
51769     },
51770     
51771     /**
51772      * Add the passed ContentPanel(s)
51773      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
51774      * @return {Roo.ContentPanel} The panel added (if only one was added)
51775      */
51776     add : function(panel){
51777         if(arguments.length > 1){
51778             for(var i = 0, len = arguments.length; i < len; i++) {
51779                 this.add(arguments[i]);
51780             }
51781             return null;
51782         }
51783         if(this.hasPanel(panel)){
51784             this.showPanel(panel);
51785             return panel;
51786         }
51787         var el = panel.getEl();
51788         if(el.dom.parentNode != this.mgr.el.dom){
51789             this.mgr.el.dom.appendChild(el.dom);
51790         }
51791         if(panel.setRegion){
51792             panel.setRegion(this);
51793         }
51794         this.panels.add(panel);
51795         el.setStyle("position", "absolute");
51796         if(!panel.background){
51797             this.setActivePanel(panel);
51798             if(this.config.initialSize && this.panels.getCount()==1){
51799                 this.resizeTo(this.config.initialSize);
51800             }
51801         }
51802         this.fireEvent("paneladded", this, panel);
51803         return panel;
51804     },
51805     
51806     /**
51807      * Returns true if the panel is in this region.
51808      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51809      * @return {Boolean}
51810      */
51811     hasPanel : function(panel){
51812         if(typeof panel == "object"){ // must be panel obj
51813             panel = panel.getId();
51814         }
51815         return this.getPanel(panel) ? true : false;
51816     },
51817     
51818     /**
51819      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
51820      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51821      * @param {Boolean} preservePanel Overrides the config preservePanel option
51822      * @return {Roo.ContentPanel} The panel that was removed
51823      */
51824     remove : function(panel, preservePanel){
51825         panel = this.getPanel(panel);
51826         if(!panel){
51827             return null;
51828         }
51829         var e = {};
51830         this.fireEvent("beforeremove", this, panel, e);
51831         if(e.cancel === true){
51832             return null;
51833         }
51834         var panelId = panel.getId();
51835         this.panels.removeKey(panelId);
51836         return panel;
51837     },
51838     
51839     /**
51840      * Returns the panel specified or null if it's not in this region.
51841      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51842      * @return {Roo.ContentPanel}
51843      */
51844     getPanel : function(id){
51845         if(typeof id == "object"){ // must be panel obj
51846             return id;
51847         }
51848         return this.panels.get(id);
51849     },
51850     
51851     /**
51852      * Returns this regions position (north/south/east/west/center).
51853      * @return {String} 
51854      */
51855     getPosition: function(){
51856         return this.position;    
51857     }
51858 });/*
51859  * Based on:
51860  * Ext JS Library 1.1.1
51861  * Copyright(c) 2006-2007, Ext JS, LLC.
51862  *
51863  * Originally Released Under LGPL - original licence link has changed is not relivant.
51864  *
51865  * Fork - LGPL
51866  * <script type="text/javascript">
51867  */
51868  
51869 /**
51870  * @class Roo.LayoutRegion
51871  * @extends Roo.BasicLayoutRegion
51872  * This class represents a region in a layout manager.
51873  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
51874  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
51875  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
51876  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
51877  * @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})
51878  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
51879  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
51880  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
51881  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
51882  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
51883  * @cfg {String}    title           The title for the region (overrides panel titles)
51884  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
51885  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
51886  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
51887  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
51888  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
51889  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
51890  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
51891  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
51892  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
51893  * @cfg {Boolean}   showPin         True to show a pin button
51894  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
51895  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
51896  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
51897  * @cfg {Number}    width           For East/West panels
51898  * @cfg {Number}    height          For North/South panels
51899  * @cfg {Boolean}   split           To show the splitter
51900  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
51901  */
51902 Roo.LayoutRegion = function(mgr, config, pos){
51903     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
51904     var dh = Roo.DomHelper;
51905     /** This region's container element 
51906     * @type Roo.Element */
51907     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
51908     /** This region's title element 
51909     * @type Roo.Element */
51910
51911     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
51912         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
51913         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
51914     ]}, true);
51915     this.titleEl.enableDisplayMode();
51916     /** This region's title text element 
51917     * @type HTMLElement */
51918     this.titleTextEl = this.titleEl.dom.firstChild;
51919     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
51920     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
51921     this.closeBtn.enableDisplayMode();
51922     this.closeBtn.on("click", this.closeClicked, this);
51923     this.closeBtn.hide();
51924
51925     this.createBody(config);
51926     this.visible = true;
51927     this.collapsed = false;
51928
51929     if(config.hideWhenEmpty){
51930         this.hide();
51931         this.on("paneladded", this.validateVisibility, this);
51932         this.on("panelremoved", this.validateVisibility, this);
51933     }
51934     this.applyConfig(config);
51935 };
51936
51937 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
51938
51939     createBody : function(){
51940         /** This region's body element 
51941         * @type Roo.Element */
51942         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
51943     },
51944
51945     applyConfig : function(c){
51946         if(c.collapsible && this.position != "center" && !this.collapsedEl){
51947             var dh = Roo.DomHelper;
51948             if(c.titlebar !== false){
51949                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
51950                 this.collapseBtn.on("click", this.collapse, this);
51951                 this.collapseBtn.enableDisplayMode();
51952
51953                 if(c.showPin === true || this.showPin){
51954                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
51955                     this.stickBtn.enableDisplayMode();
51956                     this.stickBtn.on("click", this.expand, this);
51957                     this.stickBtn.hide();
51958                 }
51959             }
51960             /** This region's collapsed element
51961             * @type Roo.Element */
51962             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
51963                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
51964             ]}, true);
51965             if(c.floatable !== false){
51966                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
51967                this.collapsedEl.on("click", this.collapseClick, this);
51968             }
51969
51970             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
51971                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
51972                    id: "message", unselectable: "on", style:{"float":"left"}});
51973                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
51974              }
51975             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
51976             this.expandBtn.on("click", this.expand, this);
51977         }
51978         if(this.collapseBtn){
51979             this.collapseBtn.setVisible(c.collapsible == true);
51980         }
51981         this.cmargins = c.cmargins || this.cmargins ||
51982                          (this.position == "west" || this.position == "east" ?
51983                              {top: 0, left: 2, right:2, bottom: 0} :
51984                              {top: 2, left: 0, right:0, bottom: 2});
51985         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51986         this.bottomTabs = c.tabPosition != "top";
51987         this.autoScroll = c.autoScroll || false;
51988         if(this.autoScroll){
51989             this.bodyEl.setStyle("overflow", "auto");
51990         }else{
51991             this.bodyEl.setStyle("overflow", "hidden");
51992         }
51993         //if(c.titlebar !== false){
51994             if((!c.titlebar && !c.title) || c.titlebar === false){
51995                 this.titleEl.hide();
51996             }else{
51997                 this.titleEl.show();
51998                 if(c.title){
51999                     this.titleTextEl.innerHTML = c.title;
52000                 }
52001             }
52002         //}
52003         this.duration = c.duration || .30;
52004         this.slideDuration = c.slideDuration || .45;
52005         this.config = c;
52006         if(c.collapsed){
52007             this.collapse(true);
52008         }
52009         if(c.hidden){
52010             this.hide();
52011         }
52012     },
52013     /**
52014      * Returns true if this region is currently visible.
52015      * @return {Boolean}
52016      */
52017     isVisible : function(){
52018         return this.visible;
52019     },
52020
52021     /**
52022      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
52023      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
52024      */
52025     setCollapsedTitle : function(title){
52026         title = title || "&#160;";
52027         if(this.collapsedTitleTextEl){
52028             this.collapsedTitleTextEl.innerHTML = title;
52029         }
52030     },
52031
52032     getBox : function(){
52033         var b;
52034         if(!this.collapsed){
52035             b = this.el.getBox(false, true);
52036         }else{
52037             b = this.collapsedEl.getBox(false, true);
52038         }
52039         return b;
52040     },
52041
52042     getMargins : function(){
52043         return this.collapsed ? this.cmargins : this.margins;
52044     },
52045
52046     highlight : function(){
52047         this.el.addClass("x-layout-panel-dragover");
52048     },
52049
52050     unhighlight : function(){
52051         this.el.removeClass("x-layout-panel-dragover");
52052     },
52053
52054     updateBox : function(box){
52055         this.box = box;
52056         if(!this.collapsed){
52057             this.el.dom.style.left = box.x + "px";
52058             this.el.dom.style.top = box.y + "px";
52059             this.updateBody(box.width, box.height);
52060         }else{
52061             this.collapsedEl.dom.style.left = box.x + "px";
52062             this.collapsedEl.dom.style.top = box.y + "px";
52063             this.collapsedEl.setSize(box.width, box.height);
52064         }
52065         if(this.tabs){
52066             this.tabs.autoSizeTabs();
52067         }
52068     },
52069
52070     updateBody : function(w, h){
52071         if(w !== null){
52072             this.el.setWidth(w);
52073             w -= this.el.getBorderWidth("rl");
52074             if(this.config.adjustments){
52075                 w += this.config.adjustments[0];
52076             }
52077         }
52078         if(h !== null){
52079             this.el.setHeight(h);
52080             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
52081             h -= this.el.getBorderWidth("tb");
52082             if(this.config.adjustments){
52083                 h += this.config.adjustments[1];
52084             }
52085             this.bodyEl.setHeight(h);
52086             if(this.tabs){
52087                 h = this.tabs.syncHeight(h);
52088             }
52089         }
52090         if(this.panelSize){
52091             w = w !== null ? w : this.panelSize.width;
52092             h = h !== null ? h : this.panelSize.height;
52093         }
52094         if(this.activePanel){
52095             var el = this.activePanel.getEl();
52096             w = w !== null ? w : el.getWidth();
52097             h = h !== null ? h : el.getHeight();
52098             this.panelSize = {width: w, height: h};
52099             this.activePanel.setSize(w, h);
52100         }
52101         if(Roo.isIE && this.tabs){
52102             this.tabs.el.repaint();
52103         }
52104     },
52105
52106     /**
52107      * Returns the container element for this region.
52108      * @return {Roo.Element}
52109      */
52110     getEl : function(){
52111         return this.el;
52112     },
52113
52114     /**
52115      * Hides this region.
52116      */
52117     hide : function(){
52118         if(!this.collapsed){
52119             this.el.dom.style.left = "-2000px";
52120             this.el.hide();
52121         }else{
52122             this.collapsedEl.dom.style.left = "-2000px";
52123             this.collapsedEl.hide();
52124         }
52125         this.visible = false;
52126         this.fireEvent("visibilitychange", this, false);
52127     },
52128
52129     /**
52130      * Shows this region if it was previously hidden.
52131      */
52132     show : function(){
52133         if(!this.collapsed){
52134             this.el.show();
52135         }else{
52136             this.collapsedEl.show();
52137         }
52138         this.visible = true;
52139         this.fireEvent("visibilitychange", this, true);
52140     },
52141
52142     closeClicked : function(){
52143         if(this.activePanel){
52144             this.remove(this.activePanel);
52145         }
52146     },
52147
52148     collapseClick : function(e){
52149         if(this.isSlid){
52150            e.stopPropagation();
52151            this.slideIn();
52152         }else{
52153            e.stopPropagation();
52154            this.slideOut();
52155         }
52156     },
52157
52158     /**
52159      * Collapses this region.
52160      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
52161      */
52162     collapse : function(skipAnim, skipCheck = false){
52163         if(this.collapsed) {
52164             return;
52165         }
52166         
52167         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
52168             
52169             this.collapsed = true;
52170             if(this.split){
52171                 this.split.el.hide();
52172             }
52173             if(this.config.animate && skipAnim !== true){
52174                 this.fireEvent("invalidated", this);
52175                 this.animateCollapse();
52176             }else{
52177                 this.el.setLocation(-20000,-20000);
52178                 this.el.hide();
52179                 this.collapsedEl.show();
52180                 this.fireEvent("collapsed", this);
52181                 this.fireEvent("invalidated", this);
52182             }
52183         }
52184         
52185     },
52186
52187     animateCollapse : function(){
52188         // overridden
52189     },
52190
52191     /**
52192      * Expands this region if it was previously collapsed.
52193      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
52194      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
52195      */
52196     expand : function(e, skipAnim){
52197         if(e) {
52198             e.stopPropagation();
52199         }
52200         if(!this.collapsed || this.el.hasActiveFx()) {
52201             return;
52202         }
52203         if(this.isSlid){
52204             this.afterSlideIn();
52205             skipAnim = true;
52206         }
52207         this.collapsed = false;
52208         if(this.config.animate && skipAnim !== true){
52209             this.animateExpand();
52210         }else{
52211             this.el.show();
52212             if(this.split){
52213                 this.split.el.show();
52214             }
52215             this.collapsedEl.setLocation(-2000,-2000);
52216             this.collapsedEl.hide();
52217             this.fireEvent("invalidated", this);
52218             this.fireEvent("expanded", this);
52219         }
52220     },
52221
52222     animateExpand : function(){
52223         // overridden
52224     },
52225
52226     initTabs : function()
52227     {
52228         this.bodyEl.setStyle("overflow", "hidden");
52229         var ts = new Roo.TabPanel(
52230                 this.bodyEl.dom,
52231                 {
52232                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
52233                     disableTooltips: this.config.disableTabTips,
52234                     toolbar : this.config.toolbar
52235                 }
52236         );
52237         if(this.config.hideTabs){
52238             ts.stripWrap.setDisplayed(false);
52239         }
52240         this.tabs = ts;
52241         ts.resizeTabs = this.config.resizeTabs === true;
52242         ts.minTabWidth = this.config.minTabWidth || 40;
52243         ts.maxTabWidth = this.config.maxTabWidth || 250;
52244         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
52245         ts.monitorResize = false;
52246         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52247         ts.bodyEl.addClass('x-layout-tabs-body');
52248         this.panels.each(this.initPanelAsTab, this);
52249     },
52250
52251     initPanelAsTab : function(panel){
52252         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
52253                     this.config.closeOnTab && panel.isClosable());
52254         if(panel.tabTip !== undefined){
52255             ti.setTooltip(panel.tabTip);
52256         }
52257         ti.on("activate", function(){
52258               this.setActivePanel(panel);
52259         }, this);
52260         if(this.config.closeOnTab){
52261             ti.on("beforeclose", function(t, e){
52262                 e.cancel = true;
52263                 this.remove(panel);
52264             }, this);
52265         }
52266         return ti;
52267     },
52268
52269     updatePanelTitle : function(panel, title){
52270         if(this.activePanel == panel){
52271             this.updateTitle(title);
52272         }
52273         if(this.tabs){
52274             var ti = this.tabs.getTab(panel.getEl().id);
52275             ti.setText(title);
52276             if(panel.tabTip !== undefined){
52277                 ti.setTooltip(panel.tabTip);
52278             }
52279         }
52280     },
52281
52282     updateTitle : function(title){
52283         if(this.titleTextEl && !this.config.title){
52284             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
52285         }
52286     },
52287
52288     setActivePanel : function(panel){
52289         panel = this.getPanel(panel);
52290         if(this.activePanel && this.activePanel != panel){
52291             this.activePanel.setActiveState(false);
52292         }
52293         this.activePanel = panel;
52294         panel.setActiveState(true);
52295         if(this.panelSize){
52296             panel.setSize(this.panelSize.width, this.panelSize.height);
52297         }
52298         if(this.closeBtn){
52299             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
52300         }
52301         this.updateTitle(panel.getTitle());
52302         if(this.tabs){
52303             this.fireEvent("invalidated", this);
52304         }
52305         this.fireEvent("panelactivated", this, panel);
52306     },
52307
52308     /**
52309      * Shows the specified panel.
52310      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
52311      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
52312      */
52313     showPanel : function(panel)
52314     {
52315         panel = this.getPanel(panel);
52316         if(panel){
52317             if(this.tabs){
52318                 var tab = this.tabs.getTab(panel.getEl().id);
52319                 if(tab.isHidden()){
52320                     this.tabs.unhideTab(tab.id);
52321                 }
52322                 tab.activate();
52323             }else{
52324                 this.setActivePanel(panel);
52325             }
52326         }
52327         return panel;
52328     },
52329
52330     /**
52331      * Get the active panel for this region.
52332      * @return {Roo.ContentPanel} The active panel or null
52333      */
52334     getActivePanel : function(){
52335         return this.activePanel;
52336     },
52337
52338     validateVisibility : function(){
52339         if(this.panels.getCount() < 1){
52340             this.updateTitle("&#160;");
52341             this.closeBtn.hide();
52342             this.hide();
52343         }else{
52344             if(!this.isVisible()){
52345                 this.show();
52346             }
52347         }
52348     },
52349
52350     /**
52351      * Adds the passed ContentPanel(s) to this region.
52352      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52353      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
52354      */
52355     add : function(panel){
52356         if(arguments.length > 1){
52357             for(var i = 0, len = arguments.length; i < len; i++) {
52358                 this.add(arguments[i]);
52359             }
52360             return null;
52361         }
52362         if(this.hasPanel(panel)){
52363             this.showPanel(panel);
52364             return panel;
52365         }
52366         panel.setRegion(this);
52367         this.panels.add(panel);
52368         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
52369             this.bodyEl.dom.appendChild(panel.getEl().dom);
52370             if(panel.background !== true){
52371                 this.setActivePanel(panel);
52372             }
52373             this.fireEvent("paneladded", this, panel);
52374             return panel;
52375         }
52376         if(!this.tabs){
52377             this.initTabs();
52378         }else{
52379             this.initPanelAsTab(panel);
52380         }
52381         if(panel.background !== true){
52382             this.tabs.activate(panel.getEl().id);
52383         }
52384         this.fireEvent("paneladded", this, panel);
52385         return panel;
52386     },
52387
52388     /**
52389      * Hides the tab for the specified panel.
52390      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52391      */
52392     hidePanel : function(panel){
52393         if(this.tabs && (panel = this.getPanel(panel))){
52394             this.tabs.hideTab(panel.getEl().id);
52395         }
52396     },
52397
52398     /**
52399      * Unhides the tab for a previously hidden panel.
52400      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52401      */
52402     unhidePanel : function(panel){
52403         if(this.tabs && (panel = this.getPanel(panel))){
52404             this.tabs.unhideTab(panel.getEl().id);
52405         }
52406     },
52407
52408     clearPanels : function(){
52409         while(this.panels.getCount() > 0){
52410              this.remove(this.panels.first());
52411         }
52412     },
52413
52414     /**
52415      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52416      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52417      * @param {Boolean} preservePanel Overrides the config preservePanel option
52418      * @return {Roo.ContentPanel} The panel that was removed
52419      */
52420     remove : function(panel, preservePanel){
52421         panel = this.getPanel(panel);
52422         if(!panel){
52423             return null;
52424         }
52425         var e = {};
52426         this.fireEvent("beforeremove", this, panel, e);
52427         if(e.cancel === true){
52428             return null;
52429         }
52430         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
52431         var panelId = panel.getId();
52432         this.panels.removeKey(panelId);
52433         if(preservePanel){
52434             document.body.appendChild(panel.getEl().dom);
52435         }
52436         if(this.tabs){
52437             this.tabs.removeTab(panel.getEl().id);
52438         }else if (!preservePanel){
52439             this.bodyEl.dom.removeChild(panel.getEl().dom);
52440         }
52441         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
52442             var p = this.panels.first();
52443             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
52444             tempEl.appendChild(p.getEl().dom);
52445             this.bodyEl.update("");
52446             this.bodyEl.dom.appendChild(p.getEl().dom);
52447             tempEl = null;
52448             this.updateTitle(p.getTitle());
52449             this.tabs = null;
52450             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52451             this.setActivePanel(p);
52452         }
52453         panel.setRegion(null);
52454         if(this.activePanel == panel){
52455             this.activePanel = null;
52456         }
52457         if(this.config.autoDestroy !== false && preservePanel !== true){
52458             try{panel.destroy();}catch(e){}
52459         }
52460         this.fireEvent("panelremoved", this, panel);
52461         return panel;
52462     },
52463
52464     /**
52465      * Returns the TabPanel component used by this region
52466      * @return {Roo.TabPanel}
52467      */
52468     getTabs : function(){
52469         return this.tabs;
52470     },
52471
52472     createTool : function(parentEl, className){
52473         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
52474             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
52475         btn.addClassOnOver("x-layout-tools-button-over");
52476         return btn;
52477     }
52478 });/*
52479  * Based on:
52480  * Ext JS Library 1.1.1
52481  * Copyright(c) 2006-2007, Ext JS, LLC.
52482  *
52483  * Originally Released Under LGPL - original licence link has changed is not relivant.
52484  *
52485  * Fork - LGPL
52486  * <script type="text/javascript">
52487  */
52488  
52489
52490
52491 /**
52492  * @class Roo.SplitLayoutRegion
52493  * @extends Roo.LayoutRegion
52494  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
52495  */
52496 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
52497     this.cursor = cursor;
52498     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
52499 };
52500
52501 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
52502     splitTip : "Drag to resize.",
52503     collapsibleSplitTip : "Drag to resize. Double click to hide.",
52504     useSplitTips : false,
52505
52506     applyConfig : function(config){
52507         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
52508         if(config.split){
52509             if(!this.split){
52510                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
52511                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
52512                 /** The SplitBar for this region 
52513                 * @type Roo.SplitBar */
52514                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
52515                 this.split.on("moved", this.onSplitMove, this);
52516                 this.split.useShim = config.useShim === true;
52517                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
52518                 if(this.useSplitTips){
52519                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
52520                 }
52521                 if(config.collapsible){
52522                     this.split.el.on("dblclick", this.collapse,  this);
52523                 }
52524             }
52525             if(typeof config.minSize != "undefined"){
52526                 this.split.minSize = config.minSize;
52527             }
52528             if(typeof config.maxSize != "undefined"){
52529                 this.split.maxSize = config.maxSize;
52530             }
52531             if(config.hideWhenEmpty || config.hidden || config.collapsed){
52532                 this.hideSplitter();
52533             }
52534         }
52535     },
52536
52537     getHMaxSize : function(){
52538          var cmax = this.config.maxSize || 10000;
52539          var center = this.mgr.getRegion("center");
52540          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
52541     },
52542
52543     getVMaxSize : function(){
52544          var cmax = this.config.maxSize || 10000;
52545          var center = this.mgr.getRegion("center");
52546          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
52547     },
52548
52549     onSplitMove : function(split, newSize){
52550         this.fireEvent("resized", this, newSize);
52551     },
52552     
52553     /** 
52554      * Returns the {@link Roo.SplitBar} for this region.
52555      * @return {Roo.SplitBar}
52556      */
52557     getSplitBar : function(){
52558         return this.split;
52559     },
52560     
52561     hide : function(){
52562         this.hideSplitter();
52563         Roo.SplitLayoutRegion.superclass.hide.call(this);
52564     },
52565
52566     hideSplitter : function(){
52567         if(this.split){
52568             this.split.el.setLocation(-2000,-2000);
52569             this.split.el.hide();
52570         }
52571     },
52572
52573     show : function(){
52574         if(this.split){
52575             this.split.el.show();
52576         }
52577         Roo.SplitLayoutRegion.superclass.show.call(this);
52578     },
52579     
52580     beforeSlide: function(){
52581         if(Roo.isGecko){// firefox overflow auto bug workaround
52582             this.bodyEl.clip();
52583             if(this.tabs) {
52584                 this.tabs.bodyEl.clip();
52585             }
52586             if(this.activePanel){
52587                 this.activePanel.getEl().clip();
52588                 
52589                 if(this.activePanel.beforeSlide){
52590                     this.activePanel.beforeSlide();
52591                 }
52592             }
52593         }
52594     },
52595     
52596     afterSlide : function(){
52597         if(Roo.isGecko){// firefox overflow auto bug workaround
52598             this.bodyEl.unclip();
52599             if(this.tabs) {
52600                 this.tabs.bodyEl.unclip();
52601             }
52602             if(this.activePanel){
52603                 this.activePanel.getEl().unclip();
52604                 if(this.activePanel.afterSlide){
52605                     this.activePanel.afterSlide();
52606                 }
52607             }
52608         }
52609     },
52610
52611     initAutoHide : function(){
52612         if(this.autoHide !== false){
52613             if(!this.autoHideHd){
52614                 var st = new Roo.util.DelayedTask(this.slideIn, this);
52615                 this.autoHideHd = {
52616                     "mouseout": function(e){
52617                         if(!e.within(this.el, true)){
52618                             st.delay(500);
52619                         }
52620                     },
52621                     "mouseover" : function(e){
52622                         st.cancel();
52623                     },
52624                     scope : this
52625                 };
52626             }
52627             this.el.on(this.autoHideHd);
52628         }
52629     },
52630
52631     clearAutoHide : function(){
52632         if(this.autoHide !== false){
52633             this.el.un("mouseout", this.autoHideHd.mouseout);
52634             this.el.un("mouseover", this.autoHideHd.mouseover);
52635         }
52636     },
52637
52638     clearMonitor : function(){
52639         Roo.get(document).un("click", this.slideInIf, this);
52640     },
52641
52642     // these names are backwards but not changed for compat
52643     slideOut : function(){
52644         if(this.isSlid || this.el.hasActiveFx()){
52645             return;
52646         }
52647         this.isSlid = true;
52648         if(this.collapseBtn){
52649             this.collapseBtn.hide();
52650         }
52651         this.closeBtnState = this.closeBtn.getStyle('display');
52652         this.closeBtn.hide();
52653         if(this.stickBtn){
52654             this.stickBtn.show();
52655         }
52656         this.el.show();
52657         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
52658         this.beforeSlide();
52659         this.el.setStyle("z-index", 10001);
52660         this.el.slideIn(this.getSlideAnchor(), {
52661             callback: function(){
52662                 this.afterSlide();
52663                 this.initAutoHide();
52664                 Roo.get(document).on("click", this.slideInIf, this);
52665                 this.fireEvent("slideshow", this);
52666             },
52667             scope: this,
52668             block: true
52669         });
52670     },
52671
52672     afterSlideIn : function(){
52673         this.clearAutoHide();
52674         this.isSlid = false;
52675         this.clearMonitor();
52676         this.el.setStyle("z-index", "");
52677         if(this.collapseBtn){
52678             this.collapseBtn.show();
52679         }
52680         this.closeBtn.setStyle('display', this.closeBtnState);
52681         if(this.stickBtn){
52682             this.stickBtn.hide();
52683         }
52684         this.fireEvent("slidehide", this);
52685     },
52686
52687     slideIn : function(cb){
52688         if(!this.isSlid || this.el.hasActiveFx()){
52689             Roo.callback(cb);
52690             return;
52691         }
52692         this.isSlid = false;
52693         this.beforeSlide();
52694         this.el.slideOut(this.getSlideAnchor(), {
52695             callback: function(){
52696                 this.el.setLeftTop(-10000, -10000);
52697                 this.afterSlide();
52698                 this.afterSlideIn();
52699                 Roo.callback(cb);
52700             },
52701             scope: this,
52702             block: true
52703         });
52704     },
52705     
52706     slideInIf : function(e){
52707         if(!e.within(this.el)){
52708             this.slideIn();
52709         }
52710     },
52711
52712     animateCollapse : function(){
52713         this.beforeSlide();
52714         this.el.setStyle("z-index", 20000);
52715         var anchor = this.getSlideAnchor();
52716         this.el.slideOut(anchor, {
52717             callback : function(){
52718                 this.el.setStyle("z-index", "");
52719                 this.collapsedEl.slideIn(anchor, {duration:.3});
52720                 this.afterSlide();
52721                 this.el.setLocation(-10000,-10000);
52722                 this.el.hide();
52723                 this.fireEvent("collapsed", this);
52724             },
52725             scope: this,
52726             block: true
52727         });
52728     },
52729
52730     animateExpand : function(){
52731         this.beforeSlide();
52732         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
52733         this.el.setStyle("z-index", 20000);
52734         this.collapsedEl.hide({
52735             duration:.1
52736         });
52737         this.el.slideIn(this.getSlideAnchor(), {
52738             callback : function(){
52739                 this.el.setStyle("z-index", "");
52740                 this.afterSlide();
52741                 if(this.split){
52742                     this.split.el.show();
52743                 }
52744                 this.fireEvent("invalidated", this);
52745                 this.fireEvent("expanded", this);
52746             },
52747             scope: this,
52748             block: true
52749         });
52750     },
52751
52752     anchors : {
52753         "west" : "left",
52754         "east" : "right",
52755         "north" : "top",
52756         "south" : "bottom"
52757     },
52758
52759     sanchors : {
52760         "west" : "l",
52761         "east" : "r",
52762         "north" : "t",
52763         "south" : "b"
52764     },
52765
52766     canchors : {
52767         "west" : "tl-tr",
52768         "east" : "tr-tl",
52769         "north" : "tl-bl",
52770         "south" : "bl-tl"
52771     },
52772
52773     getAnchor : function(){
52774         return this.anchors[this.position];
52775     },
52776
52777     getCollapseAnchor : function(){
52778         return this.canchors[this.position];
52779     },
52780
52781     getSlideAnchor : function(){
52782         return this.sanchors[this.position];
52783     },
52784
52785     getAlignAdj : function(){
52786         var cm = this.cmargins;
52787         switch(this.position){
52788             case "west":
52789                 return [0, 0];
52790             break;
52791             case "east":
52792                 return [0, 0];
52793             break;
52794             case "north":
52795                 return [0, 0];
52796             break;
52797             case "south":
52798                 return [0, 0];
52799             break;
52800         }
52801     },
52802
52803     getExpandAdj : function(){
52804         var c = this.collapsedEl, cm = this.cmargins;
52805         switch(this.position){
52806             case "west":
52807                 return [-(cm.right+c.getWidth()+cm.left), 0];
52808             break;
52809             case "east":
52810                 return [cm.right+c.getWidth()+cm.left, 0];
52811             break;
52812             case "north":
52813                 return [0, -(cm.top+cm.bottom+c.getHeight())];
52814             break;
52815             case "south":
52816                 return [0, cm.top+cm.bottom+c.getHeight()];
52817             break;
52818         }
52819     }
52820 });/*
52821  * Based on:
52822  * Ext JS Library 1.1.1
52823  * Copyright(c) 2006-2007, Ext JS, LLC.
52824  *
52825  * Originally Released Under LGPL - original licence link has changed is not relivant.
52826  *
52827  * Fork - LGPL
52828  * <script type="text/javascript">
52829  */
52830 /*
52831  * These classes are private internal classes
52832  */
52833 Roo.CenterLayoutRegion = function(mgr, config){
52834     Roo.LayoutRegion.call(this, mgr, config, "center");
52835     this.visible = true;
52836     this.minWidth = config.minWidth || 20;
52837     this.minHeight = config.minHeight || 20;
52838 };
52839
52840 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
52841     hide : function(){
52842         // center panel can't be hidden
52843     },
52844     
52845     show : function(){
52846         // center panel can't be hidden
52847     },
52848     
52849     getMinWidth: function(){
52850         return this.minWidth;
52851     },
52852     
52853     getMinHeight: function(){
52854         return this.minHeight;
52855     }
52856 });
52857
52858
52859 Roo.NorthLayoutRegion = function(mgr, config){
52860     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
52861     if(this.split){
52862         this.split.placement = Roo.SplitBar.TOP;
52863         this.split.orientation = Roo.SplitBar.VERTICAL;
52864         this.split.el.addClass("x-layout-split-v");
52865     }
52866     var size = config.initialSize || config.height;
52867     if(typeof size != "undefined"){
52868         this.el.setHeight(size);
52869     }
52870 };
52871 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
52872     orientation: Roo.SplitBar.VERTICAL,
52873     getBox : function(){
52874         if(this.collapsed){
52875             return this.collapsedEl.getBox();
52876         }
52877         var box = this.el.getBox();
52878         if(this.split){
52879             box.height += this.split.el.getHeight();
52880         }
52881         return box;
52882     },
52883     
52884     updateBox : function(box){
52885         if(this.split && !this.collapsed){
52886             box.height -= this.split.el.getHeight();
52887             this.split.el.setLeft(box.x);
52888             this.split.el.setTop(box.y+box.height);
52889             this.split.el.setWidth(box.width);
52890         }
52891         if(this.collapsed){
52892             this.updateBody(box.width, null);
52893         }
52894         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52895     }
52896 });
52897
52898 Roo.SouthLayoutRegion = function(mgr, config){
52899     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
52900     if(this.split){
52901         this.split.placement = Roo.SplitBar.BOTTOM;
52902         this.split.orientation = Roo.SplitBar.VERTICAL;
52903         this.split.el.addClass("x-layout-split-v");
52904     }
52905     var size = config.initialSize || config.height;
52906     if(typeof size != "undefined"){
52907         this.el.setHeight(size);
52908     }
52909 };
52910 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
52911     orientation: Roo.SplitBar.VERTICAL,
52912     getBox : function(){
52913         if(this.collapsed){
52914             return this.collapsedEl.getBox();
52915         }
52916         var box = this.el.getBox();
52917         if(this.split){
52918             var sh = this.split.el.getHeight();
52919             box.height += sh;
52920             box.y -= sh;
52921         }
52922         return box;
52923     },
52924     
52925     updateBox : function(box){
52926         if(this.split && !this.collapsed){
52927             var sh = this.split.el.getHeight();
52928             box.height -= sh;
52929             box.y += sh;
52930             this.split.el.setLeft(box.x);
52931             this.split.el.setTop(box.y-sh);
52932             this.split.el.setWidth(box.width);
52933         }
52934         if(this.collapsed){
52935             this.updateBody(box.width, null);
52936         }
52937         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52938     }
52939 });
52940
52941 Roo.EastLayoutRegion = function(mgr, config){
52942     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
52943     if(this.split){
52944         this.split.placement = Roo.SplitBar.RIGHT;
52945         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52946         this.split.el.addClass("x-layout-split-h");
52947     }
52948     var size = config.initialSize || config.width;
52949     if(typeof size != "undefined"){
52950         this.el.setWidth(size);
52951     }
52952 };
52953 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
52954     orientation: Roo.SplitBar.HORIZONTAL,
52955     getBox : function(){
52956         if(this.collapsed){
52957             return this.collapsedEl.getBox();
52958         }
52959         var box = this.el.getBox();
52960         if(this.split){
52961             var sw = this.split.el.getWidth();
52962             box.width += sw;
52963             box.x -= sw;
52964         }
52965         return box;
52966     },
52967
52968     updateBox : function(box){
52969         if(this.split && !this.collapsed){
52970             var sw = this.split.el.getWidth();
52971             box.width -= sw;
52972             this.split.el.setLeft(box.x);
52973             this.split.el.setTop(box.y);
52974             this.split.el.setHeight(box.height);
52975             box.x += sw;
52976         }
52977         if(this.collapsed){
52978             this.updateBody(null, box.height);
52979         }
52980         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52981     }
52982 });
52983
52984 Roo.WestLayoutRegion = function(mgr, config){
52985     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
52986     if(this.split){
52987         this.split.placement = Roo.SplitBar.LEFT;
52988         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52989         this.split.el.addClass("x-layout-split-h");
52990     }
52991     var size = config.initialSize || config.width;
52992     if(typeof size != "undefined"){
52993         this.el.setWidth(size);
52994     }
52995 };
52996 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
52997     orientation: Roo.SplitBar.HORIZONTAL,
52998     getBox : function(){
52999         if(this.collapsed){
53000             return this.collapsedEl.getBox();
53001         }
53002         var box = this.el.getBox();
53003         if(this.split){
53004             box.width += this.split.el.getWidth();
53005         }
53006         return box;
53007     },
53008     
53009     updateBox : function(box){
53010         if(this.split && !this.collapsed){
53011             var sw = this.split.el.getWidth();
53012             box.width -= sw;
53013             this.split.el.setLeft(box.x+box.width);
53014             this.split.el.setTop(box.y);
53015             this.split.el.setHeight(box.height);
53016         }
53017         if(this.collapsed){
53018             this.updateBody(null, box.height);
53019         }
53020         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53021     }
53022 });
53023 /*
53024  * Based on:
53025  * Ext JS Library 1.1.1
53026  * Copyright(c) 2006-2007, Ext JS, LLC.
53027  *
53028  * Originally Released Under LGPL - original licence link has changed is not relivant.
53029  *
53030  * Fork - LGPL
53031  * <script type="text/javascript">
53032  */
53033  
53034  
53035 /*
53036  * Private internal class for reading and applying state
53037  */
53038 Roo.LayoutStateManager = function(layout){
53039      // default empty state
53040      this.state = {
53041         north: {},
53042         south: {},
53043         east: {},
53044         west: {}       
53045     };
53046 };
53047
53048 Roo.LayoutStateManager.prototype = {
53049     init : function(layout, provider){
53050         this.provider = provider;
53051         var state = provider.get(layout.id+"-layout-state");
53052         if(state){
53053             var wasUpdating = layout.isUpdating();
53054             if(!wasUpdating){
53055                 layout.beginUpdate();
53056             }
53057             for(var key in state){
53058                 if(typeof state[key] != "function"){
53059                     var rstate = state[key];
53060                     var r = layout.getRegion(key);
53061                     if(r && rstate){
53062                         if(rstate.size){
53063                             r.resizeTo(rstate.size);
53064                         }
53065                         if(rstate.collapsed == true){
53066                             r.collapse(true);
53067                         }else{
53068                             r.expand(null, true);
53069                         }
53070                     }
53071                 }
53072             }
53073             if(!wasUpdating){
53074                 layout.endUpdate();
53075             }
53076             this.state = state; 
53077         }
53078         this.layout = layout;
53079         layout.on("regionresized", this.onRegionResized, this);
53080         layout.on("regioncollapsed", this.onRegionCollapsed, this);
53081         layout.on("regionexpanded", this.onRegionExpanded, this);
53082     },
53083     
53084     storeState : function(){
53085         this.provider.set(this.layout.id+"-layout-state", this.state);
53086     },
53087     
53088     onRegionResized : function(region, newSize){
53089         this.state[region.getPosition()].size = newSize;
53090         this.storeState();
53091     },
53092     
53093     onRegionCollapsed : function(region){
53094         this.state[region.getPosition()].collapsed = true;
53095         this.storeState();
53096     },
53097     
53098     onRegionExpanded : function(region){
53099         this.state[region.getPosition()].collapsed = false;
53100         this.storeState();
53101     }
53102 };/*
53103  * Based on:
53104  * Ext JS Library 1.1.1
53105  * Copyright(c) 2006-2007, Ext JS, LLC.
53106  *
53107  * Originally Released Under LGPL - original licence link has changed is not relivant.
53108  *
53109  * Fork - LGPL
53110  * <script type="text/javascript">
53111  */
53112 /**
53113  * @class Roo.ContentPanel
53114  * @extends Roo.util.Observable
53115  * A basic ContentPanel element.
53116  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
53117  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
53118  * @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
53119  * @cfg {Boolean}   closable      True if the panel can be closed/removed
53120  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
53121  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
53122  * @cfg {Toolbar}   toolbar       A toolbar for this panel
53123  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
53124  * @cfg {String} title          The title for this panel
53125  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
53126  * @cfg {String} url            Calls {@link #setUrl} with this value
53127  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
53128  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
53129  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
53130  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
53131
53132  * @constructor
53133  * Create a new ContentPanel.
53134  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
53135  * @param {String/Object} config A string to set only the title or a config object
53136  * @param {String} content (optional) Set the HTML content for this panel
53137  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
53138  */
53139 Roo.ContentPanel = function(el, config, content){
53140     
53141      
53142     /*
53143     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
53144         config = el;
53145         el = Roo.id();
53146     }
53147     if (config && config.parentLayout) { 
53148         el = config.parentLayout.el.createChild(); 
53149     }
53150     */
53151     if(el.autoCreate){ // xtype is available if this is called from factory
53152         config = el;
53153         el = Roo.id();
53154     }
53155     this.el = Roo.get(el);
53156     if(!this.el && config && config.autoCreate){
53157         if(typeof config.autoCreate == "object"){
53158             if(!config.autoCreate.id){
53159                 config.autoCreate.id = config.id||el;
53160             }
53161             this.el = Roo.DomHelper.append(document.body,
53162                         config.autoCreate, true);
53163         }else{
53164             this.el = Roo.DomHelper.append(document.body,
53165                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
53166         }
53167     }
53168     this.closable = false;
53169     this.loaded = false;
53170     this.active = false;
53171     if(typeof config == "string"){
53172         this.title = config;
53173     }else{
53174         Roo.apply(this, config);
53175     }
53176     
53177     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
53178         this.wrapEl = this.el.wrap();
53179         this.toolbar.container = this.el.insertSibling(false, 'before');
53180         this.toolbar = new Roo.Toolbar(this.toolbar);
53181     }
53182     
53183     // xtype created footer. - not sure if will work as we normally have to render first..
53184     if (this.footer && !this.footer.el && this.footer.xtype) {
53185         if (!this.wrapEl) {
53186             this.wrapEl = this.el.wrap();
53187         }
53188     
53189         this.footer.container = this.wrapEl.createChild();
53190          
53191         this.footer = Roo.factory(this.footer, Roo);
53192         
53193     }
53194     
53195     if(this.resizeEl){
53196         this.resizeEl = Roo.get(this.resizeEl, true);
53197     }else{
53198         this.resizeEl = this.el;
53199     }
53200     // handle view.xtype
53201     
53202  
53203     
53204     
53205     this.addEvents({
53206         /**
53207          * @event activate
53208          * Fires when this panel is activated. 
53209          * @param {Roo.ContentPanel} this
53210          */
53211         "activate" : true,
53212         /**
53213          * @event deactivate
53214          * Fires when this panel is activated. 
53215          * @param {Roo.ContentPanel} this
53216          */
53217         "deactivate" : true,
53218
53219         /**
53220          * @event resize
53221          * Fires when this panel is resized if fitToFrame is true.
53222          * @param {Roo.ContentPanel} this
53223          * @param {Number} width The width after any component adjustments
53224          * @param {Number} height The height after any component adjustments
53225          */
53226         "resize" : true,
53227         
53228          /**
53229          * @event render
53230          * Fires when this tab is created
53231          * @param {Roo.ContentPanel} this
53232          */
53233         "render" : true
53234         
53235         
53236         
53237     });
53238     
53239
53240     
53241     
53242     if(this.autoScroll){
53243         this.resizeEl.setStyle("overflow", "auto");
53244     } else {
53245         // fix randome scrolling
53246         this.el.on('scroll', function() {
53247             Roo.log('fix random scolling');
53248             this.scrollTo('top',0); 
53249         });
53250     }
53251     content = content || this.content;
53252     if(content){
53253         this.setContent(content);
53254     }
53255     if(config && config.url){
53256         this.setUrl(this.url, this.params, this.loadOnce);
53257     }
53258     
53259     
53260     
53261     Roo.ContentPanel.superclass.constructor.call(this);
53262     
53263     if (this.view && typeof(this.view.xtype) != 'undefined') {
53264         this.view.el = this.el.appendChild(document.createElement("div"));
53265         this.view = Roo.factory(this.view); 
53266         this.view.render  &&  this.view.render(false, '');  
53267     }
53268     
53269     
53270     this.fireEvent('render', this);
53271 };
53272
53273 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
53274     tabTip:'',
53275     setRegion : function(region){
53276         this.region = region;
53277         if(region){
53278            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
53279         }else{
53280            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
53281         } 
53282     },
53283     
53284     /**
53285      * Returns the toolbar for this Panel if one was configured. 
53286      * @return {Roo.Toolbar} 
53287      */
53288     getToolbar : function(){
53289         return this.toolbar;
53290     },
53291     
53292     setActiveState : function(active){
53293         this.active = active;
53294         if(!active){
53295             this.fireEvent("deactivate", this);
53296         }else{
53297             this.fireEvent("activate", this);
53298         }
53299     },
53300     /**
53301      * Updates this panel's element
53302      * @param {String} content The new content
53303      * @param {Boolean} loadScripts (optional) true to look for and process scripts
53304     */
53305     setContent : function(content, loadScripts){
53306         this.el.update(content, loadScripts);
53307     },
53308
53309     ignoreResize : function(w, h){
53310         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
53311             return true;
53312         }else{
53313             this.lastSize = {width: w, height: h};
53314             return false;
53315         }
53316     },
53317     /**
53318      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
53319      * @return {Roo.UpdateManager} The UpdateManager
53320      */
53321     getUpdateManager : function(){
53322         return this.el.getUpdateManager();
53323     },
53324      /**
53325      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
53326      * @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:
53327 <pre><code>
53328 panel.load({
53329     url: "your-url.php",
53330     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
53331     callback: yourFunction,
53332     scope: yourObject, //(optional scope)
53333     discardUrl: false,
53334     nocache: false,
53335     text: "Loading...",
53336     timeout: 30,
53337     scripts: false
53338 });
53339 </code></pre>
53340      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
53341      * 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.
53342      * @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}
53343      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
53344      * @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.
53345      * @return {Roo.ContentPanel} this
53346      */
53347     load : function(){
53348         var um = this.el.getUpdateManager();
53349         um.update.apply(um, arguments);
53350         return this;
53351     },
53352
53353
53354     /**
53355      * 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.
53356      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
53357      * @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)
53358      * @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)
53359      * @return {Roo.UpdateManager} The UpdateManager
53360      */
53361     setUrl : function(url, params, loadOnce){
53362         if(this.refreshDelegate){
53363             this.removeListener("activate", this.refreshDelegate);
53364         }
53365         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
53366         this.on("activate", this.refreshDelegate);
53367         return this.el.getUpdateManager();
53368     },
53369     
53370     _handleRefresh : function(url, params, loadOnce){
53371         if(!loadOnce || !this.loaded){
53372             var updater = this.el.getUpdateManager();
53373             updater.update(url, params, this._setLoaded.createDelegate(this));
53374         }
53375     },
53376     
53377     _setLoaded : function(){
53378         this.loaded = true;
53379     }, 
53380     
53381     /**
53382      * Returns this panel's id
53383      * @return {String} 
53384      */
53385     getId : function(){
53386         return this.el.id;
53387     },
53388     
53389     /** 
53390      * Returns this panel's element - used by regiosn to add.
53391      * @return {Roo.Element} 
53392      */
53393     getEl : function(){
53394         return this.wrapEl || this.el;
53395     },
53396     
53397     adjustForComponents : function(width, height)
53398     {
53399         //Roo.log('adjustForComponents ');
53400         if(this.resizeEl != this.el){
53401             width -= this.el.getFrameWidth('lr');
53402             height -= this.el.getFrameWidth('tb');
53403         }
53404         if(this.toolbar){
53405             var te = this.toolbar.getEl();
53406             height -= te.getHeight();
53407             te.setWidth(width);
53408         }
53409         if(this.footer){
53410             var te = this.footer.getEl();
53411             Roo.log("footer:" + te.getHeight());
53412             
53413             height -= te.getHeight();
53414             te.setWidth(width);
53415         }
53416         
53417         
53418         if(this.adjustments){
53419             width += this.adjustments[0];
53420             height += this.adjustments[1];
53421         }
53422         return {"width": width, "height": height};
53423     },
53424     
53425     setSize : function(width, height){
53426         if(this.fitToFrame && !this.ignoreResize(width, height)){
53427             if(this.fitContainer && this.resizeEl != this.el){
53428                 this.el.setSize(width, height);
53429             }
53430             var size = this.adjustForComponents(width, height);
53431             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
53432             this.fireEvent('resize', this, size.width, size.height);
53433         }
53434     },
53435     
53436     /**
53437      * Returns this panel's title
53438      * @return {String} 
53439      */
53440     getTitle : function(){
53441         return this.title;
53442     },
53443     
53444     /**
53445      * Set this panel's title
53446      * @param {String} title
53447      */
53448     setTitle : function(title){
53449         this.title = title;
53450         if(this.region){
53451             this.region.updatePanelTitle(this, title);
53452         }
53453     },
53454     
53455     /**
53456      * Returns true is this panel was configured to be closable
53457      * @return {Boolean} 
53458      */
53459     isClosable : function(){
53460         return this.closable;
53461     },
53462     
53463     beforeSlide : function(){
53464         this.el.clip();
53465         this.resizeEl.clip();
53466     },
53467     
53468     afterSlide : function(){
53469         this.el.unclip();
53470         this.resizeEl.unclip();
53471     },
53472     
53473     /**
53474      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
53475      *   Will fail silently if the {@link #setUrl} method has not been called.
53476      *   This does not activate the panel, just updates its content.
53477      */
53478     refresh : function(){
53479         if(this.refreshDelegate){
53480            this.loaded = false;
53481            this.refreshDelegate();
53482         }
53483     },
53484     
53485     /**
53486      * Destroys this panel
53487      */
53488     destroy : function(){
53489         this.el.removeAllListeners();
53490         var tempEl = document.createElement("span");
53491         tempEl.appendChild(this.el.dom);
53492         tempEl.innerHTML = "";
53493         this.el.remove();
53494         this.el = null;
53495     },
53496     
53497     /**
53498      * form - if the content panel contains a form - this is a reference to it.
53499      * @type {Roo.form.Form}
53500      */
53501     form : false,
53502     /**
53503      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
53504      *    This contains a reference to it.
53505      * @type {Roo.View}
53506      */
53507     view : false,
53508     
53509       /**
53510      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
53511      * <pre><code>
53512
53513 layout.addxtype({
53514        xtype : 'Form',
53515        items: [ .... ]
53516    }
53517 );
53518
53519 </code></pre>
53520      * @param {Object} cfg Xtype definition of item to add.
53521      */
53522     
53523     addxtype : function(cfg) {
53524         // add form..
53525         if (cfg.xtype.match(/^Form$/)) {
53526             
53527             var el;
53528             //if (this.footer) {
53529             //    el = this.footer.container.insertSibling(false, 'before');
53530             //} else {
53531                 el = this.el.createChild();
53532             //}
53533
53534             this.form = new  Roo.form.Form(cfg);
53535             
53536             
53537             if ( this.form.allItems.length) {
53538                 this.form.render(el.dom);
53539             }
53540             return this.form;
53541         }
53542         // should only have one of theses..
53543         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
53544             // views.. should not be just added - used named prop 'view''
53545             
53546             cfg.el = this.el.appendChild(document.createElement("div"));
53547             // factory?
53548             
53549             var ret = new Roo.factory(cfg);
53550              
53551              ret.render && ret.render(false, ''); // render blank..
53552             this.view = ret;
53553             return ret;
53554         }
53555         return false;
53556     }
53557 });
53558
53559 /**
53560  * @class Roo.GridPanel
53561  * @extends Roo.ContentPanel
53562  * @constructor
53563  * Create a new GridPanel.
53564  * @param {Roo.grid.Grid} grid The grid for this panel
53565  * @param {String/Object} config A string to set only the panel's title, or a config object
53566  */
53567 Roo.GridPanel = function(grid, config){
53568     
53569   
53570     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
53571         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
53572         
53573     this.wrapper.dom.appendChild(grid.getGridEl().dom);
53574     
53575     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
53576     
53577     if(this.toolbar){
53578         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
53579     }
53580     // xtype created footer. - not sure if will work as we normally have to render first..
53581     if (this.footer && !this.footer.el && this.footer.xtype) {
53582         
53583         this.footer.container = this.grid.getView().getFooterPanel(true);
53584         this.footer.dataSource = this.grid.dataSource;
53585         this.footer = Roo.factory(this.footer, Roo);
53586         
53587     }
53588     
53589     grid.monitorWindowResize = false; // turn off autosizing
53590     grid.autoHeight = false;
53591     grid.autoWidth = false;
53592     this.grid = grid;
53593     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
53594 };
53595
53596 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
53597     getId : function(){
53598         return this.grid.id;
53599     },
53600     
53601     /**
53602      * Returns the grid for this panel
53603      * @return {Roo.grid.Grid} 
53604      */
53605     getGrid : function(){
53606         return this.grid;    
53607     },
53608     
53609     setSize : function(width, height){
53610         if(!this.ignoreResize(width, height)){
53611             var grid = this.grid;
53612             var size = this.adjustForComponents(width, height);
53613             grid.getGridEl().setSize(size.width, size.height);
53614             grid.autoSize();
53615         }
53616     },
53617     
53618     beforeSlide : function(){
53619         this.grid.getView().scroller.clip();
53620     },
53621     
53622     afterSlide : function(){
53623         this.grid.getView().scroller.unclip();
53624     },
53625     
53626     destroy : function(){
53627         this.grid.destroy();
53628         delete this.grid;
53629         Roo.GridPanel.superclass.destroy.call(this); 
53630     }
53631 });
53632
53633
53634 /**
53635  * @class Roo.NestedLayoutPanel
53636  * @extends Roo.ContentPanel
53637  * @constructor
53638  * Create a new NestedLayoutPanel.
53639  * 
53640  * 
53641  * @param {Roo.BorderLayout} layout The layout for this panel
53642  * @param {String/Object} config A string to set only the title or a config object
53643  */
53644 Roo.NestedLayoutPanel = function(layout, config)
53645 {
53646     // construct with only one argument..
53647     /* FIXME - implement nicer consturctors
53648     if (layout.layout) {
53649         config = layout;
53650         layout = config.layout;
53651         delete config.layout;
53652     }
53653     if (layout.xtype && !layout.getEl) {
53654         // then layout needs constructing..
53655         layout = Roo.factory(layout, Roo);
53656     }
53657     */
53658     
53659     
53660     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
53661     
53662     layout.monitorWindowResize = false; // turn off autosizing
53663     this.layout = layout;
53664     this.layout.getEl().addClass("x-layout-nested-layout");
53665     
53666     
53667     
53668     
53669 };
53670
53671 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
53672
53673     setSize : function(width, height){
53674         if(!this.ignoreResize(width, height)){
53675             var size = this.adjustForComponents(width, height);
53676             var el = this.layout.getEl();
53677             el.setSize(size.width, size.height);
53678             var touch = el.dom.offsetWidth;
53679             this.layout.layout();
53680             // ie requires a double layout on the first pass
53681             if(Roo.isIE && !this.initialized){
53682                 this.initialized = true;
53683                 this.layout.layout();
53684             }
53685         }
53686     },
53687     
53688     // activate all subpanels if not currently active..
53689     
53690     setActiveState : function(active){
53691         this.active = active;
53692         if(!active){
53693             this.fireEvent("deactivate", this);
53694             return;
53695         }
53696         
53697         this.fireEvent("activate", this);
53698         // not sure if this should happen before or after..
53699         if (!this.layout) {
53700             return; // should not happen..
53701         }
53702         var reg = false;
53703         for (var r in this.layout.regions) {
53704             reg = this.layout.getRegion(r);
53705             if (reg.getActivePanel()) {
53706                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
53707                 reg.setActivePanel(reg.getActivePanel());
53708                 continue;
53709             }
53710             if (!reg.panels.length) {
53711                 continue;
53712             }
53713             reg.showPanel(reg.getPanel(0));
53714         }
53715         
53716         
53717         
53718         
53719     },
53720     
53721     /**
53722      * Returns the nested BorderLayout for this panel
53723      * @return {Roo.BorderLayout} 
53724      */
53725     getLayout : function(){
53726         return this.layout;
53727     },
53728     
53729      /**
53730      * Adds a xtype elements to the layout of the nested panel
53731      * <pre><code>
53732
53733 panel.addxtype({
53734        xtype : 'ContentPanel',
53735        region: 'west',
53736        items: [ .... ]
53737    }
53738 );
53739
53740 panel.addxtype({
53741         xtype : 'NestedLayoutPanel',
53742         region: 'west',
53743         layout: {
53744            center: { },
53745            west: { }   
53746         },
53747         items : [ ... list of content panels or nested layout panels.. ]
53748    }
53749 );
53750 </code></pre>
53751      * @param {Object} cfg Xtype definition of item to add.
53752      */
53753     addxtype : function(cfg) {
53754         return this.layout.addxtype(cfg);
53755     
53756     }
53757 });
53758
53759 Roo.ScrollPanel = function(el, config, content){
53760     config = config || {};
53761     config.fitToFrame = true;
53762     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
53763     
53764     this.el.dom.style.overflow = "hidden";
53765     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
53766     this.el.removeClass("x-layout-inactive-content");
53767     this.el.on("mousewheel", this.onWheel, this);
53768
53769     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
53770     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
53771     up.unselectable(); down.unselectable();
53772     up.on("click", this.scrollUp, this);
53773     down.on("click", this.scrollDown, this);
53774     up.addClassOnOver("x-scroller-btn-over");
53775     down.addClassOnOver("x-scroller-btn-over");
53776     up.addClassOnClick("x-scroller-btn-click");
53777     down.addClassOnClick("x-scroller-btn-click");
53778     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
53779
53780     this.resizeEl = this.el;
53781     this.el = wrap; this.up = up; this.down = down;
53782 };
53783
53784 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
53785     increment : 100,
53786     wheelIncrement : 5,
53787     scrollUp : function(){
53788         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
53789     },
53790
53791     scrollDown : function(){
53792         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
53793     },
53794
53795     afterScroll : function(){
53796         var el = this.resizeEl;
53797         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
53798         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53799         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53800     },
53801
53802     setSize : function(){
53803         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
53804         this.afterScroll();
53805     },
53806
53807     onWheel : function(e){
53808         var d = e.getWheelDelta();
53809         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
53810         this.afterScroll();
53811         e.stopEvent();
53812     },
53813
53814     setContent : function(content, loadScripts){
53815         this.resizeEl.update(content, loadScripts);
53816     }
53817
53818 });
53819
53820
53821
53822
53823
53824
53825
53826
53827
53828 /**
53829  * @class Roo.TreePanel
53830  * @extends Roo.ContentPanel
53831  * @constructor
53832  * Create a new TreePanel. - defaults to fit/scoll contents.
53833  * @param {String/Object} config A string to set only the panel's title, or a config object
53834  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
53835  */
53836 Roo.TreePanel = function(config){
53837     var el = config.el;
53838     var tree = config.tree;
53839     delete config.tree; 
53840     delete config.el; // hopefull!
53841     
53842     // wrapper for IE7 strict & safari scroll issue
53843     
53844     var treeEl = el.createChild();
53845     config.resizeEl = treeEl;
53846     
53847     
53848     
53849     Roo.TreePanel.superclass.constructor.call(this, el, config);
53850  
53851  
53852     this.tree = new Roo.tree.TreePanel(treeEl , tree);
53853     //console.log(tree);
53854     this.on('activate', function()
53855     {
53856         if (this.tree.rendered) {
53857             return;
53858         }
53859         //console.log('render tree');
53860         this.tree.render();
53861     });
53862     // this should not be needed.. - it's actually the 'el' that resizes?
53863     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
53864     
53865     //this.on('resize',  function (cp, w, h) {
53866     //        this.tree.innerCt.setWidth(w);
53867     //        this.tree.innerCt.setHeight(h);
53868     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
53869     //});
53870
53871         
53872     
53873 };
53874
53875 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
53876     fitToFrame : true,
53877     autoScroll : true
53878 });
53879
53880
53881
53882
53883
53884
53885
53886
53887
53888
53889
53890 /*
53891  * Based on:
53892  * Ext JS Library 1.1.1
53893  * Copyright(c) 2006-2007, Ext JS, LLC.
53894  *
53895  * Originally Released Under LGPL - original licence link has changed is not relivant.
53896  *
53897  * Fork - LGPL
53898  * <script type="text/javascript">
53899  */
53900  
53901
53902 /**
53903  * @class Roo.ReaderLayout
53904  * @extends Roo.BorderLayout
53905  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
53906  * center region containing two nested regions (a top one for a list view and one for item preview below),
53907  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
53908  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
53909  * expedites the setup of the overall layout and regions for this common application style.
53910  * Example:
53911  <pre><code>
53912 var reader = new Roo.ReaderLayout();
53913 var CP = Roo.ContentPanel;  // shortcut for adding
53914
53915 reader.beginUpdate();
53916 reader.add("north", new CP("north", "North"));
53917 reader.add("west", new CP("west", {title: "West"}));
53918 reader.add("east", new CP("east", {title: "East"}));
53919
53920 reader.regions.listView.add(new CP("listView", "List"));
53921 reader.regions.preview.add(new CP("preview", "Preview"));
53922 reader.endUpdate();
53923 </code></pre>
53924 * @constructor
53925 * Create a new ReaderLayout
53926 * @param {Object} config Configuration options
53927 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
53928 * document.body if omitted)
53929 */
53930 Roo.ReaderLayout = function(config, renderTo){
53931     var c = config || {size:{}};
53932     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
53933         north: c.north !== false ? Roo.apply({
53934             split:false,
53935             initialSize: 32,
53936             titlebar: false
53937         }, c.north) : false,
53938         west: c.west !== false ? Roo.apply({
53939             split:true,
53940             initialSize: 200,
53941             minSize: 175,
53942             maxSize: 400,
53943             titlebar: true,
53944             collapsible: true,
53945             animate: true,
53946             margins:{left:5,right:0,bottom:5,top:5},
53947             cmargins:{left:5,right:5,bottom:5,top:5}
53948         }, c.west) : false,
53949         east: c.east !== false ? Roo.apply({
53950             split:true,
53951             initialSize: 200,
53952             minSize: 175,
53953             maxSize: 400,
53954             titlebar: true,
53955             collapsible: true,
53956             animate: true,
53957             margins:{left:0,right:5,bottom:5,top:5},
53958             cmargins:{left:5,right:5,bottom:5,top:5}
53959         }, c.east) : false,
53960         center: Roo.apply({
53961             tabPosition: 'top',
53962             autoScroll:false,
53963             closeOnTab: true,
53964             titlebar:false,
53965             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
53966         }, c.center)
53967     });
53968
53969     this.el.addClass('x-reader');
53970
53971     this.beginUpdate();
53972
53973     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
53974         south: c.preview !== false ? Roo.apply({
53975             split:true,
53976             initialSize: 200,
53977             minSize: 100,
53978             autoScroll:true,
53979             collapsible:true,
53980             titlebar: true,
53981             cmargins:{top:5,left:0, right:0, bottom:0}
53982         }, c.preview) : false,
53983         center: Roo.apply({
53984             autoScroll:false,
53985             titlebar:false,
53986             minHeight:200
53987         }, c.listView)
53988     });
53989     this.add('center', new Roo.NestedLayoutPanel(inner,
53990             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
53991
53992     this.endUpdate();
53993
53994     this.regions.preview = inner.getRegion('south');
53995     this.regions.listView = inner.getRegion('center');
53996 };
53997
53998 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
53999  * Based on:
54000  * Ext JS Library 1.1.1
54001  * Copyright(c) 2006-2007, Ext JS, LLC.
54002  *
54003  * Originally Released Under LGPL - original licence link has changed is not relivant.
54004  *
54005  * Fork - LGPL
54006  * <script type="text/javascript">
54007  */
54008  
54009 /**
54010  * @class Roo.grid.Grid
54011  * @extends Roo.util.Observable
54012  * This class represents the primary interface of a component based grid control.
54013  * <br><br>Usage:<pre><code>
54014  var grid = new Roo.grid.Grid("my-container-id", {
54015      ds: myDataStore,
54016      cm: myColModel,
54017      selModel: mySelectionModel,
54018      autoSizeColumns: true,
54019      monitorWindowResize: false,
54020      trackMouseOver: true
54021  });
54022  // set any options
54023  grid.render();
54024  * </code></pre>
54025  * <b>Common Problems:</b><br/>
54026  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
54027  * element will correct this<br/>
54028  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
54029  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
54030  * are unpredictable.<br/>
54031  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
54032  * grid to calculate dimensions/offsets.<br/>
54033   * @constructor
54034  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
54035  * The container MUST have some type of size defined for the grid to fill. The container will be
54036  * automatically set to position relative if it isn't already.
54037  * @param {Object} config A config object that sets properties on this grid.
54038  */
54039 Roo.grid.Grid = function(container, config){
54040         // initialize the container
54041         this.container = Roo.get(container);
54042         this.container.update("");
54043         this.container.setStyle("overflow", "hidden");
54044     this.container.addClass('x-grid-container');
54045
54046     this.id = this.container.id;
54047
54048     Roo.apply(this, config);
54049     // check and correct shorthanded configs
54050     if(this.ds){
54051         this.dataSource = this.ds;
54052         delete this.ds;
54053     }
54054     if(this.cm){
54055         this.colModel = this.cm;
54056         delete this.cm;
54057     }
54058     if(this.sm){
54059         this.selModel = this.sm;
54060         delete this.sm;
54061     }
54062
54063     if (this.selModel) {
54064         this.selModel = Roo.factory(this.selModel, Roo.grid);
54065         this.sm = this.selModel;
54066         this.sm.xmodule = this.xmodule || false;
54067     }
54068     if (typeof(this.colModel.config) == 'undefined') {
54069         this.colModel = new Roo.grid.ColumnModel(this.colModel);
54070         this.cm = this.colModel;
54071         this.cm.xmodule = this.xmodule || false;
54072     }
54073     if (this.dataSource) {
54074         this.dataSource= Roo.factory(this.dataSource, Roo.data);
54075         this.ds = this.dataSource;
54076         this.ds.xmodule = this.xmodule || false;
54077          
54078     }
54079     
54080     
54081     
54082     if(this.width){
54083         this.container.setWidth(this.width);
54084     }
54085
54086     if(this.height){
54087         this.container.setHeight(this.height);
54088     }
54089     /** @private */
54090         this.addEvents({
54091         // raw events
54092         /**
54093          * @event click
54094          * The raw click event for the entire grid.
54095          * @param {Roo.EventObject} e
54096          */
54097         "click" : true,
54098         /**
54099          * @event dblclick
54100          * The raw dblclick event for the entire grid.
54101          * @param {Roo.EventObject} e
54102          */
54103         "dblclick" : true,
54104         /**
54105          * @event contextmenu
54106          * The raw contextmenu event for the entire grid.
54107          * @param {Roo.EventObject} e
54108          */
54109         "contextmenu" : true,
54110         /**
54111          * @event mousedown
54112          * The raw mousedown event for the entire grid.
54113          * @param {Roo.EventObject} e
54114          */
54115         "mousedown" : true,
54116         /**
54117          * @event mouseup
54118          * The raw mouseup event for the entire grid.
54119          * @param {Roo.EventObject} e
54120          */
54121         "mouseup" : true,
54122         /**
54123          * @event mouseover
54124          * The raw mouseover event for the entire grid.
54125          * @param {Roo.EventObject} e
54126          */
54127         "mouseover" : true,
54128         /**
54129          * @event mouseout
54130          * The raw mouseout event for the entire grid.
54131          * @param {Roo.EventObject} e
54132          */
54133         "mouseout" : true,
54134         /**
54135          * @event keypress
54136          * The raw keypress event for the entire grid.
54137          * @param {Roo.EventObject} e
54138          */
54139         "keypress" : true,
54140         /**
54141          * @event keydown
54142          * The raw keydown event for the entire grid.
54143          * @param {Roo.EventObject} e
54144          */
54145         "keydown" : true,
54146
54147         // custom events
54148
54149         /**
54150          * @event cellclick
54151          * Fires when a cell is clicked
54152          * @param {Grid} this
54153          * @param {Number} rowIndex
54154          * @param {Number} columnIndex
54155          * @param {Roo.EventObject} e
54156          */
54157         "cellclick" : true,
54158         /**
54159          * @event celldblclick
54160          * Fires when a cell is double clicked
54161          * @param {Grid} this
54162          * @param {Number} rowIndex
54163          * @param {Number} columnIndex
54164          * @param {Roo.EventObject} e
54165          */
54166         "celldblclick" : true,
54167         /**
54168          * @event rowclick
54169          * Fires when a row is clicked
54170          * @param {Grid} this
54171          * @param {Number} rowIndex
54172          * @param {Roo.EventObject} e
54173          */
54174         "rowclick" : true,
54175         /**
54176          * @event rowdblclick
54177          * Fires when a row is double clicked
54178          * @param {Grid} this
54179          * @param {Number} rowIndex
54180          * @param {Roo.EventObject} e
54181          */
54182         "rowdblclick" : true,
54183         /**
54184          * @event headerclick
54185          * Fires when a header is clicked
54186          * @param {Grid} this
54187          * @param {Number} columnIndex
54188          * @param {Roo.EventObject} e
54189          */
54190         "headerclick" : true,
54191         /**
54192          * @event headerdblclick
54193          * Fires when a header cell is double clicked
54194          * @param {Grid} this
54195          * @param {Number} columnIndex
54196          * @param {Roo.EventObject} e
54197          */
54198         "headerdblclick" : true,
54199         /**
54200          * @event rowcontextmenu
54201          * Fires when a row is right clicked
54202          * @param {Grid} this
54203          * @param {Number} rowIndex
54204          * @param {Roo.EventObject} e
54205          */
54206         "rowcontextmenu" : true,
54207         /**
54208          * @event cellcontextmenu
54209          * Fires when a cell is right clicked
54210          * @param {Grid} this
54211          * @param {Number} rowIndex
54212          * @param {Number} cellIndex
54213          * @param {Roo.EventObject} e
54214          */
54215          "cellcontextmenu" : true,
54216         /**
54217          * @event headercontextmenu
54218          * Fires when a header is right clicked
54219          * @param {Grid} this
54220          * @param {Number} columnIndex
54221          * @param {Roo.EventObject} e
54222          */
54223         "headercontextmenu" : true,
54224         /**
54225          * @event bodyscroll
54226          * Fires when the body element is scrolled
54227          * @param {Number} scrollLeft
54228          * @param {Number} scrollTop
54229          */
54230         "bodyscroll" : true,
54231         /**
54232          * @event columnresize
54233          * Fires when the user resizes a column
54234          * @param {Number} columnIndex
54235          * @param {Number} newSize
54236          */
54237         "columnresize" : true,
54238         /**
54239          * @event columnmove
54240          * Fires when the user moves a column
54241          * @param {Number} oldIndex
54242          * @param {Number} newIndex
54243          */
54244         "columnmove" : true,
54245         /**
54246          * @event startdrag
54247          * Fires when row(s) start being dragged
54248          * @param {Grid} this
54249          * @param {Roo.GridDD} dd The drag drop object
54250          * @param {event} e The raw browser event
54251          */
54252         "startdrag" : true,
54253         /**
54254          * @event enddrag
54255          * Fires when a drag operation is complete
54256          * @param {Grid} this
54257          * @param {Roo.GridDD} dd The drag drop object
54258          * @param {event} e The raw browser event
54259          */
54260         "enddrag" : true,
54261         /**
54262          * @event dragdrop
54263          * Fires when dragged row(s) are dropped on a valid DD target
54264          * @param {Grid} this
54265          * @param {Roo.GridDD} dd The drag drop object
54266          * @param {String} targetId The target drag drop object
54267          * @param {event} e The raw browser event
54268          */
54269         "dragdrop" : true,
54270         /**
54271          * @event dragover
54272          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
54273          * @param {Grid} this
54274          * @param {Roo.GridDD} dd The drag drop object
54275          * @param {String} targetId The target drag drop object
54276          * @param {event} e The raw browser event
54277          */
54278         "dragover" : true,
54279         /**
54280          * @event dragenter
54281          *  Fires when the dragged row(s) first cross another DD target while being dragged
54282          * @param {Grid} this
54283          * @param {Roo.GridDD} dd The drag drop object
54284          * @param {String} targetId The target drag drop object
54285          * @param {event} e The raw browser event
54286          */
54287         "dragenter" : true,
54288         /**
54289          * @event dragout
54290          * Fires when the dragged row(s) leave another DD target while being dragged
54291          * @param {Grid} this
54292          * @param {Roo.GridDD} dd The drag drop object
54293          * @param {String} targetId The target drag drop object
54294          * @param {event} e The raw browser event
54295          */
54296         "dragout" : true,
54297         /**
54298          * @event rowclass
54299          * Fires when a row is rendered, so you can change add a style to it.
54300          * @param {GridView} gridview   The grid view
54301          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
54302          */
54303         'rowclass' : true,
54304
54305         /**
54306          * @event render
54307          * Fires when the grid is rendered
54308          * @param {Grid} grid
54309          */
54310         'render' : true
54311     });
54312
54313     Roo.grid.Grid.superclass.constructor.call(this);
54314 };
54315 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
54316     
54317     /**
54318      * @cfg {String} ddGroup - drag drop group.
54319      */
54320
54321     /**
54322      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
54323      */
54324     minColumnWidth : 25,
54325
54326     /**
54327      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
54328      * <b>on initial render.</b> It is more efficient to explicitly size the columns
54329      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
54330      */
54331     autoSizeColumns : false,
54332
54333     /**
54334      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
54335      */
54336     autoSizeHeaders : true,
54337
54338     /**
54339      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
54340      */
54341     monitorWindowResize : true,
54342
54343     /**
54344      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
54345      * rows measured to get a columns size. Default is 0 (all rows).
54346      */
54347     maxRowsToMeasure : 0,
54348
54349     /**
54350      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
54351      */
54352     trackMouseOver : true,
54353
54354     /**
54355     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
54356     */
54357     
54358     /**
54359     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
54360     */
54361     enableDragDrop : false,
54362     
54363     /**
54364     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
54365     */
54366     enableColumnMove : true,
54367     
54368     /**
54369     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
54370     */
54371     enableColumnHide : true,
54372     
54373     /**
54374     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
54375     */
54376     enableRowHeightSync : false,
54377     
54378     /**
54379     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
54380     */
54381     stripeRows : true,
54382     
54383     /**
54384     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
54385     */
54386     autoHeight : false,
54387
54388     /**
54389      * @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.
54390      */
54391     autoExpandColumn : false,
54392
54393     /**
54394     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
54395     * Default is 50.
54396     */
54397     autoExpandMin : 50,
54398
54399     /**
54400     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
54401     */
54402     autoExpandMax : 1000,
54403
54404     /**
54405     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
54406     */
54407     view : null,
54408
54409     /**
54410     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
54411     */
54412     loadMask : false,
54413     /**
54414     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
54415     */
54416     dropTarget: false,
54417     
54418    
54419     
54420     // private
54421     rendered : false,
54422
54423     /**
54424     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
54425     * of a fixed width. Default is false.
54426     */
54427     /**
54428     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
54429     */
54430     /**
54431      * Called once after all setup has been completed and the grid is ready to be rendered.
54432      * @return {Roo.grid.Grid} this
54433      */
54434     render : function()
54435     {
54436         var c = this.container;
54437         // try to detect autoHeight/width mode
54438         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
54439             this.autoHeight = true;
54440         }
54441         var view = this.getView();
54442         view.init(this);
54443
54444         c.on("click", this.onClick, this);
54445         c.on("dblclick", this.onDblClick, this);
54446         c.on("contextmenu", this.onContextMenu, this);
54447         c.on("keydown", this.onKeyDown, this);
54448         if (Roo.isTouch) {
54449             c.on("touchstart", this.onTouchStart, this);
54450         }
54451
54452         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
54453
54454         this.getSelectionModel().init(this);
54455
54456         view.render();
54457
54458         if(this.loadMask){
54459             this.loadMask = new Roo.LoadMask(this.container,
54460                     Roo.apply({store:this.dataSource}, this.loadMask));
54461         }
54462         
54463         
54464         if (this.toolbar && this.toolbar.xtype) {
54465             this.toolbar.container = this.getView().getHeaderPanel(true);
54466             this.toolbar = new Roo.Toolbar(this.toolbar);
54467         }
54468         if (this.footer && this.footer.xtype) {
54469             this.footer.dataSource = this.getDataSource();
54470             this.footer.container = this.getView().getFooterPanel(true);
54471             this.footer = Roo.factory(this.footer, Roo);
54472         }
54473         if (this.dropTarget && this.dropTarget.xtype) {
54474             delete this.dropTarget.xtype;
54475             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
54476         }
54477         
54478         
54479         this.rendered = true;
54480         this.fireEvent('render', this);
54481         return this;
54482     },
54483
54484         /**
54485          * Reconfigures the grid to use a different Store and Column Model.
54486          * The View will be bound to the new objects and refreshed.
54487          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
54488          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
54489          */
54490     reconfigure : function(dataSource, colModel){
54491         if(this.loadMask){
54492             this.loadMask.destroy();
54493             this.loadMask = new Roo.LoadMask(this.container,
54494                     Roo.apply({store:dataSource}, this.loadMask));
54495         }
54496         this.view.bind(dataSource, colModel);
54497         this.dataSource = dataSource;
54498         this.colModel = colModel;
54499         this.view.refresh(true);
54500     },
54501
54502     // private
54503     onKeyDown : function(e){
54504         this.fireEvent("keydown", e);
54505     },
54506
54507     /**
54508      * Destroy this grid.
54509      * @param {Boolean} removeEl True to remove the element
54510      */
54511     destroy : function(removeEl, keepListeners){
54512         if(this.loadMask){
54513             this.loadMask.destroy();
54514         }
54515         var c = this.container;
54516         c.removeAllListeners();
54517         this.view.destroy();
54518         this.colModel.purgeListeners();
54519         if(!keepListeners){
54520             this.purgeListeners();
54521         }
54522         c.update("");
54523         if(removeEl === true){
54524             c.remove();
54525         }
54526     },
54527
54528     // private
54529     processEvent : function(name, e){
54530         // does this fire select???
54531         //Roo.log('grid:processEvent '  + name);
54532         
54533         if (name != 'touchstart' ) {
54534             this.fireEvent(name, e);    
54535         }
54536         
54537         var t = e.getTarget();
54538         var v = this.view;
54539         var header = v.findHeaderIndex(t);
54540         if(header !== false){
54541             var ename = name == 'touchstart' ? 'click' : name;
54542              
54543             this.fireEvent("header" + ename, this, header, e);
54544         }else{
54545             var row = v.findRowIndex(t);
54546             var cell = v.findCellIndex(t);
54547             if (name == 'touchstart') {
54548                 // first touch is always a click.
54549                 // hopefull this happens after selection is updated.?
54550                 name = false;
54551                 
54552                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
54553                     var cs = this.selModel.getSelectedCell();
54554                     if (row == cs[0] && cell == cs[1]){
54555                         name = 'dblclick';
54556                     }
54557                 }
54558                 if (typeof(this.selModel.getSelections) != 'undefined') {
54559                     var cs = this.selModel.getSelections();
54560                     var ds = this.dataSource;
54561                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
54562                         name = 'dblclick';
54563                     }
54564                 }
54565                 if (!name) {
54566                     return;
54567                 }
54568             }
54569             
54570             
54571             if(row !== false){
54572                 this.fireEvent("row" + name, this, row, e);
54573                 if(cell !== false){
54574                     this.fireEvent("cell" + name, this, row, cell, e);
54575                 }
54576             }
54577         }
54578     },
54579
54580     // private
54581     onClick : function(e){
54582         this.processEvent("click", e);
54583     },
54584    // private
54585     onTouchStart : function(e){
54586         this.processEvent("touchstart", e);
54587     },
54588
54589     // private
54590     onContextMenu : function(e, t){
54591         this.processEvent("contextmenu", e);
54592     },
54593
54594     // private
54595     onDblClick : function(e){
54596         this.processEvent("dblclick", e);
54597     },
54598
54599     // private
54600     walkCells : function(row, col, step, fn, scope){
54601         var cm = this.colModel, clen = cm.getColumnCount();
54602         var ds = this.dataSource, rlen = ds.getCount(), first = true;
54603         if(step < 0){
54604             if(col < 0){
54605                 row--;
54606                 first = false;
54607             }
54608             while(row >= 0){
54609                 if(!first){
54610                     col = clen-1;
54611                 }
54612                 first = false;
54613                 while(col >= 0){
54614                     if(fn.call(scope || this, row, col, cm) === true){
54615                         return [row, col];
54616                     }
54617                     col--;
54618                 }
54619                 row--;
54620             }
54621         } else {
54622             if(col >= clen){
54623                 row++;
54624                 first = false;
54625             }
54626             while(row < rlen){
54627                 if(!first){
54628                     col = 0;
54629                 }
54630                 first = false;
54631                 while(col < clen){
54632                     if(fn.call(scope || this, row, col, cm) === true){
54633                         return [row, col];
54634                     }
54635                     col++;
54636                 }
54637                 row++;
54638             }
54639         }
54640         return null;
54641     },
54642
54643     // private
54644     getSelections : function(){
54645         return this.selModel.getSelections();
54646     },
54647
54648     /**
54649      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
54650      * but if manual update is required this method will initiate it.
54651      */
54652     autoSize : function(){
54653         if(this.rendered){
54654             this.view.layout();
54655             if(this.view.adjustForScroll){
54656                 this.view.adjustForScroll();
54657             }
54658         }
54659     },
54660
54661     /**
54662      * Returns the grid's underlying element.
54663      * @return {Element} The element
54664      */
54665     getGridEl : function(){
54666         return this.container;
54667     },
54668
54669     // private for compatibility, overridden by editor grid
54670     stopEditing : function(){},
54671
54672     /**
54673      * Returns the grid's SelectionModel.
54674      * @return {SelectionModel}
54675      */
54676     getSelectionModel : function(){
54677         if(!this.selModel){
54678             this.selModel = new Roo.grid.RowSelectionModel();
54679         }
54680         return this.selModel;
54681     },
54682
54683     /**
54684      * Returns the grid's DataSource.
54685      * @return {DataSource}
54686      */
54687     getDataSource : function(){
54688         return this.dataSource;
54689     },
54690
54691     /**
54692      * Returns the grid's ColumnModel.
54693      * @return {ColumnModel}
54694      */
54695     getColumnModel : function(){
54696         return this.colModel;
54697     },
54698
54699     /**
54700      * Returns the grid's GridView object.
54701      * @return {GridView}
54702      */
54703     getView : function(){
54704         if(!this.view){
54705             this.view = new Roo.grid.GridView(this.viewConfig);
54706         }
54707         return this.view;
54708     },
54709     /**
54710      * Called to get grid's drag proxy text, by default returns this.ddText.
54711      * @return {String}
54712      */
54713     getDragDropText : function(){
54714         var count = this.selModel.getCount();
54715         return String.format(this.ddText, count, count == 1 ? '' : 's');
54716     }
54717 });
54718 /**
54719  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
54720  * %0 is replaced with the number of selected rows.
54721  * @type String
54722  */
54723 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
54724  * Based on:
54725  * Ext JS Library 1.1.1
54726  * Copyright(c) 2006-2007, Ext JS, LLC.
54727  *
54728  * Originally Released Under LGPL - original licence link has changed is not relivant.
54729  *
54730  * Fork - LGPL
54731  * <script type="text/javascript">
54732  */
54733  
54734 Roo.grid.AbstractGridView = function(){
54735         this.grid = null;
54736         
54737         this.events = {
54738             "beforerowremoved" : true,
54739             "beforerowsinserted" : true,
54740             "beforerefresh" : true,
54741             "rowremoved" : true,
54742             "rowsinserted" : true,
54743             "rowupdated" : true,
54744             "refresh" : true
54745         };
54746     Roo.grid.AbstractGridView.superclass.constructor.call(this);
54747 };
54748
54749 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
54750     rowClass : "x-grid-row",
54751     cellClass : "x-grid-cell",
54752     tdClass : "x-grid-td",
54753     hdClass : "x-grid-hd",
54754     splitClass : "x-grid-hd-split",
54755     
54756     init: function(grid){
54757         this.grid = grid;
54758                 var cid = this.grid.getGridEl().id;
54759         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
54760         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
54761         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
54762         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
54763         },
54764         
54765     getColumnRenderers : function(){
54766         var renderers = [];
54767         var cm = this.grid.colModel;
54768         var colCount = cm.getColumnCount();
54769         for(var i = 0; i < colCount; i++){
54770             renderers[i] = cm.getRenderer(i);
54771         }
54772         return renderers;
54773     },
54774     
54775     getColumnIds : function(){
54776         var ids = [];
54777         var cm = this.grid.colModel;
54778         var colCount = cm.getColumnCount();
54779         for(var i = 0; i < colCount; i++){
54780             ids[i] = cm.getColumnId(i);
54781         }
54782         return ids;
54783     },
54784     
54785     getDataIndexes : function(){
54786         if(!this.indexMap){
54787             this.indexMap = this.buildIndexMap();
54788         }
54789         return this.indexMap.colToData;
54790     },
54791     
54792     getColumnIndexByDataIndex : function(dataIndex){
54793         if(!this.indexMap){
54794             this.indexMap = this.buildIndexMap();
54795         }
54796         return this.indexMap.dataToCol[dataIndex];
54797     },
54798     
54799     /**
54800      * Set a css style for a column dynamically. 
54801      * @param {Number} colIndex The index of the column
54802      * @param {String} name The css property name
54803      * @param {String} value The css value
54804      */
54805     setCSSStyle : function(colIndex, name, value){
54806         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
54807         Roo.util.CSS.updateRule(selector, name, value);
54808     },
54809     
54810     generateRules : function(cm){
54811         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
54812         Roo.util.CSS.removeStyleSheet(rulesId);
54813         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54814             var cid = cm.getColumnId(i);
54815             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
54816                          this.tdSelector, cid, " {\n}\n",
54817                          this.hdSelector, cid, " {\n}\n",
54818                          this.splitSelector, cid, " {\n}\n");
54819         }
54820         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54821     }
54822 });/*
54823  * Based on:
54824  * Ext JS Library 1.1.1
54825  * Copyright(c) 2006-2007, Ext JS, LLC.
54826  *
54827  * Originally Released Under LGPL - original licence link has changed is not relivant.
54828  *
54829  * Fork - LGPL
54830  * <script type="text/javascript">
54831  */
54832
54833 // private
54834 // This is a support class used internally by the Grid components
54835 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
54836     this.grid = grid;
54837     this.view = grid.getView();
54838     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54839     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
54840     if(hd2){
54841         this.setHandleElId(Roo.id(hd));
54842         this.setOuterHandleElId(Roo.id(hd2));
54843     }
54844     this.scroll = false;
54845 };
54846 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
54847     maxDragWidth: 120,
54848     getDragData : function(e){
54849         var t = Roo.lib.Event.getTarget(e);
54850         var h = this.view.findHeaderCell(t);
54851         if(h){
54852             return {ddel: h.firstChild, header:h};
54853         }
54854         return false;
54855     },
54856
54857     onInitDrag : function(e){
54858         this.view.headersDisabled = true;
54859         var clone = this.dragData.ddel.cloneNode(true);
54860         clone.id = Roo.id();
54861         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
54862         this.proxy.update(clone);
54863         return true;
54864     },
54865
54866     afterValidDrop : function(){
54867         var v = this.view;
54868         setTimeout(function(){
54869             v.headersDisabled = false;
54870         }, 50);
54871     },
54872
54873     afterInvalidDrop : function(){
54874         var v = this.view;
54875         setTimeout(function(){
54876             v.headersDisabled = false;
54877         }, 50);
54878     }
54879 });
54880 /*
54881  * Based on:
54882  * Ext JS Library 1.1.1
54883  * Copyright(c) 2006-2007, Ext JS, LLC.
54884  *
54885  * Originally Released Under LGPL - original licence link has changed is not relivant.
54886  *
54887  * Fork - LGPL
54888  * <script type="text/javascript">
54889  */
54890 // private
54891 // This is a support class used internally by the Grid components
54892 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
54893     this.grid = grid;
54894     this.view = grid.getView();
54895     // split the proxies so they don't interfere with mouse events
54896     this.proxyTop = Roo.DomHelper.append(document.body, {
54897         cls:"col-move-top", html:"&#160;"
54898     }, true);
54899     this.proxyBottom = Roo.DomHelper.append(document.body, {
54900         cls:"col-move-bottom", html:"&#160;"
54901     }, true);
54902     this.proxyTop.hide = this.proxyBottom.hide = function(){
54903         this.setLeftTop(-100,-100);
54904         this.setStyle("visibility", "hidden");
54905     };
54906     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54907     // temporarily disabled
54908     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
54909     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
54910 };
54911 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
54912     proxyOffsets : [-4, -9],
54913     fly: Roo.Element.fly,
54914
54915     getTargetFromEvent : function(e){
54916         var t = Roo.lib.Event.getTarget(e);
54917         var cindex = this.view.findCellIndex(t);
54918         if(cindex !== false){
54919             return this.view.getHeaderCell(cindex);
54920         }
54921         return null;
54922     },
54923
54924     nextVisible : function(h){
54925         var v = this.view, cm = this.grid.colModel;
54926         h = h.nextSibling;
54927         while(h){
54928             if(!cm.isHidden(v.getCellIndex(h))){
54929                 return h;
54930             }
54931             h = h.nextSibling;
54932         }
54933         return null;
54934     },
54935
54936     prevVisible : function(h){
54937         var v = this.view, cm = this.grid.colModel;
54938         h = h.prevSibling;
54939         while(h){
54940             if(!cm.isHidden(v.getCellIndex(h))){
54941                 return h;
54942             }
54943             h = h.prevSibling;
54944         }
54945         return null;
54946     },
54947
54948     positionIndicator : function(h, n, e){
54949         var x = Roo.lib.Event.getPageX(e);
54950         var r = Roo.lib.Dom.getRegion(n.firstChild);
54951         var px, pt, py = r.top + this.proxyOffsets[1];
54952         if((r.right - x) <= (r.right-r.left)/2){
54953             px = r.right+this.view.borderWidth;
54954             pt = "after";
54955         }else{
54956             px = r.left;
54957             pt = "before";
54958         }
54959         var oldIndex = this.view.getCellIndex(h);
54960         var newIndex = this.view.getCellIndex(n);
54961
54962         if(this.grid.colModel.isFixed(newIndex)){
54963             return false;
54964         }
54965
54966         var locked = this.grid.colModel.isLocked(newIndex);
54967
54968         if(pt == "after"){
54969             newIndex++;
54970         }
54971         if(oldIndex < newIndex){
54972             newIndex--;
54973         }
54974         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
54975             return false;
54976         }
54977         px +=  this.proxyOffsets[0];
54978         this.proxyTop.setLeftTop(px, py);
54979         this.proxyTop.show();
54980         if(!this.bottomOffset){
54981             this.bottomOffset = this.view.mainHd.getHeight();
54982         }
54983         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
54984         this.proxyBottom.show();
54985         return pt;
54986     },
54987
54988     onNodeEnter : function(n, dd, e, data){
54989         if(data.header != n){
54990             this.positionIndicator(data.header, n, e);
54991         }
54992     },
54993
54994     onNodeOver : function(n, dd, e, data){
54995         var result = false;
54996         if(data.header != n){
54997             result = this.positionIndicator(data.header, n, e);
54998         }
54999         if(!result){
55000             this.proxyTop.hide();
55001             this.proxyBottom.hide();
55002         }
55003         return result ? this.dropAllowed : this.dropNotAllowed;
55004     },
55005
55006     onNodeOut : function(n, dd, e, data){
55007         this.proxyTop.hide();
55008         this.proxyBottom.hide();
55009     },
55010
55011     onNodeDrop : function(n, dd, e, data){
55012         var h = data.header;
55013         if(h != n){
55014             var cm = this.grid.colModel;
55015             var x = Roo.lib.Event.getPageX(e);
55016             var r = Roo.lib.Dom.getRegion(n.firstChild);
55017             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
55018             var oldIndex = this.view.getCellIndex(h);
55019             var newIndex = this.view.getCellIndex(n);
55020             var locked = cm.isLocked(newIndex);
55021             if(pt == "after"){
55022                 newIndex++;
55023             }
55024             if(oldIndex < newIndex){
55025                 newIndex--;
55026             }
55027             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
55028                 return false;
55029             }
55030             cm.setLocked(oldIndex, locked, true);
55031             cm.moveColumn(oldIndex, newIndex);
55032             this.grid.fireEvent("columnmove", oldIndex, newIndex);
55033             return true;
55034         }
55035         return false;
55036     }
55037 });
55038 /*
55039  * Based on:
55040  * Ext JS Library 1.1.1
55041  * Copyright(c) 2006-2007, Ext JS, LLC.
55042  *
55043  * Originally Released Under LGPL - original licence link has changed is not relivant.
55044  *
55045  * Fork - LGPL
55046  * <script type="text/javascript">
55047  */
55048   
55049 /**
55050  * @class Roo.grid.GridView
55051  * @extends Roo.util.Observable
55052  *
55053  * @constructor
55054  * @param {Object} config
55055  */
55056 Roo.grid.GridView = function(config){
55057     Roo.grid.GridView.superclass.constructor.call(this);
55058     this.el = null;
55059
55060     Roo.apply(this, config);
55061 };
55062
55063 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
55064
55065     unselectable :  'unselectable="on"',
55066     unselectableCls :  'x-unselectable',
55067     
55068     
55069     rowClass : "x-grid-row",
55070
55071     cellClass : "x-grid-col",
55072
55073     tdClass : "x-grid-td",
55074
55075     hdClass : "x-grid-hd",
55076
55077     splitClass : "x-grid-split",
55078
55079     sortClasses : ["sort-asc", "sort-desc"],
55080
55081     enableMoveAnim : false,
55082
55083     hlColor: "C3DAF9",
55084
55085     dh : Roo.DomHelper,
55086
55087     fly : Roo.Element.fly,
55088
55089     css : Roo.util.CSS,
55090
55091     borderWidth: 1,
55092
55093     splitOffset: 3,
55094
55095     scrollIncrement : 22,
55096
55097     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
55098
55099     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
55100
55101     bind : function(ds, cm){
55102         if(this.ds){
55103             this.ds.un("load", this.onLoad, this);
55104             this.ds.un("datachanged", this.onDataChange, this);
55105             this.ds.un("add", this.onAdd, this);
55106             this.ds.un("remove", this.onRemove, this);
55107             this.ds.un("update", this.onUpdate, this);
55108             this.ds.un("clear", this.onClear, this);
55109         }
55110         if(ds){
55111             ds.on("load", this.onLoad, this);
55112             ds.on("datachanged", this.onDataChange, this);
55113             ds.on("add", this.onAdd, this);
55114             ds.on("remove", this.onRemove, this);
55115             ds.on("update", this.onUpdate, this);
55116             ds.on("clear", this.onClear, this);
55117         }
55118         this.ds = ds;
55119
55120         if(this.cm){
55121             this.cm.un("widthchange", this.onColWidthChange, this);
55122             this.cm.un("headerchange", this.onHeaderChange, this);
55123             this.cm.un("hiddenchange", this.onHiddenChange, this);
55124             this.cm.un("columnmoved", this.onColumnMove, this);
55125             this.cm.un("columnlockchange", this.onColumnLock, this);
55126         }
55127         if(cm){
55128             this.generateRules(cm);
55129             cm.on("widthchange", this.onColWidthChange, this);
55130             cm.on("headerchange", this.onHeaderChange, this);
55131             cm.on("hiddenchange", this.onHiddenChange, this);
55132             cm.on("columnmoved", this.onColumnMove, this);
55133             cm.on("columnlockchange", this.onColumnLock, this);
55134         }
55135         this.cm = cm;
55136     },
55137
55138     init: function(grid){
55139         Roo.grid.GridView.superclass.init.call(this, grid);
55140
55141         this.bind(grid.dataSource, grid.colModel);
55142
55143         grid.on("headerclick", this.handleHeaderClick, this);
55144
55145         if(grid.trackMouseOver){
55146             grid.on("mouseover", this.onRowOver, this);
55147             grid.on("mouseout", this.onRowOut, this);
55148         }
55149         grid.cancelTextSelection = function(){};
55150         this.gridId = grid.id;
55151
55152         var tpls = this.templates || {};
55153
55154         if(!tpls.master){
55155             tpls.master = new Roo.Template(
55156                '<div class="x-grid" hidefocus="true">',
55157                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
55158                   '<div class="x-grid-topbar"></div>',
55159                   '<div class="x-grid-scroller"><div></div></div>',
55160                   '<div class="x-grid-locked">',
55161                       '<div class="x-grid-header">{lockedHeader}</div>',
55162                       '<div class="x-grid-body">{lockedBody}</div>',
55163                   "</div>",
55164                   '<div class="x-grid-viewport">',
55165                       '<div class="x-grid-header">{header}</div>',
55166                       '<div class="x-grid-body">{body}</div>',
55167                   "</div>",
55168                   '<div class="x-grid-bottombar"></div>',
55169                  
55170                   '<div class="x-grid-resize-proxy">&#160;</div>',
55171                "</div>"
55172             );
55173             tpls.master.disableformats = true;
55174         }
55175
55176         if(!tpls.header){
55177             tpls.header = new Roo.Template(
55178                '<table border="0" cellspacing="0" cellpadding="0">',
55179                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
55180                "</table>{splits}"
55181             );
55182             tpls.header.disableformats = true;
55183         }
55184         tpls.header.compile();
55185
55186         if(!tpls.hcell){
55187             tpls.hcell = new Roo.Template(
55188                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
55189                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
55190                 "</div></td>"
55191              );
55192              tpls.hcell.disableFormats = true;
55193         }
55194         tpls.hcell.compile();
55195
55196         if(!tpls.hsplit){
55197             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
55198                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
55199             tpls.hsplit.disableFormats = true;
55200         }
55201         tpls.hsplit.compile();
55202
55203         if(!tpls.body){
55204             tpls.body = new Roo.Template(
55205                '<table border="0" cellspacing="0" cellpadding="0">',
55206                "<tbody>{rows}</tbody>",
55207                "</table>"
55208             );
55209             tpls.body.disableFormats = true;
55210         }
55211         tpls.body.compile();
55212
55213         if(!tpls.row){
55214             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
55215             tpls.row.disableFormats = true;
55216         }
55217         tpls.row.compile();
55218
55219         if(!tpls.cell){
55220             tpls.cell = new Roo.Template(
55221                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
55222                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
55223                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
55224                 "</td>"
55225             );
55226             tpls.cell.disableFormats = true;
55227         }
55228         tpls.cell.compile();
55229
55230         this.templates = tpls;
55231     },
55232
55233     // remap these for backwards compat
55234     onColWidthChange : function(){
55235         this.updateColumns.apply(this, arguments);
55236     },
55237     onHeaderChange : function(){
55238         this.updateHeaders.apply(this, arguments);
55239     }, 
55240     onHiddenChange : function(){
55241         this.handleHiddenChange.apply(this, arguments);
55242     },
55243     onColumnMove : function(){
55244         this.handleColumnMove.apply(this, arguments);
55245     },
55246     onColumnLock : function(){
55247         this.handleLockChange.apply(this, arguments);
55248     },
55249
55250     onDataChange : function(){
55251         this.refresh();
55252         this.updateHeaderSortState();
55253     },
55254
55255     onClear : function(){
55256         this.refresh();
55257     },
55258
55259     onUpdate : function(ds, record){
55260         this.refreshRow(record);
55261     },
55262
55263     refreshRow : function(record){
55264         var ds = this.ds, index;
55265         if(typeof record == 'number'){
55266             index = record;
55267             record = ds.getAt(index);
55268         }else{
55269             index = ds.indexOf(record);
55270         }
55271         this.insertRows(ds, index, index, true);
55272         this.onRemove(ds, record, index+1, true);
55273         this.syncRowHeights(index, index);
55274         this.layout();
55275         this.fireEvent("rowupdated", this, index, record);
55276     },
55277
55278     onAdd : function(ds, records, index){
55279         this.insertRows(ds, index, index + (records.length-1));
55280     },
55281
55282     onRemove : function(ds, record, index, isUpdate){
55283         if(isUpdate !== true){
55284             this.fireEvent("beforerowremoved", this, index, record);
55285         }
55286         var bt = this.getBodyTable(), lt = this.getLockedTable();
55287         if(bt.rows[index]){
55288             bt.firstChild.removeChild(bt.rows[index]);
55289         }
55290         if(lt.rows[index]){
55291             lt.firstChild.removeChild(lt.rows[index]);
55292         }
55293         if(isUpdate !== true){
55294             this.stripeRows(index);
55295             this.syncRowHeights(index, index);
55296             this.layout();
55297             this.fireEvent("rowremoved", this, index, record);
55298         }
55299     },
55300
55301     onLoad : function(){
55302         this.scrollToTop();
55303     },
55304
55305     /**
55306      * Scrolls the grid to the top
55307      */
55308     scrollToTop : function(){
55309         if(this.scroller){
55310             this.scroller.dom.scrollTop = 0;
55311             this.syncScroll();
55312         }
55313     },
55314
55315     /**
55316      * Gets a panel in the header of the grid that can be used for toolbars etc.
55317      * After modifying the contents of this panel a call to grid.autoSize() may be
55318      * required to register any changes in size.
55319      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
55320      * @return Roo.Element
55321      */
55322     getHeaderPanel : function(doShow){
55323         if(doShow){
55324             this.headerPanel.show();
55325         }
55326         return this.headerPanel;
55327     },
55328
55329     /**
55330      * Gets a panel in the footer of the grid that can be used for toolbars etc.
55331      * After modifying the contents of this panel a call to grid.autoSize() may be
55332      * required to register any changes in size.
55333      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
55334      * @return Roo.Element
55335      */
55336     getFooterPanel : function(doShow){
55337         if(doShow){
55338             this.footerPanel.show();
55339         }
55340         return this.footerPanel;
55341     },
55342
55343     initElements : function(){
55344         var E = Roo.Element;
55345         var el = this.grid.getGridEl().dom.firstChild;
55346         var cs = el.childNodes;
55347
55348         this.el = new E(el);
55349         
55350          this.focusEl = new E(el.firstChild);
55351         this.focusEl.swallowEvent("click", true);
55352         
55353         this.headerPanel = new E(cs[1]);
55354         this.headerPanel.enableDisplayMode("block");
55355
55356         this.scroller = new E(cs[2]);
55357         this.scrollSizer = new E(this.scroller.dom.firstChild);
55358
55359         this.lockedWrap = new E(cs[3]);
55360         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
55361         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
55362
55363         this.mainWrap = new E(cs[4]);
55364         this.mainHd = new E(this.mainWrap.dom.firstChild);
55365         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
55366
55367         this.footerPanel = new E(cs[5]);
55368         this.footerPanel.enableDisplayMode("block");
55369
55370         this.resizeProxy = new E(cs[6]);
55371
55372         this.headerSelector = String.format(
55373            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
55374            this.lockedHd.id, this.mainHd.id
55375         );
55376
55377         this.splitterSelector = String.format(
55378            '#{0} div.x-grid-split, #{1} div.x-grid-split',
55379            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
55380         );
55381     },
55382     idToCssName : function(s)
55383     {
55384         return s.replace(/[^a-z0-9]+/ig, '-');
55385     },
55386
55387     getHeaderCell : function(index){
55388         return Roo.DomQuery.select(this.headerSelector)[index];
55389     },
55390
55391     getHeaderCellMeasure : function(index){
55392         return this.getHeaderCell(index).firstChild;
55393     },
55394
55395     getHeaderCellText : function(index){
55396         return this.getHeaderCell(index).firstChild.firstChild;
55397     },
55398
55399     getLockedTable : function(){
55400         return this.lockedBody.dom.firstChild;
55401     },
55402
55403     getBodyTable : function(){
55404         return this.mainBody.dom.firstChild;
55405     },
55406
55407     getLockedRow : function(index){
55408         return this.getLockedTable().rows[index];
55409     },
55410
55411     getRow : function(index){
55412         return this.getBodyTable().rows[index];
55413     },
55414
55415     getRowComposite : function(index){
55416         if(!this.rowEl){
55417             this.rowEl = new Roo.CompositeElementLite();
55418         }
55419         var els = [], lrow, mrow;
55420         if(lrow = this.getLockedRow(index)){
55421             els.push(lrow);
55422         }
55423         if(mrow = this.getRow(index)){
55424             els.push(mrow);
55425         }
55426         this.rowEl.elements = els;
55427         return this.rowEl;
55428     },
55429     /**
55430      * Gets the 'td' of the cell
55431      * 
55432      * @param {Integer} rowIndex row to select
55433      * @param {Integer} colIndex column to select
55434      * 
55435      * @return {Object} 
55436      */
55437     getCell : function(rowIndex, colIndex){
55438         var locked = this.cm.getLockedCount();
55439         var source;
55440         if(colIndex < locked){
55441             source = this.lockedBody.dom.firstChild;
55442         }else{
55443             source = this.mainBody.dom.firstChild;
55444             colIndex -= locked;
55445         }
55446         return source.rows[rowIndex].childNodes[colIndex];
55447     },
55448
55449     getCellText : function(rowIndex, colIndex){
55450         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
55451     },
55452
55453     getCellBox : function(cell){
55454         var b = this.fly(cell).getBox();
55455         if(Roo.isOpera){ // opera fails to report the Y
55456             b.y = cell.offsetTop + this.mainBody.getY();
55457         }
55458         return b;
55459     },
55460
55461     getCellIndex : function(cell){
55462         var id = String(cell.className).match(this.cellRE);
55463         if(id){
55464             return parseInt(id[1], 10);
55465         }
55466         return 0;
55467     },
55468
55469     findHeaderIndex : function(n){
55470         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55471         return r ? this.getCellIndex(r) : false;
55472     },
55473
55474     findHeaderCell : function(n){
55475         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55476         return r ? r : false;
55477     },
55478
55479     findRowIndex : function(n){
55480         if(!n){
55481             return false;
55482         }
55483         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
55484         return r ? r.rowIndex : false;
55485     },
55486
55487     findCellIndex : function(node){
55488         var stop = this.el.dom;
55489         while(node && node != stop){
55490             if(this.findRE.test(node.className)){
55491                 return this.getCellIndex(node);
55492             }
55493             node = node.parentNode;
55494         }
55495         return false;
55496     },
55497
55498     getColumnId : function(index){
55499         return this.cm.getColumnId(index);
55500     },
55501
55502     getSplitters : function()
55503     {
55504         if(this.splitterSelector){
55505            return Roo.DomQuery.select(this.splitterSelector);
55506         }else{
55507             return null;
55508       }
55509     },
55510
55511     getSplitter : function(index){
55512         return this.getSplitters()[index];
55513     },
55514
55515     onRowOver : function(e, t){
55516         var row;
55517         if((row = this.findRowIndex(t)) !== false){
55518             this.getRowComposite(row).addClass("x-grid-row-over");
55519         }
55520     },
55521
55522     onRowOut : function(e, t){
55523         var row;
55524         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
55525             this.getRowComposite(row).removeClass("x-grid-row-over");
55526         }
55527     },
55528
55529     renderHeaders : function(){
55530         var cm = this.cm;
55531         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
55532         var cb = [], lb = [], sb = [], lsb = [], p = {};
55533         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55534             p.cellId = "x-grid-hd-0-" + i;
55535             p.splitId = "x-grid-csplit-0-" + i;
55536             p.id = cm.getColumnId(i);
55537             p.value = cm.getColumnHeader(i) || "";
55538             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
55539             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
55540             if(!cm.isLocked(i)){
55541                 cb[cb.length] = ct.apply(p);
55542                 sb[sb.length] = st.apply(p);
55543             }else{
55544                 lb[lb.length] = ct.apply(p);
55545                 lsb[lsb.length] = st.apply(p);
55546             }
55547         }
55548         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
55549                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
55550     },
55551
55552     updateHeaders : function(){
55553         var html = this.renderHeaders();
55554         this.lockedHd.update(html[0]);
55555         this.mainHd.update(html[1]);
55556     },
55557
55558     /**
55559      * Focuses the specified row.
55560      * @param {Number} row The row index
55561      */
55562     focusRow : function(row)
55563     {
55564         //Roo.log('GridView.focusRow');
55565         var x = this.scroller.dom.scrollLeft;
55566         this.focusCell(row, 0, false);
55567         this.scroller.dom.scrollLeft = x;
55568     },
55569
55570     /**
55571      * Focuses the specified cell.
55572      * @param {Number} row The row index
55573      * @param {Number} col The column index
55574      * @param {Boolean} hscroll false to disable horizontal scrolling
55575      */
55576     focusCell : function(row, col, hscroll)
55577     {
55578         //Roo.log('GridView.focusCell');
55579         var el = this.ensureVisible(row, col, hscroll);
55580         this.focusEl.alignTo(el, "tl-tl");
55581         if(Roo.isGecko){
55582             this.focusEl.focus();
55583         }else{
55584             this.focusEl.focus.defer(1, this.focusEl);
55585         }
55586     },
55587
55588     /**
55589      * Scrolls the specified cell into view
55590      * @param {Number} row The row index
55591      * @param {Number} col The column index
55592      * @param {Boolean} hscroll false to disable horizontal scrolling
55593      */
55594     ensureVisible : function(row, col, hscroll)
55595     {
55596         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
55597         //return null; //disable for testing.
55598         if(typeof row != "number"){
55599             row = row.rowIndex;
55600         }
55601         if(row < 0 && row >= this.ds.getCount()){
55602             return  null;
55603         }
55604         col = (col !== undefined ? col : 0);
55605         var cm = this.grid.colModel;
55606         while(cm.isHidden(col)){
55607             col++;
55608         }
55609
55610         var el = this.getCell(row, col);
55611         if(!el){
55612             return null;
55613         }
55614         var c = this.scroller.dom;
55615
55616         var ctop = parseInt(el.offsetTop, 10);
55617         var cleft = parseInt(el.offsetLeft, 10);
55618         var cbot = ctop + el.offsetHeight;
55619         var cright = cleft + el.offsetWidth;
55620         
55621         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
55622         var stop = parseInt(c.scrollTop, 10);
55623         var sleft = parseInt(c.scrollLeft, 10);
55624         var sbot = stop + ch;
55625         var sright = sleft + c.clientWidth;
55626         /*
55627         Roo.log('GridView.ensureVisible:' +
55628                 ' ctop:' + ctop +
55629                 ' c.clientHeight:' + c.clientHeight +
55630                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
55631                 ' stop:' + stop +
55632                 ' cbot:' + cbot +
55633                 ' sbot:' + sbot +
55634                 ' ch:' + ch  
55635                 );
55636         */
55637         if(ctop < stop){
55638              c.scrollTop = ctop;
55639             //Roo.log("set scrolltop to ctop DISABLE?");
55640         }else if(cbot > sbot){
55641             //Roo.log("set scrolltop to cbot-ch");
55642             c.scrollTop = cbot-ch;
55643         }
55644         
55645         if(hscroll !== false){
55646             if(cleft < sleft){
55647                 c.scrollLeft = cleft;
55648             }else if(cright > sright){
55649                 c.scrollLeft = cright-c.clientWidth;
55650             }
55651         }
55652          
55653         return el;
55654     },
55655
55656     updateColumns : function(){
55657         this.grid.stopEditing();
55658         var cm = this.grid.colModel, colIds = this.getColumnIds();
55659         //var totalWidth = cm.getTotalWidth();
55660         var pos = 0;
55661         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55662             //if(cm.isHidden(i)) continue;
55663             var w = cm.getColumnWidth(i);
55664             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55665             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55666         }
55667         this.updateSplitters();
55668     },
55669
55670     generateRules : function(cm){
55671         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
55672         Roo.util.CSS.removeStyleSheet(rulesId);
55673         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55674             var cid = cm.getColumnId(i);
55675             var align = '';
55676             if(cm.config[i].align){
55677                 align = 'text-align:'+cm.config[i].align+';';
55678             }
55679             var hidden = '';
55680             if(cm.isHidden(i)){
55681                 hidden = 'display:none;';
55682             }
55683             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
55684             ruleBuf.push(
55685                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
55686                     this.hdSelector, cid, " {\n", align, width, "}\n",
55687                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
55688                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
55689         }
55690         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55691     },
55692
55693     updateSplitters : function(){
55694         var cm = this.cm, s = this.getSplitters();
55695         if(s){ // splitters not created yet
55696             var pos = 0, locked = true;
55697             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55698                 if(cm.isHidden(i)) {
55699                     continue;
55700                 }
55701                 var w = cm.getColumnWidth(i); // make sure it's a number
55702                 if(!cm.isLocked(i) && locked){
55703                     pos = 0;
55704                     locked = false;
55705                 }
55706                 pos += w;
55707                 s[i].style.left = (pos-this.splitOffset) + "px";
55708             }
55709         }
55710     },
55711
55712     handleHiddenChange : function(colModel, colIndex, hidden){
55713         if(hidden){
55714             this.hideColumn(colIndex);
55715         }else{
55716             this.unhideColumn(colIndex);
55717         }
55718     },
55719
55720     hideColumn : function(colIndex){
55721         var cid = this.getColumnId(colIndex);
55722         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
55723         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
55724         if(Roo.isSafari){
55725             this.updateHeaders();
55726         }
55727         this.updateSplitters();
55728         this.layout();
55729     },
55730
55731     unhideColumn : function(colIndex){
55732         var cid = this.getColumnId(colIndex);
55733         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
55734         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
55735
55736         if(Roo.isSafari){
55737             this.updateHeaders();
55738         }
55739         this.updateSplitters();
55740         this.layout();
55741     },
55742
55743     insertRows : function(dm, firstRow, lastRow, isUpdate){
55744         if(firstRow == 0 && lastRow == dm.getCount()-1){
55745             this.refresh();
55746         }else{
55747             if(!isUpdate){
55748                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
55749             }
55750             var s = this.getScrollState();
55751             var markup = this.renderRows(firstRow, lastRow);
55752             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
55753             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
55754             this.restoreScroll(s);
55755             if(!isUpdate){
55756                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
55757                 this.syncRowHeights(firstRow, lastRow);
55758                 this.stripeRows(firstRow);
55759                 this.layout();
55760             }
55761         }
55762     },
55763
55764     bufferRows : function(markup, target, index){
55765         var before = null, trows = target.rows, tbody = target.tBodies[0];
55766         if(index < trows.length){
55767             before = trows[index];
55768         }
55769         var b = document.createElement("div");
55770         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
55771         var rows = b.firstChild.rows;
55772         for(var i = 0, len = rows.length; i < len; i++){
55773             if(before){
55774                 tbody.insertBefore(rows[0], before);
55775             }else{
55776                 tbody.appendChild(rows[0]);
55777             }
55778         }
55779         b.innerHTML = "";
55780         b = null;
55781     },
55782
55783     deleteRows : function(dm, firstRow, lastRow){
55784         if(dm.getRowCount()<1){
55785             this.fireEvent("beforerefresh", this);
55786             this.mainBody.update("");
55787             this.lockedBody.update("");
55788             this.fireEvent("refresh", this);
55789         }else{
55790             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
55791             var bt = this.getBodyTable();
55792             var tbody = bt.firstChild;
55793             var rows = bt.rows;
55794             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
55795                 tbody.removeChild(rows[firstRow]);
55796             }
55797             this.stripeRows(firstRow);
55798             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
55799         }
55800     },
55801
55802     updateRows : function(dataSource, firstRow, lastRow){
55803         var s = this.getScrollState();
55804         this.refresh();
55805         this.restoreScroll(s);
55806     },
55807
55808     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
55809         if(!noRefresh){
55810            this.refresh();
55811         }
55812         this.updateHeaderSortState();
55813     },
55814
55815     getScrollState : function(){
55816         
55817         var sb = this.scroller.dom;
55818         return {left: sb.scrollLeft, top: sb.scrollTop};
55819     },
55820
55821     stripeRows : function(startRow){
55822         if(!this.grid.stripeRows || this.ds.getCount() < 1){
55823             return;
55824         }
55825         startRow = startRow || 0;
55826         var rows = this.getBodyTable().rows;
55827         var lrows = this.getLockedTable().rows;
55828         var cls = ' x-grid-row-alt ';
55829         for(var i = startRow, len = rows.length; i < len; i++){
55830             var row = rows[i], lrow = lrows[i];
55831             var isAlt = ((i+1) % 2 == 0);
55832             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
55833             if(isAlt == hasAlt){
55834                 continue;
55835             }
55836             if(isAlt){
55837                 row.className += " x-grid-row-alt";
55838             }else{
55839                 row.className = row.className.replace("x-grid-row-alt", "");
55840             }
55841             if(lrow){
55842                 lrow.className = row.className;
55843             }
55844         }
55845     },
55846
55847     restoreScroll : function(state){
55848         //Roo.log('GridView.restoreScroll');
55849         var sb = this.scroller.dom;
55850         sb.scrollLeft = state.left;
55851         sb.scrollTop = state.top;
55852         this.syncScroll();
55853     },
55854
55855     syncScroll : function(){
55856         //Roo.log('GridView.syncScroll');
55857         var sb = this.scroller.dom;
55858         var sh = this.mainHd.dom;
55859         var bs = this.mainBody.dom;
55860         var lv = this.lockedBody.dom;
55861         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
55862         lv.scrollTop = bs.scrollTop = sb.scrollTop;
55863     },
55864
55865     handleScroll : function(e){
55866         this.syncScroll();
55867         var sb = this.scroller.dom;
55868         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
55869         e.stopEvent();
55870     },
55871
55872     handleWheel : function(e){
55873         var d = e.getWheelDelta();
55874         this.scroller.dom.scrollTop -= d*22;
55875         // set this here to prevent jumpy scrolling on large tables
55876         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
55877         e.stopEvent();
55878     },
55879
55880     renderRows : function(startRow, endRow){
55881         // pull in all the crap needed to render rows
55882         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
55883         var colCount = cm.getColumnCount();
55884
55885         if(ds.getCount() < 1){
55886             return ["", ""];
55887         }
55888
55889         // build a map for all the columns
55890         var cs = [];
55891         for(var i = 0; i < colCount; i++){
55892             var name = cm.getDataIndex(i);
55893             cs[i] = {
55894                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
55895                 renderer : cm.getRenderer(i),
55896                 id : cm.getColumnId(i),
55897                 locked : cm.isLocked(i),
55898                 has_editor : cm.isCellEditable(i)
55899             };
55900         }
55901
55902         startRow = startRow || 0;
55903         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
55904
55905         // records to render
55906         var rs = ds.getRange(startRow, endRow);
55907
55908         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
55909     },
55910
55911     // As much as I hate to duplicate code, this was branched because FireFox really hates
55912     // [].join("") on strings. The performance difference was substantial enough to
55913     // branch this function
55914     doRender : Roo.isGecko ?
55915             function(cs, rs, ds, startRow, colCount, stripe){
55916                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55917                 // buffers
55918                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55919                 
55920                 var hasListener = this.grid.hasListener('rowclass');
55921                 var rowcfg = {};
55922                 for(var j = 0, len = rs.length; j < len; j++){
55923                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
55924                     for(var i = 0; i < colCount; i++){
55925                         c = cs[i];
55926                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55927                         p.id = c.id;
55928                         p.css = p.attr = "";
55929                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55930                         if(p.value == undefined || p.value === "") {
55931                             p.value = "&#160;";
55932                         }
55933                         if(c.has_editor){
55934                             p.css += ' x-grid-editable-cell';
55935                         }
55936                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
55937                             p.css +=  ' x-grid-dirty-cell';
55938                         }
55939                         var markup = ct.apply(p);
55940                         if(!c.locked){
55941                             cb+= markup;
55942                         }else{
55943                             lcb+= markup;
55944                         }
55945                     }
55946                     var alt = [];
55947                     if(stripe && ((rowIndex+1) % 2 == 0)){
55948                         alt.push("x-grid-row-alt")
55949                     }
55950                     if(r.dirty){
55951                         alt.push(  " x-grid-dirty-row");
55952                     }
55953                     rp.cells = lcb;
55954                     if(this.getRowClass){
55955                         alt.push(this.getRowClass(r, rowIndex));
55956                     }
55957                     if (hasListener) {
55958                         rowcfg = {
55959                              
55960                             record: r,
55961                             rowIndex : rowIndex,
55962                             rowClass : ''
55963                         };
55964                         this.grid.fireEvent('rowclass', this, rowcfg);
55965                         alt.push(rowcfg.rowClass);
55966                     }
55967                     rp.alt = alt.join(" ");
55968                     lbuf+= rt.apply(rp);
55969                     rp.cells = cb;
55970                     buf+=  rt.apply(rp);
55971                 }
55972                 return [lbuf, buf];
55973             } :
55974             function(cs, rs, ds, startRow, colCount, stripe){
55975                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55976                 // buffers
55977                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55978                 var hasListener = this.grid.hasListener('rowclass');
55979  
55980                 var rowcfg = {};
55981                 for(var j = 0, len = rs.length; j < len; j++){
55982                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
55983                     for(var i = 0; i < colCount; i++){
55984                         c = cs[i];
55985                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55986                         p.id = c.id;
55987                         p.css = p.attr = "";
55988                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55989                         if(p.value == undefined || p.value === "") {
55990                             p.value = "&#160;";
55991                         }
55992                         //Roo.log(c);
55993                          if(c.has_editor){
55994                             p.css += ' x-grid-editable-cell';
55995                         }
55996                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
55997                             p.css += ' x-grid-dirty-cell' 
55998                         }
55999                         
56000                         var markup = ct.apply(p);
56001                         if(!c.locked){
56002                             cb[cb.length] = markup;
56003                         }else{
56004                             lcb[lcb.length] = markup;
56005                         }
56006                     }
56007                     var alt = [];
56008                     if(stripe && ((rowIndex+1) % 2 == 0)){
56009                         alt.push( "x-grid-row-alt");
56010                     }
56011                     if(r.dirty){
56012                         alt.push(" x-grid-dirty-row");
56013                     }
56014                     rp.cells = lcb;
56015                     if(this.getRowClass){
56016                         alt.push( this.getRowClass(r, rowIndex));
56017                     }
56018                     if (hasListener) {
56019                         rowcfg = {
56020                              
56021                             record: r,
56022                             rowIndex : rowIndex,
56023                             rowClass : ''
56024                         };
56025                         this.grid.fireEvent('rowclass', this, rowcfg);
56026                         alt.push(rowcfg.rowClass);
56027                     }
56028                     
56029                     rp.alt = alt.join(" ");
56030                     rp.cells = lcb.join("");
56031                     lbuf[lbuf.length] = rt.apply(rp);
56032                     rp.cells = cb.join("");
56033                     buf[buf.length] =  rt.apply(rp);
56034                 }
56035                 return [lbuf.join(""), buf.join("")];
56036             },
56037
56038     renderBody : function(){
56039         var markup = this.renderRows();
56040         var bt = this.templates.body;
56041         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
56042     },
56043
56044     /**
56045      * Refreshes the grid
56046      * @param {Boolean} headersToo
56047      */
56048     refresh : function(headersToo){
56049         this.fireEvent("beforerefresh", this);
56050         this.grid.stopEditing();
56051         var result = this.renderBody();
56052         this.lockedBody.update(result[0]);
56053         this.mainBody.update(result[1]);
56054         if(headersToo === true){
56055             this.updateHeaders();
56056             this.updateColumns();
56057             this.updateSplitters();
56058             this.updateHeaderSortState();
56059         }
56060         this.syncRowHeights();
56061         this.layout();
56062         this.fireEvent("refresh", this);
56063     },
56064
56065     handleColumnMove : function(cm, oldIndex, newIndex){
56066         this.indexMap = null;
56067         var s = this.getScrollState();
56068         this.refresh(true);
56069         this.restoreScroll(s);
56070         this.afterMove(newIndex);
56071     },
56072
56073     afterMove : function(colIndex){
56074         if(this.enableMoveAnim && Roo.enableFx){
56075             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
56076         }
56077         // if multisort - fix sortOrder, and reload..
56078         if (this.grid.dataSource.multiSort) {
56079             // the we can call sort again..
56080             var dm = this.grid.dataSource;
56081             var cm = this.grid.colModel;
56082             var so = [];
56083             for(var i = 0; i < cm.config.length; i++ ) {
56084                 
56085                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
56086                     continue; // dont' bother, it's not in sort list or being set.
56087                 }
56088                 
56089                 so.push(cm.config[i].dataIndex);
56090             };
56091             dm.sortOrder = so;
56092             dm.load(dm.lastOptions);
56093             
56094             
56095         }
56096         
56097     },
56098
56099     updateCell : function(dm, rowIndex, dataIndex){
56100         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
56101         if(typeof colIndex == "undefined"){ // not present in grid
56102             return;
56103         }
56104         var cm = this.grid.colModel;
56105         var cell = this.getCell(rowIndex, colIndex);
56106         var cellText = this.getCellText(rowIndex, colIndex);
56107
56108         var p = {
56109             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
56110             id : cm.getColumnId(colIndex),
56111             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
56112         };
56113         var renderer = cm.getRenderer(colIndex);
56114         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
56115         if(typeof val == "undefined" || val === "") {
56116             val = "&#160;";
56117         }
56118         cellText.innerHTML = val;
56119         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
56120         this.syncRowHeights(rowIndex, rowIndex);
56121     },
56122
56123     calcColumnWidth : function(colIndex, maxRowsToMeasure){
56124         var maxWidth = 0;
56125         if(this.grid.autoSizeHeaders){
56126             var h = this.getHeaderCellMeasure(colIndex);
56127             maxWidth = Math.max(maxWidth, h.scrollWidth);
56128         }
56129         var tb, index;
56130         if(this.cm.isLocked(colIndex)){
56131             tb = this.getLockedTable();
56132             index = colIndex;
56133         }else{
56134             tb = this.getBodyTable();
56135             index = colIndex - this.cm.getLockedCount();
56136         }
56137         if(tb && tb.rows){
56138             var rows = tb.rows;
56139             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
56140             for(var i = 0; i < stopIndex; i++){
56141                 var cell = rows[i].childNodes[index].firstChild;
56142                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
56143             }
56144         }
56145         return maxWidth + /*margin for error in IE*/ 5;
56146     },
56147     /**
56148      * Autofit a column to its content.
56149      * @param {Number} colIndex
56150      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
56151      */
56152      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
56153          if(this.cm.isHidden(colIndex)){
56154              return; // can't calc a hidden column
56155          }
56156         if(forceMinSize){
56157             var cid = this.cm.getColumnId(colIndex);
56158             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
56159            if(this.grid.autoSizeHeaders){
56160                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
56161            }
56162         }
56163         var newWidth = this.calcColumnWidth(colIndex);
56164         this.cm.setColumnWidth(colIndex,
56165             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
56166         if(!suppressEvent){
56167             this.grid.fireEvent("columnresize", colIndex, newWidth);
56168         }
56169     },
56170
56171     /**
56172      * Autofits all columns to their content and then expands to fit any extra space in the grid
56173      */
56174      autoSizeColumns : function(){
56175         var cm = this.grid.colModel;
56176         var colCount = cm.getColumnCount();
56177         for(var i = 0; i < colCount; i++){
56178             this.autoSizeColumn(i, true, true);
56179         }
56180         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
56181             this.fitColumns();
56182         }else{
56183             this.updateColumns();
56184             this.layout();
56185         }
56186     },
56187
56188     /**
56189      * Autofits all columns to the grid's width proportionate with their current size
56190      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
56191      */
56192     fitColumns : function(reserveScrollSpace){
56193         var cm = this.grid.colModel;
56194         var colCount = cm.getColumnCount();
56195         var cols = [];
56196         var width = 0;
56197         var i, w;
56198         for (i = 0; i < colCount; i++){
56199             if(!cm.isHidden(i) && !cm.isFixed(i)){
56200                 w = cm.getColumnWidth(i);
56201                 cols.push(i);
56202                 cols.push(w);
56203                 width += w;
56204             }
56205         }
56206         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
56207         if(reserveScrollSpace){
56208             avail -= 17;
56209         }
56210         var frac = (avail - cm.getTotalWidth())/width;
56211         while (cols.length){
56212             w = cols.pop();
56213             i = cols.pop();
56214             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
56215         }
56216         this.updateColumns();
56217         this.layout();
56218     },
56219
56220     onRowSelect : function(rowIndex){
56221         var row = this.getRowComposite(rowIndex);
56222         row.addClass("x-grid-row-selected");
56223     },
56224
56225     onRowDeselect : function(rowIndex){
56226         var row = this.getRowComposite(rowIndex);
56227         row.removeClass("x-grid-row-selected");
56228     },
56229
56230     onCellSelect : function(row, col){
56231         var cell = this.getCell(row, col);
56232         if(cell){
56233             Roo.fly(cell).addClass("x-grid-cell-selected");
56234         }
56235     },
56236
56237     onCellDeselect : function(row, col){
56238         var cell = this.getCell(row, col);
56239         if(cell){
56240             Roo.fly(cell).removeClass("x-grid-cell-selected");
56241         }
56242     },
56243
56244     updateHeaderSortState : function(){
56245         
56246         // sort state can be single { field: xxx, direction : yyy}
56247         // or   { xxx=>ASC , yyy : DESC ..... }
56248         
56249         var mstate = {};
56250         if (!this.ds.multiSort) { 
56251             var state = this.ds.getSortState();
56252             if(!state){
56253                 return;
56254             }
56255             mstate[state.field] = state.direction;
56256             // FIXME... - this is not used here.. but might be elsewhere..
56257             this.sortState = state;
56258             
56259         } else {
56260             mstate = this.ds.sortToggle;
56261         }
56262         //remove existing sort classes..
56263         
56264         var sc = this.sortClasses;
56265         var hds = this.el.select(this.headerSelector).removeClass(sc);
56266         
56267         for(var f in mstate) {
56268         
56269             var sortColumn = this.cm.findColumnIndex(f);
56270             
56271             if(sortColumn != -1){
56272                 var sortDir = mstate[f];        
56273                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
56274             }
56275         }
56276         
56277          
56278         
56279     },
56280
56281
56282     handleHeaderClick : function(g, index,e){
56283         
56284         Roo.log("header click");
56285         
56286         if (Roo.isTouch) {
56287             // touch events on header are handled by context
56288             this.handleHdCtx(g,index,e);
56289             return;
56290         }
56291         
56292         
56293         if(this.headersDisabled){
56294             return;
56295         }
56296         var dm = g.dataSource, cm = g.colModel;
56297         if(!cm.isSortable(index)){
56298             return;
56299         }
56300         g.stopEditing();
56301         
56302         if (dm.multiSort) {
56303             // update the sortOrder
56304             var so = [];
56305             for(var i = 0; i < cm.config.length; i++ ) {
56306                 
56307                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
56308                     continue; // dont' bother, it's not in sort list or being set.
56309                 }
56310                 
56311                 so.push(cm.config[i].dataIndex);
56312             };
56313             dm.sortOrder = so;
56314         }
56315         
56316         
56317         dm.sort(cm.getDataIndex(index));
56318     },
56319
56320
56321     destroy : function(){
56322         if(this.colMenu){
56323             this.colMenu.removeAll();
56324             Roo.menu.MenuMgr.unregister(this.colMenu);
56325             this.colMenu.getEl().remove();
56326             delete this.colMenu;
56327         }
56328         if(this.hmenu){
56329             this.hmenu.removeAll();
56330             Roo.menu.MenuMgr.unregister(this.hmenu);
56331             this.hmenu.getEl().remove();
56332             delete this.hmenu;
56333         }
56334         if(this.grid.enableColumnMove){
56335             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56336             if(dds){
56337                 for(var dd in dds){
56338                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
56339                         var elid = dds[dd].dragElId;
56340                         dds[dd].unreg();
56341                         Roo.get(elid).remove();
56342                     } else if(dds[dd].config.isTarget){
56343                         dds[dd].proxyTop.remove();
56344                         dds[dd].proxyBottom.remove();
56345                         dds[dd].unreg();
56346                     }
56347                     if(Roo.dd.DDM.locationCache[dd]){
56348                         delete Roo.dd.DDM.locationCache[dd];
56349                     }
56350                 }
56351                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56352             }
56353         }
56354         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
56355         this.bind(null, null);
56356         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
56357     },
56358
56359     handleLockChange : function(){
56360         this.refresh(true);
56361     },
56362
56363     onDenyColumnLock : function(){
56364
56365     },
56366
56367     onDenyColumnHide : function(){
56368
56369     },
56370
56371     handleHdMenuClick : function(item){
56372         var index = this.hdCtxIndex;
56373         var cm = this.cm, ds = this.ds;
56374         switch(item.id){
56375             case "asc":
56376                 ds.sort(cm.getDataIndex(index), "ASC");
56377                 break;
56378             case "desc":
56379                 ds.sort(cm.getDataIndex(index), "DESC");
56380                 break;
56381             case "lock":
56382                 var lc = cm.getLockedCount();
56383                 if(cm.getColumnCount(true) <= lc+1){
56384                     this.onDenyColumnLock();
56385                     return;
56386                 }
56387                 if(lc != index){
56388                     cm.setLocked(index, true, true);
56389                     cm.moveColumn(index, lc);
56390                     this.grid.fireEvent("columnmove", index, lc);
56391                 }else{
56392                     cm.setLocked(index, true);
56393                 }
56394             break;
56395             case "unlock":
56396                 var lc = cm.getLockedCount();
56397                 if((lc-1) != index){
56398                     cm.setLocked(index, false, true);
56399                     cm.moveColumn(index, lc-1);
56400                     this.grid.fireEvent("columnmove", index, lc-1);
56401                 }else{
56402                     cm.setLocked(index, false);
56403                 }
56404             break;
56405             case 'wider': // used to expand cols on touch..
56406             case 'narrow':
56407                 var cw = cm.getColumnWidth(index);
56408                 cw += (item.id == 'wider' ? 1 : -1) * 50;
56409                 cw = Math.max(0, cw);
56410                 cw = Math.min(cw,4000);
56411                 cm.setColumnWidth(index, cw);
56412                 break;
56413                 
56414             default:
56415                 index = cm.getIndexById(item.id.substr(4));
56416                 if(index != -1){
56417                     if(item.checked && cm.getColumnCount(true) <= 1){
56418                         this.onDenyColumnHide();
56419                         return false;
56420                     }
56421                     cm.setHidden(index, item.checked);
56422                 }
56423         }
56424         return true;
56425     },
56426
56427     beforeColMenuShow : function(){
56428         var cm = this.cm,  colCount = cm.getColumnCount();
56429         this.colMenu.removeAll();
56430         for(var i = 0; i < colCount; i++){
56431             this.colMenu.add(new Roo.menu.CheckItem({
56432                 id: "col-"+cm.getColumnId(i),
56433                 text: cm.getColumnHeader(i),
56434                 checked: !cm.isHidden(i),
56435                 hideOnClick:false
56436             }));
56437         }
56438     },
56439
56440     handleHdCtx : function(g, index, e){
56441         e.stopEvent();
56442         var hd = this.getHeaderCell(index);
56443         this.hdCtxIndex = index;
56444         var ms = this.hmenu.items, cm = this.cm;
56445         ms.get("asc").setDisabled(!cm.isSortable(index));
56446         ms.get("desc").setDisabled(!cm.isSortable(index));
56447         if(this.grid.enableColLock !== false){
56448             ms.get("lock").setDisabled(cm.isLocked(index));
56449             ms.get("unlock").setDisabled(!cm.isLocked(index));
56450         }
56451         this.hmenu.show(hd, "tl-bl");
56452     },
56453
56454     handleHdOver : function(e){
56455         var hd = this.findHeaderCell(e.getTarget());
56456         if(hd && !this.headersDisabled){
56457             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
56458                this.fly(hd).addClass("x-grid-hd-over");
56459             }
56460         }
56461     },
56462
56463     handleHdOut : function(e){
56464         var hd = this.findHeaderCell(e.getTarget());
56465         if(hd){
56466             this.fly(hd).removeClass("x-grid-hd-over");
56467         }
56468     },
56469
56470     handleSplitDblClick : function(e, t){
56471         var i = this.getCellIndex(t);
56472         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
56473             this.autoSizeColumn(i, true);
56474             this.layout();
56475         }
56476     },
56477
56478     render : function(){
56479
56480         var cm = this.cm;
56481         var colCount = cm.getColumnCount();
56482
56483         if(this.grid.monitorWindowResize === true){
56484             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
56485         }
56486         var header = this.renderHeaders();
56487         var body = this.templates.body.apply({rows:""});
56488         var html = this.templates.master.apply({
56489             lockedBody: body,
56490             body: body,
56491             lockedHeader: header[0],
56492             header: header[1]
56493         });
56494
56495         //this.updateColumns();
56496
56497         this.grid.getGridEl().dom.innerHTML = html;
56498
56499         this.initElements();
56500         
56501         // a kludge to fix the random scolling effect in webkit
56502         this.el.on("scroll", function() {
56503             this.el.dom.scrollTop=0; // hopefully not recursive..
56504         },this);
56505
56506         this.scroller.on("scroll", this.handleScroll, this);
56507         this.lockedBody.on("mousewheel", this.handleWheel, this);
56508         this.mainBody.on("mousewheel", this.handleWheel, this);
56509
56510         this.mainHd.on("mouseover", this.handleHdOver, this);
56511         this.mainHd.on("mouseout", this.handleHdOut, this);
56512         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
56513                 {delegate: "."+this.splitClass});
56514
56515         this.lockedHd.on("mouseover", this.handleHdOver, this);
56516         this.lockedHd.on("mouseout", this.handleHdOut, this);
56517         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
56518                 {delegate: "."+this.splitClass});
56519
56520         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
56521             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56522         }
56523
56524         this.updateSplitters();
56525
56526         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
56527             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56528             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56529         }
56530
56531         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
56532             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
56533             this.hmenu.add(
56534                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
56535                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
56536             );
56537             if(this.grid.enableColLock !== false){
56538                 this.hmenu.add('-',
56539                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
56540                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
56541                 );
56542             }
56543             if (Roo.isTouch) {
56544                  this.hmenu.add('-',
56545                     {id:"wider", text: this.columnsWiderText},
56546                     {id:"narrow", text: this.columnsNarrowText }
56547                 );
56548                 
56549                  
56550             }
56551             
56552             if(this.grid.enableColumnHide !== false){
56553
56554                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
56555                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
56556                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
56557
56558                 this.hmenu.add('-',
56559                     {id:"columns", text: this.columnsText, menu: this.colMenu}
56560                 );
56561             }
56562             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
56563
56564             this.grid.on("headercontextmenu", this.handleHdCtx, this);
56565         }
56566
56567         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
56568             this.dd = new Roo.grid.GridDragZone(this.grid, {
56569                 ddGroup : this.grid.ddGroup || 'GridDD'
56570             });
56571             
56572         }
56573
56574         /*
56575         for(var i = 0; i < colCount; i++){
56576             if(cm.isHidden(i)){
56577                 this.hideColumn(i);
56578             }
56579             if(cm.config[i].align){
56580                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
56581                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
56582             }
56583         }*/
56584         
56585         this.updateHeaderSortState();
56586
56587         this.beforeInitialResize();
56588         this.layout(true);
56589
56590         // two part rendering gives faster view to the user
56591         this.renderPhase2.defer(1, this);
56592     },
56593
56594     renderPhase2 : function(){
56595         // render the rows now
56596         this.refresh();
56597         if(this.grid.autoSizeColumns){
56598             this.autoSizeColumns();
56599         }
56600     },
56601
56602     beforeInitialResize : function(){
56603
56604     },
56605
56606     onColumnSplitterMoved : function(i, w){
56607         this.userResized = true;
56608         var cm = this.grid.colModel;
56609         cm.setColumnWidth(i, w, true);
56610         var cid = cm.getColumnId(i);
56611         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56612         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56613         this.updateSplitters();
56614         this.layout();
56615         this.grid.fireEvent("columnresize", i, w);
56616     },
56617
56618     syncRowHeights : function(startIndex, endIndex){
56619         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
56620             startIndex = startIndex || 0;
56621             var mrows = this.getBodyTable().rows;
56622             var lrows = this.getLockedTable().rows;
56623             var len = mrows.length-1;
56624             endIndex = Math.min(endIndex || len, len);
56625             for(var i = startIndex; i <= endIndex; i++){
56626                 var m = mrows[i], l = lrows[i];
56627                 var h = Math.max(m.offsetHeight, l.offsetHeight);
56628                 m.style.height = l.style.height = h + "px";
56629             }
56630         }
56631     },
56632
56633     layout : function(initialRender, is2ndPass){
56634         var g = this.grid;
56635         var auto = g.autoHeight;
56636         var scrollOffset = 16;
56637         var c = g.getGridEl(), cm = this.cm,
56638                 expandCol = g.autoExpandColumn,
56639                 gv = this;
56640         //c.beginMeasure();
56641
56642         if(!c.dom.offsetWidth){ // display:none?
56643             if(initialRender){
56644                 this.lockedWrap.show();
56645                 this.mainWrap.show();
56646             }
56647             return;
56648         }
56649
56650         var hasLock = this.cm.isLocked(0);
56651
56652         var tbh = this.headerPanel.getHeight();
56653         var bbh = this.footerPanel.getHeight();
56654
56655         if(auto){
56656             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
56657             var newHeight = ch + c.getBorderWidth("tb");
56658             if(g.maxHeight){
56659                 newHeight = Math.min(g.maxHeight, newHeight);
56660             }
56661             c.setHeight(newHeight);
56662         }
56663
56664         if(g.autoWidth){
56665             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
56666         }
56667
56668         var s = this.scroller;
56669
56670         var csize = c.getSize(true);
56671
56672         this.el.setSize(csize.width, csize.height);
56673
56674         this.headerPanel.setWidth(csize.width);
56675         this.footerPanel.setWidth(csize.width);
56676
56677         var hdHeight = this.mainHd.getHeight();
56678         var vw = csize.width;
56679         var vh = csize.height - (tbh + bbh);
56680
56681         s.setSize(vw, vh);
56682
56683         var bt = this.getBodyTable();
56684         
56685         if(cm.getLockedCount() == cm.config.length){
56686             bt = this.getLockedTable();
56687         }
56688         
56689         var ltWidth = hasLock ?
56690                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
56691
56692         var scrollHeight = bt.offsetHeight;
56693         var scrollWidth = ltWidth + bt.offsetWidth;
56694         var vscroll = false, hscroll = false;
56695
56696         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
56697
56698         var lw = this.lockedWrap, mw = this.mainWrap;
56699         var lb = this.lockedBody, mb = this.mainBody;
56700
56701         setTimeout(function(){
56702             var t = s.dom.offsetTop;
56703             var w = s.dom.clientWidth,
56704                 h = s.dom.clientHeight;
56705
56706             lw.setTop(t);
56707             lw.setSize(ltWidth, h);
56708
56709             mw.setLeftTop(ltWidth, t);
56710             mw.setSize(w-ltWidth, h);
56711
56712             lb.setHeight(h-hdHeight);
56713             mb.setHeight(h-hdHeight);
56714
56715             if(is2ndPass !== true && !gv.userResized && expandCol){
56716                 // high speed resize without full column calculation
56717                 
56718                 var ci = cm.getIndexById(expandCol);
56719                 if (ci < 0) {
56720                     ci = cm.findColumnIndex(expandCol);
56721                 }
56722                 ci = Math.max(0, ci); // make sure it's got at least the first col.
56723                 var expandId = cm.getColumnId(ci);
56724                 var  tw = cm.getTotalWidth(false);
56725                 var currentWidth = cm.getColumnWidth(ci);
56726                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
56727                 if(currentWidth != cw){
56728                     cm.setColumnWidth(ci, cw, true);
56729                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56730                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56731                     gv.updateSplitters();
56732                     gv.layout(false, true);
56733                 }
56734             }
56735
56736             if(initialRender){
56737                 lw.show();
56738                 mw.show();
56739             }
56740             //c.endMeasure();
56741         }, 10);
56742     },
56743
56744     onWindowResize : function(){
56745         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
56746             return;
56747         }
56748         this.layout();
56749     },
56750
56751     appendFooter : function(parentEl){
56752         return null;
56753     },
56754
56755     sortAscText : "Sort Ascending",
56756     sortDescText : "Sort Descending",
56757     lockText : "Lock Column",
56758     unlockText : "Unlock Column",
56759     columnsText : "Columns",
56760  
56761     columnsWiderText : "Wider",
56762     columnsNarrowText : "Thinner"
56763 });
56764
56765
56766 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
56767     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
56768     this.proxy.el.addClass('x-grid3-col-dd');
56769 };
56770
56771 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
56772     handleMouseDown : function(e){
56773
56774     },
56775
56776     callHandleMouseDown : function(e){
56777         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
56778     }
56779 });
56780 /*
56781  * Based on:
56782  * Ext JS Library 1.1.1
56783  * Copyright(c) 2006-2007, Ext JS, LLC.
56784  *
56785  * Originally Released Under LGPL - original licence link has changed is not relivant.
56786  *
56787  * Fork - LGPL
56788  * <script type="text/javascript">
56789  */
56790  
56791 // private
56792 // This is a support class used internally by the Grid components
56793 Roo.grid.SplitDragZone = function(grid, hd, hd2){
56794     this.grid = grid;
56795     this.view = grid.getView();
56796     this.proxy = this.view.resizeProxy;
56797     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
56798         "gridSplitters" + this.grid.getGridEl().id, {
56799         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
56800     });
56801     this.setHandleElId(Roo.id(hd));
56802     this.setOuterHandleElId(Roo.id(hd2));
56803     this.scroll = false;
56804 };
56805 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
56806     fly: Roo.Element.fly,
56807
56808     b4StartDrag : function(x, y){
56809         this.view.headersDisabled = true;
56810         this.proxy.setHeight(this.view.mainWrap.getHeight());
56811         var w = this.cm.getColumnWidth(this.cellIndex);
56812         var minw = Math.max(w-this.grid.minColumnWidth, 0);
56813         this.resetConstraints();
56814         this.setXConstraint(minw, 1000);
56815         this.setYConstraint(0, 0);
56816         this.minX = x - minw;
56817         this.maxX = x + 1000;
56818         this.startPos = x;
56819         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
56820     },
56821
56822
56823     handleMouseDown : function(e){
56824         ev = Roo.EventObject.setEvent(e);
56825         var t = this.fly(ev.getTarget());
56826         if(t.hasClass("x-grid-split")){
56827             this.cellIndex = this.view.getCellIndex(t.dom);
56828             this.split = t.dom;
56829             this.cm = this.grid.colModel;
56830             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
56831                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
56832             }
56833         }
56834     },
56835
56836     endDrag : function(e){
56837         this.view.headersDisabled = false;
56838         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
56839         var diff = endX - this.startPos;
56840         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
56841     },
56842
56843     autoOffset : function(){
56844         this.setDelta(0,0);
56845     }
56846 });/*
56847  * Based on:
56848  * Ext JS Library 1.1.1
56849  * Copyright(c) 2006-2007, Ext JS, LLC.
56850  *
56851  * Originally Released Under LGPL - original licence link has changed is not relivant.
56852  *
56853  * Fork - LGPL
56854  * <script type="text/javascript">
56855  */
56856  
56857 // private
56858 // This is a support class used internally by the Grid components
56859 Roo.grid.GridDragZone = function(grid, config){
56860     this.view = grid.getView();
56861     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
56862     if(this.view.lockedBody){
56863         this.setHandleElId(Roo.id(this.view.mainBody.dom));
56864         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
56865     }
56866     this.scroll = false;
56867     this.grid = grid;
56868     this.ddel = document.createElement('div');
56869     this.ddel.className = 'x-grid-dd-wrap';
56870 };
56871
56872 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
56873     ddGroup : "GridDD",
56874
56875     getDragData : function(e){
56876         var t = Roo.lib.Event.getTarget(e);
56877         var rowIndex = this.view.findRowIndex(t);
56878         var sm = this.grid.selModel;
56879             
56880         //Roo.log(rowIndex);
56881         
56882         if (sm.getSelectedCell) {
56883             // cell selection..
56884             if (!sm.getSelectedCell()) {
56885                 return false;
56886             }
56887             if (rowIndex != sm.getSelectedCell()[0]) {
56888                 return false;
56889             }
56890         
56891         }
56892         
56893         if(rowIndex !== false){
56894             
56895             // if editorgrid.. 
56896             
56897             
56898             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
56899                
56900             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
56901               //  
56902             //}
56903             if (e.hasModifier()){
56904                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
56905             }
56906             
56907             Roo.log("getDragData");
56908             
56909             return {
56910                 grid: this.grid,
56911                 ddel: this.ddel,
56912                 rowIndex: rowIndex,
56913                 selections:sm.getSelections ? sm.getSelections() : (
56914                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
56915                 )
56916             };
56917         }
56918         return false;
56919     },
56920
56921     onInitDrag : function(e){
56922         var data = this.dragData;
56923         this.ddel.innerHTML = this.grid.getDragDropText();
56924         this.proxy.update(this.ddel);
56925         // fire start drag?
56926     },
56927
56928     afterRepair : function(){
56929         this.dragging = false;
56930     },
56931
56932     getRepairXY : function(e, data){
56933         return false;
56934     },
56935
56936     onEndDrag : function(data, e){
56937         // fire end drag?
56938     },
56939
56940     onValidDrop : function(dd, e, id){
56941         // fire drag drop?
56942         this.hideProxy();
56943     },
56944
56945     beforeInvalidDrop : function(e, id){
56946
56947     }
56948 });/*
56949  * Based on:
56950  * Ext JS Library 1.1.1
56951  * Copyright(c) 2006-2007, Ext JS, LLC.
56952  *
56953  * Originally Released Under LGPL - original licence link has changed is not relivant.
56954  *
56955  * Fork - LGPL
56956  * <script type="text/javascript">
56957  */
56958  
56959
56960 /**
56961  * @class Roo.grid.ColumnModel
56962  * @extends Roo.util.Observable
56963  * This is the default implementation of a ColumnModel used by the Grid. It defines
56964  * the columns in the grid.
56965  * <br>Usage:<br>
56966  <pre><code>
56967  var colModel = new Roo.grid.ColumnModel([
56968         {header: "Ticker", width: 60, sortable: true, locked: true},
56969         {header: "Company Name", width: 150, sortable: true},
56970         {header: "Market Cap.", width: 100, sortable: true},
56971         {header: "$ Sales", width: 100, sortable: true, renderer: money},
56972         {header: "Employees", width: 100, sortable: true, resizable: false}
56973  ]);
56974  </code></pre>
56975  * <p>
56976  
56977  * The config options listed for this class are options which may appear in each
56978  * individual column definition.
56979  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
56980  * @constructor
56981  * @param {Object} config An Array of column config objects. See this class's
56982  * config objects for details.
56983 */
56984 Roo.grid.ColumnModel = function(config){
56985         /**
56986      * The config passed into the constructor
56987      */
56988     this.config = config;
56989     this.lookup = {};
56990
56991     // if no id, create one
56992     // if the column does not have a dataIndex mapping,
56993     // map it to the order it is in the config
56994     for(var i = 0, len = config.length; i < len; i++){
56995         var c = config[i];
56996         if(typeof c.dataIndex == "undefined"){
56997             c.dataIndex = i;
56998         }
56999         if(typeof c.renderer == "string"){
57000             c.renderer = Roo.util.Format[c.renderer];
57001         }
57002         if(typeof c.id == "undefined"){
57003             c.id = Roo.id();
57004         }
57005         if(c.editor && c.editor.xtype){
57006             c.editor  = Roo.factory(c.editor, Roo.grid);
57007         }
57008         if(c.editor && c.editor.isFormField){
57009             c.editor = new Roo.grid.GridEditor(c.editor);
57010         }
57011         this.lookup[c.id] = c;
57012     }
57013
57014     /**
57015      * The width of columns which have no width specified (defaults to 100)
57016      * @type Number
57017      */
57018     this.defaultWidth = 100;
57019
57020     /**
57021      * Default sortable of columns which have no sortable specified (defaults to false)
57022      * @type Boolean
57023      */
57024     this.defaultSortable = false;
57025
57026     this.addEvents({
57027         /**
57028              * @event widthchange
57029              * Fires when the width of a column changes.
57030              * @param {ColumnModel} this
57031              * @param {Number} columnIndex The column index
57032              * @param {Number} newWidth The new width
57033              */
57034             "widthchange": true,
57035         /**
57036              * @event headerchange
57037              * Fires when the text of a header changes.
57038              * @param {ColumnModel} this
57039              * @param {Number} columnIndex The column index
57040              * @param {Number} newText The new header text
57041              */
57042             "headerchange": true,
57043         /**
57044              * @event hiddenchange
57045              * Fires when a column is hidden or "unhidden".
57046              * @param {ColumnModel} this
57047              * @param {Number} columnIndex The column index
57048              * @param {Boolean} hidden true if hidden, false otherwise
57049              */
57050             "hiddenchange": true,
57051             /**
57052          * @event columnmoved
57053          * Fires when a column is moved.
57054          * @param {ColumnModel} this
57055          * @param {Number} oldIndex
57056          * @param {Number} newIndex
57057          */
57058         "columnmoved" : true,
57059         /**
57060          * @event columlockchange
57061          * Fires when a column's locked state is changed
57062          * @param {ColumnModel} this
57063          * @param {Number} colIndex
57064          * @param {Boolean} locked true if locked
57065          */
57066         "columnlockchange" : true
57067     });
57068     Roo.grid.ColumnModel.superclass.constructor.call(this);
57069 };
57070 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
57071     /**
57072      * @cfg {String} header The header text to display in the Grid view.
57073      */
57074     /**
57075      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
57076      * {@link Roo.data.Record} definition from which to draw the column's value. If not
57077      * specified, the column's index is used as an index into the Record's data Array.
57078      */
57079     /**
57080      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
57081      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
57082      */
57083     /**
57084      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
57085      * Defaults to the value of the {@link #defaultSortable} property.
57086      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
57087      */
57088     /**
57089      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
57090      */
57091     /**
57092      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
57093      */
57094     /**
57095      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
57096      */
57097     /**
57098      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
57099      */
57100     /**
57101      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
57102      * given the cell's data value. See {@link #setRenderer}. If not specified, the
57103      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
57104      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
57105      */
57106        /**
57107      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
57108      */
57109     /**
57110      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
57111      */
57112     /**
57113      * @cfg {String} cursor (Optional)
57114      */
57115     /**
57116      * @cfg {String} tooltip (Optional)
57117      */
57118     /**
57119      * @cfg {Number} xs (Optional)
57120      */
57121     /**
57122      * @cfg {Number} sm (Optional)
57123      */
57124     /**
57125      * @cfg {Number} md (Optional)
57126      */
57127     /**
57128      * @cfg {Number} lg (Optional)
57129      */
57130     /**
57131      * Returns the id of the column at the specified index.
57132      * @param {Number} index The column index
57133      * @return {String} the id
57134      */
57135     getColumnId : function(index){
57136         return this.config[index].id;
57137     },
57138
57139     /**
57140      * Returns the column for a specified id.
57141      * @param {String} id The column id
57142      * @return {Object} the column
57143      */
57144     getColumnById : function(id){
57145         return this.lookup[id];
57146     },
57147
57148     
57149     /**
57150      * Returns the column for a specified dataIndex.
57151      * @param {String} dataIndex The column dataIndex
57152      * @return {Object|Boolean} the column or false if not found
57153      */
57154     getColumnByDataIndex: function(dataIndex){
57155         var index = this.findColumnIndex(dataIndex);
57156         return index > -1 ? this.config[index] : false;
57157     },
57158     
57159     /**
57160      * Returns the index for a specified column id.
57161      * @param {String} id The column id
57162      * @return {Number} the index, or -1 if not found
57163      */
57164     getIndexById : function(id){
57165         for(var i = 0, len = this.config.length; i < len; i++){
57166             if(this.config[i].id == id){
57167                 return i;
57168             }
57169         }
57170         return -1;
57171     },
57172     
57173     /**
57174      * Returns the index for a specified column dataIndex.
57175      * @param {String} dataIndex The column dataIndex
57176      * @return {Number} the index, or -1 if not found
57177      */
57178     
57179     findColumnIndex : function(dataIndex){
57180         for(var i = 0, len = this.config.length; i < len; i++){
57181             if(this.config[i].dataIndex == dataIndex){
57182                 return i;
57183             }
57184         }
57185         return -1;
57186     },
57187     
57188     
57189     moveColumn : function(oldIndex, newIndex){
57190         var c = this.config[oldIndex];
57191         this.config.splice(oldIndex, 1);
57192         this.config.splice(newIndex, 0, c);
57193         this.dataMap = null;
57194         this.fireEvent("columnmoved", this, oldIndex, newIndex);
57195     },
57196
57197     isLocked : function(colIndex){
57198         return this.config[colIndex].locked === true;
57199     },
57200
57201     setLocked : function(colIndex, value, suppressEvent){
57202         if(this.isLocked(colIndex) == value){
57203             return;
57204         }
57205         this.config[colIndex].locked = value;
57206         if(!suppressEvent){
57207             this.fireEvent("columnlockchange", this, colIndex, value);
57208         }
57209     },
57210
57211     getTotalLockedWidth : function(){
57212         var totalWidth = 0;
57213         for(var i = 0; i < this.config.length; i++){
57214             if(this.isLocked(i) && !this.isHidden(i)){
57215                 this.totalWidth += this.getColumnWidth(i);
57216             }
57217         }
57218         return totalWidth;
57219     },
57220
57221     getLockedCount : function(){
57222         for(var i = 0, len = this.config.length; i < len; i++){
57223             if(!this.isLocked(i)){
57224                 return i;
57225             }
57226         }
57227         
57228         return this.config.length;
57229     },
57230
57231     /**
57232      * Returns the number of columns.
57233      * @return {Number}
57234      */
57235     getColumnCount : function(visibleOnly){
57236         if(visibleOnly === true){
57237             var c = 0;
57238             for(var i = 0, len = this.config.length; i < len; i++){
57239                 if(!this.isHidden(i)){
57240                     c++;
57241                 }
57242             }
57243             return c;
57244         }
57245         return this.config.length;
57246     },
57247
57248     /**
57249      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
57250      * @param {Function} fn
57251      * @param {Object} scope (optional)
57252      * @return {Array} result
57253      */
57254     getColumnsBy : function(fn, scope){
57255         var r = [];
57256         for(var i = 0, len = this.config.length; i < len; i++){
57257             var c = this.config[i];
57258             if(fn.call(scope||this, c, i) === true){
57259                 r[r.length] = c;
57260             }
57261         }
57262         return r;
57263     },
57264
57265     /**
57266      * Returns true if the specified column is sortable.
57267      * @param {Number} col The column index
57268      * @return {Boolean}
57269      */
57270     isSortable : function(col){
57271         if(typeof this.config[col].sortable == "undefined"){
57272             return this.defaultSortable;
57273         }
57274         return this.config[col].sortable;
57275     },
57276
57277     /**
57278      * Returns the rendering (formatting) function defined for the column.
57279      * @param {Number} col The column index.
57280      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
57281      */
57282     getRenderer : function(col){
57283         if(!this.config[col].renderer){
57284             return Roo.grid.ColumnModel.defaultRenderer;
57285         }
57286         return this.config[col].renderer;
57287     },
57288
57289     /**
57290      * Sets the rendering (formatting) function for a column.
57291      * @param {Number} col The column index
57292      * @param {Function} fn The function to use to process the cell's raw data
57293      * to return HTML markup for the grid view. The render function is called with
57294      * the following parameters:<ul>
57295      * <li>Data value.</li>
57296      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
57297      * <li>css A CSS style string to apply to the table cell.</li>
57298      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
57299      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
57300      * <li>Row index</li>
57301      * <li>Column index</li>
57302      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
57303      */
57304     setRenderer : function(col, fn){
57305         this.config[col].renderer = fn;
57306     },
57307
57308     /**
57309      * Returns the width for the specified column.
57310      * @param {Number} col The column index
57311      * @return {Number}
57312      */
57313     getColumnWidth : function(col){
57314         return this.config[col].width * 1 || this.defaultWidth;
57315     },
57316
57317     /**
57318      * Sets the width for a column.
57319      * @param {Number} col The column index
57320      * @param {Number} width The new width
57321      */
57322     setColumnWidth : function(col, width, suppressEvent){
57323         this.config[col].width = width;
57324         this.totalWidth = null;
57325         if(!suppressEvent){
57326              this.fireEvent("widthchange", this, col, width);
57327         }
57328     },
57329
57330     /**
57331      * Returns the total width of all columns.
57332      * @param {Boolean} includeHidden True to include hidden column widths
57333      * @return {Number}
57334      */
57335     getTotalWidth : function(includeHidden){
57336         if(!this.totalWidth){
57337             this.totalWidth = 0;
57338             for(var i = 0, len = this.config.length; i < len; i++){
57339                 if(includeHidden || !this.isHidden(i)){
57340                     this.totalWidth += this.getColumnWidth(i);
57341                 }
57342             }
57343         }
57344         return this.totalWidth;
57345     },
57346
57347     /**
57348      * Returns the header for the specified column.
57349      * @param {Number} col The column index
57350      * @return {String}
57351      */
57352     getColumnHeader : function(col){
57353         return this.config[col].header;
57354     },
57355
57356     /**
57357      * Sets the header for a column.
57358      * @param {Number} col The column index
57359      * @param {String} header The new header
57360      */
57361     setColumnHeader : function(col, header){
57362         this.config[col].header = header;
57363         this.fireEvent("headerchange", this, col, header);
57364     },
57365
57366     /**
57367      * Returns the tooltip for the specified column.
57368      * @param {Number} col The column index
57369      * @return {String}
57370      */
57371     getColumnTooltip : function(col){
57372             return this.config[col].tooltip;
57373     },
57374     /**
57375      * Sets the tooltip for a column.
57376      * @param {Number} col The column index
57377      * @param {String} tooltip The new tooltip
57378      */
57379     setColumnTooltip : function(col, tooltip){
57380             this.config[col].tooltip = tooltip;
57381     },
57382
57383     /**
57384      * Returns the dataIndex for the specified column.
57385      * @param {Number} col The column index
57386      * @return {Number}
57387      */
57388     getDataIndex : function(col){
57389         return this.config[col].dataIndex;
57390     },
57391
57392     /**
57393      * Sets the dataIndex for a column.
57394      * @param {Number} col The column index
57395      * @param {Number} dataIndex The new dataIndex
57396      */
57397     setDataIndex : function(col, dataIndex){
57398         this.config[col].dataIndex = dataIndex;
57399     },
57400
57401     
57402     
57403     /**
57404      * Returns true if the cell is editable.
57405      * @param {Number} colIndex The column index
57406      * @param {Number} rowIndex The row index - this is nto actually used..?
57407      * @return {Boolean}
57408      */
57409     isCellEditable : function(colIndex, rowIndex){
57410         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
57411     },
57412
57413     /**
57414      * Returns the editor defined for the cell/column.
57415      * return false or null to disable editing.
57416      * @param {Number} colIndex The column index
57417      * @param {Number} rowIndex The row index
57418      * @return {Object}
57419      */
57420     getCellEditor : function(colIndex, rowIndex){
57421         return this.config[colIndex].editor;
57422     },
57423
57424     /**
57425      * Sets if a column is editable.
57426      * @param {Number} col The column index
57427      * @param {Boolean} editable True if the column is editable
57428      */
57429     setEditable : function(col, editable){
57430         this.config[col].editable = editable;
57431     },
57432
57433
57434     /**
57435      * Returns true if the column is hidden.
57436      * @param {Number} colIndex The column index
57437      * @return {Boolean}
57438      */
57439     isHidden : function(colIndex){
57440         return this.config[colIndex].hidden;
57441     },
57442
57443
57444     /**
57445      * Returns true if the column width cannot be changed
57446      */
57447     isFixed : function(colIndex){
57448         return this.config[colIndex].fixed;
57449     },
57450
57451     /**
57452      * Returns true if the column can be resized
57453      * @return {Boolean}
57454      */
57455     isResizable : function(colIndex){
57456         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
57457     },
57458     /**
57459      * Sets if a column is hidden.
57460      * @param {Number} colIndex The column index
57461      * @param {Boolean} hidden True if the column is hidden
57462      */
57463     setHidden : function(colIndex, hidden){
57464         this.config[colIndex].hidden = hidden;
57465         this.totalWidth = null;
57466         this.fireEvent("hiddenchange", this, colIndex, hidden);
57467     },
57468
57469     /**
57470      * Sets the editor for a column.
57471      * @param {Number} col The column index
57472      * @param {Object} editor The editor object
57473      */
57474     setEditor : function(col, editor){
57475         this.config[col].editor = editor;
57476     }
57477 });
57478
57479 Roo.grid.ColumnModel.defaultRenderer = function(value)
57480 {
57481     if(typeof value == "object") {
57482         return value;
57483     }
57484         if(typeof value == "string" && value.length < 1){
57485             return "&#160;";
57486         }
57487     
57488         return String.format("{0}", value);
57489 };
57490
57491 // Alias for backwards compatibility
57492 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
57493 /*
57494  * Based on:
57495  * Ext JS Library 1.1.1
57496  * Copyright(c) 2006-2007, Ext JS, LLC.
57497  *
57498  * Originally Released Under LGPL - original licence link has changed is not relivant.
57499  *
57500  * Fork - LGPL
57501  * <script type="text/javascript">
57502  */
57503
57504 /**
57505  * @class Roo.grid.AbstractSelectionModel
57506  * @extends Roo.util.Observable
57507  * Abstract base class for grid SelectionModels.  It provides the interface that should be
57508  * implemented by descendant classes.  This class should not be directly instantiated.
57509  * @constructor
57510  */
57511 Roo.grid.AbstractSelectionModel = function(){
57512     this.locked = false;
57513     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
57514 };
57515
57516 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
57517     /** @ignore Called by the grid automatically. Do not call directly. */
57518     init : function(grid){
57519         this.grid = grid;
57520         this.initEvents();
57521     },
57522
57523     /**
57524      * Locks the selections.
57525      */
57526     lock : function(){
57527         this.locked = true;
57528     },
57529
57530     /**
57531      * Unlocks the selections.
57532      */
57533     unlock : function(){
57534         this.locked = false;
57535     },
57536
57537     /**
57538      * Returns true if the selections are locked.
57539      * @return {Boolean}
57540      */
57541     isLocked : function(){
57542         return this.locked;
57543     }
57544 });/*
57545  * Based on:
57546  * Ext JS Library 1.1.1
57547  * Copyright(c) 2006-2007, Ext JS, LLC.
57548  *
57549  * Originally Released Under LGPL - original licence link has changed is not relivant.
57550  *
57551  * Fork - LGPL
57552  * <script type="text/javascript">
57553  */
57554 /**
57555  * @extends Roo.grid.AbstractSelectionModel
57556  * @class Roo.grid.RowSelectionModel
57557  * The default SelectionModel used by {@link Roo.grid.Grid}.
57558  * It supports multiple selections and keyboard selection/navigation. 
57559  * @constructor
57560  * @param {Object} config
57561  */
57562 Roo.grid.RowSelectionModel = function(config){
57563     Roo.apply(this, config);
57564     this.selections = new Roo.util.MixedCollection(false, function(o){
57565         return o.id;
57566     });
57567
57568     this.last = false;
57569     this.lastActive = false;
57570
57571     this.addEvents({
57572         /**
57573              * @event selectionchange
57574              * Fires when the selection changes
57575              * @param {SelectionModel} this
57576              */
57577             "selectionchange" : true,
57578         /**
57579              * @event afterselectionchange
57580              * Fires after the selection changes (eg. by key press or clicking)
57581              * @param {SelectionModel} this
57582              */
57583             "afterselectionchange" : true,
57584         /**
57585              * @event beforerowselect
57586              * Fires when a row is selected being selected, return false to cancel.
57587              * @param {SelectionModel} this
57588              * @param {Number} rowIndex The selected index
57589              * @param {Boolean} keepExisting False if other selections will be cleared
57590              */
57591             "beforerowselect" : true,
57592         /**
57593              * @event rowselect
57594              * Fires when a row is selected.
57595              * @param {SelectionModel} this
57596              * @param {Number} rowIndex The selected index
57597              * @param {Roo.data.Record} r The record
57598              */
57599             "rowselect" : true,
57600         /**
57601              * @event rowdeselect
57602              * Fires when a row is deselected.
57603              * @param {SelectionModel} this
57604              * @param {Number} rowIndex The selected index
57605              */
57606         "rowdeselect" : true
57607     });
57608     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
57609     this.locked = false;
57610 };
57611
57612 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
57613     /**
57614      * @cfg {Boolean} singleSelect
57615      * True to allow selection of only one row at a time (defaults to false)
57616      */
57617     singleSelect : false,
57618
57619     // private
57620     initEvents : function(){
57621
57622         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
57623             this.grid.on("mousedown", this.handleMouseDown, this);
57624         }else{ // allow click to work like normal
57625             this.grid.on("rowclick", this.handleDragableRowClick, this);
57626         }
57627
57628         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
57629             "up" : function(e){
57630                 if(!e.shiftKey){
57631                     this.selectPrevious(e.shiftKey);
57632                 }else if(this.last !== false && this.lastActive !== false){
57633                     var last = this.last;
57634                     this.selectRange(this.last,  this.lastActive-1);
57635                     this.grid.getView().focusRow(this.lastActive);
57636                     if(last !== false){
57637                         this.last = last;
57638                     }
57639                 }else{
57640                     this.selectFirstRow();
57641                 }
57642                 this.fireEvent("afterselectionchange", this);
57643             },
57644             "down" : function(e){
57645                 if(!e.shiftKey){
57646                     this.selectNext(e.shiftKey);
57647                 }else if(this.last !== false && this.lastActive !== false){
57648                     var last = this.last;
57649                     this.selectRange(this.last,  this.lastActive+1);
57650                     this.grid.getView().focusRow(this.lastActive);
57651                     if(last !== false){
57652                         this.last = last;
57653                     }
57654                 }else{
57655                     this.selectFirstRow();
57656                 }
57657                 this.fireEvent("afterselectionchange", this);
57658             },
57659             scope: this
57660         });
57661
57662         var view = this.grid.view;
57663         view.on("refresh", this.onRefresh, this);
57664         view.on("rowupdated", this.onRowUpdated, this);
57665         view.on("rowremoved", this.onRemove, this);
57666     },
57667
57668     // private
57669     onRefresh : function(){
57670         var ds = this.grid.dataSource, i, v = this.grid.view;
57671         var s = this.selections;
57672         s.each(function(r){
57673             if((i = ds.indexOfId(r.id)) != -1){
57674                 v.onRowSelect(i);
57675                 s.add(ds.getAt(i)); // updating the selection relate data
57676             }else{
57677                 s.remove(r);
57678             }
57679         });
57680     },
57681
57682     // private
57683     onRemove : function(v, index, r){
57684         this.selections.remove(r);
57685     },
57686
57687     // private
57688     onRowUpdated : function(v, index, r){
57689         if(this.isSelected(r)){
57690             v.onRowSelect(index);
57691         }
57692     },
57693
57694     /**
57695      * Select records.
57696      * @param {Array} records The records to select
57697      * @param {Boolean} keepExisting (optional) True to keep existing selections
57698      */
57699     selectRecords : function(records, keepExisting){
57700         if(!keepExisting){
57701             this.clearSelections();
57702         }
57703         var ds = this.grid.dataSource;
57704         for(var i = 0, len = records.length; i < len; i++){
57705             this.selectRow(ds.indexOf(records[i]), true);
57706         }
57707     },
57708
57709     /**
57710      * Gets the number of selected rows.
57711      * @return {Number}
57712      */
57713     getCount : function(){
57714         return this.selections.length;
57715     },
57716
57717     /**
57718      * Selects the first row in the grid.
57719      */
57720     selectFirstRow : function(){
57721         this.selectRow(0);
57722     },
57723
57724     /**
57725      * Select the last row.
57726      * @param {Boolean} keepExisting (optional) True to keep existing selections
57727      */
57728     selectLastRow : function(keepExisting){
57729         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
57730     },
57731
57732     /**
57733      * Selects the row immediately following the last selected row.
57734      * @param {Boolean} keepExisting (optional) True to keep existing selections
57735      */
57736     selectNext : function(keepExisting){
57737         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
57738             this.selectRow(this.last+1, keepExisting);
57739             this.grid.getView().focusRow(this.last);
57740         }
57741     },
57742
57743     /**
57744      * Selects the row that precedes the last selected row.
57745      * @param {Boolean} keepExisting (optional) True to keep existing selections
57746      */
57747     selectPrevious : function(keepExisting){
57748         if(this.last){
57749             this.selectRow(this.last-1, keepExisting);
57750             this.grid.getView().focusRow(this.last);
57751         }
57752     },
57753
57754     /**
57755      * Returns the selected records
57756      * @return {Array} Array of selected records
57757      */
57758     getSelections : function(){
57759         return [].concat(this.selections.items);
57760     },
57761
57762     /**
57763      * Returns the first selected record.
57764      * @return {Record}
57765      */
57766     getSelected : function(){
57767         return this.selections.itemAt(0);
57768     },
57769
57770
57771     /**
57772      * Clears all selections.
57773      */
57774     clearSelections : function(fast){
57775         if(this.locked) {
57776             return;
57777         }
57778         if(fast !== true){
57779             var ds = this.grid.dataSource;
57780             var s = this.selections;
57781             s.each(function(r){
57782                 this.deselectRow(ds.indexOfId(r.id));
57783             }, this);
57784             s.clear();
57785         }else{
57786             this.selections.clear();
57787         }
57788         this.last = false;
57789     },
57790
57791
57792     /**
57793      * Selects all rows.
57794      */
57795     selectAll : function(){
57796         if(this.locked) {
57797             return;
57798         }
57799         this.selections.clear();
57800         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
57801             this.selectRow(i, true);
57802         }
57803     },
57804
57805     /**
57806      * Returns True if there is a selection.
57807      * @return {Boolean}
57808      */
57809     hasSelection : function(){
57810         return this.selections.length > 0;
57811     },
57812
57813     /**
57814      * Returns True if the specified row is selected.
57815      * @param {Number/Record} record The record or index of the record to check
57816      * @return {Boolean}
57817      */
57818     isSelected : function(index){
57819         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
57820         return (r && this.selections.key(r.id) ? true : false);
57821     },
57822
57823     /**
57824      * Returns True if the specified record id is selected.
57825      * @param {String} id The id of record to check
57826      * @return {Boolean}
57827      */
57828     isIdSelected : function(id){
57829         return (this.selections.key(id) ? true : false);
57830     },
57831
57832     // private
57833     handleMouseDown : function(e, t){
57834         var view = this.grid.getView(), rowIndex;
57835         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
57836             return;
57837         };
57838         if(e.shiftKey && this.last !== false){
57839             var last = this.last;
57840             this.selectRange(last, rowIndex, e.ctrlKey);
57841             this.last = last; // reset the last
57842             view.focusRow(rowIndex);
57843         }else{
57844             var isSelected = this.isSelected(rowIndex);
57845             if(e.button !== 0 && isSelected){
57846                 view.focusRow(rowIndex);
57847             }else if(e.ctrlKey && isSelected){
57848                 this.deselectRow(rowIndex);
57849             }else if(!isSelected){
57850                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
57851                 view.focusRow(rowIndex);
57852             }
57853         }
57854         this.fireEvent("afterselectionchange", this);
57855     },
57856     // private
57857     handleDragableRowClick :  function(grid, rowIndex, e) 
57858     {
57859         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
57860             this.selectRow(rowIndex, false);
57861             grid.view.focusRow(rowIndex);
57862              this.fireEvent("afterselectionchange", this);
57863         }
57864     },
57865     
57866     /**
57867      * Selects multiple rows.
57868      * @param {Array} rows Array of the indexes of the row to select
57869      * @param {Boolean} keepExisting (optional) True to keep existing selections
57870      */
57871     selectRows : function(rows, keepExisting){
57872         if(!keepExisting){
57873             this.clearSelections();
57874         }
57875         for(var i = 0, len = rows.length; i < len; i++){
57876             this.selectRow(rows[i], true);
57877         }
57878     },
57879
57880     /**
57881      * Selects a range of rows. All rows in between startRow and endRow are also selected.
57882      * @param {Number} startRow The index of the first row in the range
57883      * @param {Number} endRow The index of the last row in the range
57884      * @param {Boolean} keepExisting (optional) True to retain existing selections
57885      */
57886     selectRange : function(startRow, endRow, keepExisting){
57887         if(this.locked) {
57888             return;
57889         }
57890         if(!keepExisting){
57891             this.clearSelections();
57892         }
57893         if(startRow <= endRow){
57894             for(var i = startRow; i <= endRow; i++){
57895                 this.selectRow(i, true);
57896             }
57897         }else{
57898             for(var i = startRow; i >= endRow; i--){
57899                 this.selectRow(i, true);
57900             }
57901         }
57902     },
57903
57904     /**
57905      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
57906      * @param {Number} startRow The index of the first row in the range
57907      * @param {Number} endRow The index of the last row in the range
57908      */
57909     deselectRange : function(startRow, endRow, preventViewNotify){
57910         if(this.locked) {
57911             return;
57912         }
57913         for(var i = startRow; i <= endRow; i++){
57914             this.deselectRow(i, preventViewNotify);
57915         }
57916     },
57917
57918     /**
57919      * Selects a row.
57920      * @param {Number} row The index of the row to select
57921      * @param {Boolean} keepExisting (optional) True to keep existing selections
57922      */
57923     selectRow : function(index, keepExisting, preventViewNotify){
57924         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
57925             return;
57926         }
57927         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
57928             if(!keepExisting || this.singleSelect){
57929                 this.clearSelections();
57930             }
57931             var r = this.grid.dataSource.getAt(index);
57932             this.selections.add(r);
57933             this.last = this.lastActive = index;
57934             if(!preventViewNotify){
57935                 this.grid.getView().onRowSelect(index);
57936             }
57937             this.fireEvent("rowselect", this, index, r);
57938             this.fireEvent("selectionchange", this);
57939         }
57940     },
57941
57942     /**
57943      * Deselects a row.
57944      * @param {Number} row The index of the row to deselect
57945      */
57946     deselectRow : function(index, preventViewNotify){
57947         if(this.locked) {
57948             return;
57949         }
57950         if(this.last == index){
57951             this.last = false;
57952         }
57953         if(this.lastActive == index){
57954             this.lastActive = false;
57955         }
57956         var r = this.grid.dataSource.getAt(index);
57957         this.selections.remove(r);
57958         if(!preventViewNotify){
57959             this.grid.getView().onRowDeselect(index);
57960         }
57961         this.fireEvent("rowdeselect", this, index);
57962         this.fireEvent("selectionchange", this);
57963     },
57964
57965     // private
57966     restoreLast : function(){
57967         if(this._last){
57968             this.last = this._last;
57969         }
57970     },
57971
57972     // private
57973     acceptsNav : function(row, col, cm){
57974         return !cm.isHidden(col) && cm.isCellEditable(col, row);
57975     },
57976
57977     // private
57978     onEditorKey : function(field, e){
57979         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
57980         if(k == e.TAB){
57981             e.stopEvent();
57982             ed.completeEdit();
57983             if(e.shiftKey){
57984                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
57985             }else{
57986                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
57987             }
57988         }else if(k == e.ENTER && !e.ctrlKey){
57989             e.stopEvent();
57990             ed.completeEdit();
57991             if(e.shiftKey){
57992                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
57993             }else{
57994                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
57995             }
57996         }else if(k == e.ESC){
57997             ed.cancelEdit();
57998         }
57999         if(newCell){
58000             g.startEditing(newCell[0], newCell[1]);
58001         }
58002     }
58003 });/*
58004  * Based on:
58005  * Ext JS Library 1.1.1
58006  * Copyright(c) 2006-2007, Ext JS, LLC.
58007  *
58008  * Originally Released Under LGPL - original licence link has changed is not relivant.
58009  *
58010  * Fork - LGPL
58011  * <script type="text/javascript">
58012  */
58013 /**
58014  * @class Roo.grid.CellSelectionModel
58015  * @extends Roo.grid.AbstractSelectionModel
58016  * This class provides the basic implementation for cell selection in a grid.
58017  * @constructor
58018  * @param {Object} config The object containing the configuration of this model.
58019  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
58020  */
58021 Roo.grid.CellSelectionModel = function(config){
58022     Roo.apply(this, config);
58023
58024     this.selection = null;
58025
58026     this.addEvents({
58027         /**
58028              * @event beforerowselect
58029              * Fires before a cell is selected.
58030              * @param {SelectionModel} this
58031              * @param {Number} rowIndex The selected row index
58032              * @param {Number} colIndex The selected cell index
58033              */
58034             "beforecellselect" : true,
58035         /**
58036              * @event cellselect
58037              * Fires when a cell is selected.
58038              * @param {SelectionModel} this
58039              * @param {Number} rowIndex The selected row index
58040              * @param {Number} colIndex The selected cell index
58041              */
58042             "cellselect" : true,
58043         /**
58044              * @event selectionchange
58045              * Fires when the active selection changes.
58046              * @param {SelectionModel} this
58047              * @param {Object} selection null for no selection or an object (o) with two properties
58048                 <ul>
58049                 <li>o.record: the record object for the row the selection is in</li>
58050                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
58051                 </ul>
58052              */
58053             "selectionchange" : true,
58054         /**
58055              * @event tabend
58056              * Fires when the tab (or enter) was pressed on the last editable cell
58057              * You can use this to trigger add new row.
58058              * @param {SelectionModel} this
58059              */
58060             "tabend" : true,
58061          /**
58062              * @event beforeeditnext
58063              * Fires before the next editable sell is made active
58064              * You can use this to skip to another cell or fire the tabend
58065              *    if you set cell to false
58066              * @param {Object} eventdata object : { cell : [ row, col ] } 
58067              */
58068             "beforeeditnext" : true
58069     });
58070     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
58071 };
58072
58073 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
58074     
58075     enter_is_tab: false,
58076
58077     /** @ignore */
58078     initEvents : function(){
58079         this.grid.on("mousedown", this.handleMouseDown, this);
58080         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
58081         var view = this.grid.view;
58082         view.on("refresh", this.onViewChange, this);
58083         view.on("rowupdated", this.onRowUpdated, this);
58084         view.on("beforerowremoved", this.clearSelections, this);
58085         view.on("beforerowsinserted", this.clearSelections, this);
58086         if(this.grid.isEditor){
58087             this.grid.on("beforeedit", this.beforeEdit,  this);
58088         }
58089     },
58090
58091         //private
58092     beforeEdit : function(e){
58093         this.select(e.row, e.column, false, true, e.record);
58094     },
58095
58096         //private
58097     onRowUpdated : function(v, index, r){
58098         if(this.selection && this.selection.record == r){
58099             v.onCellSelect(index, this.selection.cell[1]);
58100         }
58101     },
58102
58103         //private
58104     onViewChange : function(){
58105         this.clearSelections(true);
58106     },
58107
58108         /**
58109          * Returns the currently selected cell,.
58110          * @return {Array} The selected cell (row, column) or null if none selected.
58111          */
58112     getSelectedCell : function(){
58113         return this.selection ? this.selection.cell : null;
58114     },
58115
58116     /**
58117      * Clears all selections.
58118      * @param {Boolean} true to prevent the gridview from being notified about the change.
58119      */
58120     clearSelections : function(preventNotify){
58121         var s = this.selection;
58122         if(s){
58123             if(preventNotify !== true){
58124                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
58125             }
58126             this.selection = null;
58127             this.fireEvent("selectionchange", this, null);
58128         }
58129     },
58130
58131     /**
58132      * Returns true if there is a selection.
58133      * @return {Boolean}
58134      */
58135     hasSelection : function(){
58136         return this.selection ? true : false;
58137     },
58138
58139     /** @ignore */
58140     handleMouseDown : function(e, t){
58141         var v = this.grid.getView();
58142         if(this.isLocked()){
58143             return;
58144         };
58145         var row = v.findRowIndex(t);
58146         var cell = v.findCellIndex(t);
58147         if(row !== false && cell !== false){
58148             this.select(row, cell);
58149         }
58150     },
58151
58152     /**
58153      * Selects a cell.
58154      * @param {Number} rowIndex
58155      * @param {Number} collIndex
58156      */
58157     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
58158         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
58159             this.clearSelections();
58160             r = r || this.grid.dataSource.getAt(rowIndex);
58161             this.selection = {
58162                 record : r,
58163                 cell : [rowIndex, colIndex]
58164             };
58165             if(!preventViewNotify){
58166                 var v = this.grid.getView();
58167                 v.onCellSelect(rowIndex, colIndex);
58168                 if(preventFocus !== true){
58169                     v.focusCell(rowIndex, colIndex);
58170                 }
58171             }
58172             this.fireEvent("cellselect", this, rowIndex, colIndex);
58173             this.fireEvent("selectionchange", this, this.selection);
58174         }
58175     },
58176
58177         //private
58178     isSelectable : function(rowIndex, colIndex, cm){
58179         return !cm.isHidden(colIndex);
58180     },
58181
58182     /** @ignore */
58183     handleKeyDown : function(e){
58184         //Roo.log('Cell Sel Model handleKeyDown');
58185         if(!e.isNavKeyPress()){
58186             return;
58187         }
58188         var g = this.grid, s = this.selection;
58189         if(!s){
58190             e.stopEvent();
58191             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
58192             if(cell){
58193                 this.select(cell[0], cell[1]);
58194             }
58195             return;
58196         }
58197         var sm = this;
58198         var walk = function(row, col, step){
58199             return g.walkCells(row, col, step, sm.isSelectable,  sm);
58200         };
58201         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
58202         var newCell;
58203
58204       
58205
58206         switch(k){
58207             case e.TAB:
58208                 // handled by onEditorKey
58209                 if (g.isEditor && g.editing) {
58210                     return;
58211                 }
58212                 if(e.shiftKey) {
58213                     newCell = walk(r, c-1, -1);
58214                 } else {
58215                     newCell = walk(r, c+1, 1);
58216                 }
58217                 break;
58218             
58219             case e.DOWN:
58220                newCell = walk(r+1, c, 1);
58221                 break;
58222             
58223             case e.UP:
58224                 newCell = walk(r-1, c, -1);
58225                 break;
58226             
58227             case e.RIGHT:
58228                 newCell = walk(r, c+1, 1);
58229                 break;
58230             
58231             case e.LEFT:
58232                 newCell = walk(r, c-1, -1);
58233                 break;
58234             
58235             case e.ENTER:
58236                 
58237                 if(g.isEditor && !g.editing){
58238                    g.startEditing(r, c);
58239                    e.stopEvent();
58240                    return;
58241                 }
58242                 
58243                 
58244              break;
58245         };
58246         if(newCell){
58247             this.select(newCell[0], newCell[1]);
58248             e.stopEvent();
58249             
58250         }
58251     },
58252
58253     acceptsNav : function(row, col, cm){
58254         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58255     },
58256     /**
58257      * Selects a cell.
58258      * @param {Number} field (not used) - as it's normally used as a listener
58259      * @param {Number} e - event - fake it by using
58260      *
58261      * var e = Roo.EventObjectImpl.prototype;
58262      * e.keyCode = e.TAB
58263      *
58264      * 
58265      */
58266     onEditorKey : function(field, e){
58267         
58268         var k = e.getKey(),
58269             newCell,
58270             g = this.grid,
58271             ed = g.activeEditor,
58272             forward = false;
58273         ///Roo.log('onEditorKey' + k);
58274         
58275         
58276         if (this.enter_is_tab && k == e.ENTER) {
58277             k = e.TAB;
58278         }
58279         
58280         if(k == e.TAB){
58281             if(e.shiftKey){
58282                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58283             }else{
58284                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58285                 forward = true;
58286             }
58287             
58288             e.stopEvent();
58289             
58290         } else if(k == e.ENTER &&  !e.ctrlKey){
58291             ed.completeEdit();
58292             e.stopEvent();
58293             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58294         
58295                 } else if(k == e.ESC){
58296             ed.cancelEdit();
58297         }
58298                 
58299         if (newCell) {
58300             var ecall = { cell : newCell, forward : forward };
58301             this.fireEvent('beforeeditnext', ecall );
58302             newCell = ecall.cell;
58303                         forward = ecall.forward;
58304         }
58305                 
58306         if(newCell){
58307             //Roo.log('next cell after edit');
58308             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
58309         } else if (forward) {
58310             // tabbed past last
58311             this.fireEvent.defer(100, this, ['tabend',this]);
58312         }
58313     }
58314 });/*
58315  * Based on:
58316  * Ext JS Library 1.1.1
58317  * Copyright(c) 2006-2007, Ext JS, LLC.
58318  *
58319  * Originally Released Under LGPL - original licence link has changed is not relivant.
58320  *
58321  * Fork - LGPL
58322  * <script type="text/javascript">
58323  */
58324  
58325 /**
58326  * @class Roo.grid.EditorGrid
58327  * @extends Roo.grid.Grid
58328  * Class for creating and editable grid.
58329  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
58330  * The container MUST have some type of size defined for the grid to fill. The container will be 
58331  * automatically set to position relative if it isn't already.
58332  * @param {Object} dataSource The data model to bind to
58333  * @param {Object} colModel The column model with info about this grid's columns
58334  */
58335 Roo.grid.EditorGrid = function(container, config){
58336     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
58337     this.getGridEl().addClass("xedit-grid");
58338
58339     if(!this.selModel){
58340         this.selModel = new Roo.grid.CellSelectionModel();
58341     }
58342
58343     this.activeEditor = null;
58344
58345         this.addEvents({
58346             /**
58347              * @event beforeedit
58348              * Fires before cell editing is triggered. The edit event object has the following properties <br />
58349              * <ul style="padding:5px;padding-left:16px;">
58350              * <li>grid - This grid</li>
58351              * <li>record - The record being edited</li>
58352              * <li>field - The field name being edited</li>
58353              * <li>value - The value for the field being edited.</li>
58354              * <li>row - The grid row index</li>
58355              * <li>column - The grid column index</li>
58356              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58357              * </ul>
58358              * @param {Object} e An edit event (see above for description)
58359              */
58360             "beforeedit" : true,
58361             /**
58362              * @event afteredit
58363              * Fires after a cell is edited. <br />
58364              * <ul style="padding:5px;padding-left:16px;">
58365              * <li>grid - This grid</li>
58366              * <li>record - The record being edited</li>
58367              * <li>field - The field name being edited</li>
58368              * <li>value - The value being set</li>
58369              * <li>originalValue - The original value for the field, before the edit.</li>
58370              * <li>row - The grid row index</li>
58371              * <li>column - The grid column index</li>
58372              * </ul>
58373              * @param {Object} e An edit event (see above for description)
58374              */
58375             "afteredit" : true,
58376             /**
58377              * @event validateedit
58378              * Fires after a cell is edited, but before the value is set in the record. 
58379          * You can use this to modify the value being set in the field, Return false
58380              * to cancel the change. The edit event object has the following properties <br />
58381              * <ul style="padding:5px;padding-left:16px;">
58382          * <li>editor - This editor</li>
58383              * <li>grid - This grid</li>
58384              * <li>record - The record being edited</li>
58385              * <li>field - The field name being edited</li>
58386              * <li>value - The value being set</li>
58387              * <li>originalValue - The original value for the field, before the edit.</li>
58388              * <li>row - The grid row index</li>
58389              * <li>column - The grid column index</li>
58390              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58391              * </ul>
58392              * @param {Object} e An edit event (see above for description)
58393              */
58394             "validateedit" : true
58395         });
58396     this.on("bodyscroll", this.stopEditing,  this);
58397     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
58398 };
58399
58400 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
58401     /**
58402      * @cfg {Number} clicksToEdit
58403      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
58404      */
58405     clicksToEdit: 2,
58406
58407     // private
58408     isEditor : true,
58409     // private
58410     trackMouseOver: false, // causes very odd FF errors
58411
58412     onCellDblClick : function(g, row, col){
58413         this.startEditing(row, col);
58414     },
58415
58416     onEditComplete : function(ed, value, startValue){
58417         this.editing = false;
58418         this.activeEditor = null;
58419         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
58420         var r = ed.record;
58421         var field = this.colModel.getDataIndex(ed.col);
58422         var e = {
58423             grid: this,
58424             record: r,
58425             field: field,
58426             originalValue: startValue,
58427             value: value,
58428             row: ed.row,
58429             column: ed.col,
58430             cancel:false,
58431             editor: ed
58432         };
58433         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
58434         cell.show();
58435           
58436         if(String(value) !== String(startValue)){
58437             
58438             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
58439                 r.set(field, e.value);
58440                 // if we are dealing with a combo box..
58441                 // then we also set the 'name' colum to be the displayField
58442                 if (ed.field.displayField && ed.field.name) {
58443                     r.set(ed.field.name, ed.field.el.dom.value);
58444                 }
58445                 
58446                 delete e.cancel; //?? why!!!
58447                 this.fireEvent("afteredit", e);
58448             }
58449         } else {
58450             this.fireEvent("afteredit", e); // always fire it!
58451         }
58452         this.view.focusCell(ed.row, ed.col);
58453     },
58454
58455     /**
58456      * Starts editing the specified for the specified row/column
58457      * @param {Number} rowIndex
58458      * @param {Number} colIndex
58459      */
58460     startEditing : function(row, col){
58461         this.stopEditing();
58462         if(this.colModel.isCellEditable(col, row)){
58463             this.view.ensureVisible(row, col, true);
58464           
58465             var r = this.dataSource.getAt(row);
58466             var field = this.colModel.getDataIndex(col);
58467             var cell = Roo.get(this.view.getCell(row,col));
58468             var e = {
58469                 grid: this,
58470                 record: r,
58471                 field: field,
58472                 value: r.data[field],
58473                 row: row,
58474                 column: col,
58475                 cancel:false 
58476             };
58477             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
58478                 this.editing = true;
58479                 var ed = this.colModel.getCellEditor(col, row);
58480                 
58481                 if (!ed) {
58482                     return;
58483                 }
58484                 if(!ed.rendered){
58485                     ed.render(ed.parentEl || document.body);
58486                 }
58487                 ed.field.reset();
58488                
58489                 cell.hide();
58490                 
58491                 (function(){ // complex but required for focus issues in safari, ie and opera
58492                     ed.row = row;
58493                     ed.col = col;
58494                     ed.record = r;
58495                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
58496                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
58497                     this.activeEditor = ed;
58498                     var v = r.data[field];
58499                     ed.startEdit(this.view.getCell(row, col), v);
58500                     // combo's with 'displayField and name set
58501                     if (ed.field.displayField && ed.field.name) {
58502                         ed.field.el.dom.value = r.data[ed.field.name];
58503                     }
58504                     
58505                     
58506                 }).defer(50, this);
58507             }
58508         }
58509     },
58510         
58511     /**
58512      * Stops any active editing
58513      */
58514     stopEditing : function(){
58515         if(this.activeEditor){
58516             this.activeEditor.completeEdit();
58517         }
58518         this.activeEditor = null;
58519     },
58520         
58521          /**
58522      * Called to get grid's drag proxy text, by default returns this.ddText.
58523      * @return {String}
58524      */
58525     getDragDropText : function(){
58526         var count = this.selModel.getSelectedCell() ? 1 : 0;
58527         return String.format(this.ddText, count, count == 1 ? '' : 's');
58528     }
58529         
58530 });/*
58531  * Based on:
58532  * Ext JS Library 1.1.1
58533  * Copyright(c) 2006-2007, Ext JS, LLC.
58534  *
58535  * Originally Released Under LGPL - original licence link has changed is not relivant.
58536  *
58537  * Fork - LGPL
58538  * <script type="text/javascript">
58539  */
58540
58541 // private - not really -- you end up using it !
58542 // This is a support class used internally by the Grid components
58543
58544 /**
58545  * @class Roo.grid.GridEditor
58546  * @extends Roo.Editor
58547  * Class for creating and editable grid elements.
58548  * @param {Object} config any settings (must include field)
58549  */
58550 Roo.grid.GridEditor = function(field, config){
58551     if (!config && field.field) {
58552         config = field;
58553         field = Roo.factory(config.field, Roo.form);
58554     }
58555     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
58556     field.monitorTab = false;
58557 };
58558
58559 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
58560     
58561     /**
58562      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
58563      */
58564     
58565     alignment: "tl-tl",
58566     autoSize: "width",
58567     hideEl : false,
58568     cls: "x-small-editor x-grid-editor",
58569     shim:false,
58570     shadow:"frame"
58571 });/*
58572  * Based on:
58573  * Ext JS Library 1.1.1
58574  * Copyright(c) 2006-2007, Ext JS, LLC.
58575  *
58576  * Originally Released Under LGPL - original licence link has changed is not relivant.
58577  *
58578  * Fork - LGPL
58579  * <script type="text/javascript">
58580  */
58581   
58582
58583   
58584 Roo.grid.PropertyRecord = Roo.data.Record.create([
58585     {name:'name',type:'string'},  'value'
58586 ]);
58587
58588
58589 Roo.grid.PropertyStore = function(grid, source){
58590     this.grid = grid;
58591     this.store = new Roo.data.Store({
58592         recordType : Roo.grid.PropertyRecord
58593     });
58594     this.store.on('update', this.onUpdate,  this);
58595     if(source){
58596         this.setSource(source);
58597     }
58598     Roo.grid.PropertyStore.superclass.constructor.call(this);
58599 };
58600
58601
58602
58603 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
58604     setSource : function(o){
58605         this.source = o;
58606         this.store.removeAll();
58607         var data = [];
58608         for(var k in o){
58609             if(this.isEditableValue(o[k])){
58610                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
58611             }
58612         }
58613         this.store.loadRecords({records: data}, {}, true);
58614     },
58615
58616     onUpdate : function(ds, record, type){
58617         if(type == Roo.data.Record.EDIT){
58618             var v = record.data['value'];
58619             var oldValue = record.modified['value'];
58620             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
58621                 this.source[record.id] = v;
58622                 record.commit();
58623                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
58624             }else{
58625                 record.reject();
58626             }
58627         }
58628     },
58629
58630     getProperty : function(row){
58631        return this.store.getAt(row);
58632     },
58633
58634     isEditableValue: function(val){
58635         if(val && val instanceof Date){
58636             return true;
58637         }else if(typeof val == 'object' || typeof val == 'function'){
58638             return false;
58639         }
58640         return true;
58641     },
58642
58643     setValue : function(prop, value){
58644         this.source[prop] = value;
58645         this.store.getById(prop).set('value', value);
58646     },
58647
58648     getSource : function(){
58649         return this.source;
58650     }
58651 });
58652
58653 Roo.grid.PropertyColumnModel = function(grid, store){
58654     this.grid = grid;
58655     var g = Roo.grid;
58656     g.PropertyColumnModel.superclass.constructor.call(this, [
58657         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
58658         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
58659     ]);
58660     this.store = store;
58661     this.bselect = Roo.DomHelper.append(document.body, {
58662         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
58663             {tag: 'option', value: 'true', html: 'true'},
58664             {tag: 'option', value: 'false', html: 'false'}
58665         ]
58666     });
58667     Roo.id(this.bselect);
58668     var f = Roo.form;
58669     this.editors = {
58670         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
58671         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
58672         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
58673         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
58674         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
58675     };
58676     this.renderCellDelegate = this.renderCell.createDelegate(this);
58677     this.renderPropDelegate = this.renderProp.createDelegate(this);
58678 };
58679
58680 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
58681     
58682     
58683     nameText : 'Name',
58684     valueText : 'Value',
58685     
58686     dateFormat : 'm/j/Y',
58687     
58688     
58689     renderDate : function(dateVal){
58690         return dateVal.dateFormat(this.dateFormat);
58691     },
58692
58693     renderBool : function(bVal){
58694         return bVal ? 'true' : 'false';
58695     },
58696
58697     isCellEditable : function(colIndex, rowIndex){
58698         return colIndex == 1;
58699     },
58700
58701     getRenderer : function(col){
58702         return col == 1 ?
58703             this.renderCellDelegate : this.renderPropDelegate;
58704     },
58705
58706     renderProp : function(v){
58707         return this.getPropertyName(v);
58708     },
58709
58710     renderCell : function(val){
58711         var rv = val;
58712         if(val instanceof Date){
58713             rv = this.renderDate(val);
58714         }else if(typeof val == 'boolean'){
58715             rv = this.renderBool(val);
58716         }
58717         return Roo.util.Format.htmlEncode(rv);
58718     },
58719
58720     getPropertyName : function(name){
58721         var pn = this.grid.propertyNames;
58722         return pn && pn[name] ? pn[name] : name;
58723     },
58724
58725     getCellEditor : function(colIndex, rowIndex){
58726         var p = this.store.getProperty(rowIndex);
58727         var n = p.data['name'], val = p.data['value'];
58728         
58729         if(typeof(this.grid.customEditors[n]) == 'string'){
58730             return this.editors[this.grid.customEditors[n]];
58731         }
58732         if(typeof(this.grid.customEditors[n]) != 'undefined'){
58733             return this.grid.customEditors[n];
58734         }
58735         if(val instanceof Date){
58736             return this.editors['date'];
58737         }else if(typeof val == 'number'){
58738             return this.editors['number'];
58739         }else if(typeof val == 'boolean'){
58740             return this.editors['boolean'];
58741         }else{
58742             return this.editors['string'];
58743         }
58744     }
58745 });
58746
58747 /**
58748  * @class Roo.grid.PropertyGrid
58749  * @extends Roo.grid.EditorGrid
58750  * This class represents the  interface of a component based property grid control.
58751  * <br><br>Usage:<pre><code>
58752  var grid = new Roo.grid.PropertyGrid("my-container-id", {
58753       
58754  });
58755  // set any options
58756  grid.render();
58757  * </code></pre>
58758   
58759  * @constructor
58760  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58761  * The container MUST have some type of size defined for the grid to fill. The container will be
58762  * automatically set to position relative if it isn't already.
58763  * @param {Object} config A config object that sets properties on this grid.
58764  */
58765 Roo.grid.PropertyGrid = function(container, config){
58766     config = config || {};
58767     var store = new Roo.grid.PropertyStore(this);
58768     this.store = store;
58769     var cm = new Roo.grid.PropertyColumnModel(this, store);
58770     store.store.sort('name', 'ASC');
58771     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
58772         ds: store.store,
58773         cm: cm,
58774         enableColLock:false,
58775         enableColumnMove:false,
58776         stripeRows:false,
58777         trackMouseOver: false,
58778         clicksToEdit:1
58779     }, config));
58780     this.getGridEl().addClass('x-props-grid');
58781     this.lastEditRow = null;
58782     this.on('columnresize', this.onColumnResize, this);
58783     this.addEvents({
58784          /**
58785              * @event beforepropertychange
58786              * Fires before a property changes (return false to stop?)
58787              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58788              * @param {String} id Record Id
58789              * @param {String} newval New Value
58790          * @param {String} oldval Old Value
58791              */
58792         "beforepropertychange": true,
58793         /**
58794              * @event propertychange
58795              * Fires after a property changes
58796              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58797              * @param {String} id Record Id
58798              * @param {String} newval New Value
58799          * @param {String} oldval Old Value
58800              */
58801         "propertychange": true
58802     });
58803     this.customEditors = this.customEditors || {};
58804 };
58805 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
58806     
58807      /**
58808      * @cfg {Object} customEditors map of colnames=> custom editors.
58809      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
58810      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
58811      * false disables editing of the field.
58812          */
58813     
58814       /**
58815      * @cfg {Object} propertyNames map of property Names to their displayed value
58816          */
58817     
58818     render : function(){
58819         Roo.grid.PropertyGrid.superclass.render.call(this);
58820         this.autoSize.defer(100, this);
58821     },
58822
58823     autoSize : function(){
58824         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
58825         if(this.view){
58826             this.view.fitColumns();
58827         }
58828     },
58829
58830     onColumnResize : function(){
58831         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
58832         this.autoSize();
58833     },
58834     /**
58835      * Sets the data for the Grid
58836      * accepts a Key => Value object of all the elements avaiable.
58837      * @param {Object} data  to appear in grid.
58838      */
58839     setSource : function(source){
58840         this.store.setSource(source);
58841         //this.autoSize();
58842     },
58843     /**
58844      * Gets all the data from the grid.
58845      * @return {Object} data  data stored in grid
58846      */
58847     getSource : function(){
58848         return this.store.getSource();
58849     }
58850 });/*
58851   
58852  * Licence LGPL
58853  
58854  */
58855  
58856 /**
58857  * @class Roo.grid.Calendar
58858  * @extends Roo.util.Grid
58859  * This class extends the Grid to provide a calendar widget
58860  * <br><br>Usage:<pre><code>
58861  var grid = new Roo.grid.Calendar("my-container-id", {
58862      ds: myDataStore,
58863      cm: myColModel,
58864      selModel: mySelectionModel,
58865      autoSizeColumns: true,
58866      monitorWindowResize: false,
58867      trackMouseOver: true
58868      eventstore : real data store..
58869  });
58870  // set any options
58871  grid.render();
58872   
58873   * @constructor
58874  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58875  * The container MUST have some type of size defined for the grid to fill. The container will be
58876  * automatically set to position relative if it isn't already.
58877  * @param {Object} config A config object that sets properties on this grid.
58878  */
58879 Roo.grid.Calendar = function(container, config){
58880         // initialize the container
58881         this.container = Roo.get(container);
58882         this.container.update("");
58883         this.container.setStyle("overflow", "hidden");
58884     this.container.addClass('x-grid-container');
58885
58886     this.id = this.container.id;
58887
58888     Roo.apply(this, config);
58889     // check and correct shorthanded configs
58890     
58891     var rows = [];
58892     var d =1;
58893     for (var r = 0;r < 6;r++) {
58894         
58895         rows[r]=[];
58896         for (var c =0;c < 7;c++) {
58897             rows[r][c]= '';
58898         }
58899     }
58900     if (this.eventStore) {
58901         this.eventStore= Roo.factory(this.eventStore, Roo.data);
58902         this.eventStore.on('load',this.onLoad, this);
58903         this.eventStore.on('beforeload',this.clearEvents, this);
58904          
58905     }
58906     
58907     this.dataSource = new Roo.data.Store({
58908             proxy: new Roo.data.MemoryProxy(rows),
58909             reader: new Roo.data.ArrayReader({}, [
58910                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
58911     });
58912
58913     this.dataSource.load();
58914     this.ds = this.dataSource;
58915     this.ds.xmodule = this.xmodule || false;
58916     
58917     
58918     var cellRender = function(v,x,r)
58919     {
58920         return String.format(
58921             '<div class="fc-day  fc-widget-content"><div>' +
58922                 '<div class="fc-event-container"></div>' +
58923                 '<div class="fc-day-number">{0}</div>'+
58924                 
58925                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
58926             '</div></div>', v);
58927     
58928     }
58929     
58930     
58931     this.colModel = new Roo.grid.ColumnModel( [
58932         {
58933             xtype: 'ColumnModel',
58934             xns: Roo.grid,
58935             dataIndex : 'weekday0',
58936             header : 'Sunday',
58937             renderer : cellRender
58938         },
58939         {
58940             xtype: 'ColumnModel',
58941             xns: Roo.grid,
58942             dataIndex : 'weekday1',
58943             header : 'Monday',
58944             renderer : cellRender
58945         },
58946         {
58947             xtype: 'ColumnModel',
58948             xns: Roo.grid,
58949             dataIndex : 'weekday2',
58950             header : 'Tuesday',
58951             renderer : cellRender
58952         },
58953         {
58954             xtype: 'ColumnModel',
58955             xns: Roo.grid,
58956             dataIndex : 'weekday3',
58957             header : 'Wednesday',
58958             renderer : cellRender
58959         },
58960         {
58961             xtype: 'ColumnModel',
58962             xns: Roo.grid,
58963             dataIndex : 'weekday4',
58964             header : 'Thursday',
58965             renderer : cellRender
58966         },
58967         {
58968             xtype: 'ColumnModel',
58969             xns: Roo.grid,
58970             dataIndex : 'weekday5',
58971             header : 'Friday',
58972             renderer : cellRender
58973         },
58974         {
58975             xtype: 'ColumnModel',
58976             xns: Roo.grid,
58977             dataIndex : 'weekday6',
58978             header : 'Saturday',
58979             renderer : cellRender
58980         }
58981     ]);
58982     this.cm = this.colModel;
58983     this.cm.xmodule = this.xmodule || false;
58984  
58985         
58986           
58987     //this.selModel = new Roo.grid.CellSelectionModel();
58988     //this.sm = this.selModel;
58989     //this.selModel.init(this);
58990     
58991     
58992     if(this.width){
58993         this.container.setWidth(this.width);
58994     }
58995
58996     if(this.height){
58997         this.container.setHeight(this.height);
58998     }
58999     /** @private */
59000         this.addEvents({
59001         // raw events
59002         /**
59003          * @event click
59004          * The raw click event for the entire grid.
59005          * @param {Roo.EventObject} e
59006          */
59007         "click" : true,
59008         /**
59009          * @event dblclick
59010          * The raw dblclick event for the entire grid.
59011          * @param {Roo.EventObject} e
59012          */
59013         "dblclick" : true,
59014         /**
59015          * @event contextmenu
59016          * The raw contextmenu event for the entire grid.
59017          * @param {Roo.EventObject} e
59018          */
59019         "contextmenu" : true,
59020         /**
59021          * @event mousedown
59022          * The raw mousedown event for the entire grid.
59023          * @param {Roo.EventObject} e
59024          */
59025         "mousedown" : true,
59026         /**
59027          * @event mouseup
59028          * The raw mouseup event for the entire grid.
59029          * @param {Roo.EventObject} e
59030          */
59031         "mouseup" : true,
59032         /**
59033          * @event mouseover
59034          * The raw mouseover event for the entire grid.
59035          * @param {Roo.EventObject} e
59036          */
59037         "mouseover" : true,
59038         /**
59039          * @event mouseout
59040          * The raw mouseout event for the entire grid.
59041          * @param {Roo.EventObject} e
59042          */
59043         "mouseout" : true,
59044         /**
59045          * @event keypress
59046          * The raw keypress event for the entire grid.
59047          * @param {Roo.EventObject} e
59048          */
59049         "keypress" : true,
59050         /**
59051          * @event keydown
59052          * The raw keydown event for the entire grid.
59053          * @param {Roo.EventObject} e
59054          */
59055         "keydown" : true,
59056
59057         // custom events
59058
59059         /**
59060          * @event cellclick
59061          * Fires when a cell is clicked
59062          * @param {Grid} this
59063          * @param {Number} rowIndex
59064          * @param {Number} columnIndex
59065          * @param {Roo.EventObject} e
59066          */
59067         "cellclick" : true,
59068         /**
59069          * @event celldblclick
59070          * Fires when a cell is double clicked
59071          * @param {Grid} this
59072          * @param {Number} rowIndex
59073          * @param {Number} columnIndex
59074          * @param {Roo.EventObject} e
59075          */
59076         "celldblclick" : true,
59077         /**
59078          * @event rowclick
59079          * Fires when a row is clicked
59080          * @param {Grid} this
59081          * @param {Number} rowIndex
59082          * @param {Roo.EventObject} e
59083          */
59084         "rowclick" : true,
59085         /**
59086          * @event rowdblclick
59087          * Fires when a row is double clicked
59088          * @param {Grid} this
59089          * @param {Number} rowIndex
59090          * @param {Roo.EventObject} e
59091          */
59092         "rowdblclick" : true,
59093         /**
59094          * @event headerclick
59095          * Fires when a header is clicked
59096          * @param {Grid} this
59097          * @param {Number} columnIndex
59098          * @param {Roo.EventObject} e
59099          */
59100         "headerclick" : true,
59101         /**
59102          * @event headerdblclick
59103          * Fires when a header cell is double clicked
59104          * @param {Grid} this
59105          * @param {Number} columnIndex
59106          * @param {Roo.EventObject} e
59107          */
59108         "headerdblclick" : true,
59109         /**
59110          * @event rowcontextmenu
59111          * Fires when a row is right clicked
59112          * @param {Grid} this
59113          * @param {Number} rowIndex
59114          * @param {Roo.EventObject} e
59115          */
59116         "rowcontextmenu" : true,
59117         /**
59118          * @event cellcontextmenu
59119          * Fires when a cell is right clicked
59120          * @param {Grid} this
59121          * @param {Number} rowIndex
59122          * @param {Number} cellIndex
59123          * @param {Roo.EventObject} e
59124          */
59125          "cellcontextmenu" : true,
59126         /**
59127          * @event headercontextmenu
59128          * Fires when a header is right clicked
59129          * @param {Grid} this
59130          * @param {Number} columnIndex
59131          * @param {Roo.EventObject} e
59132          */
59133         "headercontextmenu" : true,
59134         /**
59135          * @event bodyscroll
59136          * Fires when the body element is scrolled
59137          * @param {Number} scrollLeft
59138          * @param {Number} scrollTop
59139          */
59140         "bodyscroll" : true,
59141         /**
59142          * @event columnresize
59143          * Fires when the user resizes a column
59144          * @param {Number} columnIndex
59145          * @param {Number} newSize
59146          */
59147         "columnresize" : true,
59148         /**
59149          * @event columnmove
59150          * Fires when the user moves a column
59151          * @param {Number} oldIndex
59152          * @param {Number} newIndex
59153          */
59154         "columnmove" : true,
59155         /**
59156          * @event startdrag
59157          * Fires when row(s) start being dragged
59158          * @param {Grid} this
59159          * @param {Roo.GridDD} dd The drag drop object
59160          * @param {event} e The raw browser event
59161          */
59162         "startdrag" : true,
59163         /**
59164          * @event enddrag
59165          * Fires when a drag operation is complete
59166          * @param {Grid} this
59167          * @param {Roo.GridDD} dd The drag drop object
59168          * @param {event} e The raw browser event
59169          */
59170         "enddrag" : true,
59171         /**
59172          * @event dragdrop
59173          * Fires when dragged row(s) are dropped on a valid DD target
59174          * @param {Grid} this
59175          * @param {Roo.GridDD} dd The drag drop object
59176          * @param {String} targetId The target drag drop object
59177          * @param {event} e The raw browser event
59178          */
59179         "dragdrop" : true,
59180         /**
59181          * @event dragover
59182          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
59183          * @param {Grid} this
59184          * @param {Roo.GridDD} dd The drag drop object
59185          * @param {String} targetId The target drag drop object
59186          * @param {event} e The raw browser event
59187          */
59188         "dragover" : true,
59189         /**
59190          * @event dragenter
59191          *  Fires when the dragged row(s) first cross another DD target while being dragged
59192          * @param {Grid} this
59193          * @param {Roo.GridDD} dd The drag drop object
59194          * @param {String} targetId The target drag drop object
59195          * @param {event} e The raw browser event
59196          */
59197         "dragenter" : true,
59198         /**
59199          * @event dragout
59200          * Fires when the dragged row(s) leave another DD target while being dragged
59201          * @param {Grid} this
59202          * @param {Roo.GridDD} dd The drag drop object
59203          * @param {String} targetId The target drag drop object
59204          * @param {event} e The raw browser event
59205          */
59206         "dragout" : true,
59207         /**
59208          * @event rowclass
59209          * Fires when a row is rendered, so you can change add a style to it.
59210          * @param {GridView} gridview   The grid view
59211          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
59212          */
59213         'rowclass' : true,
59214
59215         /**
59216          * @event render
59217          * Fires when the grid is rendered
59218          * @param {Grid} grid
59219          */
59220         'render' : true,
59221             /**
59222              * @event select
59223              * Fires when a date is selected
59224              * @param {DatePicker} this
59225              * @param {Date} date The selected date
59226              */
59227         'select': true,
59228         /**
59229              * @event monthchange
59230              * Fires when the displayed month changes 
59231              * @param {DatePicker} this
59232              * @param {Date} date The selected month
59233              */
59234         'monthchange': true,
59235         /**
59236              * @event evententer
59237              * Fires when mouse over an event
59238              * @param {Calendar} this
59239              * @param {event} Event
59240              */
59241         'evententer': true,
59242         /**
59243              * @event eventleave
59244              * Fires when the mouse leaves an
59245              * @param {Calendar} this
59246              * @param {event}
59247              */
59248         'eventleave': true,
59249         /**
59250              * @event eventclick
59251              * Fires when the mouse click an
59252              * @param {Calendar} this
59253              * @param {event}
59254              */
59255         'eventclick': true,
59256         /**
59257              * @event eventrender
59258              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
59259              * @param {Calendar} this
59260              * @param {data} data to be modified
59261              */
59262         'eventrender': true
59263         
59264     });
59265
59266     Roo.grid.Grid.superclass.constructor.call(this);
59267     this.on('render', function() {
59268         this.view.el.addClass('x-grid-cal'); 
59269         
59270         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
59271
59272     },this);
59273     
59274     if (!Roo.grid.Calendar.style) {
59275         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
59276             
59277             
59278             '.x-grid-cal .x-grid-col' :  {
59279                 height: 'auto !important',
59280                 'vertical-align': 'top'
59281             },
59282             '.x-grid-cal  .fc-event-hori' : {
59283                 height: '14px'
59284             }
59285              
59286             
59287         }, Roo.id());
59288     }
59289
59290     
59291     
59292 };
59293 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
59294     /**
59295      * @cfg {Store} eventStore The store that loads events.
59296      */
59297     eventStore : 25,
59298
59299      
59300     activeDate : false,
59301     startDay : 0,
59302     autoWidth : true,
59303     monitorWindowResize : false,
59304
59305     
59306     resizeColumns : function() {
59307         var col = (this.view.el.getWidth() / 7) - 3;
59308         // loop through cols, and setWidth
59309         for(var i =0 ; i < 7 ; i++){
59310             this.cm.setColumnWidth(i, col);
59311         }
59312     },
59313      setDate :function(date) {
59314         
59315         Roo.log('setDate?');
59316         
59317         this.resizeColumns();
59318         var vd = this.activeDate;
59319         this.activeDate = date;
59320 //        if(vd && this.el){
59321 //            var t = date.getTime();
59322 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
59323 //                Roo.log('using add remove');
59324 //                
59325 //                this.fireEvent('monthchange', this, date);
59326 //                
59327 //                this.cells.removeClass("fc-state-highlight");
59328 //                this.cells.each(function(c){
59329 //                   if(c.dateValue == t){
59330 //                       c.addClass("fc-state-highlight");
59331 //                       setTimeout(function(){
59332 //                            try{c.dom.firstChild.focus();}catch(e){}
59333 //                       }, 50);
59334 //                       return false;
59335 //                   }
59336 //                   return true;
59337 //                });
59338 //                return;
59339 //            }
59340 //        }
59341         
59342         var days = date.getDaysInMonth();
59343         
59344         var firstOfMonth = date.getFirstDateOfMonth();
59345         var startingPos = firstOfMonth.getDay()-this.startDay;
59346         
59347         if(startingPos < this.startDay){
59348             startingPos += 7;
59349         }
59350         
59351         var pm = date.add(Date.MONTH, -1);
59352         var prevStart = pm.getDaysInMonth()-startingPos;
59353 //        
59354         
59355         
59356         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59357         
59358         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
59359         //this.cells.addClassOnOver('fc-state-hover');
59360         
59361         var cells = this.cells.elements;
59362         var textEls = this.textNodes;
59363         
59364         //Roo.each(cells, function(cell){
59365         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
59366         //});
59367         
59368         days += startingPos;
59369
59370         // convert everything to numbers so it's fast
59371         var day = 86400000;
59372         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
59373         //Roo.log(d);
59374         //Roo.log(pm);
59375         //Roo.log(prevStart);
59376         
59377         var today = new Date().clearTime().getTime();
59378         var sel = date.clearTime().getTime();
59379         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
59380         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
59381         var ddMatch = this.disabledDatesRE;
59382         var ddText = this.disabledDatesText;
59383         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
59384         var ddaysText = this.disabledDaysText;
59385         var format = this.format;
59386         
59387         var setCellClass = function(cal, cell){
59388             
59389             //Roo.log('set Cell Class');
59390             cell.title = "";
59391             var t = d.getTime();
59392             
59393             //Roo.log(d);
59394             
59395             
59396             cell.dateValue = t;
59397             if(t == today){
59398                 cell.className += " fc-today";
59399                 cell.className += " fc-state-highlight";
59400                 cell.title = cal.todayText;
59401             }
59402             if(t == sel){
59403                 // disable highlight in other month..
59404                 cell.className += " fc-state-highlight";
59405                 
59406             }
59407             // disabling
59408             if(t < min) {
59409                 //cell.className = " fc-state-disabled";
59410                 cell.title = cal.minText;
59411                 return;
59412             }
59413             if(t > max) {
59414                 //cell.className = " fc-state-disabled";
59415                 cell.title = cal.maxText;
59416                 return;
59417             }
59418             if(ddays){
59419                 if(ddays.indexOf(d.getDay()) != -1){
59420                     // cell.title = ddaysText;
59421                    // cell.className = " fc-state-disabled";
59422                 }
59423             }
59424             if(ddMatch && format){
59425                 var fvalue = d.dateFormat(format);
59426                 if(ddMatch.test(fvalue)){
59427                     cell.title = ddText.replace("%0", fvalue);
59428                    cell.className = " fc-state-disabled";
59429                 }
59430             }
59431             
59432             if (!cell.initialClassName) {
59433                 cell.initialClassName = cell.dom.className;
59434             }
59435             
59436             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
59437         };
59438
59439         var i = 0;
59440         
59441         for(; i < startingPos; i++) {
59442             cells[i].dayName =  (++prevStart);
59443             Roo.log(textEls[i]);
59444             d.setDate(d.getDate()+1);
59445             
59446             //cells[i].className = "fc-past fc-other-month";
59447             setCellClass(this, cells[i]);
59448         }
59449         
59450         var intDay = 0;
59451         
59452         for(; i < days; i++){
59453             intDay = i - startingPos + 1;
59454             cells[i].dayName =  (intDay);
59455             d.setDate(d.getDate()+1);
59456             
59457             cells[i].className = ''; // "x-date-active";
59458             setCellClass(this, cells[i]);
59459         }
59460         var extraDays = 0;
59461         
59462         for(; i < 42; i++) {
59463             //textEls[i].innerHTML = (++extraDays);
59464             
59465             d.setDate(d.getDate()+1);
59466             cells[i].dayName = (++extraDays);
59467             cells[i].className = "fc-future fc-other-month";
59468             setCellClass(this, cells[i]);
59469         }
59470         
59471         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
59472         
59473         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
59474         
59475         // this will cause all the cells to mis
59476         var rows= [];
59477         var i =0;
59478         for (var r = 0;r < 6;r++) {
59479             for (var c =0;c < 7;c++) {
59480                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
59481             }    
59482         }
59483         
59484         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59485         for(i=0;i<cells.length;i++) {
59486             
59487             this.cells.elements[i].dayName = cells[i].dayName ;
59488             this.cells.elements[i].className = cells[i].className;
59489             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
59490             this.cells.elements[i].title = cells[i].title ;
59491             this.cells.elements[i].dateValue = cells[i].dateValue ;
59492         }
59493         
59494         
59495         
59496         
59497         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
59498         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
59499         
59500         ////if(totalRows != 6){
59501             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
59502            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
59503        // }
59504         
59505         this.fireEvent('monthchange', this, date);
59506         
59507         
59508     },
59509  /**
59510      * Returns the grid's SelectionModel.
59511      * @return {SelectionModel}
59512      */
59513     getSelectionModel : function(){
59514         if(!this.selModel){
59515             this.selModel = new Roo.grid.CellSelectionModel();
59516         }
59517         return this.selModel;
59518     },
59519
59520     load: function() {
59521         this.eventStore.load()
59522         
59523         
59524         
59525     },
59526     
59527     findCell : function(dt) {
59528         dt = dt.clearTime().getTime();
59529         var ret = false;
59530         this.cells.each(function(c){
59531             //Roo.log("check " +c.dateValue + '?=' + dt);
59532             if(c.dateValue == dt){
59533                 ret = c;
59534                 return false;
59535             }
59536             return true;
59537         });
59538         
59539         return ret;
59540     },
59541     
59542     findCells : function(rec) {
59543         var s = rec.data.start_dt.clone().clearTime().getTime();
59544        // Roo.log(s);
59545         var e= rec.data.end_dt.clone().clearTime().getTime();
59546        // Roo.log(e);
59547         var ret = [];
59548         this.cells.each(function(c){
59549              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
59550             
59551             if(c.dateValue > e){
59552                 return ;
59553             }
59554             if(c.dateValue < s){
59555                 return ;
59556             }
59557             ret.push(c);
59558         });
59559         
59560         return ret;    
59561     },
59562     
59563     findBestRow: function(cells)
59564     {
59565         var ret = 0;
59566         
59567         for (var i =0 ; i < cells.length;i++) {
59568             ret  = Math.max(cells[i].rows || 0,ret);
59569         }
59570         return ret;
59571         
59572     },
59573     
59574     
59575     addItem : function(rec)
59576     {
59577         // look for vertical location slot in
59578         var cells = this.findCells(rec);
59579         
59580         rec.row = this.findBestRow(cells);
59581         
59582         // work out the location.
59583         
59584         var crow = false;
59585         var rows = [];
59586         for(var i =0; i < cells.length; i++) {
59587             if (!crow) {
59588                 crow = {
59589                     start : cells[i],
59590                     end :  cells[i]
59591                 };
59592                 continue;
59593             }
59594             if (crow.start.getY() == cells[i].getY()) {
59595                 // on same row.
59596                 crow.end = cells[i];
59597                 continue;
59598             }
59599             // different row.
59600             rows.push(crow);
59601             crow = {
59602                 start: cells[i],
59603                 end : cells[i]
59604             };
59605             
59606         }
59607         
59608         rows.push(crow);
59609         rec.els = [];
59610         rec.rows = rows;
59611         rec.cells = cells;
59612         for (var i = 0; i < cells.length;i++) {
59613             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
59614             
59615         }
59616         
59617         
59618     },
59619     
59620     clearEvents: function() {
59621         
59622         if (!this.eventStore.getCount()) {
59623             return;
59624         }
59625         // reset number of rows in cells.
59626         Roo.each(this.cells.elements, function(c){
59627             c.rows = 0;
59628         });
59629         
59630         this.eventStore.each(function(e) {
59631             this.clearEvent(e);
59632         },this);
59633         
59634     },
59635     
59636     clearEvent : function(ev)
59637     {
59638         if (ev.els) {
59639             Roo.each(ev.els, function(el) {
59640                 el.un('mouseenter' ,this.onEventEnter, this);
59641                 el.un('mouseleave' ,this.onEventLeave, this);
59642                 el.remove();
59643             },this);
59644             ev.els = [];
59645         }
59646     },
59647     
59648     
59649     renderEvent : function(ev,ctr) {
59650         if (!ctr) {
59651              ctr = this.view.el.select('.fc-event-container',true).first();
59652         }
59653         
59654          
59655         this.clearEvent(ev);
59656             //code
59657        
59658         
59659         
59660         ev.els = [];
59661         var cells = ev.cells;
59662         var rows = ev.rows;
59663         this.fireEvent('eventrender', this, ev);
59664         
59665         for(var i =0; i < rows.length; i++) {
59666             
59667             cls = '';
59668             if (i == 0) {
59669                 cls += ' fc-event-start';
59670             }
59671             if ((i+1) == rows.length) {
59672                 cls += ' fc-event-end';
59673             }
59674             
59675             //Roo.log(ev.data);
59676             // how many rows should it span..
59677             var cg = this.eventTmpl.append(ctr,Roo.apply({
59678                 fccls : cls
59679                 
59680             }, ev.data) , true);
59681             
59682             
59683             cg.on('mouseenter' ,this.onEventEnter, this, ev);
59684             cg.on('mouseleave' ,this.onEventLeave, this, ev);
59685             cg.on('click', this.onEventClick, this, ev);
59686             
59687             ev.els.push(cg);
59688             
59689             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
59690             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
59691             //Roo.log(cg);
59692              
59693             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
59694             cg.setWidth(ebox.right - sbox.x -2);
59695         }
59696     },
59697     
59698     renderEvents: function()
59699     {   
59700         // first make sure there is enough space..
59701         
59702         if (!this.eventTmpl) {
59703             this.eventTmpl = new Roo.Template(
59704                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
59705                     '<div class="fc-event-inner">' +
59706                         '<span class="fc-event-time">{time}</span>' +
59707                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
59708                     '</div>' +
59709                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
59710                 '</div>'
59711             );
59712                 
59713         }
59714                
59715         
59716         
59717         this.cells.each(function(c) {
59718             //Roo.log(c.select('.fc-day-content div',true).first());
59719             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
59720         });
59721         
59722         var ctr = this.view.el.select('.fc-event-container',true).first();
59723         
59724         var cls;
59725         this.eventStore.each(function(ev){
59726             
59727             this.renderEvent(ev);
59728              
59729              
59730         }, this);
59731         this.view.layout();
59732         
59733     },
59734     
59735     onEventEnter: function (e, el,event,d) {
59736         this.fireEvent('evententer', this, el, event);
59737     },
59738     
59739     onEventLeave: function (e, el,event,d) {
59740         this.fireEvent('eventleave', this, el, event);
59741     },
59742     
59743     onEventClick: function (e, el,event,d) {
59744         this.fireEvent('eventclick', this, el, event);
59745     },
59746     
59747     onMonthChange: function () {
59748         this.store.load();
59749     },
59750     
59751     onLoad: function () {
59752         
59753         //Roo.log('calendar onload');
59754 //         
59755         if(this.eventStore.getCount() > 0){
59756             
59757            
59758             
59759             this.eventStore.each(function(d){
59760                 
59761                 
59762                 // FIXME..
59763                 var add =   d.data;
59764                 if (typeof(add.end_dt) == 'undefined')  {
59765                     Roo.log("Missing End time in calendar data: ");
59766                     Roo.log(d);
59767                     return;
59768                 }
59769                 if (typeof(add.start_dt) == 'undefined')  {
59770                     Roo.log("Missing Start time in calendar data: ");
59771                     Roo.log(d);
59772                     return;
59773                 }
59774                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
59775                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
59776                 add.id = add.id || d.id;
59777                 add.title = add.title || '??';
59778                 
59779                 this.addItem(d);
59780                 
59781              
59782             },this);
59783         }
59784         
59785         this.renderEvents();
59786     }
59787     
59788
59789 });
59790 /*
59791  grid : {
59792                 xtype: 'Grid',
59793                 xns: Roo.grid,
59794                 listeners : {
59795                     render : function ()
59796                     {
59797                         _this.grid = this;
59798                         
59799                         if (!this.view.el.hasClass('course-timesheet')) {
59800                             this.view.el.addClass('course-timesheet');
59801                         }
59802                         if (this.tsStyle) {
59803                             this.ds.load({});
59804                             return; 
59805                         }
59806                         Roo.log('width');
59807                         Roo.log(_this.grid.view.el.getWidth());
59808                         
59809                         
59810                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
59811                             '.course-timesheet .x-grid-row' : {
59812                                 height: '80px'
59813                             },
59814                             '.x-grid-row td' : {
59815                                 'vertical-align' : 0
59816                             },
59817                             '.course-edit-link' : {
59818                                 'color' : 'blue',
59819                                 'text-overflow' : 'ellipsis',
59820                                 'overflow' : 'hidden',
59821                                 'white-space' : 'nowrap',
59822                                 'cursor' : 'pointer'
59823                             },
59824                             '.sub-link' : {
59825                                 'color' : 'green'
59826                             },
59827                             '.de-act-sup-link' : {
59828                                 'color' : 'purple',
59829                                 'text-decoration' : 'line-through'
59830                             },
59831                             '.de-act-link' : {
59832                                 'color' : 'red',
59833                                 'text-decoration' : 'line-through'
59834                             },
59835                             '.course-timesheet .course-highlight' : {
59836                                 'border-top-style': 'dashed !important',
59837                                 'border-bottom-bottom': 'dashed !important'
59838                             },
59839                             '.course-timesheet .course-item' : {
59840                                 'font-family'   : 'tahoma, arial, helvetica',
59841                                 'font-size'     : '11px',
59842                                 'overflow'      : 'hidden',
59843                                 'padding-left'  : '10px',
59844                                 'padding-right' : '10px',
59845                                 'padding-top' : '10px' 
59846                             }
59847                             
59848                         }, Roo.id());
59849                                 this.ds.load({});
59850                     }
59851                 },
59852                 autoWidth : true,
59853                 monitorWindowResize : false,
59854                 cellrenderer : function(v,x,r)
59855                 {
59856                     return v;
59857                 },
59858                 sm : {
59859                     xtype: 'CellSelectionModel',
59860                     xns: Roo.grid
59861                 },
59862                 dataSource : {
59863                     xtype: 'Store',
59864                     xns: Roo.data,
59865                     listeners : {
59866                         beforeload : function (_self, options)
59867                         {
59868                             options.params = options.params || {};
59869                             options.params._month = _this.monthField.getValue();
59870                             options.params.limit = 9999;
59871                             options.params['sort'] = 'when_dt';    
59872                             options.params['dir'] = 'ASC';    
59873                             this.proxy.loadResponse = this.loadResponse;
59874                             Roo.log("load?");
59875                             //this.addColumns();
59876                         },
59877                         load : function (_self, records, options)
59878                         {
59879                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
59880                                 // if you click on the translation.. you can edit it...
59881                                 var el = Roo.get(this);
59882                                 var id = el.dom.getAttribute('data-id');
59883                                 var d = el.dom.getAttribute('data-date');
59884                                 var t = el.dom.getAttribute('data-time');
59885                                 //var id = this.child('span').dom.textContent;
59886                                 
59887                                 //Roo.log(this);
59888                                 Pman.Dialog.CourseCalendar.show({
59889                                     id : id,
59890                                     when_d : d,
59891                                     when_t : t,
59892                                     productitem_active : id ? 1 : 0
59893                                 }, function() {
59894                                     _this.grid.ds.load({});
59895                                 });
59896                            
59897                            });
59898                            
59899                            _this.panel.fireEvent('resize', [ '', '' ]);
59900                         }
59901                     },
59902                     loadResponse : function(o, success, response){
59903                             // this is overridden on before load..
59904                             
59905                             Roo.log("our code?");       
59906                             //Roo.log(success);
59907                             //Roo.log(response)
59908                             delete this.activeRequest;
59909                             if(!success){
59910                                 this.fireEvent("loadexception", this, o, response);
59911                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59912                                 return;
59913                             }
59914                             var result;
59915                             try {
59916                                 result = o.reader.read(response);
59917                             }catch(e){
59918                                 Roo.log("load exception?");
59919                                 this.fireEvent("loadexception", this, o, response, e);
59920                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59921                                 return;
59922                             }
59923                             Roo.log("ready...");        
59924                             // loop through result.records;
59925                             // and set this.tdate[date] = [] << array of records..
59926                             _this.tdata  = {};
59927                             Roo.each(result.records, function(r){
59928                                 //Roo.log(r.data);
59929                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
59930                                     _this.tdata[r.data.when_dt.format('j')] = [];
59931                                 }
59932                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
59933                             });
59934                             
59935                             //Roo.log(_this.tdata);
59936                             
59937                             result.records = [];
59938                             result.totalRecords = 6;
59939                     
59940                             // let's generate some duumy records for the rows.
59941                             //var st = _this.dateField.getValue();
59942                             
59943                             // work out monday..
59944                             //st = st.add(Date.DAY, -1 * st.format('w'));
59945                             
59946                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
59947                             
59948                             var firstOfMonth = date.getFirstDayOfMonth();
59949                             var days = date.getDaysInMonth();
59950                             var d = 1;
59951                             var firstAdded = false;
59952                             for (var i = 0; i < result.totalRecords ; i++) {
59953                                 //var d= st.add(Date.DAY, i);
59954                                 var row = {};
59955                                 var added = 0;
59956                                 for(var w = 0 ; w < 7 ; w++){
59957                                     if(!firstAdded && firstOfMonth != w){
59958                                         continue;
59959                                     }
59960                                     if(d > days){
59961                                         continue;
59962                                     }
59963                                     firstAdded = true;
59964                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
59965                                     row['weekday'+w] = String.format(
59966                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
59967                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
59968                                                     d,
59969                                                     date.format('Y-m-')+dd
59970                                                 );
59971                                     added++;
59972                                     if(typeof(_this.tdata[d]) != 'undefined'){
59973                                         Roo.each(_this.tdata[d], function(r){
59974                                             var is_sub = '';
59975                                             var deactive = '';
59976                                             var id = r.id;
59977                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
59978                                             if(r.parent_id*1>0){
59979                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
59980                                                 id = r.parent_id;
59981                                             }
59982                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
59983                                                 deactive = 'de-act-link';
59984                                             }
59985                                             
59986                                             row['weekday'+w] += String.format(
59987                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
59988                                                     id, //0
59989                                                     r.product_id_name, //1
59990                                                     r.when_dt.format('h:ia'), //2
59991                                                     is_sub, //3
59992                                                     deactive, //4
59993                                                     desc // 5
59994                                             );
59995                                         });
59996                                     }
59997                                     d++;
59998                                 }
59999                                 
60000                                 // only do this if something added..
60001                                 if(added > 0){ 
60002                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
60003                                 }
60004                                 
60005                                 
60006                                 // push it twice. (second one with an hour..
60007                                 
60008                             }
60009                             //Roo.log(result);
60010                             this.fireEvent("load", this, o, o.request.arg);
60011                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
60012                         },
60013                     sortInfo : {field: 'when_dt', direction : 'ASC' },
60014                     proxy : {
60015                         xtype: 'HttpProxy',
60016                         xns: Roo.data,
60017                         method : 'GET',
60018                         url : baseURL + '/Roo/Shop_course.php'
60019                     },
60020                     reader : {
60021                         xtype: 'JsonReader',
60022                         xns: Roo.data,
60023                         id : 'id',
60024                         fields : [
60025                             {
60026                                 'name': 'id',
60027                                 'type': 'int'
60028                             },
60029                             {
60030                                 'name': 'when_dt',
60031                                 'type': 'string'
60032                             },
60033                             {
60034                                 'name': 'end_dt',
60035                                 'type': 'string'
60036                             },
60037                             {
60038                                 'name': 'parent_id',
60039                                 'type': 'int'
60040                             },
60041                             {
60042                                 'name': 'product_id',
60043                                 'type': 'int'
60044                             },
60045                             {
60046                                 'name': 'productitem_id',
60047                                 'type': 'int'
60048                             },
60049                             {
60050                                 'name': 'guid',
60051                                 'type': 'int'
60052                             }
60053                         ]
60054                     }
60055                 },
60056                 toolbar : {
60057                     xtype: 'Toolbar',
60058                     xns: Roo,
60059                     items : [
60060                         {
60061                             xtype: 'Button',
60062                             xns: Roo.Toolbar,
60063                             listeners : {
60064                                 click : function (_self, e)
60065                                 {
60066                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60067                                     sd.setMonth(sd.getMonth()-1);
60068                                     _this.monthField.setValue(sd.format('Y-m-d'));
60069                                     _this.grid.ds.load({});
60070                                 }
60071                             },
60072                             text : "Back"
60073                         },
60074                         {
60075                             xtype: 'Separator',
60076                             xns: Roo.Toolbar
60077                         },
60078                         {
60079                             xtype: 'MonthField',
60080                             xns: Roo.form,
60081                             listeners : {
60082                                 render : function (_self)
60083                                 {
60084                                     _this.monthField = _self;
60085                                    // _this.monthField.set  today
60086                                 },
60087                                 select : function (combo, date)
60088                                 {
60089                                     _this.grid.ds.load({});
60090                                 }
60091                             },
60092                             value : (function() { return new Date(); })()
60093                         },
60094                         {
60095                             xtype: 'Separator',
60096                             xns: Roo.Toolbar
60097                         },
60098                         {
60099                             xtype: 'TextItem',
60100                             xns: Roo.Toolbar,
60101                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
60102                         },
60103                         {
60104                             xtype: 'Fill',
60105                             xns: Roo.Toolbar
60106                         },
60107                         {
60108                             xtype: 'Button',
60109                             xns: Roo.Toolbar,
60110                             listeners : {
60111                                 click : function (_self, e)
60112                                 {
60113                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60114                                     sd.setMonth(sd.getMonth()+1);
60115                                     _this.monthField.setValue(sd.format('Y-m-d'));
60116                                     _this.grid.ds.load({});
60117                                 }
60118                             },
60119                             text : "Next"
60120                         }
60121                     ]
60122                 },
60123                  
60124             }
60125         };
60126         
60127         *//*
60128  * Based on:
60129  * Ext JS Library 1.1.1
60130  * Copyright(c) 2006-2007, Ext JS, LLC.
60131  *
60132  * Originally Released Under LGPL - original licence link has changed is not relivant.
60133  *
60134  * Fork - LGPL
60135  * <script type="text/javascript">
60136  */
60137  
60138 /**
60139  * @class Roo.LoadMask
60140  * A simple utility class for generically masking elements while loading data.  If the element being masked has
60141  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
60142  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
60143  * element's UpdateManager load indicator and will be destroyed after the initial load.
60144  * @constructor
60145  * Create a new LoadMask
60146  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
60147  * @param {Object} config The config object
60148  */
60149 Roo.LoadMask = function(el, config){
60150     this.el = Roo.get(el);
60151     Roo.apply(this, config);
60152     if(this.store){
60153         this.store.on('beforeload', this.onBeforeLoad, this);
60154         this.store.on('load', this.onLoad, this);
60155         this.store.on('loadexception', this.onLoadException, this);
60156         this.removeMask = false;
60157     }else{
60158         var um = this.el.getUpdateManager();
60159         um.showLoadIndicator = false; // disable the default indicator
60160         um.on('beforeupdate', this.onBeforeLoad, this);
60161         um.on('update', this.onLoad, this);
60162         um.on('failure', this.onLoad, this);
60163         this.removeMask = true;
60164     }
60165 };
60166
60167 Roo.LoadMask.prototype = {
60168     /**
60169      * @cfg {Boolean} removeMask
60170      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
60171      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
60172      */
60173     /**
60174      * @cfg {String} msg
60175      * The text to display in a centered loading message box (defaults to 'Loading...')
60176      */
60177     msg : 'Loading...',
60178     /**
60179      * @cfg {String} msgCls
60180      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
60181      */
60182     msgCls : 'x-mask-loading',
60183
60184     /**
60185      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
60186      * @type Boolean
60187      */
60188     disabled: false,
60189
60190     /**
60191      * Disables the mask to prevent it from being displayed
60192      */
60193     disable : function(){
60194        this.disabled = true;
60195     },
60196
60197     /**
60198      * Enables the mask so that it can be displayed
60199      */
60200     enable : function(){
60201         this.disabled = false;
60202     },
60203     
60204     onLoadException : function()
60205     {
60206         Roo.log(arguments);
60207         
60208         if (typeof(arguments[3]) != 'undefined') {
60209             Roo.MessageBox.alert("Error loading",arguments[3]);
60210         } 
60211         /*
60212         try {
60213             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
60214                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
60215             }   
60216         } catch(e) {
60217             
60218         }
60219         */
60220     
60221         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60222     },
60223     // private
60224     onLoad : function()
60225     {
60226         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60227     },
60228
60229     // private
60230     onBeforeLoad : function(){
60231         if(!this.disabled){
60232             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
60233         }
60234     },
60235
60236     // private
60237     destroy : function(){
60238         if(this.store){
60239             this.store.un('beforeload', this.onBeforeLoad, this);
60240             this.store.un('load', this.onLoad, this);
60241             this.store.un('loadexception', this.onLoadException, this);
60242         }else{
60243             var um = this.el.getUpdateManager();
60244             um.un('beforeupdate', this.onBeforeLoad, this);
60245             um.un('update', this.onLoad, this);
60246             um.un('failure', this.onLoad, this);
60247         }
60248     }
60249 };/*
60250  * Based on:
60251  * Ext JS Library 1.1.1
60252  * Copyright(c) 2006-2007, Ext JS, LLC.
60253  *
60254  * Originally Released Under LGPL - original licence link has changed is not relivant.
60255  *
60256  * Fork - LGPL
60257  * <script type="text/javascript">
60258  */
60259
60260
60261 /**
60262  * @class Roo.XTemplate
60263  * @extends Roo.Template
60264  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
60265 <pre><code>
60266 var t = new Roo.XTemplate(
60267         '&lt;select name="{name}"&gt;',
60268                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
60269         '&lt;/select&gt;'
60270 );
60271  
60272 // then append, applying the master template values
60273  </code></pre>
60274  *
60275  * Supported features:
60276  *
60277  *  Tags:
60278
60279 <pre><code>
60280       {a_variable} - output encoded.
60281       {a_variable.format:("Y-m-d")} - call a method on the variable
60282       {a_variable:raw} - unencoded output
60283       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
60284       {a_variable:this.method_on_template(...)} - call a method on the template object.
60285  
60286 </code></pre>
60287  *  The tpl tag:
60288 <pre><code>
60289         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
60290         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
60291         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
60292         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
60293   
60294         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
60295         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
60296 </code></pre>
60297  *      
60298  */
60299 Roo.XTemplate = function()
60300 {
60301     Roo.XTemplate.superclass.constructor.apply(this, arguments);
60302     if (this.html) {
60303         this.compile();
60304     }
60305 };
60306
60307
60308 Roo.extend(Roo.XTemplate, Roo.Template, {
60309
60310     /**
60311      * The various sub templates
60312      */
60313     tpls : false,
60314     /**
60315      *
60316      * basic tag replacing syntax
60317      * WORD:WORD()
60318      *
60319      * // you can fake an object call by doing this
60320      *  x.t:(test,tesT) 
60321      * 
60322      */
60323     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
60324
60325     /**
60326      * compile the template
60327      *
60328      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
60329      *
60330      */
60331     compile: function()
60332     {
60333         var s = this.html;
60334      
60335         s = ['<tpl>', s, '</tpl>'].join('');
60336     
60337         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
60338             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
60339             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
60340             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
60341             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
60342             m,
60343             id     = 0,
60344             tpls   = [];
60345     
60346         while(true == !!(m = s.match(re))){
60347             var forMatch   = m[0].match(nameRe),
60348                 ifMatch   = m[0].match(ifRe),
60349                 execMatch   = m[0].match(execRe),
60350                 namedMatch   = m[0].match(namedRe),
60351                 
60352                 exp  = null, 
60353                 fn   = null,
60354                 exec = null,
60355                 name = forMatch && forMatch[1] ? forMatch[1] : '';
60356                 
60357             if (ifMatch) {
60358                 // if - puts fn into test..
60359                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
60360                 if(exp){
60361                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
60362                 }
60363             }
60364             
60365             if (execMatch) {
60366                 // exec - calls a function... returns empty if true is  returned.
60367                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
60368                 if(exp){
60369                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
60370                 }
60371             }
60372             
60373             
60374             if (name) {
60375                 // for = 
60376                 switch(name){
60377                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
60378                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
60379                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
60380                 }
60381             }
60382             var uid = namedMatch ? namedMatch[1] : id;
60383             
60384             
60385             tpls.push({
60386                 id:     namedMatch ? namedMatch[1] : id,
60387                 target: name,
60388                 exec:   exec,
60389                 test:   fn,
60390                 body:   m[1] || ''
60391             });
60392             if (namedMatch) {
60393                 s = s.replace(m[0], '');
60394             } else { 
60395                 s = s.replace(m[0], '{xtpl'+ id + '}');
60396             }
60397             ++id;
60398         }
60399         this.tpls = [];
60400         for(var i = tpls.length-1; i >= 0; --i){
60401             this.compileTpl(tpls[i]);
60402             this.tpls[tpls[i].id] = tpls[i];
60403         }
60404         this.master = tpls[tpls.length-1];
60405         return this;
60406     },
60407     /**
60408      * same as applyTemplate, except it's done to one of the subTemplates
60409      * when using named templates, you can do:
60410      *
60411      * var str = pl.applySubTemplate('your-name', values);
60412      *
60413      * 
60414      * @param {Number} id of the template
60415      * @param {Object} values to apply to template
60416      * @param {Object} parent (normaly the instance of this object)
60417      */
60418     applySubTemplate : function(id, values, parent)
60419     {
60420         
60421         
60422         var t = this.tpls[id];
60423         
60424         
60425         try { 
60426             if(t.test && !t.test.call(this, values, parent)){
60427                 return '';
60428             }
60429         } catch(e) {
60430             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
60431             Roo.log(e.toString());
60432             Roo.log(t.test);
60433             return ''
60434         }
60435         try { 
60436             
60437             if(t.exec && t.exec.call(this, values, parent)){
60438                 return '';
60439             }
60440         } catch(e) {
60441             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
60442             Roo.log(e.toString());
60443             Roo.log(t.exec);
60444             return ''
60445         }
60446         try {
60447             var vs = t.target ? t.target.call(this, values, parent) : values;
60448             parent = t.target ? values : parent;
60449             if(t.target && vs instanceof Array){
60450                 var buf = [];
60451                 for(var i = 0, len = vs.length; i < len; i++){
60452                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
60453                 }
60454                 return buf.join('');
60455             }
60456             return t.compiled.call(this, vs, parent);
60457         } catch (e) {
60458             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
60459             Roo.log(e.toString());
60460             Roo.log(t.compiled);
60461             return '';
60462         }
60463     },
60464
60465     compileTpl : function(tpl)
60466     {
60467         var fm = Roo.util.Format;
60468         var useF = this.disableFormats !== true;
60469         var sep = Roo.isGecko ? "+" : ",";
60470         var undef = function(str) {
60471             Roo.log("Property not found :"  + str);
60472             return '';
60473         };
60474         
60475         var fn = function(m, name, format, args)
60476         {
60477             //Roo.log(arguments);
60478             args = args ? args.replace(/\\'/g,"'") : args;
60479             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
60480             if (typeof(format) == 'undefined') {
60481                 format= 'htmlEncode';
60482             }
60483             if (format == 'raw' ) {
60484                 format = false;
60485             }
60486             
60487             if(name.substr(0, 4) == 'xtpl'){
60488                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
60489             }
60490             
60491             // build an array of options to determine if value is undefined..
60492             
60493             // basically get 'xxxx.yyyy' then do
60494             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
60495             //    (function () { Roo.log("Property not found"); return ''; })() :
60496             //    ......
60497             
60498             var udef_ar = [];
60499             var lookfor = '';
60500             Roo.each(name.split('.'), function(st) {
60501                 lookfor += (lookfor.length ? '.': '') + st;
60502                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
60503             });
60504             
60505             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
60506             
60507             
60508             if(format && useF){
60509                 
60510                 args = args ? ',' + args : "";
60511                  
60512                 if(format.substr(0, 5) != "this."){
60513                     format = "fm." + format + '(';
60514                 }else{
60515                     format = 'this.call("'+ format.substr(5) + '", ';
60516                     args = ", values";
60517                 }
60518                 
60519                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
60520             }
60521              
60522             if (args.length) {
60523                 // called with xxyx.yuu:(test,test)
60524                 // change to ()
60525                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
60526             }
60527             // raw.. - :raw modifier..
60528             return "'"+ sep + udef_st  + name + ")"+sep+"'";
60529             
60530         };
60531         var body;
60532         // branched to use + in gecko and [].join() in others
60533         if(Roo.isGecko){
60534             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
60535                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
60536                     "';};};";
60537         }else{
60538             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
60539             body.push(tpl.body.replace(/(\r\n|\n)/g,
60540                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
60541             body.push("'].join('');};};");
60542             body = body.join('');
60543         }
60544         
60545         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
60546        
60547         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
60548         eval(body);
60549         
60550         return this;
60551     },
60552
60553     applyTemplate : function(values){
60554         return this.master.compiled.call(this, values, {});
60555         //var s = this.subs;
60556     },
60557
60558     apply : function(){
60559         return this.applyTemplate.apply(this, arguments);
60560     }
60561
60562  });
60563
60564 Roo.XTemplate.from = function(el){
60565     el = Roo.getDom(el);
60566     return new Roo.XTemplate(el.value || el.innerHTML);
60567 };