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         isGecko = !isSafari && ua.indexOf("gecko") > -1,
61         isBorderBox = isIE && !isStrict,
62         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
63         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
64         isLinux = (ua.indexOf("linux") != -1),
65         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
66         isIOS = /iphone|ipad/.test(ua),
67         isTouch =  (function() {
68             try {  
69                 document.createEvent("TouchEvent");  
70                 return true;  
71             } catch (e) {  
72                 return false;  
73             } 
74             
75         })();
76     // remove css image flicker
77         if(isIE && !isIE7){
78         try{
79             document.execCommand("BackgroundImageCache", false, true);
80         }catch(e){}
81     }
82     
83     Roo.apply(Roo, {
84         /**
85          * True if the browser is in strict mode
86          * @type Boolean
87          */
88         isStrict : isStrict,
89         /**
90          * True if the page is running over SSL
91          * @type Boolean
92          */
93         isSecure : isSecure,
94         /**
95          * True when the document is fully initialized and ready for action
96          * @type Boolean
97          */
98         isReady : false,
99         /**
100          * Turn on debugging output (currently only the factory uses this)
101          * @type Boolean
102          */
103         
104         debug: false,
105
106         /**
107          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
108          * @type Boolean
109          */
110         enableGarbageCollector : true,
111
112         /**
113          * True to automatically purge event listeners after uncaching an element (defaults to false).
114          * Note: this only happens if enableGarbageCollector is true.
115          * @type Boolean
116          */
117         enableListenerCollection:false,
118
119         /**
120          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
121          * the IE insecure content warning (defaults to javascript:false).
122          * @type String
123          */
124         SSL_SECURE_URL : "javascript:false",
125
126         /**
127          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
128          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
129          * @type String
130          */
131         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
132
133         emptyFn : function(){},
134         
135         /**
136          * Copies all the properties of config to obj if they don't already exist.
137          * @param {Object} obj The receiver of the properties
138          * @param {Object} config The source of the properties
139          * @return {Object} returns obj
140          */
141         applyIf : function(o, c){
142             if(o && c){
143                 for(var p in c){
144                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
145                 }
146             }
147             return o;
148         },
149
150         /**
151          * Applies event listeners to elements by selectors when the document is ready.
152          * The event name is specified with an @ suffix.
153 <pre><code>
154 Roo.addBehaviors({
155    // add a listener for click on all anchors in element with id foo
156    '#foo a@click' : function(e, t){
157        // do something
158    },
159
160    // add the same listener to multiple selectors (separated by comma BEFORE the @)
161    '#foo a, #bar span.some-class@mouseover' : function(){
162        // do something
163    }
164 });
165 </code></pre>
166          * @param {Object} obj The list of behaviors to apply
167          */
168         addBehaviors : function(o){
169             if(!Roo.isReady){
170                 Roo.onReady(function(){
171                     Roo.addBehaviors(o);
172                 });
173                 return;
174             }
175             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
176             for(var b in o){
177                 var parts = b.split('@');
178                 if(parts[1]){ // for Object prototype breakers
179                     var s = parts[0];
180                     if(!cache[s]){
181                         cache[s] = Roo.select(s);
182                     }
183                     cache[s].on(parts[1], o[b]);
184                 }
185             }
186             cache = null;
187         },
188
189         /**
190          * Generates unique ids. If the element already has an id, it is unchanged
191          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
192          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
193          * @return {String} The generated Id.
194          */
195         id : function(el, prefix){
196             prefix = prefix || "roo-gen";
197             el = Roo.getDom(el);
198             var id = prefix + (++idSeed);
199             return el ? (el.id ? el.id : (el.id = id)) : id;
200         },
201          
202        
203         /**
204          * Extends one class with another class and optionally overrides members with the passed literal. This class
205          * also adds the function "override()" to the class that can be used to override
206          * members on an instance.
207          * @param {Object} subclass The class inheriting the functionality
208          * @param {Object} superclass The class being extended
209          * @param {Object} overrides (optional) A literal with members
210          * @method extend
211          */
212         extend : function(){
213             // inline overrides
214             var io = function(o){
215                 for(var m in o){
216                     this[m] = o[m];
217                 }
218             };
219             return function(sb, sp, overrides){
220                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
221                     overrides = sp;
222                     sp = sb;
223                     sb = function(){sp.apply(this, arguments);};
224                 }
225                 var F = function(){}, sbp, spp = sp.prototype;
226                 F.prototype = spp;
227                 sbp = sb.prototype = new F();
228                 sbp.constructor=sb;
229                 sb.superclass=spp;
230                 
231                 if(spp.constructor == Object.prototype.constructor){
232                     spp.constructor=sp;
233                    
234                 }
235                 
236                 sb.override = function(o){
237                     Roo.override(sb, o);
238                 };
239                 sbp.override = io;
240                 Roo.override(sb, overrides);
241                 return sb;
242             };
243         }(),
244
245         /**
246          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
247          * Usage:<pre><code>
248 Roo.override(MyClass, {
249     newMethod1: function(){
250         // etc.
251     },
252     newMethod2: function(foo){
253         // etc.
254     }
255 });
256  </code></pre>
257          * @param {Object} origclass The class to override
258          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
259          * containing one or more methods.
260          * @method override
261          */
262         override : function(origclass, overrides){
263             if(overrides){
264                 var p = origclass.prototype;
265                 for(var method in overrides){
266                     p[method] = overrides[method];
267                 }
268             }
269         },
270         /**
271          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
272          * <pre><code>
273 Roo.namespace('Company', 'Company.data');
274 Company.Widget = function() { ... }
275 Company.data.CustomStore = function(config) { ... }
276 </code></pre>
277          * @param {String} namespace1
278          * @param {String} namespace2
279          * @param {String} etc
280          * @method namespace
281          */
282         namespace : function(){
283             var a=arguments, o=null, i, j, d, rt;
284             for (i=0; i<a.length; ++i) {
285                 d=a[i].split(".");
286                 rt = d[0];
287                 /** eval:var:o */
288                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
289                 for (j=1; j<d.length; ++j) {
290                     o[d[j]]=o[d[j]] || {};
291                     o=o[d[j]];
292                 }
293             }
294         },
295         /**
296          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
297          * <pre><code>
298 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
299 Roo.factory(conf, Roo.data);
300 </code></pre>
301          * @param {String} classname
302          * @param {String} namespace (optional)
303          * @method factory
304          */
305          
306         factory : function(c, ns)
307         {
308             // no xtype, no ns or c.xns - or forced off by c.xns
309             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
310                 return c;
311             }
312             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
313             if (c.constructor == ns[c.xtype]) {// already created...
314                 return c;
315             }
316             if (ns[c.xtype]) {
317                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
318                 var ret = new ns[c.xtype](c);
319                 ret.xns = false;
320                 return ret;
321             }
322             c.xns = false; // prevent recursion..
323             return c;
324         },
325          /**
326          * Logs to console if it can.
327          *
328          * @param {String|Object} string
329          * @method log
330          */
331         log : function(s)
332         {
333             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
334                 return; // alerT?
335             }
336             console.log(s);
337             
338         },
339         /**
340          * 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.
341          * @param {Object} o
342          * @return {String}
343          */
344         urlEncode : function(o){
345             if(!o){
346                 return "";
347             }
348             var buf = [];
349             for(var key in o){
350                 var ov = o[key], k = Roo.encodeURIComponent(key);
351                 var type = typeof ov;
352                 if(type == 'undefined'){
353                     buf.push(k, "=&");
354                 }else if(type != "function" && type != "object"){
355                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
356                 }else if(ov instanceof Array){
357                     if (ov.length) {
358                             for(var i = 0, len = ov.length; i < len; i++) {
359                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
360                             }
361                         } else {
362                             buf.push(k, "=&");
363                         }
364                 }
365             }
366             buf.pop();
367             return buf.join("");
368         },
369          /**
370          * Safe version of encodeURIComponent
371          * @param {String} data 
372          * @return {String} 
373          */
374         
375         encodeURIComponent : function (data)
376         {
377             try {
378                 return encodeURIComponent(data);
379             } catch(e) {} // should be an uri encode error.
380             
381             if (data == '' || data == null){
382                return '';
383             }
384             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
385             function nibble_to_hex(nibble){
386                 var chars = '0123456789ABCDEF';
387                 return chars.charAt(nibble);
388             }
389             data = data.toString();
390             var buffer = '';
391             for(var i=0; i<data.length; i++){
392                 var c = data.charCodeAt(i);
393                 var bs = new Array();
394                 if (c > 0x10000){
395                         // 4 bytes
396                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
397                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
398                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
399                     bs[3] = 0x80 | (c & 0x3F);
400                 }else if (c > 0x800){
401                          // 3 bytes
402                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
403                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
404                     bs[2] = 0x80 | (c & 0x3F);
405                 }else if (c > 0x80){
406                        // 2 bytes
407                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
408                     bs[1] = 0x80 | (c & 0x3F);
409                 }else{
410                         // 1 byte
411                     bs[0] = c;
412                 }
413                 for(var j=0; j<bs.length; j++){
414                     var b = bs[j];
415                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
416                             + nibble_to_hex(b &0x0F);
417                     buffer += '%'+hex;
418                }
419             }
420             return buffer;    
421              
422         },
423
424         /**
425          * 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]}.
426          * @param {String} string
427          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
428          * @return {Object} A literal with members
429          */
430         urlDecode : function(string, overwrite){
431             if(!string || !string.length){
432                 return {};
433             }
434             var obj = {};
435             var pairs = string.split('&');
436             var pair, name, value;
437             for(var i = 0, len = pairs.length; i < len; i++){
438                 pair = pairs[i].split('=');
439                 name = decodeURIComponent(pair[0]);
440                 value = decodeURIComponent(pair[1]);
441                 if(overwrite !== true){
442                     if(typeof obj[name] == "undefined"){
443                         obj[name] = value;
444                     }else if(typeof obj[name] == "string"){
445                         obj[name] = [obj[name]];
446                         obj[name].push(value);
447                     }else{
448                         obj[name].push(value);
449                     }
450                 }else{
451                     obj[name] = value;
452                 }
453             }
454             return obj;
455         },
456
457         /**
458          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
459          * passed array is not really an array, your function is called once with it.
460          * The supplied function is called with (Object item, Number index, Array allItems).
461          * @param {Array/NodeList/Mixed} array
462          * @param {Function} fn
463          * @param {Object} scope
464          */
465         each : function(array, fn, scope){
466             if(typeof array.length == "undefined" || typeof array == "string"){
467                 array = [array];
468             }
469             for(var i = 0, len = array.length; i < len; i++){
470                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
471             }
472         },
473
474         // deprecated
475         combine : function(){
476             var as = arguments, l = as.length, r = [];
477             for(var i = 0; i < l; i++){
478                 var a = as[i];
479                 if(a instanceof Array){
480                     r = r.concat(a);
481                 }else if(a.length !== undefined && !a.substr){
482                     r = r.concat(Array.prototype.slice.call(a, 0));
483                 }else{
484                     r.push(a);
485                 }
486             }
487             return r;
488         },
489
490         /**
491          * Escapes the passed string for use in a regular expression
492          * @param {String} str
493          * @return {String}
494          */
495         escapeRe : function(s) {
496             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
497         },
498
499         // internal
500         callback : function(cb, scope, args, delay){
501             if(typeof cb == "function"){
502                 if(delay){
503                     cb.defer(delay, scope, args || []);
504                 }else{
505                     cb.apply(scope, args || []);
506                 }
507             }
508         },
509
510         /**
511          * Return the dom node for the passed string (id), dom node, or Roo.Element
512          * @param {String/HTMLElement/Roo.Element} el
513          * @return HTMLElement
514          */
515         getDom : function(el){
516             if(!el){
517                 return null;
518             }
519             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
520         },
521
522         /**
523         * Shorthand for {@link Roo.ComponentMgr#get}
524         * @param {String} id
525         * @return Roo.Component
526         */
527         getCmp : function(id){
528             return Roo.ComponentMgr.get(id);
529         },
530          
531         num : function(v, defaultValue){
532             if(typeof v != 'number'){
533                 return defaultValue;
534             }
535             return v;
536         },
537
538         destroy : function(){
539             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
540                 var as = a[i];
541                 if(as){
542                     if(as.dom){
543                         as.removeAllListeners();
544                         as.remove();
545                         continue;
546                     }
547                     if(typeof as.purgeListeners == 'function'){
548                         as.purgeListeners();
549                     }
550                     if(typeof as.destroy == 'function'){
551                         as.destroy();
552                     }
553                 }
554             }
555         },
556
557         // inpired by a similar function in mootools library
558         /**
559          * Returns the type of object that is passed in. If the object passed in is null or undefined it
560          * return false otherwise it returns one of the following values:<ul>
561          * <li><b>string</b>: If the object passed is a string</li>
562          * <li><b>number</b>: If the object passed is a number</li>
563          * <li><b>boolean</b>: If the object passed is a boolean value</li>
564          * <li><b>function</b>: If the object passed is a function reference</li>
565          * <li><b>object</b>: If the object passed is an object</li>
566          * <li><b>array</b>: If the object passed is an array</li>
567          * <li><b>regexp</b>: If the object passed is a regular expression</li>
568          * <li><b>element</b>: If the object passed is a DOM Element</li>
569          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
570          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
571          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
572          * @param {Mixed} object
573          * @return {String}
574          */
575         type : function(o){
576             if(o === undefined || o === null){
577                 return false;
578             }
579             if(o.htmlElement){
580                 return 'element';
581             }
582             var t = typeof o;
583             if(t == 'object' && o.nodeName) {
584                 switch(o.nodeType) {
585                     case 1: return 'element';
586                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
587                 }
588             }
589             if(t == 'object' || t == 'function') {
590                 switch(o.constructor) {
591                     case Array: return 'array';
592                     case RegExp: return 'regexp';
593                 }
594                 if(typeof o.length == 'number' && typeof o.item == 'function') {
595                     return 'nodelist';
596                 }
597             }
598             return t;
599         },
600
601         /**
602          * Returns true if the passed value is null, undefined or an empty string (optional).
603          * @param {Mixed} value The value to test
604          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
605          * @return {Boolean}
606          */
607         isEmpty : function(v, allowBlank){
608             return v === null || v === undefined || (!allowBlank ? v === '' : false);
609         },
610         
611         /** @type Boolean */
612         isOpera : isOpera,
613         /** @type Boolean */
614         isSafari : isSafari,
615         /** @type Boolean */
616         isFirefox : isFirefox,
617         /** @type Boolean */
618         isIE : isIE,
619         /** @type Boolean */
620         isIE7 : isIE7,
621         /** @type Boolean */
622         isIE11 : isIE11,
623         /** @type Boolean */
624         isGecko : isGecko,
625         /** @type Boolean */
626         isBorderBox : isBorderBox,
627         /** @type Boolean */
628         isWindows : isWindows,
629         /** @type Boolean */
630         isLinux : isLinux,
631         /** @type Boolean */
632         isMac : isMac,
633         /** @type Boolean */
634         isIOS : isIOS,
635         /** @type Boolean */
636         isTouch : isTouch,
637
638         /**
639          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
640          * you may want to set this to true.
641          * @type Boolean
642          */
643         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
644         
645         
646                 
647         /**
648          * Selects a single element as a Roo Element
649          * This is about as close as you can get to jQuery's $('do crazy stuff')
650          * @param {String} selector The selector/xpath query
651          * @param {Node} root (optional) The start of the query (defaults to document).
652          * @return {Roo.Element}
653          */
654         selectNode : function(selector, root) 
655         {
656             var node = Roo.DomQuery.selectNode(selector,root);
657             return node ? Roo.get(node) : new Roo.Element(false);
658         }
659         
660     });
661
662
663 })();
664
665 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
666                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
667                 "Roo.app", "Roo.ux",
668                 "Roo.bootstrap",
669                 "Roo.bootstrap.dash");
670 /*
671  * Based on:
672  * Ext JS Library 1.1.1
673  * Copyright(c) 2006-2007, Ext JS, LLC.
674  *
675  * Originally Released Under LGPL - original licence link has changed is not relivant.
676  *
677  * Fork - LGPL
678  * <script type="text/javascript">
679  */
680
681 (function() {    
682     // wrappedn so fnCleanup is not in global scope...
683     if(Roo.isIE) {
684         function fnCleanUp() {
685             var p = Function.prototype;
686             delete p.createSequence;
687             delete p.defer;
688             delete p.createDelegate;
689             delete p.createCallback;
690             delete p.createInterceptor;
691
692             window.detachEvent("onunload", fnCleanUp);
693         }
694         window.attachEvent("onunload", fnCleanUp);
695     }
696 })();
697
698
699 /**
700  * @class Function
701  * These functions are available on every Function object (any JavaScript function).
702  */
703 Roo.apply(Function.prototype, {
704      /**
705      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
706      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
707      * Will create a function that is bound to those 2 args.
708      * @return {Function} The new function
709     */
710     createCallback : function(/*args...*/){
711         // make args available, in function below
712         var args = arguments;
713         var method = this;
714         return function() {
715             return method.apply(window, args);
716         };
717     },
718
719     /**
720      * Creates a delegate (callback) that sets the scope to obj.
721      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
722      * Will create a function that is automatically scoped to this.
723      * @param {Object} obj (optional) The object for which the scope is set
724      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
725      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
726      *                                             if a number the args are inserted at the specified position
727      * @return {Function} The new function
728      */
729     createDelegate : function(obj, args, appendArgs){
730         var method = this;
731         return function() {
732             var callArgs = args || arguments;
733             if(appendArgs === true){
734                 callArgs = Array.prototype.slice.call(arguments, 0);
735                 callArgs = callArgs.concat(args);
736             }else if(typeof appendArgs == "number"){
737                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
738                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
739                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
740             }
741             return method.apply(obj || window, callArgs);
742         };
743     },
744
745     /**
746      * Calls this function after the number of millseconds specified.
747      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
748      * @param {Object} obj (optional) The object for which the scope is set
749      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
750      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
751      *                                             if a number the args are inserted at the specified position
752      * @return {Number} The timeout id that can be used with clearTimeout
753      */
754     defer : function(millis, obj, args, appendArgs){
755         var fn = this.createDelegate(obj, args, appendArgs);
756         if(millis){
757             return setTimeout(fn, millis);
758         }
759         fn();
760         return 0;
761     },
762     /**
763      * Create a combined function call sequence of the original function + the passed function.
764      * The resulting function returns the results of the original function.
765      * The passed fcn is called with the parameters of the original function
766      * @param {Function} fcn The function to sequence
767      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
768      * @return {Function} The new function
769      */
770     createSequence : function(fcn, scope){
771         if(typeof fcn != "function"){
772             return this;
773         }
774         var method = this;
775         return function() {
776             var retval = method.apply(this || window, arguments);
777             fcn.apply(scope || this || window, arguments);
778             return retval;
779         };
780     },
781
782     /**
783      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
784      * The resulting function returns the results of the original function.
785      * The passed fcn is called with the parameters of the original function.
786      * @addon
787      * @param {Function} fcn The function to call before the original
788      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
789      * @return {Function} The new function
790      */
791     createInterceptor : function(fcn, scope){
792         if(typeof fcn != "function"){
793             return this;
794         }
795         var method = this;
796         return function() {
797             fcn.target = this;
798             fcn.method = method;
799             if(fcn.apply(scope || this || window, arguments) === false){
800                 return;
801             }
802             return method.apply(this || window, arguments);
803         };
804     }
805 });
806 /*
807  * Based on:
808  * Ext JS Library 1.1.1
809  * Copyright(c) 2006-2007, Ext JS, LLC.
810  *
811  * Originally Released Under LGPL - original licence link has changed is not relivant.
812  *
813  * Fork - LGPL
814  * <script type="text/javascript">
815  */
816
817 Roo.applyIf(String, {
818     
819     /** @scope String */
820     
821     /**
822      * Escapes the passed string for ' and \
823      * @param {String} string The string to escape
824      * @return {String} The escaped string
825      * @static
826      */
827     escape : function(string) {
828         return string.replace(/('|\\)/g, "\\$1");
829     },
830
831     /**
832      * Pads the left side of a string with a specified character.  This is especially useful
833      * for normalizing number and date strings.  Example usage:
834      * <pre><code>
835 var s = String.leftPad('123', 5, '0');
836 // s now contains the string: '00123'
837 </code></pre>
838      * @param {String} string The original string
839      * @param {Number} size The total length of the output string
840      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
841      * @return {String} The padded string
842      * @static
843      */
844     leftPad : function (val, size, ch) {
845         var result = new String(val);
846         if(ch === null || ch === undefined || ch === '') {
847             ch = " ";
848         }
849         while (result.length < size) {
850             result = ch + result;
851         }
852         return result;
853     },
854
855     /**
856      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
857      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
858      * <pre><code>
859 var cls = 'my-class', text = 'Some text';
860 var s = String.format('<div class="{0}">{1}</div>', cls, text);
861 // s now contains the string: '<div class="my-class">Some text</div>'
862 </code></pre>
863      * @param {String} string The tokenized string to be formatted
864      * @param {String} value1 The value to replace token {0}
865      * @param {String} value2 Etc...
866      * @return {String} The formatted string
867      * @static
868      */
869     format : function(format){
870         var args = Array.prototype.slice.call(arguments, 1);
871         return format.replace(/\{(\d+)\}/g, function(m, i){
872             return Roo.util.Format.htmlEncode(args[i]);
873         });
874     }
875 });
876
877 /**
878  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
879  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
880  * they are already different, the first value passed in is returned.  Note that this method returns the new value
881  * but does not change the current string.
882  * <pre><code>
883 // alternate sort directions
884 sort = sort.toggle('ASC', 'DESC');
885
886 // instead of conditional logic:
887 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
888 </code></pre>
889  * @param {String} value The value to compare to the current string
890  * @param {String} other The new value to use if the string already equals the first value passed in
891  * @return {String} The new value
892  */
893  
894 String.prototype.toggle = function(value, other){
895     return this == value ? other : value;
896 };/*
897  * Based on:
898  * Ext JS Library 1.1.1
899  * Copyright(c) 2006-2007, Ext JS, LLC.
900  *
901  * Originally Released Under LGPL - original licence link has changed is not relivant.
902  *
903  * Fork - LGPL
904  * <script type="text/javascript">
905  */
906
907  /**
908  * @class Number
909  */
910 Roo.applyIf(Number.prototype, {
911     /**
912      * Checks whether or not the current number is within a desired range.  If the number is already within the
913      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
914      * exceeded.  Note that this method returns the constrained value but does not change the current number.
915      * @param {Number} min The minimum number in the range
916      * @param {Number} max The maximum number in the range
917      * @return {Number} The constrained value if outside the range, otherwise the current value
918      */
919     constrain : function(min, max){
920         return Math.min(Math.max(this, min), max);
921     }
922 });/*
923  * Based on:
924  * Ext JS Library 1.1.1
925  * Copyright(c) 2006-2007, Ext JS, LLC.
926  *
927  * Originally Released Under LGPL - original licence link has changed is not relivant.
928  *
929  * Fork - LGPL
930  * <script type="text/javascript">
931  */
932  /**
933  * @class Array
934  */
935 Roo.applyIf(Array.prototype, {
936     /**
937      * 
938      * Checks whether or not the specified object exists in the array.
939      * @param {Object} o The object to check for
940      * @return {Number} The index of o in the array (or -1 if it is not found)
941      */
942     indexOf : function(o){
943        for (var i = 0, len = this.length; i < len; i++){
944               if(this[i] == o) return i;
945        }
946            return -1;
947     },
948
949     /**
950      * Removes the specified object from the array.  If the object is not found nothing happens.
951      * @param {Object} o The object to remove
952      */
953     remove : function(o){
954        var index = this.indexOf(o);
955        if(index != -1){
956            this.splice(index, 1);
957        }
958     },
959     /**
960      * Map (JS 1.6 compatibility)
961      * @param {Function} function  to call
962      */
963     map : function(fun )
964     {
965         var len = this.length >>> 0;
966         if (typeof fun != "function")
967             throw new TypeError();
968
969         var res = new Array(len);
970         var thisp = arguments[1];
971         for (var i = 0; i < len; i++)
972         {
973             if (i in this)
974                 res[i] = fun.call(thisp, this[i], i, this);
975         }
976
977         return res;
978     }
979     
980 });
981
982
983  /*
984  * Based on:
985  * Ext JS Library 1.1.1
986  * Copyright(c) 2006-2007, Ext JS, LLC.
987  *
988  * Originally Released Under LGPL - original licence link has changed is not relivant.
989  *
990  * Fork - LGPL
991  * <script type="text/javascript">
992  */
993
994 /**
995  * @class Date
996  *
997  * The date parsing and format syntax is a subset of
998  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
999  * supported will provide results equivalent to their PHP versions.
1000  *
1001  * Following is the list of all currently supported formats:
1002  *<pre>
1003 Sample date:
1004 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1005
1006 Format  Output      Description
1007 ------  ----------  --------------------------------------------------------------
1008   d      10         Day of the month, 2 digits with leading zeros
1009   D      Wed        A textual representation of a day, three letters
1010   j      10         Day of the month without leading zeros
1011   l      Wednesday  A full textual representation of the day of the week
1012   S      th         English ordinal day of month suffix, 2 chars (use with j)
1013   w      3          Numeric representation of the day of the week
1014   z      9          The julian date, or day of the year (0-365)
1015   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1016   F      January    A full textual representation of the month
1017   m      01         Numeric representation of a month, with leading zeros
1018   M      Jan        Month name abbreviation, three letters
1019   n      1          Numeric representation of a month, without leading zeros
1020   t      31         Number of days in the given month
1021   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1022   Y      2007       A full numeric representation of a year, 4 digits
1023   y      07         A two digit representation of a year
1024   a      pm         Lowercase Ante meridiem and Post meridiem
1025   A      PM         Uppercase Ante meridiem and Post meridiem
1026   g      3          12-hour format of an hour without leading zeros
1027   G      15         24-hour format of an hour without leading zeros
1028   h      03         12-hour format of an hour with leading zeros
1029   H      15         24-hour format of an hour with leading zeros
1030   i      05         Minutes with leading zeros
1031   s      01         Seconds, with leading zeros
1032   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1033   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1034   T      CST        Timezone setting of the machine running the code
1035   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1036 </pre>
1037  *
1038  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1039  * <pre><code>
1040 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1041 document.write(dt.format('Y-m-d'));                         //2007-01-10
1042 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1043 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
1044  </code></pre>
1045  *
1046  * Here are some standard date/time patterns that you might find helpful.  They
1047  * are not part of the source of Date.js, but to use them you can simply copy this
1048  * block of code into any script that is included after Date.js and they will also become
1049  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1050  * <pre><code>
1051 Date.patterns = {
1052     ISO8601Long:"Y-m-d H:i:s",
1053     ISO8601Short:"Y-m-d",
1054     ShortDate: "n/j/Y",
1055     LongDate: "l, F d, Y",
1056     FullDateTime: "l, F d, Y g:i:s A",
1057     MonthDay: "F d",
1058     ShortTime: "g:i A",
1059     LongTime: "g:i:s A",
1060     SortableDateTime: "Y-m-d\\TH:i:s",
1061     UniversalSortableDateTime: "Y-m-d H:i:sO",
1062     YearMonth: "F, Y"
1063 };
1064 </code></pre>
1065  *
1066  * Example usage:
1067  * <pre><code>
1068 var dt = new Date();
1069 document.write(dt.format(Date.patterns.ShortDate));
1070  </code></pre>
1071  */
1072
1073 /*
1074  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1075  * They generate precompiled functions from date formats instead of parsing and
1076  * processing the pattern every time you format a date.  These functions are available
1077  * on every Date object (any javascript function).
1078  *
1079  * The original article and download are here:
1080  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1081  *
1082  */
1083  
1084  
1085  // was in core
1086 /**
1087  Returns the number of milliseconds between this date and date
1088  @param {Date} date (optional) Defaults to now
1089  @return {Number} The diff in milliseconds
1090  @member Date getElapsed
1091  */
1092 Date.prototype.getElapsed = function(date) {
1093         return Math.abs((date || new Date()).getTime()-this.getTime());
1094 };
1095 // was in date file..
1096
1097
1098 // private
1099 Date.parseFunctions = {count:0};
1100 // private
1101 Date.parseRegexes = [];
1102 // private
1103 Date.formatFunctions = {count:0};
1104
1105 // private
1106 Date.prototype.dateFormat = function(format) {
1107     if (Date.formatFunctions[format] == null) {
1108         Date.createNewFormat(format);
1109     }
1110     var func = Date.formatFunctions[format];
1111     return this[func]();
1112 };
1113
1114
1115 /**
1116  * Formats a date given the supplied format string
1117  * @param {String} format The format string
1118  * @return {String} The formatted date
1119  * @method
1120  */
1121 Date.prototype.format = Date.prototype.dateFormat;
1122
1123 // private
1124 Date.createNewFormat = function(format) {
1125     var funcName = "format" + Date.formatFunctions.count++;
1126     Date.formatFunctions[format] = funcName;
1127     var code = "Date.prototype." + funcName + " = function(){return ";
1128     var special = false;
1129     var ch = '';
1130     for (var i = 0; i < format.length; ++i) {
1131         ch = format.charAt(i);
1132         if (!special && ch == "\\") {
1133             special = true;
1134         }
1135         else if (special) {
1136             special = false;
1137             code += "'" + String.escape(ch) + "' + ";
1138         }
1139         else {
1140             code += Date.getFormatCode(ch);
1141         }
1142     }
1143     /** eval:var:zzzzzzzzzzzzz */
1144     eval(code.substring(0, code.length - 3) + ";}");
1145 };
1146
1147 // private
1148 Date.getFormatCode = function(character) {
1149     switch (character) {
1150     case "d":
1151         return "String.leftPad(this.getDate(), 2, '0') + ";
1152     case "D":
1153         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1154     case "j":
1155         return "this.getDate() + ";
1156     case "l":
1157         return "Date.dayNames[this.getDay()] + ";
1158     case "S":
1159         return "this.getSuffix() + ";
1160     case "w":
1161         return "this.getDay() + ";
1162     case "z":
1163         return "this.getDayOfYear() + ";
1164     case "W":
1165         return "this.getWeekOfYear() + ";
1166     case "F":
1167         return "Date.monthNames[this.getMonth()] + ";
1168     case "m":
1169         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1170     case "M":
1171         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1172     case "n":
1173         return "(this.getMonth() + 1) + ";
1174     case "t":
1175         return "this.getDaysInMonth() + ";
1176     case "L":
1177         return "(this.isLeapYear() ? 1 : 0) + ";
1178     case "Y":
1179         return "this.getFullYear() + ";
1180     case "y":
1181         return "('' + this.getFullYear()).substring(2, 4) + ";
1182     case "a":
1183         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1184     case "A":
1185         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1186     case "g":
1187         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1188     case "G":
1189         return "this.getHours() + ";
1190     case "h":
1191         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1192     case "H":
1193         return "String.leftPad(this.getHours(), 2, '0') + ";
1194     case "i":
1195         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1196     case "s":
1197         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1198     case "O":
1199         return "this.getGMTOffset() + ";
1200     case "P":
1201         return "this.getGMTColonOffset() + ";
1202     case "T":
1203         return "this.getTimezone() + ";
1204     case "Z":
1205         return "(this.getTimezoneOffset() * -60) + ";
1206     default:
1207         return "'" + String.escape(character) + "' + ";
1208     }
1209 };
1210
1211 /**
1212  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1213  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1214  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1215  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1216  * string or the parse operation will fail.
1217  * Example Usage:
1218 <pre><code>
1219 //dt = Fri May 25 2007 (current date)
1220 var dt = new Date();
1221
1222 //dt = Thu May 25 2006 (today's month/day in 2006)
1223 dt = Date.parseDate("2006", "Y");
1224
1225 //dt = Sun Jan 15 2006 (all date parts specified)
1226 dt = Date.parseDate("2006-1-15", "Y-m-d");
1227
1228 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1229 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1230 </code></pre>
1231  * @param {String} input The unparsed date as a string
1232  * @param {String} format The format the date is in
1233  * @return {Date} The parsed date
1234  * @static
1235  */
1236 Date.parseDate = function(input, format) {
1237     if (Date.parseFunctions[format] == null) {
1238         Date.createParser(format);
1239     }
1240     var func = Date.parseFunctions[format];
1241     return Date[func](input);
1242 };
1243 /**
1244  * @private
1245  */
1246
1247 Date.createParser = function(format) {
1248     var funcName = "parse" + Date.parseFunctions.count++;
1249     var regexNum = Date.parseRegexes.length;
1250     var currentGroup = 1;
1251     Date.parseFunctions[format] = funcName;
1252
1253     var code = "Date." + funcName + " = function(input){\n"
1254         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1255         + "var d = new Date();\n"
1256         + "y = d.getFullYear();\n"
1257         + "m = d.getMonth();\n"
1258         + "d = d.getDate();\n"
1259         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1260         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1261         + "if (results && results.length > 0) {";
1262     var regex = "";
1263
1264     var special = false;
1265     var ch = '';
1266     for (var i = 0; i < format.length; ++i) {
1267         ch = format.charAt(i);
1268         if (!special && ch == "\\") {
1269             special = true;
1270         }
1271         else if (special) {
1272             special = false;
1273             regex += String.escape(ch);
1274         }
1275         else {
1276             var obj = Date.formatCodeToRegex(ch, currentGroup);
1277             currentGroup += obj.g;
1278             regex += obj.s;
1279             if (obj.g && obj.c) {
1280                 code += obj.c;
1281             }
1282         }
1283     }
1284
1285     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1286         + "{v = new Date(y, m, d, h, i, s);}\n"
1287         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1288         + "{v = new Date(y, m, d, h, i);}\n"
1289         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1290         + "{v = new Date(y, m, d, h);}\n"
1291         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1292         + "{v = new Date(y, m, d);}\n"
1293         + "else if (y >= 0 && m >= 0)\n"
1294         + "{v = new Date(y, m);}\n"
1295         + "else if (y >= 0)\n"
1296         + "{v = new Date(y);}\n"
1297         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1298         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1299         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1300         + ";}";
1301
1302     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1303     /** eval:var:zzzzzzzzzzzzz */
1304     eval(code);
1305 };
1306
1307 // private
1308 Date.formatCodeToRegex = function(character, currentGroup) {
1309     switch (character) {
1310     case "D":
1311         return {g:0,
1312         c:null,
1313         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1314     case "j":
1315         return {g:1,
1316             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1317             s:"(\\d{1,2})"}; // day of month without leading zeroes
1318     case "d":
1319         return {g:1,
1320             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1321             s:"(\\d{2})"}; // day of month with leading zeroes
1322     case "l":
1323         return {g:0,
1324             c:null,
1325             s:"(?:" + Date.dayNames.join("|") + ")"};
1326     case "S":
1327         return {g:0,
1328             c:null,
1329             s:"(?:st|nd|rd|th)"};
1330     case "w":
1331         return {g:0,
1332             c:null,
1333             s:"\\d"};
1334     case "z":
1335         return {g:0,
1336             c:null,
1337             s:"(?:\\d{1,3})"};
1338     case "W":
1339         return {g:0,
1340             c:null,
1341             s:"(?:\\d{2})"};
1342     case "F":
1343         return {g:1,
1344             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1345             s:"(" + Date.monthNames.join("|") + ")"};
1346     case "M":
1347         return {g:1,
1348             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1349             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1350     case "n":
1351         return {g:1,
1352             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1353             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1354     case "m":
1355         return {g:1,
1356             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1357             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1358     case "t":
1359         return {g:0,
1360             c:null,
1361             s:"\\d{1,2}"};
1362     case "L":
1363         return {g:0,
1364             c:null,
1365             s:"(?:1|0)"};
1366     case "Y":
1367         return {g:1,
1368             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1369             s:"(\\d{4})"};
1370     case "y":
1371         return {g:1,
1372             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1373                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1374             s:"(\\d{1,2})"};
1375     case "a":
1376         return {g:1,
1377             c:"if (results[" + currentGroup + "] == 'am') {\n"
1378                 + "if (h == 12) { h = 0; }\n"
1379                 + "} else { if (h < 12) { h += 12; }}",
1380             s:"(am|pm)"};
1381     case "A":
1382         return {g:1,
1383             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1384                 + "if (h == 12) { h = 0; }\n"
1385                 + "} else { if (h < 12) { h += 12; }}",
1386             s:"(AM|PM)"};
1387     case "g":
1388     case "G":
1389         return {g:1,
1390             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1391             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1392     case "h":
1393     case "H":
1394         return {g:1,
1395             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1396             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1397     case "i":
1398         return {g:1,
1399             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1400             s:"(\\d{2})"};
1401     case "s":
1402         return {g:1,
1403             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1404             s:"(\\d{2})"};
1405     case "O":
1406         return {g:1,
1407             c:[
1408                 "o = results[", currentGroup, "];\n",
1409                 "var sn = o.substring(0,1);\n", // get + / - sign
1410                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1411                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1412                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1413                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1414             ].join(""),
1415             s:"([+\-]\\d{2,4})"};
1416     
1417     
1418     case "P":
1419         return {g:1,
1420                 c:[
1421                    "o = results[", currentGroup, "];\n",
1422                    "var sn = o.substring(0,1);\n",
1423                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1424                    "var mn = o.substring(4,6) % 60;\n",
1425                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1426                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1427             ].join(""),
1428             s:"([+\-]\\d{4})"};
1429     case "T":
1430         return {g:0,
1431             c:null,
1432             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1433     case "Z":
1434         return {g:1,
1435             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1436                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1437             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1438     default:
1439         return {g:0,
1440             c:null,
1441             s:String.escape(character)};
1442     }
1443 };
1444
1445 /**
1446  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1447  * @return {String} The abbreviated timezone name (e.g. 'CST')
1448  */
1449 Date.prototype.getTimezone = function() {
1450     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1451 };
1452
1453 /**
1454  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1455  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1456  */
1457 Date.prototype.getGMTOffset = function() {
1458     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1459         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1460         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1461 };
1462
1463 /**
1464  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1465  * @return {String} 2-characters representing hours and 2-characters representing minutes
1466  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1467  */
1468 Date.prototype.getGMTColonOffset = function() {
1469         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1470                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1471                 + ":"
1472                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1473 }
1474
1475 /**
1476  * Get the numeric day number of the year, adjusted for leap year.
1477  * @return {Number} 0 through 364 (365 in leap years)
1478  */
1479 Date.prototype.getDayOfYear = function() {
1480     var num = 0;
1481     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1482     for (var i = 0; i < this.getMonth(); ++i) {
1483         num += Date.daysInMonth[i];
1484     }
1485     return num + this.getDate() - 1;
1486 };
1487
1488 /**
1489  * Get the string representation of the numeric week number of the year
1490  * (equivalent to the format specifier 'W').
1491  * @return {String} '00' through '52'
1492  */
1493 Date.prototype.getWeekOfYear = function() {
1494     // Skip to Thursday of this week
1495     var now = this.getDayOfYear() + (4 - this.getDay());
1496     // Find the first Thursday of the year
1497     var jan1 = new Date(this.getFullYear(), 0, 1);
1498     var then = (7 - jan1.getDay() + 4);
1499     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1500 };
1501
1502 /**
1503  * Whether or not the current date is in a leap year.
1504  * @return {Boolean} True if the current date is in a leap year, else false
1505  */
1506 Date.prototype.isLeapYear = function() {
1507     var year = this.getFullYear();
1508     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1509 };
1510
1511 /**
1512  * Get the first day of the current month, adjusted for leap year.  The returned value
1513  * is the numeric day index within the week (0-6) which can be used in conjunction with
1514  * the {@link #monthNames} array to retrieve the textual day name.
1515  * Example:
1516  *<pre><code>
1517 var dt = new Date('1/10/2007');
1518 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1519 </code></pre>
1520  * @return {Number} The day number (0-6)
1521  */
1522 Date.prototype.getFirstDayOfMonth = function() {
1523     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1524     return (day < 0) ? (day + 7) : day;
1525 };
1526
1527 /**
1528  * Get the last day of the current month, adjusted for leap year.  The returned value
1529  * is the numeric day index within the week (0-6) which can be used in conjunction with
1530  * the {@link #monthNames} array to retrieve the textual day name.
1531  * Example:
1532  *<pre><code>
1533 var dt = new Date('1/10/2007');
1534 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1535 </code></pre>
1536  * @return {Number} The day number (0-6)
1537  */
1538 Date.prototype.getLastDayOfMonth = function() {
1539     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1540     return (day < 0) ? (day + 7) : day;
1541 };
1542
1543
1544 /**
1545  * Get the first date of this date's month
1546  * @return {Date}
1547  */
1548 Date.prototype.getFirstDateOfMonth = function() {
1549     return new Date(this.getFullYear(), this.getMonth(), 1);
1550 };
1551
1552 /**
1553  * Get the last date of this date's month
1554  * @return {Date}
1555  */
1556 Date.prototype.getLastDateOfMonth = function() {
1557     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1558 };
1559 /**
1560  * Get the number of days in the current month, adjusted for leap year.
1561  * @return {Number} The number of days in the month
1562  */
1563 Date.prototype.getDaysInMonth = function() {
1564     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1565     return Date.daysInMonth[this.getMonth()];
1566 };
1567
1568 /**
1569  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1570  * @return {String} 'st, 'nd', 'rd' or 'th'
1571  */
1572 Date.prototype.getSuffix = function() {
1573     switch (this.getDate()) {
1574         case 1:
1575         case 21:
1576         case 31:
1577             return "st";
1578         case 2:
1579         case 22:
1580             return "nd";
1581         case 3:
1582         case 23:
1583             return "rd";
1584         default:
1585             return "th";
1586     }
1587 };
1588
1589 // private
1590 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1591
1592 /**
1593  * An array of textual month names.
1594  * Override these values for international dates, for example...
1595  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1596  * @type Array
1597  * @static
1598  */
1599 Date.monthNames =
1600    ["January",
1601     "February",
1602     "March",
1603     "April",
1604     "May",
1605     "June",
1606     "July",
1607     "August",
1608     "September",
1609     "October",
1610     "November",
1611     "December"];
1612
1613 /**
1614  * An array of textual day names.
1615  * Override these values for international dates, for example...
1616  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1617  * @type Array
1618  * @static
1619  */
1620 Date.dayNames =
1621    ["Sunday",
1622     "Monday",
1623     "Tuesday",
1624     "Wednesday",
1625     "Thursday",
1626     "Friday",
1627     "Saturday"];
1628
1629 // private
1630 Date.y2kYear = 50;
1631 // private
1632 Date.monthNumbers = {
1633     Jan:0,
1634     Feb:1,
1635     Mar:2,
1636     Apr:3,
1637     May:4,
1638     Jun:5,
1639     Jul:6,
1640     Aug:7,
1641     Sep:8,
1642     Oct:9,
1643     Nov:10,
1644     Dec:11};
1645
1646 /**
1647  * Creates and returns a new Date instance with the exact same date value as the called instance.
1648  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1649  * variable will also be changed.  When the intention is to create a new variable that will not
1650  * modify the original instance, you should create a clone.
1651  *
1652  * Example of correctly cloning a date:
1653  * <pre><code>
1654 //wrong way:
1655 var orig = new Date('10/1/2006');
1656 var copy = orig;
1657 copy.setDate(5);
1658 document.write(orig);  //returns 'Thu Oct 05 2006'!
1659
1660 //correct way:
1661 var orig = new Date('10/1/2006');
1662 var copy = orig.clone();
1663 copy.setDate(5);
1664 document.write(orig);  //returns 'Thu Oct 01 2006'
1665 </code></pre>
1666  * @return {Date} The new Date instance
1667  */
1668 Date.prototype.clone = function() {
1669         return new Date(this.getTime());
1670 };
1671
1672 /**
1673  * Clears any time information from this date
1674  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1675  @return {Date} this or the clone
1676  */
1677 Date.prototype.clearTime = function(clone){
1678     if(clone){
1679         return this.clone().clearTime();
1680     }
1681     this.setHours(0);
1682     this.setMinutes(0);
1683     this.setSeconds(0);
1684     this.setMilliseconds(0);
1685     return this;
1686 };
1687
1688 // private
1689 // safari setMonth is broken
1690 if(Roo.isSafari){
1691     Date.brokenSetMonth = Date.prototype.setMonth;
1692         Date.prototype.setMonth = function(num){
1693                 if(num <= -1){
1694                         var n = Math.ceil(-num);
1695                         var back_year = Math.ceil(n/12);
1696                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1697                         this.setFullYear(this.getFullYear() - back_year);
1698                         return Date.brokenSetMonth.call(this, month);
1699                 } else {
1700                         return Date.brokenSetMonth.apply(this, arguments);
1701                 }
1702         };
1703 }
1704
1705 /** Date interval constant 
1706 * @static 
1707 * @type String */
1708 Date.MILLI = "ms";
1709 /** Date interval constant 
1710 * @static 
1711 * @type String */
1712 Date.SECOND = "s";
1713 /** Date interval constant 
1714 * @static 
1715 * @type String */
1716 Date.MINUTE = "mi";
1717 /** Date interval constant 
1718 * @static 
1719 * @type String */
1720 Date.HOUR = "h";
1721 /** Date interval constant 
1722 * @static 
1723 * @type String */
1724 Date.DAY = "d";
1725 /** Date interval constant 
1726 * @static 
1727 * @type String */
1728 Date.MONTH = "mo";
1729 /** Date interval constant 
1730 * @static 
1731 * @type String */
1732 Date.YEAR = "y";
1733
1734 /**
1735  * Provides a convenient method of performing basic date arithmetic.  This method
1736  * does not modify the Date instance being called - it creates and returns
1737  * a new Date instance containing the resulting date value.
1738  *
1739  * Examples:
1740  * <pre><code>
1741 //Basic usage:
1742 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1743 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1744
1745 //Negative values will subtract correctly:
1746 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1747 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1748
1749 //You can even chain several calls together in one line!
1750 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1751 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1752  </code></pre>
1753  *
1754  * @param {String} interval   A valid date interval enum value
1755  * @param {Number} value      The amount to add to the current date
1756  * @return {Date} The new Date instance
1757  */
1758 Date.prototype.add = function(interval, value){
1759   var d = this.clone();
1760   if (!interval || value === 0) return d;
1761   switch(interval.toLowerCase()){
1762     case Date.MILLI:
1763       d.setMilliseconds(this.getMilliseconds() + value);
1764       break;
1765     case Date.SECOND:
1766       d.setSeconds(this.getSeconds() + value);
1767       break;
1768     case Date.MINUTE:
1769       d.setMinutes(this.getMinutes() + value);
1770       break;
1771     case Date.HOUR:
1772       d.setHours(this.getHours() + value);
1773       break;
1774     case Date.DAY:
1775       d.setDate(this.getDate() + value);
1776       break;
1777     case Date.MONTH:
1778       var day = this.getDate();
1779       if(day > 28){
1780           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1781       }
1782       d.setDate(day);
1783       d.setMonth(this.getMonth() + value);
1784       break;
1785     case Date.YEAR:
1786       d.setFullYear(this.getFullYear() + value);
1787       break;
1788   }
1789   return d;
1790 };
1791 /*
1792  * Based on:
1793  * Ext JS Library 1.1.1
1794  * Copyright(c) 2006-2007, Ext JS, LLC.
1795  *
1796  * Originally Released Under LGPL - original licence link has changed is not relivant.
1797  *
1798  * Fork - LGPL
1799  * <script type="text/javascript">
1800  */
1801
1802 /**
1803  * @class Roo.lib.Dom
1804  * @static
1805  * 
1806  * Dom utils (from YIU afaik)
1807  * 
1808  **/
1809 Roo.lib.Dom = {
1810     /**
1811      * Get the view width
1812      * @param {Boolean} full True will get the full document, otherwise it's the view width
1813      * @return {Number} The width
1814      */
1815      
1816     getViewWidth : function(full) {
1817         return full ? this.getDocumentWidth() : this.getViewportWidth();
1818     },
1819     /**
1820      * Get the view height
1821      * @param {Boolean} full True will get the full document, otherwise it's the view height
1822      * @return {Number} The height
1823      */
1824     getViewHeight : function(full) {
1825         return full ? this.getDocumentHeight() : this.getViewportHeight();
1826     },
1827
1828     getDocumentHeight: function() {
1829         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1830         return Math.max(scrollHeight, this.getViewportHeight());
1831     },
1832
1833     getDocumentWidth: function() {
1834         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1835         return Math.max(scrollWidth, this.getViewportWidth());
1836     },
1837
1838     getViewportHeight: function() {
1839         var height = self.innerHeight;
1840         var mode = document.compatMode;
1841
1842         if ((mode || Roo.isIE) && !Roo.isOpera) {
1843             height = (mode == "CSS1Compat") ?
1844                      document.documentElement.clientHeight :
1845                      document.body.clientHeight;
1846         }
1847
1848         return height;
1849     },
1850
1851     getViewportWidth: function() {
1852         var width = self.innerWidth;
1853         var mode = document.compatMode;
1854
1855         if (mode || Roo.isIE) {
1856             width = (mode == "CSS1Compat") ?
1857                     document.documentElement.clientWidth :
1858                     document.body.clientWidth;
1859         }
1860         return width;
1861     },
1862
1863     isAncestor : function(p, c) {
1864         p = Roo.getDom(p);
1865         c = Roo.getDom(c);
1866         if (!p || !c) {
1867             return false;
1868         }
1869
1870         if (p.contains && !Roo.isSafari) {
1871             return p.contains(c);
1872         } else if (p.compareDocumentPosition) {
1873             return !!(p.compareDocumentPosition(c) & 16);
1874         } else {
1875             var parent = c.parentNode;
1876             while (parent) {
1877                 if (parent == p) {
1878                     return true;
1879                 }
1880                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1881                     return false;
1882                 }
1883                 parent = parent.parentNode;
1884             }
1885             return false;
1886         }
1887     },
1888
1889     getRegion : function(el) {
1890         return Roo.lib.Region.getRegion(el);
1891     },
1892
1893     getY : function(el) {
1894         return this.getXY(el)[1];
1895     },
1896
1897     getX : function(el) {
1898         return this.getXY(el)[0];
1899     },
1900
1901     getXY : function(el) {
1902         var p, pe, b, scroll, bd = document.body;
1903         el = Roo.getDom(el);
1904         var fly = Roo.lib.AnimBase.fly;
1905         if (el.getBoundingClientRect) {
1906             b = el.getBoundingClientRect();
1907             scroll = fly(document).getScroll();
1908             return [b.left + scroll.left, b.top + scroll.top];
1909         }
1910         var x = 0, y = 0;
1911
1912         p = el;
1913
1914         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1915
1916         while (p) {
1917
1918             x += p.offsetLeft;
1919             y += p.offsetTop;
1920
1921             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1922                 hasAbsolute = true;
1923             }
1924
1925             if (Roo.isGecko) {
1926                 pe = fly(p);
1927
1928                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1929                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1930
1931
1932                 x += bl;
1933                 y += bt;
1934
1935
1936                 if (p != el && pe.getStyle('overflow') != 'visible') {
1937                     x += bl;
1938                     y += bt;
1939                 }
1940             }
1941             p = p.offsetParent;
1942         }
1943
1944         if (Roo.isSafari && hasAbsolute) {
1945             x -= bd.offsetLeft;
1946             y -= bd.offsetTop;
1947         }
1948
1949         if (Roo.isGecko && !hasAbsolute) {
1950             var dbd = fly(bd);
1951             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1952             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1953         }
1954
1955         p = el.parentNode;
1956         while (p && p != bd) {
1957             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1958                 x -= p.scrollLeft;
1959                 y -= p.scrollTop;
1960             }
1961             p = p.parentNode;
1962         }
1963         return [x, y];
1964     },
1965  
1966   
1967
1968
1969     setXY : function(el, xy) {
1970         el = Roo.fly(el, '_setXY');
1971         el.position();
1972         var pts = el.translatePoints(xy);
1973         if (xy[0] !== false) {
1974             el.dom.style.left = pts.left + "px";
1975         }
1976         if (xy[1] !== false) {
1977             el.dom.style.top = pts.top + "px";
1978         }
1979     },
1980
1981     setX : function(el, x) {
1982         this.setXY(el, [x, false]);
1983     },
1984
1985     setY : function(el, y) {
1986         this.setXY(el, [false, y]);
1987     }
1988 };
1989 /*
1990  * Portions of this file are based on pieces of Yahoo User Interface Library
1991  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1992  * YUI licensed under the BSD License:
1993  * http://developer.yahoo.net/yui/license.txt
1994  * <script type="text/javascript">
1995  *
1996  */
1997
1998 Roo.lib.Event = function() {
1999     var loadComplete = false;
2000     var listeners = [];
2001     var unloadListeners = [];
2002     var retryCount = 0;
2003     var onAvailStack = [];
2004     var counter = 0;
2005     var lastError = null;
2006
2007     return {
2008         POLL_RETRYS: 200,
2009         POLL_INTERVAL: 20,
2010         EL: 0,
2011         TYPE: 1,
2012         FN: 2,
2013         WFN: 3,
2014         OBJ: 3,
2015         ADJ_SCOPE: 4,
2016         _interval: null,
2017
2018         startInterval: function() {
2019             if (!this._interval) {
2020                 var self = this;
2021                 var callback = function() {
2022                     self._tryPreloadAttach();
2023                 };
2024                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2025
2026             }
2027         },
2028
2029         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2030             onAvailStack.push({ id:         p_id,
2031                 fn:         p_fn,
2032                 obj:        p_obj,
2033                 override:   p_override,
2034                 checkReady: false    });
2035
2036             retryCount = this.POLL_RETRYS;
2037             this.startInterval();
2038         },
2039
2040
2041         addListener: function(el, eventName, fn) {
2042             el = Roo.getDom(el);
2043             if (!el || !fn) {
2044                 return false;
2045             }
2046
2047             if ("unload" == eventName) {
2048                 unloadListeners[unloadListeners.length] =
2049                 [el, eventName, fn];
2050                 return true;
2051             }
2052
2053             var wrappedFn = function(e) {
2054                 return fn(Roo.lib.Event.getEvent(e));
2055             };
2056
2057             var li = [el, eventName, fn, wrappedFn];
2058
2059             var index = listeners.length;
2060             listeners[index] = li;
2061
2062             this.doAdd(el, eventName, wrappedFn, false);
2063             return true;
2064
2065         },
2066
2067
2068         removeListener: function(el, eventName, fn) {
2069             var i, len;
2070
2071             el = Roo.getDom(el);
2072
2073             if(!fn) {
2074                 return this.purgeElement(el, false, eventName);
2075             }
2076
2077
2078             if ("unload" == eventName) {
2079
2080                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2081                     var li = unloadListeners[i];
2082                     if (li &&
2083                         li[0] == el &&
2084                         li[1] == eventName &&
2085                         li[2] == fn) {
2086                         unloadListeners.splice(i, 1);
2087                         return true;
2088                     }
2089                 }
2090
2091                 return false;
2092             }
2093
2094             var cacheItem = null;
2095
2096
2097             var index = arguments[3];
2098
2099             if ("undefined" == typeof index) {
2100                 index = this._getCacheIndex(el, eventName, fn);
2101             }
2102
2103             if (index >= 0) {
2104                 cacheItem = listeners[index];
2105             }
2106
2107             if (!el || !cacheItem) {
2108                 return false;
2109             }
2110
2111             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2112
2113             delete listeners[index][this.WFN];
2114             delete listeners[index][this.FN];
2115             listeners.splice(index, 1);
2116
2117             return true;
2118
2119         },
2120
2121
2122         getTarget: function(ev, resolveTextNode) {
2123             ev = ev.browserEvent || ev;
2124             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2125             var t = ev.target || ev.srcElement;
2126             return this.resolveTextNode(t);
2127         },
2128
2129
2130         resolveTextNode: function(node) {
2131             if (Roo.isSafari && node && 3 == node.nodeType) {
2132                 return node.parentNode;
2133             } else {
2134                 return node;
2135             }
2136         },
2137
2138
2139         getPageX: function(ev) {
2140             ev = ev.browserEvent || ev;
2141             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2142             var x = ev.pageX;
2143             if (!x && 0 !== x) {
2144                 x = ev.clientX || 0;
2145
2146                 if (Roo.isIE) {
2147                     x += this.getScroll()[1];
2148                 }
2149             }
2150
2151             return x;
2152         },
2153
2154
2155         getPageY: function(ev) {
2156             ev = ev.browserEvent || ev;
2157             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2158             var y = ev.pageY;
2159             if (!y && 0 !== y) {
2160                 y = ev.clientY || 0;
2161
2162                 if (Roo.isIE) {
2163                     y += this.getScroll()[0];
2164                 }
2165             }
2166
2167
2168             return y;
2169         },
2170
2171
2172         getXY: function(ev) {
2173             ev = ev.browserEvent || ev;
2174             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2175             return [this.getPageX(ev), this.getPageY(ev)];
2176         },
2177
2178
2179         getRelatedTarget: function(ev) {
2180             ev = ev.browserEvent || ev;
2181             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2182             var t = ev.relatedTarget;
2183             if (!t) {
2184                 if (ev.type == "mouseout") {
2185                     t = ev.toElement;
2186                 } else if (ev.type == "mouseover") {
2187                     t = ev.fromElement;
2188                 }
2189             }
2190
2191             return this.resolveTextNode(t);
2192         },
2193
2194
2195         getTime: function(ev) {
2196             ev = ev.browserEvent || ev;
2197             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2198             if (!ev.time) {
2199                 var t = new Date().getTime();
2200                 try {
2201                     ev.time = t;
2202                 } catch(ex) {
2203                     this.lastError = ex;
2204                     return t;
2205                 }
2206             }
2207
2208             return ev.time;
2209         },
2210
2211
2212         stopEvent: function(ev) {
2213             this.stopPropagation(ev);
2214             this.preventDefault(ev);
2215         },
2216
2217
2218         stopPropagation: function(ev) {
2219             ev = ev.browserEvent || ev;
2220             if (ev.stopPropagation) {
2221                 ev.stopPropagation();
2222             } else {
2223                 ev.cancelBubble = true;
2224             }
2225         },
2226
2227
2228         preventDefault: function(ev) {
2229             ev = ev.browserEvent || ev;
2230             if(ev.preventDefault) {
2231                 ev.preventDefault();
2232             } else {
2233                 ev.returnValue = false;
2234             }
2235         },
2236
2237
2238         getEvent: function(e) {
2239             var ev = e || window.event;
2240             if (!ev) {
2241                 var c = this.getEvent.caller;
2242                 while (c) {
2243                     ev = c.arguments[0];
2244                     if (ev && Event == ev.constructor) {
2245                         break;
2246                     }
2247                     c = c.caller;
2248                 }
2249             }
2250             return ev;
2251         },
2252
2253
2254         getCharCode: function(ev) {
2255             ev = ev.browserEvent || ev;
2256             return ev.charCode || ev.keyCode || 0;
2257         },
2258
2259
2260         _getCacheIndex: function(el, eventName, fn) {
2261             for (var i = 0,len = listeners.length; i < len; ++i) {
2262                 var li = listeners[i];
2263                 if (li &&
2264                     li[this.FN] == fn &&
2265                     li[this.EL] == el &&
2266                     li[this.TYPE] == eventName) {
2267                     return i;
2268                 }
2269             }
2270
2271             return -1;
2272         },
2273
2274
2275         elCache: {},
2276
2277
2278         getEl: function(id) {
2279             return document.getElementById(id);
2280         },
2281
2282
2283         clearCache: function() {
2284         },
2285
2286
2287         _load: function(e) {
2288             loadComplete = true;
2289             var EU = Roo.lib.Event;
2290
2291
2292             if (Roo.isIE) {
2293                 EU.doRemove(window, "load", EU._load);
2294             }
2295         },
2296
2297
2298         _tryPreloadAttach: function() {
2299
2300             if (this.locked) {
2301                 return false;
2302             }
2303
2304             this.locked = true;
2305
2306
2307             var tryAgain = !loadComplete;
2308             if (!tryAgain) {
2309                 tryAgain = (retryCount > 0);
2310             }
2311
2312
2313             var notAvail = [];
2314             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2315                 var item = onAvailStack[i];
2316                 if (item) {
2317                     var el = this.getEl(item.id);
2318
2319                     if (el) {
2320                         if (!item.checkReady ||
2321                             loadComplete ||
2322                             el.nextSibling ||
2323                             (document && document.body)) {
2324
2325                             var scope = el;
2326                             if (item.override) {
2327                                 if (item.override === true) {
2328                                     scope = item.obj;
2329                                 } else {
2330                                     scope = item.override;
2331                                 }
2332                             }
2333                             item.fn.call(scope, item.obj);
2334                             onAvailStack[i] = null;
2335                         }
2336                     } else {
2337                         notAvail.push(item);
2338                     }
2339                 }
2340             }
2341
2342             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2343
2344             if (tryAgain) {
2345
2346                 this.startInterval();
2347             } else {
2348                 clearInterval(this._interval);
2349                 this._interval = null;
2350             }
2351
2352             this.locked = false;
2353
2354             return true;
2355
2356         },
2357
2358
2359         purgeElement: function(el, recurse, eventName) {
2360             var elListeners = this.getListeners(el, eventName);
2361             if (elListeners) {
2362                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2363                     var l = elListeners[i];
2364                     this.removeListener(el, l.type, l.fn);
2365                 }
2366             }
2367
2368             if (recurse && el && el.childNodes) {
2369                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2370                     this.purgeElement(el.childNodes[i], recurse, eventName);
2371                 }
2372             }
2373         },
2374
2375
2376         getListeners: function(el, eventName) {
2377             var results = [], searchLists;
2378             if (!eventName) {
2379                 searchLists = [listeners, unloadListeners];
2380             } else if (eventName == "unload") {
2381                 searchLists = [unloadListeners];
2382             } else {
2383                 searchLists = [listeners];
2384             }
2385
2386             for (var j = 0; j < searchLists.length; ++j) {
2387                 var searchList = searchLists[j];
2388                 if (searchList && searchList.length > 0) {
2389                     for (var i = 0,len = searchList.length; i < len; ++i) {
2390                         var l = searchList[i];
2391                         if (l && l[this.EL] === el &&
2392                             (!eventName || eventName === l[this.TYPE])) {
2393                             results.push({
2394                                 type:   l[this.TYPE],
2395                                 fn:     l[this.FN],
2396                                 obj:    l[this.OBJ],
2397                                 adjust: l[this.ADJ_SCOPE],
2398                                 index:  i
2399                             });
2400                         }
2401                     }
2402                 }
2403             }
2404
2405             return (results.length) ? results : null;
2406         },
2407
2408
2409         _unload: function(e) {
2410
2411             var EU = Roo.lib.Event, i, j, l, len, index;
2412
2413             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2414                 l = unloadListeners[i];
2415                 if (l) {
2416                     var scope = window;
2417                     if (l[EU.ADJ_SCOPE]) {
2418                         if (l[EU.ADJ_SCOPE] === true) {
2419                             scope = l[EU.OBJ];
2420                         } else {
2421                             scope = l[EU.ADJ_SCOPE];
2422                         }
2423                     }
2424                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2425                     unloadListeners[i] = null;
2426                     l = null;
2427                     scope = null;
2428                 }
2429             }
2430
2431             unloadListeners = null;
2432
2433             if (listeners && listeners.length > 0) {
2434                 j = listeners.length;
2435                 while (j) {
2436                     index = j - 1;
2437                     l = listeners[index];
2438                     if (l) {
2439                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2440                                 l[EU.FN], index);
2441                     }
2442                     j = j - 1;
2443                 }
2444                 l = null;
2445
2446                 EU.clearCache();
2447             }
2448
2449             EU.doRemove(window, "unload", EU._unload);
2450
2451         },
2452
2453
2454         getScroll: function() {
2455             var dd = document.documentElement, db = document.body;
2456             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2457                 return [dd.scrollTop, dd.scrollLeft];
2458             } else if (db) {
2459                 return [db.scrollTop, db.scrollLeft];
2460             } else {
2461                 return [0, 0];
2462             }
2463         },
2464
2465
2466         doAdd: function () {
2467             if (window.addEventListener) {
2468                 return function(el, eventName, fn, capture) {
2469                     el.addEventListener(eventName, fn, (capture));
2470                 };
2471             } else if (window.attachEvent) {
2472                 return function(el, eventName, fn, capture) {
2473                     el.attachEvent("on" + eventName, fn);
2474                 };
2475             } else {
2476                 return function() {
2477                 };
2478             }
2479         }(),
2480
2481
2482         doRemove: function() {
2483             if (window.removeEventListener) {
2484                 return function (el, eventName, fn, capture) {
2485                     el.removeEventListener(eventName, fn, (capture));
2486                 };
2487             } else if (window.detachEvent) {
2488                 return function (el, eventName, fn) {
2489                     el.detachEvent("on" + eventName, fn);
2490                 };
2491             } else {
2492                 return function() {
2493                 };
2494             }
2495         }()
2496     };
2497     
2498 }();
2499 (function() {     
2500    
2501     var E = Roo.lib.Event;
2502     E.on = E.addListener;
2503     E.un = E.removeListener;
2504
2505     if (document && document.body) {
2506         E._load();
2507     } else {
2508         E.doAdd(window, "load", E._load);
2509     }
2510     E.doAdd(window, "unload", E._unload);
2511     E._tryPreloadAttach();
2512 })();
2513
2514 /*
2515  * Portions of this file are based on pieces of Yahoo User Interface Library
2516  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2517  * YUI licensed under the BSD License:
2518  * http://developer.yahoo.net/yui/license.txt
2519  * <script type="text/javascript">
2520  *
2521  */
2522
2523 (function() {
2524     /**
2525      * @class Roo.lib.Ajax
2526      *
2527      */
2528     Roo.lib.Ajax = {
2529         /**
2530          * @static 
2531          */
2532         request : function(method, uri, cb, data, options) {
2533             if(options){
2534                 var hs = options.headers;
2535                 if(hs){
2536                     for(var h in hs){
2537                         if(hs.hasOwnProperty(h)){
2538                             this.initHeader(h, hs[h], false);
2539                         }
2540                     }
2541                 }
2542                 if(options.xmlData){
2543                     this.initHeader('Content-Type', 'text/xml', false);
2544                     method = 'POST';
2545                     data = options.xmlData;
2546                 }
2547             }
2548
2549             return this.asyncRequest(method, uri, cb, data);
2550         },
2551
2552         serializeForm : function(form) {
2553             if(typeof form == 'string') {
2554                 form = (document.getElementById(form) || document.forms[form]);
2555             }
2556
2557             var el, name, val, disabled, data = '', hasSubmit = false;
2558             for (var i = 0; i < form.elements.length; i++) {
2559                 el = form.elements[i];
2560                 disabled = form.elements[i].disabled;
2561                 name = form.elements[i].name;
2562                 val = form.elements[i].value;
2563
2564                 if (!disabled && name){
2565                     switch (el.type)
2566                             {
2567                         case 'select-one':
2568                         case 'select-multiple':
2569                             for (var j = 0; j < el.options.length; j++) {
2570                                 if (el.options[j].selected) {
2571                                     if (Roo.isIE) {
2572                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2573                                     }
2574                                     else {
2575                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2576                                     }
2577                                 }
2578                             }
2579                             break;
2580                         case 'radio':
2581                         case 'checkbox':
2582                             if (el.checked) {
2583                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2584                             }
2585                             break;
2586                         case 'file':
2587
2588                         case undefined:
2589
2590                         case 'reset':
2591
2592                         case 'button':
2593
2594                             break;
2595                         case 'submit':
2596                             if(hasSubmit == false) {
2597                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2598                                 hasSubmit = true;
2599                             }
2600                             break;
2601                         default:
2602                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2603                             break;
2604                     }
2605                 }
2606             }
2607             data = data.substr(0, data.length - 1);
2608             return data;
2609         },
2610
2611         headers:{},
2612
2613         hasHeaders:false,
2614
2615         useDefaultHeader:true,
2616
2617         defaultPostHeader:'application/x-www-form-urlencoded',
2618
2619         useDefaultXhrHeader:true,
2620
2621         defaultXhrHeader:'XMLHttpRequest',
2622
2623         hasDefaultHeaders:true,
2624
2625         defaultHeaders:{},
2626
2627         poll:{},
2628
2629         timeout:{},
2630
2631         pollInterval:50,
2632
2633         transactionId:0,
2634
2635         setProgId:function(id)
2636         {
2637             this.activeX.unshift(id);
2638         },
2639
2640         setDefaultPostHeader:function(b)
2641         {
2642             this.useDefaultHeader = b;
2643         },
2644
2645         setDefaultXhrHeader:function(b)
2646         {
2647             this.useDefaultXhrHeader = b;
2648         },
2649
2650         setPollingInterval:function(i)
2651         {
2652             if (typeof i == 'number' && isFinite(i)) {
2653                 this.pollInterval = i;
2654             }
2655         },
2656
2657         createXhrObject:function(transactionId)
2658         {
2659             var obj,http;
2660             try
2661             {
2662
2663                 http = new XMLHttpRequest();
2664
2665                 obj = { conn:http, tId:transactionId };
2666             }
2667             catch(e)
2668             {
2669                 for (var i = 0; i < this.activeX.length; ++i) {
2670                     try
2671                     {
2672
2673                         http = new ActiveXObject(this.activeX[i]);
2674
2675                         obj = { conn:http, tId:transactionId };
2676                         break;
2677                     }
2678                     catch(e) {
2679                     }
2680                 }
2681             }
2682             finally
2683             {
2684                 return obj;
2685             }
2686         },
2687
2688         getConnectionObject:function()
2689         {
2690             var o;
2691             var tId = this.transactionId;
2692
2693             try
2694             {
2695                 o = this.createXhrObject(tId);
2696                 if (o) {
2697                     this.transactionId++;
2698                 }
2699             }
2700             catch(e) {
2701             }
2702             finally
2703             {
2704                 return o;
2705             }
2706         },
2707
2708         asyncRequest:function(method, uri, callback, postData)
2709         {
2710             var o = this.getConnectionObject();
2711
2712             if (!o) {
2713                 return null;
2714             }
2715             else {
2716                 o.conn.open(method, uri, true);
2717
2718                 if (this.useDefaultXhrHeader) {
2719                     if (!this.defaultHeaders['X-Requested-With']) {
2720                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2721                     }
2722                 }
2723
2724                 if(postData && this.useDefaultHeader){
2725                     this.initHeader('Content-Type', this.defaultPostHeader);
2726                 }
2727
2728                  if (this.hasDefaultHeaders || this.hasHeaders) {
2729                     this.setHeader(o);
2730                 }
2731
2732                 this.handleReadyState(o, callback);
2733                 o.conn.send(postData || null);
2734
2735                 return o;
2736             }
2737         },
2738
2739         handleReadyState:function(o, callback)
2740         {
2741             var oConn = this;
2742
2743             if (callback && callback.timeout) {
2744                 
2745                 this.timeout[o.tId] = window.setTimeout(function() {
2746                     oConn.abort(o, callback, true);
2747                 }, callback.timeout);
2748             }
2749
2750             this.poll[o.tId] = window.setInterval(
2751                     function() {
2752                         if (o.conn && o.conn.readyState == 4) {
2753                             window.clearInterval(oConn.poll[o.tId]);
2754                             delete oConn.poll[o.tId];
2755
2756                             if(callback && callback.timeout) {
2757                                 window.clearTimeout(oConn.timeout[o.tId]);
2758                                 delete oConn.timeout[o.tId];
2759                             }
2760
2761                             oConn.handleTransactionResponse(o, callback);
2762                         }
2763                     }
2764                     , this.pollInterval);
2765         },
2766
2767         handleTransactionResponse:function(o, callback, isAbort)
2768         {
2769
2770             if (!callback) {
2771                 this.releaseObject(o);
2772                 return;
2773             }
2774
2775             var httpStatus, responseObject;
2776
2777             try
2778             {
2779                 if (o.conn.status !== undefined && o.conn.status != 0) {
2780                     httpStatus = o.conn.status;
2781                 }
2782                 else {
2783                     httpStatus = 13030;
2784                 }
2785             }
2786             catch(e) {
2787
2788
2789                 httpStatus = 13030;
2790             }
2791
2792             if (httpStatus >= 200 && httpStatus < 300) {
2793                 responseObject = this.createResponseObject(o, callback.argument);
2794                 if (callback.success) {
2795                     if (!callback.scope) {
2796                         callback.success(responseObject);
2797                     }
2798                     else {
2799
2800
2801                         callback.success.apply(callback.scope, [responseObject]);
2802                     }
2803                 }
2804             }
2805             else {
2806                 switch (httpStatus) {
2807
2808                     case 12002:
2809                     case 12029:
2810                     case 12030:
2811                     case 12031:
2812                     case 12152:
2813                     case 13030:
2814                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2815                         if (callback.failure) {
2816                             if (!callback.scope) {
2817                                 callback.failure(responseObject);
2818                             }
2819                             else {
2820                                 callback.failure.apply(callback.scope, [responseObject]);
2821                             }
2822                         }
2823                         break;
2824                     default:
2825                         responseObject = this.createResponseObject(o, callback.argument);
2826                         if (callback.failure) {
2827                             if (!callback.scope) {
2828                                 callback.failure(responseObject);
2829                             }
2830                             else {
2831                                 callback.failure.apply(callback.scope, [responseObject]);
2832                             }
2833                         }
2834                 }
2835             }
2836
2837             this.releaseObject(o);
2838             responseObject = null;
2839         },
2840
2841         createResponseObject:function(o, callbackArg)
2842         {
2843             var obj = {};
2844             var headerObj = {};
2845
2846             try
2847             {
2848                 var headerStr = o.conn.getAllResponseHeaders();
2849                 var header = headerStr.split('\n');
2850                 for (var i = 0; i < header.length; i++) {
2851                     var delimitPos = header[i].indexOf(':');
2852                     if (delimitPos != -1) {
2853                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2854                     }
2855                 }
2856             }
2857             catch(e) {
2858             }
2859
2860             obj.tId = o.tId;
2861             obj.status = o.conn.status;
2862             obj.statusText = o.conn.statusText;
2863             obj.getResponseHeader = headerObj;
2864             obj.getAllResponseHeaders = headerStr;
2865             obj.responseText = o.conn.responseText;
2866             obj.responseXML = o.conn.responseXML;
2867
2868             if (typeof callbackArg !== undefined) {
2869                 obj.argument = callbackArg;
2870             }
2871
2872             return obj;
2873         },
2874
2875         createExceptionObject:function(tId, callbackArg, isAbort)
2876         {
2877             var COMM_CODE = 0;
2878             var COMM_ERROR = 'communication failure';
2879             var ABORT_CODE = -1;
2880             var ABORT_ERROR = 'transaction aborted';
2881
2882             var obj = {};
2883
2884             obj.tId = tId;
2885             if (isAbort) {
2886                 obj.status = ABORT_CODE;
2887                 obj.statusText = ABORT_ERROR;
2888             }
2889             else {
2890                 obj.status = COMM_CODE;
2891                 obj.statusText = COMM_ERROR;
2892             }
2893
2894             if (callbackArg) {
2895                 obj.argument = callbackArg;
2896             }
2897
2898             return obj;
2899         },
2900
2901         initHeader:function(label, value, isDefault)
2902         {
2903             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2904
2905             if (headerObj[label] === undefined) {
2906                 headerObj[label] = value;
2907             }
2908             else {
2909
2910
2911                 headerObj[label] = value + "," + headerObj[label];
2912             }
2913
2914             if (isDefault) {
2915                 this.hasDefaultHeaders = true;
2916             }
2917             else {
2918                 this.hasHeaders = true;
2919             }
2920         },
2921
2922
2923         setHeader:function(o)
2924         {
2925             if (this.hasDefaultHeaders) {
2926                 for (var prop in this.defaultHeaders) {
2927                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2928                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2929                     }
2930                 }
2931             }
2932
2933             if (this.hasHeaders) {
2934                 for (var prop in this.headers) {
2935                     if (this.headers.hasOwnProperty(prop)) {
2936                         o.conn.setRequestHeader(prop, this.headers[prop]);
2937                     }
2938                 }
2939                 this.headers = {};
2940                 this.hasHeaders = false;
2941             }
2942         },
2943
2944         resetDefaultHeaders:function() {
2945             delete this.defaultHeaders;
2946             this.defaultHeaders = {};
2947             this.hasDefaultHeaders = false;
2948         },
2949
2950         abort:function(o, callback, isTimeout)
2951         {
2952             if(this.isCallInProgress(o)) {
2953                 o.conn.abort();
2954                 window.clearInterval(this.poll[o.tId]);
2955                 delete this.poll[o.tId];
2956                 if (isTimeout) {
2957                     delete this.timeout[o.tId];
2958                 }
2959
2960                 this.handleTransactionResponse(o, callback, true);
2961
2962                 return true;
2963             }
2964             else {
2965                 return false;
2966             }
2967         },
2968
2969
2970         isCallInProgress:function(o)
2971         {
2972             if (o && o.conn) {
2973                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2974             }
2975             else {
2976
2977                 return false;
2978             }
2979         },
2980
2981
2982         releaseObject:function(o)
2983         {
2984
2985             o.conn = null;
2986
2987             o = null;
2988         },
2989
2990         activeX:[
2991         'MSXML2.XMLHTTP.3.0',
2992         'MSXML2.XMLHTTP',
2993         'Microsoft.XMLHTTP'
2994         ]
2995
2996
2997     };
2998 })();/*
2999  * Portions of this file are based on pieces of Yahoo User Interface Library
3000  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3001  * YUI licensed under the BSD License:
3002  * http://developer.yahoo.net/yui/license.txt
3003  * <script type="text/javascript">
3004  *
3005  */
3006
3007 Roo.lib.Region = function(t, r, b, l) {
3008     this.top = t;
3009     this[1] = t;
3010     this.right = r;
3011     this.bottom = b;
3012     this.left = l;
3013     this[0] = l;
3014 };
3015
3016
3017 Roo.lib.Region.prototype = {
3018     contains : function(region) {
3019         return ( region.left >= this.left &&
3020                  region.right <= this.right &&
3021                  region.top >= this.top &&
3022                  region.bottom <= this.bottom    );
3023
3024     },
3025
3026     getArea : function() {
3027         return ( (this.bottom - this.top) * (this.right - this.left) );
3028     },
3029
3030     intersect : function(region) {
3031         var t = Math.max(this.top, region.top);
3032         var r = Math.min(this.right, region.right);
3033         var b = Math.min(this.bottom, region.bottom);
3034         var l = Math.max(this.left, region.left);
3035
3036         if (b >= t && r >= l) {
3037             return new Roo.lib.Region(t, r, b, l);
3038         } else {
3039             return null;
3040         }
3041     },
3042     union : function(region) {
3043         var t = Math.min(this.top, region.top);
3044         var r = Math.max(this.right, region.right);
3045         var b = Math.max(this.bottom, region.bottom);
3046         var l = Math.min(this.left, region.left);
3047
3048         return new Roo.lib.Region(t, r, b, l);
3049     },
3050
3051     adjust : function(t, l, b, r) {
3052         this.top += t;
3053         this.left += l;
3054         this.right += r;
3055         this.bottom += b;
3056         return this;
3057     }
3058 };
3059
3060 Roo.lib.Region.getRegion = function(el) {
3061     var p = Roo.lib.Dom.getXY(el);
3062
3063     var t = p[1];
3064     var r = p[0] + el.offsetWidth;
3065     var b = p[1] + el.offsetHeight;
3066     var l = p[0];
3067
3068     return new Roo.lib.Region(t, r, b, l);
3069 };
3070 /*
3071  * Portions of this file are based on pieces of Yahoo User Interface Library
3072  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3073  * YUI licensed under the BSD License:
3074  * http://developer.yahoo.net/yui/license.txt
3075  * <script type="text/javascript">
3076  *
3077  */
3078 //@@dep Roo.lib.Region
3079
3080
3081 Roo.lib.Point = function(x, y) {
3082     if (x instanceof Array) {
3083         y = x[1];
3084         x = x[0];
3085     }
3086     this.x = this.right = this.left = this[0] = x;
3087     this.y = this.top = this.bottom = this[1] = y;
3088 };
3089
3090 Roo.lib.Point.prototype = new Roo.lib.Region();
3091 /*
3092  * Portions of this file are based on pieces of Yahoo User Interface Library
3093  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3094  * YUI licensed under the BSD License:
3095  * http://developer.yahoo.net/yui/license.txt
3096  * <script type="text/javascript">
3097  *
3098  */
3099  
3100 (function() {   
3101
3102     Roo.lib.Anim = {
3103         scroll : function(el, args, duration, easing, cb, scope) {
3104             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3105         },
3106
3107         motion : function(el, args, duration, easing, cb, scope) {
3108             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3109         },
3110
3111         color : function(el, args, duration, easing, cb, scope) {
3112             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3113         },
3114
3115         run : function(el, args, duration, easing, cb, scope, type) {
3116             type = type || Roo.lib.AnimBase;
3117             if (typeof easing == "string") {
3118                 easing = Roo.lib.Easing[easing];
3119             }
3120             var anim = new type(el, args, duration, easing);
3121             anim.animateX(function() {
3122                 Roo.callback(cb, scope);
3123             });
3124             return anim;
3125         }
3126     };
3127 })();/*
3128  * Portions of this file are based on pieces of Yahoo User Interface Library
3129  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3130  * YUI licensed under the BSD License:
3131  * http://developer.yahoo.net/yui/license.txt
3132  * <script type="text/javascript">
3133  *
3134  */
3135
3136 (function() {    
3137     var libFlyweight;
3138     
3139     function fly(el) {
3140         if (!libFlyweight) {
3141             libFlyweight = new Roo.Element.Flyweight();
3142         }
3143         libFlyweight.dom = el;
3144         return libFlyweight;
3145     }
3146
3147     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3148     
3149    
3150     
3151     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3152         if (el) {
3153             this.init(el, attributes, duration, method);
3154         }
3155     };
3156
3157     Roo.lib.AnimBase.fly = fly;
3158     
3159     
3160     
3161     Roo.lib.AnimBase.prototype = {
3162
3163         toString: function() {
3164             var el = this.getEl();
3165             var id = el.id || el.tagName;
3166             return ("Anim " + id);
3167         },
3168
3169         patterns: {
3170             noNegatives:        /width|height|opacity|padding/i,
3171             offsetAttribute:  /^((width|height)|(top|left))$/,
3172             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3173             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3174         },
3175
3176
3177         doMethod: function(attr, start, end) {
3178             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3179         },
3180
3181
3182         setAttribute: function(attr, val, unit) {
3183             if (this.patterns.noNegatives.test(attr)) {
3184                 val = (val > 0) ? val : 0;
3185             }
3186
3187             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3188         },
3189
3190
3191         getAttribute: function(attr) {
3192             var el = this.getEl();
3193             var val = fly(el).getStyle(attr);
3194
3195             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3196                 return parseFloat(val);
3197             }
3198
3199             var a = this.patterns.offsetAttribute.exec(attr) || [];
3200             var pos = !!( a[3] );
3201             var box = !!( a[2] );
3202
3203
3204             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3205                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3206             } else {
3207                 val = 0;
3208             }
3209
3210             return val;
3211         },
3212
3213
3214         getDefaultUnit: function(attr) {
3215             if (this.patterns.defaultUnit.test(attr)) {
3216                 return 'px';
3217             }
3218
3219             return '';
3220         },
3221
3222         animateX : function(callback, scope) {
3223             var f = function() {
3224                 this.onComplete.removeListener(f);
3225                 if (typeof callback == "function") {
3226                     callback.call(scope || this, this);
3227                 }
3228             };
3229             this.onComplete.addListener(f, this);
3230             this.animate();
3231         },
3232
3233
3234         setRuntimeAttribute: function(attr) {
3235             var start;
3236             var end;
3237             var attributes = this.attributes;
3238
3239             this.runtimeAttributes[attr] = {};
3240
3241             var isset = function(prop) {
3242                 return (typeof prop !== 'undefined');
3243             };
3244
3245             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3246                 return false;
3247             }
3248
3249             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3250
3251
3252             if (isset(attributes[attr]['to'])) {
3253                 end = attributes[attr]['to'];
3254             } else if (isset(attributes[attr]['by'])) {
3255                 if (start.constructor == Array) {
3256                     end = [];
3257                     for (var i = 0, len = start.length; i < len; ++i) {
3258                         end[i] = start[i] + attributes[attr]['by'][i];
3259                     }
3260                 } else {
3261                     end = start + attributes[attr]['by'];
3262                 }
3263             }
3264
3265             this.runtimeAttributes[attr].start = start;
3266             this.runtimeAttributes[attr].end = end;
3267
3268
3269             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3270         },
3271
3272
3273         init: function(el, attributes, duration, method) {
3274
3275             var isAnimated = false;
3276
3277
3278             var startTime = null;
3279
3280
3281             var actualFrames = 0;
3282
3283
3284             el = Roo.getDom(el);
3285
3286
3287             this.attributes = attributes || {};
3288
3289
3290             this.duration = duration || 1;
3291
3292
3293             this.method = method || Roo.lib.Easing.easeNone;
3294
3295
3296             this.useSeconds = true;
3297
3298
3299             this.currentFrame = 0;
3300
3301
3302             this.totalFrames = Roo.lib.AnimMgr.fps;
3303
3304
3305             this.getEl = function() {
3306                 return el;
3307             };
3308
3309
3310             this.isAnimated = function() {
3311                 return isAnimated;
3312             };
3313
3314
3315             this.getStartTime = function() {
3316                 return startTime;
3317             };
3318
3319             this.runtimeAttributes = {};
3320
3321
3322             this.animate = function() {
3323                 if (this.isAnimated()) {
3324                     return false;
3325                 }
3326
3327                 this.currentFrame = 0;
3328
3329                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3330
3331                 Roo.lib.AnimMgr.registerElement(this);
3332             };
3333
3334
3335             this.stop = function(finish) {
3336                 if (finish) {
3337                     this.currentFrame = this.totalFrames;
3338                     this._onTween.fire();
3339                 }
3340                 Roo.lib.AnimMgr.stop(this);
3341             };
3342
3343             var onStart = function() {
3344                 this.onStart.fire();
3345
3346                 this.runtimeAttributes = {};
3347                 for (var attr in this.attributes) {
3348                     this.setRuntimeAttribute(attr);
3349                 }
3350
3351                 isAnimated = true;
3352                 actualFrames = 0;
3353                 startTime = new Date();
3354             };
3355
3356
3357             var onTween = function() {
3358                 var data = {
3359                     duration: new Date() - this.getStartTime(),
3360                     currentFrame: this.currentFrame
3361                 };
3362
3363                 data.toString = function() {
3364                     return (
3365                             'duration: ' + data.duration +
3366                             ', currentFrame: ' + data.currentFrame
3367                             );
3368                 };
3369
3370                 this.onTween.fire(data);
3371
3372                 var runtimeAttributes = this.runtimeAttributes;
3373
3374                 for (var attr in runtimeAttributes) {
3375                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3376                 }
3377
3378                 actualFrames += 1;
3379             };
3380
3381             var onComplete = function() {
3382                 var actual_duration = (new Date() - startTime) / 1000 ;
3383
3384                 var data = {
3385                     duration: actual_duration,
3386                     frames: actualFrames,
3387                     fps: actualFrames / actual_duration
3388                 };
3389
3390                 data.toString = function() {
3391                     return (
3392                             'duration: ' + data.duration +
3393                             ', frames: ' + data.frames +
3394                             ', fps: ' + data.fps
3395                             );
3396                 };
3397
3398                 isAnimated = false;
3399                 actualFrames = 0;
3400                 this.onComplete.fire(data);
3401             };
3402
3403
3404             this._onStart = new Roo.util.Event(this);
3405             this.onStart = new Roo.util.Event(this);
3406             this.onTween = new Roo.util.Event(this);
3407             this._onTween = new Roo.util.Event(this);
3408             this.onComplete = new Roo.util.Event(this);
3409             this._onComplete = new Roo.util.Event(this);
3410             this._onStart.addListener(onStart);
3411             this._onTween.addListener(onTween);
3412             this._onComplete.addListener(onComplete);
3413         }
3414     };
3415 })();
3416 /*
3417  * Portions of this file are based on pieces of Yahoo User Interface Library
3418  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3419  * YUI licensed under the BSD License:
3420  * http://developer.yahoo.net/yui/license.txt
3421  * <script type="text/javascript">
3422  *
3423  */
3424
3425 Roo.lib.AnimMgr = new function() {
3426
3427     var thread = null;
3428
3429
3430     var queue = [];
3431
3432
3433     var tweenCount = 0;
3434
3435
3436     this.fps = 1000;
3437
3438
3439     this.delay = 1;
3440
3441
3442     this.registerElement = function(tween) {
3443         queue[queue.length] = tween;
3444         tweenCount += 1;
3445         tween._onStart.fire();
3446         this.start();
3447     };
3448
3449
3450     this.unRegister = function(tween, index) {
3451         tween._onComplete.fire();
3452         index = index || getIndex(tween);
3453         if (index != -1) {
3454             queue.splice(index, 1);
3455         }
3456
3457         tweenCount -= 1;
3458         if (tweenCount <= 0) {
3459             this.stop();
3460         }
3461     };
3462
3463
3464     this.start = function() {
3465         if (thread === null) {
3466             thread = setInterval(this.run, this.delay);
3467         }
3468     };
3469
3470
3471     this.stop = function(tween) {
3472         if (!tween) {
3473             clearInterval(thread);
3474
3475             for (var i = 0, len = queue.length; i < len; ++i) {
3476                 if (queue[0].isAnimated()) {
3477                     this.unRegister(queue[0], 0);
3478                 }
3479             }
3480
3481             queue = [];
3482             thread = null;
3483             tweenCount = 0;
3484         }
3485         else {
3486             this.unRegister(tween);
3487         }
3488     };
3489
3490
3491     this.run = function() {
3492         for (var i = 0, len = queue.length; i < len; ++i) {
3493             var tween = queue[i];
3494             if (!tween || !tween.isAnimated()) {
3495                 continue;
3496             }
3497
3498             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3499             {
3500                 tween.currentFrame += 1;
3501
3502                 if (tween.useSeconds) {
3503                     correctFrame(tween);
3504                 }
3505                 tween._onTween.fire();
3506             }
3507             else {
3508                 Roo.lib.AnimMgr.stop(tween, i);
3509             }
3510         }
3511     };
3512
3513     var getIndex = function(anim) {
3514         for (var i = 0, len = queue.length; i < len; ++i) {
3515             if (queue[i] == anim) {
3516                 return i;
3517             }
3518         }
3519         return -1;
3520     };
3521
3522
3523     var correctFrame = function(tween) {
3524         var frames = tween.totalFrames;
3525         var frame = tween.currentFrame;
3526         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3527         var elapsed = (new Date() - tween.getStartTime());
3528         var tweak = 0;
3529
3530         if (elapsed < tween.duration * 1000) {
3531             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3532         } else {
3533             tweak = frames - (frame + 1);
3534         }
3535         if (tweak > 0 && isFinite(tweak)) {
3536             if (tween.currentFrame + tweak >= frames) {
3537                 tweak = frames - (frame + 1);
3538             }
3539
3540             tween.currentFrame += tweak;
3541         }
3542     };
3543 };
3544
3545     /*
3546  * Portions of this file are based on pieces of Yahoo User Interface Library
3547  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3548  * YUI licensed under the BSD License:
3549  * http://developer.yahoo.net/yui/license.txt
3550  * <script type="text/javascript">
3551  *
3552  */
3553 Roo.lib.Bezier = new function() {
3554
3555         this.getPosition = function(points, t) {
3556             var n = points.length;
3557             var tmp = [];
3558
3559             for (var i = 0; i < n; ++i) {
3560                 tmp[i] = [points[i][0], points[i][1]];
3561             }
3562
3563             for (var j = 1; j < n; ++j) {
3564                 for (i = 0; i < n - j; ++i) {
3565                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3566                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3567                 }
3568             }
3569
3570             return [ tmp[0][0], tmp[0][1] ];
3571
3572         };
3573     };/*
3574  * Portions of this file are based on pieces of Yahoo User Interface Library
3575  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3576  * YUI licensed under the BSD License:
3577  * http://developer.yahoo.net/yui/license.txt
3578  * <script type="text/javascript">
3579  *
3580  */
3581 (function() {
3582
3583     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3584         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3585     };
3586
3587     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3588
3589     var fly = Roo.lib.AnimBase.fly;
3590     var Y = Roo.lib;
3591     var superclass = Y.ColorAnim.superclass;
3592     var proto = Y.ColorAnim.prototype;
3593
3594     proto.toString = function() {
3595         var el = this.getEl();
3596         var id = el.id || el.tagName;
3597         return ("ColorAnim " + id);
3598     };
3599
3600     proto.patterns.color = /color$/i;
3601     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3602     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3603     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3604     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3605
3606
3607     proto.parseColor = function(s) {
3608         if (s.length == 3) {
3609             return s;
3610         }
3611
3612         var c = this.patterns.hex.exec(s);
3613         if (c && c.length == 4) {
3614             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3615         }
3616
3617         c = this.patterns.rgb.exec(s);
3618         if (c && c.length == 4) {
3619             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3620         }
3621
3622         c = this.patterns.hex3.exec(s);
3623         if (c && c.length == 4) {
3624             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3625         }
3626
3627         return null;
3628     };
3629     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3630     proto.getAttribute = function(attr) {
3631         var el = this.getEl();
3632         if (this.patterns.color.test(attr)) {
3633             var val = fly(el).getStyle(attr);
3634
3635             if (this.patterns.transparent.test(val)) {
3636                 var parent = el.parentNode;
3637                 val = fly(parent).getStyle(attr);
3638
3639                 while (parent && this.patterns.transparent.test(val)) {
3640                     parent = parent.parentNode;
3641                     val = fly(parent).getStyle(attr);
3642                     if (parent.tagName.toUpperCase() == 'HTML') {
3643                         val = '#fff';
3644                     }
3645                 }
3646             }
3647         } else {
3648             val = superclass.getAttribute.call(this, attr);
3649         }
3650
3651         return val;
3652     };
3653     proto.getAttribute = function(attr) {
3654         var el = this.getEl();
3655         if (this.patterns.color.test(attr)) {
3656             var val = fly(el).getStyle(attr);
3657
3658             if (this.patterns.transparent.test(val)) {
3659                 var parent = el.parentNode;
3660                 val = fly(parent).getStyle(attr);
3661
3662                 while (parent && this.patterns.transparent.test(val)) {
3663                     parent = parent.parentNode;
3664                     val = fly(parent).getStyle(attr);
3665                     if (parent.tagName.toUpperCase() == 'HTML') {
3666                         val = '#fff';
3667                     }
3668                 }
3669             }
3670         } else {
3671             val = superclass.getAttribute.call(this, attr);
3672         }
3673
3674         return val;
3675     };
3676
3677     proto.doMethod = function(attr, start, end) {
3678         var val;
3679
3680         if (this.patterns.color.test(attr)) {
3681             val = [];
3682             for (var i = 0, len = start.length; i < len; ++i) {
3683                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3684             }
3685
3686             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3687         }
3688         else {
3689             val = superclass.doMethod.call(this, attr, start, end);
3690         }
3691
3692         return val;
3693     };
3694
3695     proto.setRuntimeAttribute = function(attr) {
3696         superclass.setRuntimeAttribute.call(this, attr);
3697
3698         if (this.patterns.color.test(attr)) {
3699             var attributes = this.attributes;
3700             var start = this.parseColor(this.runtimeAttributes[attr].start);
3701             var end = this.parseColor(this.runtimeAttributes[attr].end);
3702
3703             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3704                 end = this.parseColor(attributes[attr].by);
3705
3706                 for (var i = 0, len = start.length; i < len; ++i) {
3707                     end[i] = start[i] + end[i];
3708                 }
3709             }
3710
3711             this.runtimeAttributes[attr].start = start;
3712             this.runtimeAttributes[attr].end = end;
3713         }
3714     };
3715 })();
3716
3717 /*
3718  * Portions of this file are based on pieces of Yahoo User Interface Library
3719  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3720  * YUI licensed under the BSD License:
3721  * http://developer.yahoo.net/yui/license.txt
3722  * <script type="text/javascript">
3723  *
3724  */
3725 Roo.lib.Easing = {
3726
3727
3728     easeNone: function (t, b, c, d) {
3729         return c * t / d + b;
3730     },
3731
3732
3733     easeIn: function (t, b, c, d) {
3734         return c * (t /= d) * t + b;
3735     },
3736
3737
3738     easeOut: function (t, b, c, d) {
3739         return -c * (t /= d) * (t - 2) + b;
3740     },
3741
3742
3743     easeBoth: function (t, b, c, d) {
3744         if ((t /= d / 2) < 1) {
3745             return c / 2 * t * t + b;
3746         }
3747
3748         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3749     },
3750
3751
3752     easeInStrong: function (t, b, c, d) {
3753         return c * (t /= d) * t * t * t + b;
3754     },
3755
3756
3757     easeOutStrong: function (t, b, c, d) {
3758         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3759     },
3760
3761
3762     easeBothStrong: function (t, b, c, d) {
3763         if ((t /= d / 2) < 1) {
3764             return c / 2 * t * t * t * t + b;
3765         }
3766
3767         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3768     },
3769
3770
3771
3772     elasticIn: function (t, b, c, d, a, p) {
3773         if (t == 0) {
3774             return b;
3775         }
3776         if ((t /= d) == 1) {
3777             return b + c;
3778         }
3779         if (!p) {
3780             p = d * .3;
3781         }
3782
3783         if (!a || a < Math.abs(c)) {
3784             a = c;
3785             var s = p / 4;
3786         }
3787         else {
3788             var s = p / (2 * Math.PI) * Math.asin(c / a);
3789         }
3790
3791         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3792     },
3793
3794
3795     elasticOut: function (t, b, c, d, a, p) {
3796         if (t == 0) {
3797             return b;
3798         }
3799         if ((t /= d) == 1) {
3800             return b + c;
3801         }
3802         if (!p) {
3803             p = d * .3;
3804         }
3805
3806         if (!a || a < Math.abs(c)) {
3807             a = c;
3808             var s = p / 4;
3809         }
3810         else {
3811             var s = p / (2 * Math.PI) * Math.asin(c / a);
3812         }
3813
3814         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3815     },
3816
3817
3818     elasticBoth: function (t, b, c, d, a, p) {
3819         if (t == 0) {
3820             return b;
3821         }
3822
3823         if ((t /= d / 2) == 2) {
3824             return b + c;
3825         }
3826
3827         if (!p) {
3828             p = d * (.3 * 1.5);
3829         }
3830
3831         if (!a || a < Math.abs(c)) {
3832             a = c;
3833             var s = p / 4;
3834         }
3835         else {
3836             var s = p / (2 * Math.PI) * Math.asin(c / a);
3837         }
3838
3839         if (t < 1) {
3840             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3841                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3842         }
3843         return a * Math.pow(2, -10 * (t -= 1)) *
3844                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3845     },
3846
3847
3848
3849     backIn: function (t, b, c, d, s) {
3850         if (typeof s == 'undefined') {
3851             s = 1.70158;
3852         }
3853         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3854     },
3855
3856
3857     backOut: function (t, b, c, d, s) {
3858         if (typeof s == 'undefined') {
3859             s = 1.70158;
3860         }
3861         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3862     },
3863
3864
3865     backBoth: function (t, b, c, d, s) {
3866         if (typeof s == 'undefined') {
3867             s = 1.70158;
3868         }
3869
3870         if ((t /= d / 2 ) < 1) {
3871             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3872         }
3873         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3874     },
3875
3876
3877     bounceIn: function (t, b, c, d) {
3878         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3879     },
3880
3881
3882     bounceOut: function (t, b, c, d) {
3883         if ((t /= d) < (1 / 2.75)) {
3884             return c * (7.5625 * t * t) + b;
3885         } else if (t < (2 / 2.75)) {
3886             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3887         } else if (t < (2.5 / 2.75)) {
3888             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3889         }
3890         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3891     },
3892
3893
3894     bounceBoth: function (t, b, c, d) {
3895         if (t < d / 2) {
3896             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3897         }
3898         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3899     }
3900 };/*
3901  * Portions of this file are based on pieces of Yahoo User Interface Library
3902  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3903  * YUI licensed under the BSD License:
3904  * http://developer.yahoo.net/yui/license.txt
3905  * <script type="text/javascript">
3906  *
3907  */
3908     (function() {
3909         Roo.lib.Motion = function(el, attributes, duration, method) {
3910             if (el) {
3911                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3912             }
3913         };
3914
3915         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3916
3917
3918         var Y = Roo.lib;
3919         var superclass = Y.Motion.superclass;
3920         var proto = Y.Motion.prototype;
3921
3922         proto.toString = function() {
3923             var el = this.getEl();
3924             var id = el.id || el.tagName;
3925             return ("Motion " + id);
3926         };
3927
3928         proto.patterns.points = /^points$/i;
3929
3930         proto.setAttribute = function(attr, val, unit) {
3931             if (this.patterns.points.test(attr)) {
3932                 unit = unit || 'px';
3933                 superclass.setAttribute.call(this, 'left', val[0], unit);
3934                 superclass.setAttribute.call(this, 'top', val[1], unit);
3935             } else {
3936                 superclass.setAttribute.call(this, attr, val, unit);
3937             }
3938         };
3939
3940         proto.getAttribute = function(attr) {
3941             if (this.patterns.points.test(attr)) {
3942                 var val = [
3943                         superclass.getAttribute.call(this, 'left'),
3944                         superclass.getAttribute.call(this, 'top')
3945                         ];
3946             } else {
3947                 val = superclass.getAttribute.call(this, attr);
3948             }
3949
3950             return val;
3951         };
3952
3953         proto.doMethod = function(attr, start, end) {
3954             var val = null;
3955
3956             if (this.patterns.points.test(attr)) {
3957                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3958                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3959             } else {
3960                 val = superclass.doMethod.call(this, attr, start, end);
3961             }
3962             return val;
3963         };
3964
3965         proto.setRuntimeAttribute = function(attr) {
3966             if (this.patterns.points.test(attr)) {
3967                 var el = this.getEl();
3968                 var attributes = this.attributes;
3969                 var start;
3970                 var control = attributes['points']['control'] || [];
3971                 var end;
3972                 var i, len;
3973
3974                 if (control.length > 0 && !(control[0] instanceof Array)) {
3975                     control = [control];
3976                 } else {
3977                     var tmp = [];
3978                     for (i = 0,len = control.length; i < len; ++i) {
3979                         tmp[i] = control[i];
3980                     }
3981                     control = tmp;
3982                 }
3983
3984                 Roo.fly(el).position();
3985
3986                 if (isset(attributes['points']['from'])) {
3987                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3988                 }
3989                 else {
3990                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3991                 }
3992
3993                 start = this.getAttribute('points');
3994
3995
3996                 if (isset(attributes['points']['to'])) {
3997                     end = translateValues.call(this, attributes['points']['to'], start);
3998
3999                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4000                     for (i = 0,len = control.length; i < len; ++i) {
4001                         control[i] = translateValues.call(this, control[i], start);
4002                     }
4003
4004
4005                 } else if (isset(attributes['points']['by'])) {
4006                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4007
4008                     for (i = 0,len = control.length; i < len; ++i) {
4009                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4010                     }
4011                 }
4012
4013                 this.runtimeAttributes[attr] = [start];
4014
4015                 if (control.length > 0) {
4016                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4017                 }
4018
4019                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4020             }
4021             else {
4022                 superclass.setRuntimeAttribute.call(this, attr);
4023             }
4024         };
4025
4026         var translateValues = function(val, start) {
4027             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4028             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4029
4030             return val;
4031         };
4032
4033         var isset = function(prop) {
4034             return (typeof prop !== 'undefined');
4035         };
4036     })();
4037 /*
4038  * Portions of this file are based on pieces of Yahoo User Interface Library
4039  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4040  * YUI licensed under the BSD License:
4041  * http://developer.yahoo.net/yui/license.txt
4042  * <script type="text/javascript">
4043  *
4044  */
4045     (function() {
4046         Roo.lib.Scroll = function(el, attributes, duration, method) {
4047             if (el) {
4048                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4049             }
4050         };
4051
4052         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4053
4054
4055         var Y = Roo.lib;
4056         var superclass = Y.Scroll.superclass;
4057         var proto = Y.Scroll.prototype;
4058
4059         proto.toString = function() {
4060             var el = this.getEl();
4061             var id = el.id || el.tagName;
4062             return ("Scroll " + id);
4063         };
4064
4065         proto.doMethod = function(attr, start, end) {
4066             var val = null;
4067
4068             if (attr == 'scroll') {
4069                 val = [
4070                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4071                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4072                         ];
4073
4074             } else {
4075                 val = superclass.doMethod.call(this, attr, start, end);
4076             }
4077             return val;
4078         };
4079
4080         proto.getAttribute = function(attr) {
4081             var val = null;
4082             var el = this.getEl();
4083
4084             if (attr == 'scroll') {
4085                 val = [ el.scrollLeft, el.scrollTop ];
4086             } else {
4087                 val = superclass.getAttribute.call(this, attr);
4088             }
4089
4090             return val;
4091         };
4092
4093         proto.setAttribute = function(attr, val, unit) {
4094             var el = this.getEl();
4095
4096             if (attr == 'scroll') {
4097                 el.scrollLeft = val[0];
4098                 el.scrollTop = val[1];
4099             } else {
4100                 superclass.setAttribute.call(this, attr, val, unit);
4101             }
4102         };
4103     })();
4104 /*
4105  * Based on:
4106  * Ext JS Library 1.1.1
4107  * Copyright(c) 2006-2007, Ext JS, LLC.
4108  *
4109  * Originally Released Under LGPL - original licence link has changed is not relivant.
4110  *
4111  * Fork - LGPL
4112  * <script type="text/javascript">
4113  */
4114
4115
4116 // nasty IE9 hack - what a pile of crap that is..
4117
4118  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4119     Range.prototype.createContextualFragment = function (html) {
4120         var doc = window.document;
4121         var container = doc.createElement("div");
4122         container.innerHTML = html;
4123         var frag = doc.createDocumentFragment(), n;
4124         while ((n = container.firstChild)) {
4125             frag.appendChild(n);
4126         }
4127         return frag;
4128     };
4129 }
4130
4131 /**
4132  * @class Roo.DomHelper
4133  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4134  * 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>.
4135  * @singleton
4136  */
4137 Roo.DomHelper = function(){
4138     var tempTableEl = null;
4139     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4140     var tableRe = /^table|tbody|tr|td$/i;
4141     var xmlns = {};
4142     // build as innerHTML where available
4143     /** @ignore */
4144     var createHtml = function(o){
4145         if(typeof o == 'string'){
4146             return o;
4147         }
4148         var b = "";
4149         if(!o.tag){
4150             o.tag = "div";
4151         }
4152         b += "<" + o.tag;
4153         for(var attr in o){
4154             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4155             if(attr == "style"){
4156                 var s = o["style"];
4157                 if(typeof s == "function"){
4158                     s = s.call();
4159                 }
4160                 if(typeof s == "string"){
4161                     b += ' style="' + s + '"';
4162                 }else if(typeof s == "object"){
4163                     b += ' style="';
4164                     for(var key in s){
4165                         if(typeof s[key] != "function"){
4166                             b += key + ":" + s[key] + ";";
4167                         }
4168                     }
4169                     b += '"';
4170                 }
4171             }else{
4172                 if(attr == "cls"){
4173                     b += ' class="' + o["cls"] + '"';
4174                 }else if(attr == "htmlFor"){
4175                     b += ' for="' + o["htmlFor"] + '"';
4176                 }else{
4177                     b += " " + attr + '="' + o[attr] + '"';
4178                 }
4179             }
4180         }
4181         if(emptyTags.test(o.tag)){
4182             b += "/>";
4183         }else{
4184             b += ">";
4185             var cn = o.children || o.cn;
4186             if(cn){
4187                 //http://bugs.kde.org/show_bug.cgi?id=71506
4188                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4189                     for(var i = 0, len = cn.length; i < len; i++) {
4190                         b += createHtml(cn[i], b);
4191                     }
4192                 }else{
4193                     b += createHtml(cn, b);
4194                 }
4195             }
4196             if(o.html){
4197                 b += o.html;
4198             }
4199             b += "</" + o.tag + ">";
4200         }
4201         return b;
4202     };
4203
4204     // build as dom
4205     /** @ignore */
4206     var createDom = function(o, parentNode){
4207          
4208         // defininition craeted..
4209         var ns = false;
4210         if (o.ns && o.ns != 'html') {
4211                
4212             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4213                 xmlns[o.ns] = o.xmlns;
4214                 ns = o.xmlns;
4215             }
4216             if (typeof(xmlns[o.ns]) == 'undefined') {
4217                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4218             }
4219             ns = xmlns[o.ns];
4220         }
4221         
4222         
4223         if (typeof(o) == 'string') {
4224             return parentNode.appendChild(document.createTextNode(o));
4225         }
4226         o.tag = o.tag || div;
4227         if (o.ns && Roo.isIE) {
4228             ns = false;
4229             o.tag = o.ns + ':' + o.tag;
4230             
4231         }
4232         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4233         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4234         for(var attr in o){
4235             
4236             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4237                     attr == "style" || typeof o[attr] == "function") continue;
4238                     
4239             if(attr=="cls" && Roo.isIE){
4240                 el.className = o["cls"];
4241             }else{
4242                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4243                 else { 
4244                     el[attr] = o[attr];
4245                 }
4246             }
4247         }
4248         Roo.DomHelper.applyStyles(el, o.style);
4249         var cn = o.children || o.cn;
4250         if(cn){
4251             //http://bugs.kde.org/show_bug.cgi?id=71506
4252              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4253                 for(var i = 0, len = cn.length; i < len; i++) {
4254                     createDom(cn[i], el);
4255                 }
4256             }else{
4257                 createDom(cn, el);
4258             }
4259         }
4260         if(o.html){
4261             el.innerHTML = o.html;
4262         }
4263         if(parentNode){
4264            parentNode.appendChild(el);
4265         }
4266         return el;
4267     };
4268
4269     var ieTable = function(depth, s, h, e){
4270         tempTableEl.innerHTML = [s, h, e].join('');
4271         var i = -1, el = tempTableEl;
4272         while(++i < depth){
4273             el = el.firstChild;
4274         }
4275         return el;
4276     };
4277
4278     // kill repeat to save bytes
4279     var ts = '<table>',
4280         te = '</table>',
4281         tbs = ts+'<tbody>',
4282         tbe = '</tbody>'+te,
4283         trs = tbs + '<tr>',
4284         tre = '</tr>'+tbe;
4285
4286     /**
4287      * @ignore
4288      * Nasty code for IE's broken table implementation
4289      */
4290     var insertIntoTable = function(tag, where, el, html){
4291         if(!tempTableEl){
4292             tempTableEl = document.createElement('div');
4293         }
4294         var node;
4295         var before = null;
4296         if(tag == 'td'){
4297             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4298                 return;
4299             }
4300             if(where == 'beforebegin'){
4301                 before = el;
4302                 el = el.parentNode;
4303             } else{
4304                 before = el.nextSibling;
4305                 el = el.parentNode;
4306             }
4307             node = ieTable(4, trs, html, tre);
4308         }
4309         else if(tag == 'tr'){
4310             if(where == 'beforebegin'){
4311                 before = el;
4312                 el = el.parentNode;
4313                 node = ieTable(3, tbs, html, tbe);
4314             } else if(where == 'afterend'){
4315                 before = el.nextSibling;
4316                 el = el.parentNode;
4317                 node = ieTable(3, tbs, html, tbe);
4318             } else{ // INTO a TR
4319                 if(where == 'afterbegin'){
4320                     before = el.firstChild;
4321                 }
4322                 node = ieTable(4, trs, html, tre);
4323             }
4324         } else if(tag == 'tbody'){
4325             if(where == 'beforebegin'){
4326                 before = el;
4327                 el = el.parentNode;
4328                 node = ieTable(2, ts, html, te);
4329             } else if(where == 'afterend'){
4330                 before = el.nextSibling;
4331                 el = el.parentNode;
4332                 node = ieTable(2, ts, html, te);
4333             } else{
4334                 if(where == 'afterbegin'){
4335                     before = el.firstChild;
4336                 }
4337                 node = ieTable(3, tbs, html, tbe);
4338             }
4339         } else{ // TABLE
4340             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4341                 return;
4342             }
4343             if(where == 'afterbegin'){
4344                 before = el.firstChild;
4345             }
4346             node = ieTable(2, ts, html, te);
4347         }
4348         el.insertBefore(node, before);
4349         return node;
4350     };
4351
4352     return {
4353     /** True to force the use of DOM instead of html fragments @type Boolean */
4354     useDom : false,
4355
4356     /**
4357      * Returns the markup for the passed Element(s) config
4358      * @param {Object} o The Dom object spec (and children)
4359      * @return {String}
4360      */
4361     markup : function(o){
4362         return createHtml(o);
4363     },
4364
4365     /**
4366      * Applies a style specification to an element
4367      * @param {String/HTMLElement} el The element to apply styles to
4368      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4369      * a function which returns such a specification.
4370      */
4371     applyStyles : function(el, styles){
4372         if(styles){
4373            el = Roo.fly(el);
4374            if(typeof styles == "string"){
4375                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4376                var matches;
4377                while ((matches = re.exec(styles)) != null){
4378                    el.setStyle(matches[1], matches[2]);
4379                }
4380            }else if (typeof styles == "object"){
4381                for (var style in styles){
4382                   el.setStyle(style, styles[style]);
4383                }
4384            }else if (typeof styles == "function"){
4385                 Roo.DomHelper.applyStyles(el, styles.call());
4386            }
4387         }
4388     },
4389
4390     /**
4391      * Inserts an HTML fragment into the Dom
4392      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4393      * @param {HTMLElement} el The context element
4394      * @param {String} html The HTML fragmenet
4395      * @return {HTMLElement} The new node
4396      */
4397     insertHtml : function(where, el, html){
4398         where = where.toLowerCase();
4399         if(el.insertAdjacentHTML){
4400             if(tableRe.test(el.tagName)){
4401                 var rs;
4402                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4403                     return rs;
4404                 }
4405             }
4406             switch(where){
4407                 case "beforebegin":
4408                     el.insertAdjacentHTML('BeforeBegin', html);
4409                     return el.previousSibling;
4410                 case "afterbegin":
4411                     el.insertAdjacentHTML('AfterBegin', html);
4412                     return el.firstChild;
4413                 case "beforeend":
4414                     el.insertAdjacentHTML('BeforeEnd', html);
4415                     return el.lastChild;
4416                 case "afterend":
4417                     el.insertAdjacentHTML('AfterEnd', html);
4418                     return el.nextSibling;
4419             }
4420             throw 'Illegal insertion point -> "' + where + '"';
4421         }
4422         var range = el.ownerDocument.createRange();
4423         var frag;
4424         switch(where){
4425              case "beforebegin":
4426                 range.setStartBefore(el);
4427                 frag = range.createContextualFragment(html);
4428                 el.parentNode.insertBefore(frag, el);
4429                 return el.previousSibling;
4430              case "afterbegin":
4431                 if(el.firstChild){
4432                     range.setStartBefore(el.firstChild);
4433                     frag = range.createContextualFragment(html);
4434                     el.insertBefore(frag, el.firstChild);
4435                     return el.firstChild;
4436                 }else{
4437                     el.innerHTML = html;
4438                     return el.firstChild;
4439                 }
4440             case "beforeend":
4441                 if(el.lastChild){
4442                     range.setStartAfter(el.lastChild);
4443                     frag = range.createContextualFragment(html);
4444                     el.appendChild(frag);
4445                     return el.lastChild;
4446                 }else{
4447                     el.innerHTML = html;
4448                     return el.lastChild;
4449                 }
4450             case "afterend":
4451                 range.setStartAfter(el);
4452                 frag = range.createContextualFragment(html);
4453                 el.parentNode.insertBefore(frag, el.nextSibling);
4454                 return el.nextSibling;
4455             }
4456             throw 'Illegal insertion point -> "' + where + '"';
4457     },
4458
4459     /**
4460      * Creates new Dom element(s) and inserts them before el
4461      * @param {String/HTMLElement/Element} el The context element
4462      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4463      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4464      * @return {HTMLElement/Roo.Element} The new node
4465      */
4466     insertBefore : function(el, o, returnElement){
4467         return this.doInsert(el, o, returnElement, "beforeBegin");
4468     },
4469
4470     /**
4471      * Creates new Dom element(s) and inserts them after el
4472      * @param {String/HTMLElement/Element} el The context element
4473      * @param {Object} o The Dom object spec (and children)
4474      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4475      * @return {HTMLElement/Roo.Element} The new node
4476      */
4477     insertAfter : function(el, o, returnElement){
4478         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4479     },
4480
4481     /**
4482      * Creates new Dom element(s) and inserts them as the first child of el
4483      * @param {String/HTMLElement/Element} el The context element
4484      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4485      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4486      * @return {HTMLElement/Roo.Element} The new node
4487      */
4488     insertFirst : function(el, o, returnElement){
4489         return this.doInsert(el, o, returnElement, "afterBegin");
4490     },
4491
4492     // private
4493     doInsert : function(el, o, returnElement, pos, sibling){
4494         el = Roo.getDom(el);
4495         var newNode;
4496         if(this.useDom || o.ns){
4497             newNode = createDom(o, null);
4498             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4499         }else{
4500             var html = createHtml(o);
4501             newNode = this.insertHtml(pos, el, html);
4502         }
4503         return returnElement ? Roo.get(newNode, true) : newNode;
4504     },
4505
4506     /**
4507      * Creates new Dom element(s) and appends them to el
4508      * @param {String/HTMLElement/Element} el The context element
4509      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4510      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4511      * @return {HTMLElement/Roo.Element} The new node
4512      */
4513     append : function(el, o, returnElement){
4514         el = Roo.getDom(el);
4515         var newNode;
4516         if(this.useDom || o.ns){
4517             newNode = createDom(o, null);
4518             el.appendChild(newNode);
4519         }else{
4520             var html = createHtml(o);
4521             newNode = this.insertHtml("beforeEnd", el, html);
4522         }
4523         return returnElement ? Roo.get(newNode, true) : newNode;
4524     },
4525
4526     /**
4527      * Creates new Dom element(s) and overwrites the contents of el with them
4528      * @param {String/HTMLElement/Element} el The context element
4529      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4530      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4531      * @return {HTMLElement/Roo.Element} The new node
4532      */
4533     overwrite : function(el, o, returnElement){
4534         el = Roo.getDom(el);
4535         if (o.ns) {
4536           
4537             while (el.childNodes.length) {
4538                 el.removeChild(el.firstChild);
4539             }
4540             createDom(o, el);
4541         } else {
4542             el.innerHTML = createHtml(o);   
4543         }
4544         
4545         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4546     },
4547
4548     /**
4549      * Creates a new Roo.DomHelper.Template from the Dom object spec
4550      * @param {Object} o The Dom object spec (and children)
4551      * @return {Roo.DomHelper.Template} The new template
4552      */
4553     createTemplate : function(o){
4554         var html = createHtml(o);
4555         return new Roo.Template(html);
4556     }
4557     };
4558 }();
4559 /*
4560  * Based on:
4561  * Ext JS Library 1.1.1
4562  * Copyright(c) 2006-2007, Ext JS, LLC.
4563  *
4564  * Originally Released Under LGPL - original licence link has changed is not relivant.
4565  *
4566  * Fork - LGPL
4567  * <script type="text/javascript">
4568  */
4569  
4570 /**
4571 * @class Roo.Template
4572 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4573 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4574 * Usage:
4575 <pre><code>
4576 var t = new Roo.Template({
4577     html :  '&lt;div name="{id}"&gt;' + 
4578         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4579         '&lt;/div&gt;',
4580     myformat: function (value, allValues) {
4581         return 'XX' + value;
4582     }
4583 });
4584 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4585 </code></pre>
4586 * For more information see this blog post with examples:
4587 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4588      - Create Elements using DOM, HTML fragments and Templates</a>. 
4589 * @constructor
4590 * @param {Object} cfg - Configuration object.
4591 */
4592 Roo.Template = function(cfg){
4593     // BC!
4594     if(cfg instanceof Array){
4595         cfg = cfg.join("");
4596     }else if(arguments.length > 1){
4597         cfg = Array.prototype.join.call(arguments, "");
4598     }
4599     
4600     
4601     if (typeof(cfg) == 'object') {
4602         Roo.apply(this,cfg)
4603     } else {
4604         // bc
4605         this.html = cfg;
4606     }
4607     if (this.url) {
4608         this.load();
4609     }
4610     
4611 };
4612 Roo.Template.prototype = {
4613     
4614     /**
4615      * @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..
4616      *                    it should be fixed so that template is observable...
4617      */
4618     url : false,
4619     /**
4620      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4621      */
4622     html : '',
4623     /**
4624      * Returns an HTML fragment of this template with the specified values applied.
4625      * @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'})
4626      * @return {String} The HTML fragment
4627      */
4628     applyTemplate : function(values){
4629         try {
4630            
4631             if(this.compiled){
4632                 return this.compiled(values);
4633             }
4634             var useF = this.disableFormats !== true;
4635             var fm = Roo.util.Format, tpl = this;
4636             var fn = function(m, name, format, args){
4637                 if(format && useF){
4638                     if(format.substr(0, 5) == "this."){
4639                         return tpl.call(format.substr(5), values[name], values);
4640                     }else{
4641                         if(args){
4642                             // quoted values are required for strings in compiled templates, 
4643                             // but for non compiled we need to strip them
4644                             // quoted reversed for jsmin
4645                             var re = /^\s*['"](.*)["']\s*$/;
4646                             args = args.split(',');
4647                             for(var i = 0, len = args.length; i < len; i++){
4648                                 args[i] = args[i].replace(re, "$1");
4649                             }
4650                             args = [values[name]].concat(args);
4651                         }else{
4652                             args = [values[name]];
4653                         }
4654                         return fm[format].apply(fm, args);
4655                     }
4656                 }else{
4657                     return values[name] !== undefined ? values[name] : "";
4658                 }
4659             };
4660             return this.html.replace(this.re, fn);
4661         } catch (e) {
4662             Roo.log(e);
4663             throw e;
4664         }
4665          
4666     },
4667     
4668     loading : false,
4669       
4670     load : function ()
4671     {
4672          
4673         if (this.loading) {
4674             return;
4675         }
4676         var _t = this;
4677         
4678         this.loading = true;
4679         this.compiled = false;
4680         
4681         var cx = new Roo.data.Connection();
4682         cx.request({
4683             url : this.url,
4684             method : 'GET',
4685             success : function (response) {
4686                 _t.loading = false;
4687                 _t.html = response.responseText;
4688                 _t.url = false;
4689                 _t.compile();
4690              },
4691             failure : function(response) {
4692                 Roo.log("Template failed to load from " + _t.url);
4693                 _t.loading = false;
4694             }
4695         });
4696     },
4697
4698     /**
4699      * Sets the HTML used as the template and optionally compiles it.
4700      * @param {String} html
4701      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4702      * @return {Roo.Template} this
4703      */
4704     set : function(html, compile){
4705         this.html = html;
4706         this.compiled = null;
4707         if(compile){
4708             this.compile();
4709         }
4710         return this;
4711     },
4712     
4713     /**
4714      * True to disable format functions (defaults to false)
4715      * @type Boolean
4716      */
4717     disableFormats : false,
4718     
4719     /**
4720     * The regular expression used to match template variables 
4721     * @type RegExp
4722     * @property 
4723     */
4724     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4725     
4726     /**
4727      * Compiles the template into an internal function, eliminating the RegEx overhead.
4728      * @return {Roo.Template} this
4729      */
4730     compile : function(){
4731         var fm = Roo.util.Format;
4732         var useF = this.disableFormats !== true;
4733         var sep = Roo.isGecko ? "+" : ",";
4734         var fn = function(m, name, format, args){
4735             if(format && useF){
4736                 args = args ? ',' + args : "";
4737                 if(format.substr(0, 5) != "this."){
4738                     format = "fm." + format + '(';
4739                 }else{
4740                     format = 'this.call("'+ format.substr(5) + '", ';
4741                     args = ", values";
4742                 }
4743             }else{
4744                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4745             }
4746             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4747         };
4748         var body;
4749         // branched to use + in gecko and [].join() in others
4750         if(Roo.isGecko){
4751             body = "this.compiled = function(values){ return '" +
4752                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4753                     "';};";
4754         }else{
4755             body = ["this.compiled = function(values){ return ['"];
4756             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4757             body.push("'].join('');};");
4758             body = body.join('');
4759         }
4760         /**
4761          * eval:var:values
4762          * eval:var:fm
4763          */
4764         eval(body);
4765         return this;
4766     },
4767     
4768     // private function used to call members
4769     call : function(fnName, value, allValues){
4770         return this[fnName](value, allValues);
4771     },
4772     
4773     /**
4774      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4775      * @param {String/HTMLElement/Roo.Element} el The context element
4776      * @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'})
4777      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4778      * @return {HTMLElement/Roo.Element} The new node or Element
4779      */
4780     insertFirst: function(el, values, returnElement){
4781         return this.doInsert('afterBegin', el, values, returnElement);
4782     },
4783
4784     /**
4785      * Applies the supplied values to the template and inserts the new node(s) before el.
4786      * @param {String/HTMLElement/Roo.Element} el The context element
4787      * @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'})
4788      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4789      * @return {HTMLElement/Roo.Element} The new node or Element
4790      */
4791     insertBefore: function(el, values, returnElement){
4792         return this.doInsert('beforeBegin', el, values, returnElement);
4793     },
4794
4795     /**
4796      * Applies the supplied values to the template and inserts the new node(s) after el.
4797      * @param {String/HTMLElement/Roo.Element} el The context element
4798      * @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'})
4799      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4800      * @return {HTMLElement/Roo.Element} The new node or Element
4801      */
4802     insertAfter : function(el, values, returnElement){
4803         return this.doInsert('afterEnd', el, values, returnElement);
4804     },
4805     
4806     /**
4807      * Applies the supplied values to the template and appends the new node(s) to el.
4808      * @param {String/HTMLElement/Roo.Element} el The context element
4809      * @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'})
4810      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4811      * @return {HTMLElement/Roo.Element} The new node or Element
4812      */
4813     append : function(el, values, returnElement){
4814         return this.doInsert('beforeEnd', el, values, returnElement);
4815     },
4816
4817     doInsert : function(where, el, values, returnEl){
4818         el = Roo.getDom(el);
4819         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4820         return returnEl ? Roo.get(newNode, true) : newNode;
4821     },
4822
4823     /**
4824      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4825      * @param {String/HTMLElement/Roo.Element} el The context element
4826      * @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'})
4827      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4828      * @return {HTMLElement/Roo.Element} The new node or Element
4829      */
4830     overwrite : function(el, values, returnElement){
4831         el = Roo.getDom(el);
4832         el.innerHTML = this.applyTemplate(values);
4833         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4834     }
4835 };
4836 /**
4837  * Alias for {@link #applyTemplate}
4838  * @method
4839  */
4840 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4841
4842 // backwards compat
4843 Roo.DomHelper.Template = Roo.Template;
4844
4845 /**
4846  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4847  * @param {String/HTMLElement} el A DOM element or its id
4848  * @returns {Roo.Template} The created template
4849  * @static
4850  */
4851 Roo.Template.from = function(el){
4852     el = Roo.getDom(el);
4853     return new Roo.Template(el.value || el.innerHTML);
4854 };/*
4855  * Based on:
4856  * Ext JS Library 1.1.1
4857  * Copyright(c) 2006-2007, Ext JS, LLC.
4858  *
4859  * Originally Released Under LGPL - original licence link has changed is not relivant.
4860  *
4861  * Fork - LGPL
4862  * <script type="text/javascript">
4863  */
4864  
4865
4866 /*
4867  * This is code is also distributed under MIT license for use
4868  * with jQuery and prototype JavaScript libraries.
4869  */
4870 /**
4871  * @class Roo.DomQuery
4872 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).
4873 <p>
4874 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>
4875
4876 <p>
4877 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.
4878 </p>
4879 <h4>Element Selectors:</h4>
4880 <ul class="list">
4881     <li> <b>*</b> any element</li>
4882     <li> <b>E</b> an element with the tag E</li>
4883     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4884     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4885     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4886     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4887 </ul>
4888 <h4>Attribute Selectors:</h4>
4889 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4890 <ul class="list">
4891     <li> <b>E[foo]</b> has an attribute "foo"</li>
4892     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4893     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4894     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4895     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4896     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4897     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4898 </ul>
4899 <h4>Pseudo Classes:</h4>
4900 <ul class="list">
4901     <li> <b>E:first-child</b> E is the first child of its parent</li>
4902     <li> <b>E:last-child</b> E is the last child of its parent</li>
4903     <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>
4904     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4905     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4906     <li> <b>E:only-child</b> E is the only child of its parent</li>
4907     <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>
4908     <li> <b>E:first</b> the first E in the resultset</li>
4909     <li> <b>E:last</b> the last E in the resultset</li>
4910     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4911     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4912     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4913     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4914     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4915     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4916     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4917     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4918     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4919 </ul>
4920 <h4>CSS Value Selectors:</h4>
4921 <ul class="list">
4922     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4923     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4924     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4925     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4926     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4927     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4928 </ul>
4929  * @singleton
4930  */
4931 Roo.DomQuery = function(){
4932     var cache = {}, simpleCache = {}, valueCache = {};
4933     var nonSpace = /\S/;
4934     var trimRe = /^\s+|\s+$/g;
4935     var tplRe = /\{(\d+)\}/g;
4936     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4937     var tagTokenRe = /^(#)?([\w-\*]+)/;
4938     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4939
4940     function child(p, index){
4941         var i = 0;
4942         var n = p.firstChild;
4943         while(n){
4944             if(n.nodeType == 1){
4945                if(++i == index){
4946                    return n;
4947                }
4948             }
4949             n = n.nextSibling;
4950         }
4951         return null;
4952     };
4953
4954     function next(n){
4955         while((n = n.nextSibling) && n.nodeType != 1);
4956         return n;
4957     };
4958
4959     function prev(n){
4960         while((n = n.previousSibling) && n.nodeType != 1);
4961         return n;
4962     };
4963
4964     function children(d){
4965         var n = d.firstChild, ni = -1;
4966             while(n){
4967                 var nx = n.nextSibling;
4968                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4969                     d.removeChild(n);
4970                 }else{
4971                     n.nodeIndex = ++ni;
4972                 }
4973                 n = nx;
4974             }
4975             return this;
4976         };
4977
4978     function byClassName(c, a, v){
4979         if(!v){
4980             return c;
4981         }
4982         var r = [], ri = -1, cn;
4983         for(var i = 0, ci; ci = c[i]; i++){
4984             if((' '+ci.className+' ').indexOf(v) != -1){
4985                 r[++ri] = ci;
4986             }
4987         }
4988         return r;
4989     };
4990
4991     function attrValue(n, attr){
4992         if(!n.tagName && typeof n.length != "undefined"){
4993             n = n[0];
4994         }
4995         if(!n){
4996             return null;
4997         }
4998         if(attr == "for"){
4999             return n.htmlFor;
5000         }
5001         if(attr == "class" || attr == "className"){
5002             return n.className;
5003         }
5004         return n.getAttribute(attr) || n[attr];
5005
5006     };
5007
5008     function getNodes(ns, mode, tagName){
5009         var result = [], ri = -1, cs;
5010         if(!ns){
5011             return result;
5012         }
5013         tagName = tagName || "*";
5014         if(typeof ns.getElementsByTagName != "undefined"){
5015             ns = [ns];
5016         }
5017         if(!mode){
5018             for(var i = 0, ni; ni = ns[i]; i++){
5019                 cs = ni.getElementsByTagName(tagName);
5020                 for(var j = 0, ci; ci = cs[j]; j++){
5021                     result[++ri] = ci;
5022                 }
5023             }
5024         }else if(mode == "/" || mode == ">"){
5025             var utag = tagName.toUpperCase();
5026             for(var i = 0, ni, cn; ni = ns[i]; i++){
5027                 cn = ni.children || ni.childNodes;
5028                 for(var j = 0, cj; cj = cn[j]; j++){
5029                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5030                         result[++ri] = cj;
5031                     }
5032                 }
5033             }
5034         }else if(mode == "+"){
5035             var utag = tagName.toUpperCase();
5036             for(var i = 0, n; n = ns[i]; i++){
5037                 while((n = n.nextSibling) && n.nodeType != 1);
5038                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5039                     result[++ri] = n;
5040                 }
5041             }
5042         }else if(mode == "~"){
5043             for(var i = 0, n; n = ns[i]; i++){
5044                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5045                 if(n){
5046                     result[++ri] = n;
5047                 }
5048             }
5049         }
5050         return result;
5051     };
5052
5053     function concat(a, b){
5054         if(b.slice){
5055             return a.concat(b);
5056         }
5057         for(var i = 0, l = b.length; i < l; i++){
5058             a[a.length] = b[i];
5059         }
5060         return a;
5061     }
5062
5063     function byTag(cs, tagName){
5064         if(cs.tagName || cs == document){
5065             cs = [cs];
5066         }
5067         if(!tagName){
5068             return cs;
5069         }
5070         var r = [], ri = -1;
5071         tagName = tagName.toLowerCase();
5072         for(var i = 0, ci; ci = cs[i]; i++){
5073             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5074                 r[++ri] = ci;
5075             }
5076         }
5077         return r;
5078     };
5079
5080     function byId(cs, attr, id){
5081         if(cs.tagName || cs == document){
5082             cs = [cs];
5083         }
5084         if(!id){
5085             return cs;
5086         }
5087         var r = [], ri = -1;
5088         for(var i = 0,ci; ci = cs[i]; i++){
5089             if(ci && ci.id == id){
5090                 r[++ri] = ci;
5091                 return r;
5092             }
5093         }
5094         return r;
5095     };
5096
5097     function byAttribute(cs, attr, value, op, custom){
5098         var r = [], ri = -1, st = custom=="{";
5099         var f = Roo.DomQuery.operators[op];
5100         for(var i = 0, ci; ci = cs[i]; i++){
5101             var a;
5102             if(st){
5103                 a = Roo.DomQuery.getStyle(ci, attr);
5104             }
5105             else if(attr == "class" || attr == "className"){
5106                 a = ci.className;
5107             }else if(attr == "for"){
5108                 a = ci.htmlFor;
5109             }else if(attr == "href"){
5110                 a = ci.getAttribute("href", 2);
5111             }else{
5112                 a = ci.getAttribute(attr);
5113             }
5114             if((f && f(a, value)) || (!f && a)){
5115                 r[++ri] = ci;
5116             }
5117         }
5118         return r;
5119     };
5120
5121     function byPseudo(cs, name, value){
5122         return Roo.DomQuery.pseudos[name](cs, value);
5123     };
5124
5125     // This is for IE MSXML which does not support expandos.
5126     // IE runs the same speed using setAttribute, however FF slows way down
5127     // and Safari completely fails so they need to continue to use expandos.
5128     var isIE = window.ActiveXObject ? true : false;
5129
5130     // this eval is stop the compressor from
5131     // renaming the variable to something shorter
5132     
5133     /** eval:var:batch */
5134     var batch = 30803; 
5135
5136     var key = 30803;
5137
5138     function nodupIEXml(cs){
5139         var d = ++key;
5140         cs[0].setAttribute("_nodup", d);
5141         var r = [cs[0]];
5142         for(var i = 1, len = cs.length; i < len; i++){
5143             var c = cs[i];
5144             if(!c.getAttribute("_nodup") != d){
5145                 c.setAttribute("_nodup", d);
5146                 r[r.length] = c;
5147             }
5148         }
5149         for(var i = 0, len = cs.length; i < len; i++){
5150             cs[i].removeAttribute("_nodup");
5151         }
5152         return r;
5153     }
5154
5155     function nodup(cs){
5156         if(!cs){
5157             return [];
5158         }
5159         var len = cs.length, c, i, r = cs, cj, ri = -1;
5160         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5161             return cs;
5162         }
5163         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5164             return nodupIEXml(cs);
5165         }
5166         var d = ++key;
5167         cs[0]._nodup = d;
5168         for(i = 1; c = cs[i]; i++){
5169             if(c._nodup != d){
5170                 c._nodup = d;
5171             }else{
5172                 r = [];
5173                 for(var j = 0; j < i; j++){
5174                     r[++ri] = cs[j];
5175                 }
5176                 for(j = i+1; cj = cs[j]; j++){
5177                     if(cj._nodup != d){
5178                         cj._nodup = d;
5179                         r[++ri] = cj;
5180                     }
5181                 }
5182                 return r;
5183             }
5184         }
5185         return r;
5186     }
5187
5188     function quickDiffIEXml(c1, c2){
5189         var d = ++key;
5190         for(var i = 0, len = c1.length; i < len; i++){
5191             c1[i].setAttribute("_qdiff", d);
5192         }
5193         var r = [];
5194         for(var i = 0, len = c2.length; i < len; i++){
5195             if(c2[i].getAttribute("_qdiff") != d){
5196                 r[r.length] = c2[i];
5197             }
5198         }
5199         for(var i = 0, len = c1.length; i < len; i++){
5200            c1[i].removeAttribute("_qdiff");
5201         }
5202         return r;
5203     }
5204
5205     function quickDiff(c1, c2){
5206         var len1 = c1.length;
5207         if(!len1){
5208             return c2;
5209         }
5210         if(isIE && c1[0].selectSingleNode){
5211             return quickDiffIEXml(c1, c2);
5212         }
5213         var d = ++key;
5214         for(var i = 0; i < len1; i++){
5215             c1[i]._qdiff = d;
5216         }
5217         var r = [];
5218         for(var i = 0, len = c2.length; i < len; i++){
5219             if(c2[i]._qdiff != d){
5220                 r[r.length] = c2[i];
5221             }
5222         }
5223         return r;
5224     }
5225
5226     function quickId(ns, mode, root, id){
5227         if(ns == root){
5228            var d = root.ownerDocument || root;
5229            return d.getElementById(id);
5230         }
5231         ns = getNodes(ns, mode, "*");
5232         return byId(ns, null, id);
5233     }
5234
5235     return {
5236         getStyle : function(el, name){
5237             return Roo.fly(el).getStyle(name);
5238         },
5239         /**
5240          * Compiles a selector/xpath query into a reusable function. The returned function
5241          * takes one parameter "root" (optional), which is the context node from where the query should start.
5242          * @param {String} selector The selector/xpath query
5243          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5244          * @return {Function}
5245          */
5246         compile : function(path, type){
5247             type = type || "select";
5248             
5249             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5250             var q = path, mode, lq;
5251             var tk = Roo.DomQuery.matchers;
5252             var tklen = tk.length;
5253             var mm;
5254
5255             // accept leading mode switch
5256             var lmode = q.match(modeRe);
5257             if(lmode && lmode[1]){
5258                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5259                 q = q.replace(lmode[1], "");
5260             }
5261             // strip leading slashes
5262             while(path.substr(0, 1)=="/"){
5263                 path = path.substr(1);
5264             }
5265
5266             while(q && lq != q){
5267                 lq = q;
5268                 var tm = q.match(tagTokenRe);
5269                 if(type == "select"){
5270                     if(tm){
5271                         if(tm[1] == "#"){
5272                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5273                         }else{
5274                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5275                         }
5276                         q = q.replace(tm[0], "");
5277                     }else if(q.substr(0, 1) != '@'){
5278                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5279                     }
5280                 }else{
5281                     if(tm){
5282                         if(tm[1] == "#"){
5283                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5284                         }else{
5285                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5286                         }
5287                         q = q.replace(tm[0], "");
5288                     }
5289                 }
5290                 while(!(mm = q.match(modeRe))){
5291                     var matched = false;
5292                     for(var j = 0; j < tklen; j++){
5293                         var t = tk[j];
5294                         var m = q.match(t.re);
5295                         if(m){
5296                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5297                                                     return m[i];
5298                                                 });
5299                             q = q.replace(m[0], "");
5300                             matched = true;
5301                             break;
5302                         }
5303                     }
5304                     // prevent infinite loop on bad selector
5305                     if(!matched){
5306                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5307                     }
5308                 }
5309                 if(mm[1]){
5310                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5311                     q = q.replace(mm[1], "");
5312                 }
5313             }
5314             fn[fn.length] = "return nodup(n);\n}";
5315             
5316              /** 
5317               * list of variables that need from compression as they are used by eval.
5318              *  eval:var:batch 
5319              *  eval:var:nodup
5320              *  eval:var:byTag
5321              *  eval:var:ById
5322              *  eval:var:getNodes
5323              *  eval:var:quickId
5324              *  eval:var:mode
5325              *  eval:var:root
5326              *  eval:var:n
5327              *  eval:var:byClassName
5328              *  eval:var:byPseudo
5329              *  eval:var:byAttribute
5330              *  eval:var:attrValue
5331              * 
5332              **/ 
5333             eval(fn.join(""));
5334             return f;
5335         },
5336
5337         /**
5338          * Selects a group of elements.
5339          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5340          * @param {Node} root (optional) The start of the query (defaults to document).
5341          * @return {Array}
5342          */
5343         select : function(path, root, type){
5344             if(!root || root == document){
5345                 root = document;
5346             }
5347             if(typeof root == "string"){
5348                 root = document.getElementById(root);
5349             }
5350             var paths = path.split(",");
5351             var results = [];
5352             for(var i = 0, len = paths.length; i < len; i++){
5353                 var p = paths[i].replace(trimRe, "");
5354                 if(!cache[p]){
5355                     cache[p] = Roo.DomQuery.compile(p);
5356                     if(!cache[p]){
5357                         throw p + " is not a valid selector";
5358                     }
5359                 }
5360                 var result = cache[p](root);
5361                 if(result && result != document){
5362                     results = results.concat(result);
5363                 }
5364             }
5365             if(paths.length > 1){
5366                 return nodup(results);
5367             }
5368             return results;
5369         },
5370
5371         /**
5372          * Selects a single element.
5373          * @param {String} selector The selector/xpath query
5374          * @param {Node} root (optional) The start of the query (defaults to document).
5375          * @return {Element}
5376          */
5377         selectNode : function(path, root){
5378             return Roo.DomQuery.select(path, root)[0];
5379         },
5380
5381         /**
5382          * Selects the value of a node, optionally replacing null with the defaultValue.
5383          * @param {String} selector The selector/xpath query
5384          * @param {Node} root (optional) The start of the query (defaults to document).
5385          * @param {String} defaultValue
5386          */
5387         selectValue : function(path, root, defaultValue){
5388             path = path.replace(trimRe, "");
5389             if(!valueCache[path]){
5390                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5391             }
5392             var n = valueCache[path](root);
5393             n = n[0] ? n[0] : n;
5394             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5395             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5396         },
5397
5398         /**
5399          * Selects the value of a node, parsing integers and floats.
5400          * @param {String} selector The selector/xpath query
5401          * @param {Node} root (optional) The start of the query (defaults to document).
5402          * @param {Number} defaultValue
5403          * @return {Number}
5404          */
5405         selectNumber : function(path, root, defaultValue){
5406             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5407             return parseFloat(v);
5408         },
5409
5410         /**
5411          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5412          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5413          * @param {String} selector The simple selector to test
5414          * @return {Boolean}
5415          */
5416         is : function(el, ss){
5417             if(typeof el == "string"){
5418                 el = document.getElementById(el);
5419             }
5420             var isArray = (el instanceof Array);
5421             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5422             return isArray ? (result.length == el.length) : (result.length > 0);
5423         },
5424
5425         /**
5426          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5427          * @param {Array} el An array of elements to filter
5428          * @param {String} selector The simple selector to test
5429          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5430          * the selector instead of the ones that match
5431          * @return {Array}
5432          */
5433         filter : function(els, ss, nonMatches){
5434             ss = ss.replace(trimRe, "");
5435             if(!simpleCache[ss]){
5436                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5437             }
5438             var result = simpleCache[ss](els);
5439             return nonMatches ? quickDiff(result, els) : result;
5440         },
5441
5442         /**
5443          * Collection of matching regular expressions and code snippets.
5444          */
5445         matchers : [{
5446                 re: /^\.([\w-]+)/,
5447                 select: 'n = byClassName(n, null, " {1} ");'
5448             }, {
5449                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5450                 select: 'n = byPseudo(n, "{1}", "{2}");'
5451             },{
5452                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5453                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5454             }, {
5455                 re: /^#([\w-]+)/,
5456                 select: 'n = byId(n, null, "{1}");'
5457             },{
5458                 re: /^@([\w-]+)/,
5459                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5460             }
5461         ],
5462
5463         /**
5464          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5465          * 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;.
5466          */
5467         operators : {
5468             "=" : function(a, v){
5469                 return a == v;
5470             },
5471             "!=" : function(a, v){
5472                 return a != v;
5473             },
5474             "^=" : function(a, v){
5475                 return a && a.substr(0, v.length) == v;
5476             },
5477             "$=" : function(a, v){
5478                 return a && a.substr(a.length-v.length) == v;
5479             },
5480             "*=" : function(a, v){
5481                 return a && a.indexOf(v) !== -1;
5482             },
5483             "%=" : function(a, v){
5484                 return (a % v) == 0;
5485             },
5486             "|=" : function(a, v){
5487                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5488             },
5489             "~=" : function(a, v){
5490                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5491             }
5492         },
5493
5494         /**
5495          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5496          * and the argument (if any) supplied in the selector.
5497          */
5498         pseudos : {
5499             "first-child" : function(c){
5500                 var r = [], ri = -1, n;
5501                 for(var i = 0, ci; ci = n = c[i]; i++){
5502                     while((n = n.previousSibling) && n.nodeType != 1);
5503                     if(!n){
5504                         r[++ri] = ci;
5505                     }
5506                 }
5507                 return r;
5508             },
5509
5510             "last-child" : function(c){
5511                 var r = [], ri = -1, n;
5512                 for(var i = 0, ci; ci = n = c[i]; i++){
5513                     while((n = n.nextSibling) && n.nodeType != 1);
5514                     if(!n){
5515                         r[++ri] = ci;
5516                     }
5517                 }
5518                 return r;
5519             },
5520
5521             "nth-child" : function(c, a) {
5522                 var r = [], ri = -1;
5523                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5524                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5525                 for(var i = 0, n; n = c[i]; i++){
5526                     var pn = n.parentNode;
5527                     if (batch != pn._batch) {
5528                         var j = 0;
5529                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5530                             if(cn.nodeType == 1){
5531                                cn.nodeIndex = ++j;
5532                             }
5533                         }
5534                         pn._batch = batch;
5535                     }
5536                     if (f == 1) {
5537                         if (l == 0 || n.nodeIndex == l){
5538                             r[++ri] = n;
5539                         }
5540                     } else if ((n.nodeIndex + l) % f == 0){
5541                         r[++ri] = n;
5542                     }
5543                 }
5544
5545                 return r;
5546             },
5547
5548             "only-child" : function(c){
5549                 var r = [], ri = -1;;
5550                 for(var i = 0, ci; ci = c[i]; i++){
5551                     if(!prev(ci) && !next(ci)){
5552                         r[++ri] = ci;
5553                     }
5554                 }
5555                 return r;
5556             },
5557
5558             "empty" : function(c){
5559                 var r = [], ri = -1;
5560                 for(var i = 0, ci; ci = c[i]; i++){
5561                     var cns = ci.childNodes, j = 0, cn, empty = true;
5562                     while(cn = cns[j]){
5563                         ++j;
5564                         if(cn.nodeType == 1 || cn.nodeType == 3){
5565                             empty = false;
5566                             break;
5567                         }
5568                     }
5569                     if(empty){
5570                         r[++ri] = ci;
5571                     }
5572                 }
5573                 return r;
5574             },
5575
5576             "contains" : function(c, v){
5577                 var r = [], ri = -1;
5578                 for(var i = 0, ci; ci = c[i]; i++){
5579                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5580                         r[++ri] = ci;
5581                     }
5582                 }
5583                 return r;
5584             },
5585
5586             "nodeValue" : function(c, v){
5587                 var r = [], ri = -1;
5588                 for(var i = 0, ci; ci = c[i]; i++){
5589                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5590                         r[++ri] = ci;
5591                     }
5592                 }
5593                 return r;
5594             },
5595
5596             "checked" : function(c){
5597                 var r = [], ri = -1;
5598                 for(var i = 0, ci; ci = c[i]; i++){
5599                     if(ci.checked == true){
5600                         r[++ri] = ci;
5601                     }
5602                 }
5603                 return r;
5604             },
5605
5606             "not" : function(c, ss){
5607                 return Roo.DomQuery.filter(c, ss, true);
5608             },
5609
5610             "odd" : function(c){
5611                 return this["nth-child"](c, "odd");
5612             },
5613
5614             "even" : function(c){
5615                 return this["nth-child"](c, "even");
5616             },
5617
5618             "nth" : function(c, a){
5619                 return c[a-1] || [];
5620             },
5621
5622             "first" : function(c){
5623                 return c[0] || [];
5624             },
5625
5626             "last" : function(c){
5627                 return c[c.length-1] || [];
5628             },
5629
5630             "has" : function(c, ss){
5631                 var s = Roo.DomQuery.select;
5632                 var r = [], ri = -1;
5633                 for(var i = 0, ci; ci = c[i]; i++){
5634                     if(s(ss, ci).length > 0){
5635                         r[++ri] = ci;
5636                     }
5637                 }
5638                 return r;
5639             },
5640
5641             "next" : function(c, ss){
5642                 var is = Roo.DomQuery.is;
5643                 var r = [], ri = -1;
5644                 for(var i = 0, ci; ci = c[i]; i++){
5645                     var n = next(ci);
5646                     if(n && is(n, ss)){
5647                         r[++ri] = ci;
5648                     }
5649                 }
5650                 return r;
5651             },
5652
5653             "prev" : function(c, ss){
5654                 var is = Roo.DomQuery.is;
5655                 var r = [], ri = -1;
5656                 for(var i = 0, ci; ci = c[i]; i++){
5657                     var n = prev(ci);
5658                     if(n && is(n, ss)){
5659                         r[++ri] = ci;
5660                     }
5661                 }
5662                 return r;
5663             }
5664         }
5665     };
5666 }();
5667
5668 /**
5669  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5670  * @param {String} path The selector/xpath query
5671  * @param {Node} root (optional) The start of the query (defaults to document).
5672  * @return {Array}
5673  * @member Roo
5674  * @method query
5675  */
5676 Roo.query = Roo.DomQuery.select;
5677 /*
5678  * Based on:
5679  * Ext JS Library 1.1.1
5680  * Copyright(c) 2006-2007, Ext JS, LLC.
5681  *
5682  * Originally Released Under LGPL - original licence link has changed is not relivant.
5683  *
5684  * Fork - LGPL
5685  * <script type="text/javascript">
5686  */
5687
5688 /**
5689  * @class Roo.util.Observable
5690  * Base class that provides a common interface for publishing events. Subclasses are expected to
5691  * to have a property "events" with all the events defined.<br>
5692  * For example:
5693  * <pre><code>
5694  Employee = function(name){
5695     this.name = name;
5696     this.addEvents({
5697         "fired" : true,
5698         "quit" : true
5699     });
5700  }
5701  Roo.extend(Employee, Roo.util.Observable);
5702 </code></pre>
5703  * @param {Object} config properties to use (incuding events / listeners)
5704  */
5705
5706 Roo.util.Observable = function(cfg){
5707     
5708     cfg = cfg|| {};
5709     this.addEvents(cfg.events || {});
5710     if (cfg.events) {
5711         delete cfg.events; // make sure
5712     }
5713      
5714     Roo.apply(this, cfg);
5715     
5716     if(this.listeners){
5717         this.on(this.listeners);
5718         delete this.listeners;
5719     }
5720 };
5721 Roo.util.Observable.prototype = {
5722     /** 
5723  * @cfg {Object} listeners  list of events and functions to call for this object, 
5724  * For example :
5725  * <pre><code>
5726     listeners :  { 
5727        'click' : function(e) {
5728            ..... 
5729         } ,
5730         .... 
5731     } 
5732   </code></pre>
5733  */
5734     
5735     
5736     /**
5737      * Fires the specified event with the passed parameters (minus the event name).
5738      * @param {String} eventName
5739      * @param {Object...} args Variable number of parameters are passed to handlers
5740      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5741      */
5742     fireEvent : function(){
5743         var ce = this.events[arguments[0].toLowerCase()];
5744         if(typeof ce == "object"){
5745             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5746         }else{
5747             return true;
5748         }
5749     },
5750
5751     // private
5752     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5753
5754     /**
5755      * Appends an event handler to this component
5756      * @param {String}   eventName The type of event to listen for
5757      * @param {Function} handler The method the event invokes
5758      * @param {Object}   scope (optional) The scope in which to execute the handler
5759      * function. The handler function's "this" context.
5760      * @param {Object}   options (optional) An object containing handler configuration
5761      * properties. This may contain any of the following properties:<ul>
5762      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5763      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5764      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5765      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5766      * by the specified number of milliseconds. If the event fires again within that time, the original
5767      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5768      * </ul><br>
5769      * <p>
5770      * <b>Combining Options</b><br>
5771      * Using the options argument, it is possible to combine different types of listeners:<br>
5772      * <br>
5773      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5774                 <pre><code>
5775                 el.on('click', this.onClick, this, {
5776                         single: true,
5777                 delay: 100,
5778                 forumId: 4
5779                 });
5780                 </code></pre>
5781      * <p>
5782      * <b>Attaching multiple handlers in 1 call</b><br>
5783      * The method also allows for a single argument to be passed which is a config object containing properties
5784      * which specify multiple handlers.
5785      * <pre><code>
5786                 el.on({
5787                         'click': {
5788                         fn: this.onClick,
5789                         scope: this,
5790                         delay: 100
5791                 }, 
5792                 'mouseover': {
5793                         fn: this.onMouseOver,
5794                         scope: this
5795                 },
5796                 'mouseout': {
5797                         fn: this.onMouseOut,
5798                         scope: this
5799                 }
5800                 });
5801                 </code></pre>
5802      * <p>
5803      * Or a shorthand syntax which passes the same scope object to all handlers:
5804         <pre><code>
5805                 el.on({
5806                         'click': this.onClick,
5807                 'mouseover': this.onMouseOver,
5808                 'mouseout': this.onMouseOut,
5809                 scope: this
5810                 });
5811                 </code></pre>
5812      */
5813     addListener : function(eventName, fn, scope, o){
5814         if(typeof eventName == "object"){
5815             o = eventName;
5816             for(var e in o){
5817                 if(this.filterOptRe.test(e)){
5818                     continue;
5819                 }
5820                 if(typeof o[e] == "function"){
5821                     // shared options
5822                     this.addListener(e, o[e], o.scope,  o);
5823                 }else{
5824                     // individual options
5825                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5826                 }
5827             }
5828             return;
5829         }
5830         o = (!o || typeof o == "boolean") ? {} : o;
5831         eventName = eventName.toLowerCase();
5832         var ce = this.events[eventName] || true;
5833         if(typeof ce == "boolean"){
5834             ce = new Roo.util.Event(this, eventName);
5835             this.events[eventName] = ce;
5836         }
5837         ce.addListener(fn, scope, o);
5838     },
5839
5840     /**
5841      * Removes a listener
5842      * @param {String}   eventName     The type of event to listen for
5843      * @param {Function} handler        The handler to remove
5844      * @param {Object}   scope  (optional) The scope (this object) for the handler
5845      */
5846     removeListener : function(eventName, fn, scope){
5847         var ce = this.events[eventName.toLowerCase()];
5848         if(typeof ce == "object"){
5849             ce.removeListener(fn, scope);
5850         }
5851     },
5852
5853     /**
5854      * Removes all listeners for this object
5855      */
5856     purgeListeners : function(){
5857         for(var evt in this.events){
5858             if(typeof this.events[evt] == "object"){
5859                  this.events[evt].clearListeners();
5860             }
5861         }
5862     },
5863
5864     relayEvents : function(o, events){
5865         var createHandler = function(ename){
5866             return function(){
5867                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5868             };
5869         };
5870         for(var i = 0, len = events.length; i < len; i++){
5871             var ename = events[i];
5872             if(!this.events[ename]){ this.events[ename] = true; };
5873             o.on(ename, createHandler(ename), this);
5874         }
5875     },
5876
5877     /**
5878      * Used to define events on this Observable
5879      * @param {Object} object The object with the events defined
5880      */
5881     addEvents : function(o){
5882         if(!this.events){
5883             this.events = {};
5884         }
5885         Roo.applyIf(this.events, o);
5886     },
5887
5888     /**
5889      * Checks to see if this object has any listeners for a specified event
5890      * @param {String} eventName The name of the event to check for
5891      * @return {Boolean} True if the event is being listened for, else false
5892      */
5893     hasListener : function(eventName){
5894         var e = this.events[eventName];
5895         return typeof e == "object" && e.listeners.length > 0;
5896     }
5897 };
5898 /**
5899  * Appends an event handler to this element (shorthand for addListener)
5900  * @param {String}   eventName     The type of event to listen for
5901  * @param {Function} handler        The method the event invokes
5902  * @param {Object}   scope (optional) The scope in which to execute the handler
5903  * function. The handler function's "this" context.
5904  * @param {Object}   options  (optional)
5905  * @method
5906  */
5907 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5908 /**
5909  * Removes a listener (shorthand for removeListener)
5910  * @param {String}   eventName     The type of event to listen for
5911  * @param {Function} handler        The handler to remove
5912  * @param {Object}   scope  (optional) The scope (this object) for the handler
5913  * @method
5914  */
5915 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5916
5917 /**
5918  * Starts capture on the specified Observable. All events will be passed
5919  * to the supplied function with the event name + standard signature of the event
5920  * <b>before</b> the event is fired. If the supplied function returns false,
5921  * the event will not fire.
5922  * @param {Observable} o The Observable to capture
5923  * @param {Function} fn The function to call
5924  * @param {Object} scope (optional) The scope (this object) for the fn
5925  * @static
5926  */
5927 Roo.util.Observable.capture = function(o, fn, scope){
5928     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5929 };
5930
5931 /**
5932  * Removes <b>all</b> added captures from the Observable.
5933  * @param {Observable} o The Observable to release
5934  * @static
5935  */
5936 Roo.util.Observable.releaseCapture = function(o){
5937     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5938 };
5939
5940 (function(){
5941
5942     var createBuffered = function(h, o, scope){
5943         var task = new Roo.util.DelayedTask();
5944         return function(){
5945             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5946         };
5947     };
5948
5949     var createSingle = function(h, e, fn, scope){
5950         return function(){
5951             e.removeListener(fn, scope);
5952             return h.apply(scope, arguments);
5953         };
5954     };
5955
5956     var createDelayed = function(h, o, scope){
5957         return function(){
5958             var args = Array.prototype.slice.call(arguments, 0);
5959             setTimeout(function(){
5960                 h.apply(scope, args);
5961             }, o.delay || 10);
5962         };
5963     };
5964
5965     Roo.util.Event = function(obj, name){
5966         this.name = name;
5967         this.obj = obj;
5968         this.listeners = [];
5969     };
5970
5971     Roo.util.Event.prototype = {
5972         addListener : function(fn, scope, options){
5973             var o = options || {};
5974             scope = scope || this.obj;
5975             if(!this.isListening(fn, scope)){
5976                 var l = {fn: fn, scope: scope, options: o};
5977                 var h = fn;
5978                 if(o.delay){
5979                     h = createDelayed(h, o, scope);
5980                 }
5981                 if(o.single){
5982                     h = createSingle(h, this, fn, scope);
5983                 }
5984                 if(o.buffer){
5985                     h = createBuffered(h, o, scope);
5986                 }
5987                 l.fireFn = h;
5988                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5989                     this.listeners.push(l);
5990                 }else{
5991                     this.listeners = this.listeners.slice(0);
5992                     this.listeners.push(l);
5993                 }
5994             }
5995         },
5996
5997         findListener : function(fn, scope){
5998             scope = scope || this.obj;
5999             var ls = this.listeners;
6000             for(var i = 0, len = ls.length; i < len; i++){
6001                 var l = ls[i];
6002                 if(l.fn == fn && l.scope == scope){
6003                     return i;
6004                 }
6005             }
6006             return -1;
6007         },
6008
6009         isListening : function(fn, scope){
6010             return this.findListener(fn, scope) != -1;
6011         },
6012
6013         removeListener : function(fn, scope){
6014             var index;
6015             if((index = this.findListener(fn, scope)) != -1){
6016                 if(!this.firing){
6017                     this.listeners.splice(index, 1);
6018                 }else{
6019                     this.listeners = this.listeners.slice(0);
6020                     this.listeners.splice(index, 1);
6021                 }
6022                 return true;
6023             }
6024             return false;
6025         },
6026
6027         clearListeners : function(){
6028             this.listeners = [];
6029         },
6030
6031         fire : function(){
6032             var ls = this.listeners, scope, len = ls.length;
6033             if(len > 0){
6034                 this.firing = true;
6035                 var args = Array.prototype.slice.call(arguments, 0);
6036                 for(var i = 0; i < len; i++){
6037                     var l = ls[i];
6038                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6039                         this.firing = false;
6040                         return false;
6041                     }
6042                 }
6043                 this.firing = false;
6044             }
6045             return true;
6046         }
6047     };
6048 })();/*
6049  * Based on:
6050  * Ext JS Library 1.1.1
6051  * Copyright(c) 2006-2007, Ext JS, LLC.
6052  *
6053  * Originally Released Under LGPL - original licence link has changed is not relivant.
6054  *
6055  * Fork - LGPL
6056  * <script type="text/javascript">
6057  */
6058
6059 /**
6060  * @class Roo.EventManager
6061  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6062  * several useful events directly.
6063  * See {@link Roo.EventObject} for more details on normalized event objects.
6064  * @singleton
6065  */
6066 Roo.EventManager = function(){
6067     var docReadyEvent, docReadyProcId, docReadyState = false;
6068     var resizeEvent, resizeTask, textEvent, textSize;
6069     var E = Roo.lib.Event;
6070     var D = Roo.lib.Dom;
6071
6072     
6073     
6074
6075     var fireDocReady = function(){
6076         if(!docReadyState){
6077             docReadyState = true;
6078             Roo.isReady = true;
6079             if(docReadyProcId){
6080                 clearInterval(docReadyProcId);
6081             }
6082             if(Roo.isGecko || Roo.isOpera) {
6083                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6084             }
6085             if(Roo.isIE){
6086                 var defer = document.getElementById("ie-deferred-loader");
6087                 if(defer){
6088                     defer.onreadystatechange = null;
6089                     defer.parentNode.removeChild(defer);
6090                 }
6091             }
6092             if(docReadyEvent){
6093                 docReadyEvent.fire();
6094                 docReadyEvent.clearListeners();
6095             }
6096         }
6097     };
6098     
6099     var initDocReady = function(){
6100         docReadyEvent = new Roo.util.Event();
6101         if(Roo.isGecko || Roo.isOpera) {
6102             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6103         }else if(Roo.isIE){
6104             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6105             var defer = document.getElementById("ie-deferred-loader");
6106             defer.onreadystatechange = function(){
6107                 if(this.readyState == "complete"){
6108                     fireDocReady();
6109                 }
6110             };
6111         }else if(Roo.isSafari){ 
6112             docReadyProcId = setInterval(function(){
6113                 var rs = document.readyState;
6114                 if(rs == "complete") {
6115                     fireDocReady();     
6116                  }
6117             }, 10);
6118         }
6119         // no matter what, make sure it fires on load
6120         E.on(window, "load", fireDocReady);
6121     };
6122
6123     var createBuffered = function(h, o){
6124         var task = new Roo.util.DelayedTask(h);
6125         return function(e){
6126             // create new event object impl so new events don't wipe out properties
6127             e = new Roo.EventObjectImpl(e);
6128             task.delay(o.buffer, h, null, [e]);
6129         };
6130     };
6131
6132     var createSingle = function(h, el, ename, fn){
6133         return function(e){
6134             Roo.EventManager.removeListener(el, ename, fn);
6135             h(e);
6136         };
6137     };
6138
6139     var createDelayed = function(h, o){
6140         return function(e){
6141             // create new event object impl so new events don't wipe out properties
6142             e = new Roo.EventObjectImpl(e);
6143             setTimeout(function(){
6144                 h(e);
6145             }, o.delay || 10);
6146         };
6147     };
6148     var transitionEndVal = false;
6149     
6150     var transitionEnd = function()
6151     {
6152         if (transitionEndVal) {
6153             return transitionEndVal;
6154         }
6155         var el = document.createElement('div');
6156
6157         var transEndEventNames = {
6158             WebkitTransition : 'webkitTransitionEnd',
6159             MozTransition    : 'transitionend',
6160             OTransition      : 'oTransitionEnd otransitionend',
6161             transition       : 'transitionend'
6162         };
6163     
6164         for (var name in transEndEventNames) {
6165             if (el.style[name] !== undefined) {
6166                 transitionEndVal = transEndEventNames[name];
6167                 return  transitionEndVal ;
6168             }
6169         }
6170     }
6171     
6172
6173     var listen = function(element, ename, opt, fn, scope){
6174         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6175         fn = fn || o.fn; scope = scope || o.scope;
6176         var el = Roo.getDom(element);
6177         
6178         
6179         if(!el){
6180             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6181         }
6182         
6183         if (ename == 'transitionend') {
6184             ename = transitionEnd();
6185         }
6186         var h = function(e){
6187             e = Roo.EventObject.setEvent(e);
6188             var t;
6189             if(o.delegate){
6190                 t = e.getTarget(o.delegate, el);
6191                 if(!t){
6192                     return;
6193                 }
6194             }else{
6195                 t = e.target;
6196             }
6197             if(o.stopEvent === true){
6198                 e.stopEvent();
6199             }
6200             if(o.preventDefault === true){
6201                e.preventDefault();
6202             }
6203             if(o.stopPropagation === true){
6204                 e.stopPropagation();
6205             }
6206
6207             if(o.normalized === false){
6208                 e = e.browserEvent;
6209             }
6210
6211             fn.call(scope || el, e, t, o);
6212         };
6213         if(o.delay){
6214             h = createDelayed(h, o);
6215         }
6216         if(o.single){
6217             h = createSingle(h, el, ename, fn);
6218         }
6219         if(o.buffer){
6220             h = createBuffered(h, o);
6221         }
6222         fn._handlers = fn._handlers || [];
6223         
6224         
6225         fn._handlers.push([Roo.id(el), ename, h]);
6226         
6227         
6228          
6229         E.on(el, ename, h);
6230         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6231             el.addEventListener("DOMMouseScroll", h, false);
6232             E.on(window, 'unload', function(){
6233                 el.removeEventListener("DOMMouseScroll", h, false);
6234             });
6235         }
6236         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6237             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6238         }
6239         return h;
6240     };
6241
6242     var stopListening = function(el, ename, fn){
6243         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6244         if(hds){
6245             for(var i = 0, len = hds.length; i < len; i++){
6246                 var h = hds[i];
6247                 if(h[0] == id && h[1] == ename){
6248                     hd = h[2];
6249                     hds.splice(i, 1);
6250                     break;
6251                 }
6252             }
6253         }
6254         E.un(el, ename, hd);
6255         el = Roo.getDom(el);
6256         if(ename == "mousewheel" && el.addEventListener){
6257             el.removeEventListener("DOMMouseScroll", hd, false);
6258         }
6259         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6260             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6261         }
6262     };
6263
6264     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6265     
6266     var pub = {
6267         
6268         
6269         /** 
6270          * Fix for doc tools
6271          * @scope Roo.EventManager
6272          */
6273         
6274         
6275         /** 
6276          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6277          * object with a Roo.EventObject
6278          * @param {Function} fn        The method the event invokes
6279          * @param {Object}   scope    An object that becomes the scope of the handler
6280          * @param {boolean}  override If true, the obj passed in becomes
6281          *                             the execution scope of the listener
6282          * @return {Function} The wrapped function
6283          * @deprecated
6284          */
6285         wrap : function(fn, scope, override){
6286             return function(e){
6287                 Roo.EventObject.setEvent(e);
6288                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6289             };
6290         },
6291         
6292         /**
6293      * Appends an event handler to an element (shorthand for addListener)
6294      * @param {String/HTMLElement}   element        The html element or id to assign the
6295      * @param {String}   eventName The type of event to listen for
6296      * @param {Function} handler The method the event invokes
6297      * @param {Object}   scope (optional) The scope in which to execute the handler
6298      * function. The handler function's "this" context.
6299      * @param {Object}   options (optional) An object containing handler configuration
6300      * properties. This may contain any of the following properties:<ul>
6301      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6302      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6303      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6304      * <li>preventDefault {Boolean} True to prevent the default action</li>
6305      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6306      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6307      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6308      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6309      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6310      * by the specified number of milliseconds. If the event fires again within that time, the original
6311      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6312      * </ul><br>
6313      * <p>
6314      * <b>Combining Options</b><br>
6315      * Using the options argument, it is possible to combine different types of listeners:<br>
6316      * <br>
6317      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6318      * Code:<pre><code>
6319 el.on('click', this.onClick, this, {
6320     single: true,
6321     delay: 100,
6322     stopEvent : true,
6323     forumId: 4
6324 });</code></pre>
6325      * <p>
6326      * <b>Attaching multiple handlers in 1 call</b><br>
6327       * The method also allows for a single argument to be passed which is a config object containing properties
6328      * which specify multiple handlers.
6329      * <p>
6330      * Code:<pre><code>
6331 el.on({
6332     'click' : {
6333         fn: this.onClick
6334         scope: this,
6335         delay: 100
6336     },
6337     'mouseover' : {
6338         fn: this.onMouseOver
6339         scope: this
6340     },
6341     'mouseout' : {
6342         fn: this.onMouseOut
6343         scope: this
6344     }
6345 });</code></pre>
6346      * <p>
6347      * Or a shorthand syntax:<br>
6348      * Code:<pre><code>
6349 el.on({
6350     'click' : this.onClick,
6351     'mouseover' : this.onMouseOver,
6352     'mouseout' : this.onMouseOut
6353     scope: this
6354 });</code></pre>
6355      */
6356         addListener : function(element, eventName, fn, scope, options){
6357             if(typeof eventName == "object"){
6358                 var o = eventName;
6359                 for(var e in o){
6360                     if(propRe.test(e)){
6361                         continue;
6362                     }
6363                     if(typeof o[e] == "function"){
6364                         // shared options
6365                         listen(element, e, o, o[e], o.scope);
6366                     }else{
6367                         // individual options
6368                         listen(element, e, o[e]);
6369                     }
6370                 }
6371                 return;
6372             }
6373             return listen(element, eventName, options, fn, scope);
6374         },
6375         
6376         /**
6377          * Removes an event handler
6378          *
6379          * @param {String/HTMLElement}   element        The id or html element to remove the 
6380          *                             event from
6381          * @param {String}   eventName     The type of event
6382          * @param {Function} fn
6383          * @return {Boolean} True if a listener was actually removed
6384          */
6385         removeListener : function(element, eventName, fn){
6386             return stopListening(element, eventName, fn);
6387         },
6388         
6389         /**
6390          * Fires when the document is ready (before onload and before images are loaded). Can be 
6391          * accessed shorthanded Roo.onReady().
6392          * @param {Function} fn        The method the event invokes
6393          * @param {Object}   scope    An  object that becomes the scope of the handler
6394          * @param {boolean}  options
6395          */
6396         onDocumentReady : function(fn, scope, options){
6397             if(docReadyState){ // if it already fired
6398                 docReadyEvent.addListener(fn, scope, options);
6399                 docReadyEvent.fire();
6400                 docReadyEvent.clearListeners();
6401                 return;
6402             }
6403             if(!docReadyEvent){
6404                 initDocReady();
6405             }
6406             docReadyEvent.addListener(fn, scope, options);
6407         },
6408         
6409         /**
6410          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6411          * @param {Function} fn        The method the event invokes
6412          * @param {Object}   scope    An object that becomes the scope of the handler
6413          * @param {boolean}  options
6414          */
6415         onWindowResize : function(fn, scope, options){
6416             if(!resizeEvent){
6417                 resizeEvent = new Roo.util.Event();
6418                 resizeTask = new Roo.util.DelayedTask(function(){
6419                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6420                 });
6421                 E.on(window, "resize", function(){
6422                     if(Roo.isIE){
6423                         resizeTask.delay(50);
6424                     }else{
6425                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6426                     }
6427                 });
6428             }
6429             resizeEvent.addListener(fn, scope, options);
6430         },
6431
6432         /**
6433          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6434          * @param {Function} fn        The method the event invokes
6435          * @param {Object}   scope    An object that becomes the scope of the handler
6436          * @param {boolean}  options
6437          */
6438         onTextResize : function(fn, scope, options){
6439             if(!textEvent){
6440                 textEvent = new Roo.util.Event();
6441                 var textEl = new Roo.Element(document.createElement('div'));
6442                 textEl.dom.className = 'x-text-resize';
6443                 textEl.dom.innerHTML = 'X';
6444                 textEl.appendTo(document.body);
6445                 textSize = textEl.dom.offsetHeight;
6446                 setInterval(function(){
6447                     if(textEl.dom.offsetHeight != textSize){
6448                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6449                     }
6450                 }, this.textResizeInterval);
6451             }
6452             textEvent.addListener(fn, scope, options);
6453         },
6454
6455         /**
6456          * Removes the passed window resize listener.
6457          * @param {Function} fn        The method the event invokes
6458          * @param {Object}   scope    The scope of handler
6459          */
6460         removeResizeListener : function(fn, scope){
6461             if(resizeEvent){
6462                 resizeEvent.removeListener(fn, scope);
6463             }
6464         },
6465
6466         // private
6467         fireResize : function(){
6468             if(resizeEvent){
6469                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6470             }   
6471         },
6472         /**
6473          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6474          */
6475         ieDeferSrc : false,
6476         /**
6477          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6478          */
6479         textResizeInterval : 50
6480     };
6481     
6482     /**
6483      * Fix for doc tools
6484      * @scopeAlias pub=Roo.EventManager
6485      */
6486     
6487      /**
6488      * Appends an event handler to an element (shorthand for addListener)
6489      * @param {String/HTMLElement}   element        The html element or id to assign the
6490      * @param {String}   eventName The type of event to listen for
6491      * @param {Function} handler The method the event invokes
6492      * @param {Object}   scope (optional) The scope in which to execute the handler
6493      * function. The handler function's "this" context.
6494      * @param {Object}   options (optional) An object containing handler configuration
6495      * properties. This may contain any of the following properties:<ul>
6496      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6497      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6498      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6499      * <li>preventDefault {Boolean} True to prevent the default action</li>
6500      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6501      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6502      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6503      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6504      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6505      * by the specified number of milliseconds. If the event fires again within that time, the original
6506      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6507      * </ul><br>
6508      * <p>
6509      * <b>Combining Options</b><br>
6510      * Using the options argument, it is possible to combine different types of listeners:<br>
6511      * <br>
6512      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6513      * Code:<pre><code>
6514 el.on('click', this.onClick, this, {
6515     single: true,
6516     delay: 100,
6517     stopEvent : true,
6518     forumId: 4
6519 });</code></pre>
6520      * <p>
6521      * <b>Attaching multiple handlers in 1 call</b><br>
6522       * The method also allows for a single argument to be passed which is a config object containing properties
6523      * which specify multiple handlers.
6524      * <p>
6525      * Code:<pre><code>
6526 el.on({
6527     'click' : {
6528         fn: this.onClick
6529         scope: this,
6530         delay: 100
6531     },
6532     'mouseover' : {
6533         fn: this.onMouseOver
6534         scope: this
6535     },
6536     'mouseout' : {
6537         fn: this.onMouseOut
6538         scope: this
6539     }
6540 });</code></pre>
6541      * <p>
6542      * Or a shorthand syntax:<br>
6543      * Code:<pre><code>
6544 el.on({
6545     'click' : this.onClick,
6546     'mouseover' : this.onMouseOver,
6547     'mouseout' : this.onMouseOut
6548     scope: this
6549 });</code></pre>
6550      */
6551     pub.on = pub.addListener;
6552     pub.un = pub.removeListener;
6553
6554     pub.stoppedMouseDownEvent = new Roo.util.Event();
6555     return pub;
6556 }();
6557 /**
6558   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6559   * @param {Function} fn        The method the event invokes
6560   * @param {Object}   scope    An  object that becomes the scope of the handler
6561   * @param {boolean}  override If true, the obj passed in becomes
6562   *                             the execution scope of the listener
6563   * @member Roo
6564   * @method onReady
6565  */
6566 Roo.onReady = Roo.EventManager.onDocumentReady;
6567
6568 Roo.onReady(function(){
6569     var bd = Roo.get(document.body);
6570     if(!bd){ return; }
6571
6572     var cls = [
6573             Roo.isIE ? "roo-ie"
6574             : Roo.isGecko ? "roo-gecko"
6575             : Roo.isOpera ? "roo-opera"
6576             : Roo.isSafari ? "roo-safari" : ""];
6577
6578     if(Roo.isMac){
6579         cls.push("roo-mac");
6580     }
6581     if(Roo.isLinux){
6582         cls.push("roo-linux");
6583     }
6584     if(Roo.isIOS){
6585         cls.push("roo-ios");
6586     }
6587     if(Roo.isBorderBox){
6588         cls.push('roo-border-box');
6589     }
6590     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6591         var p = bd.dom.parentNode;
6592         if(p){
6593             p.className += ' roo-strict';
6594         }
6595     }
6596     bd.addClass(cls.join(' '));
6597 });
6598
6599 /**
6600  * @class Roo.EventObject
6601  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6602  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6603  * Example:
6604  * <pre><code>
6605  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6606     e.preventDefault();
6607     var target = e.getTarget();
6608     ...
6609  }
6610  var myDiv = Roo.get("myDiv");
6611  myDiv.on("click", handleClick);
6612  //or
6613  Roo.EventManager.on("myDiv", 'click', handleClick);
6614  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6615  </code></pre>
6616  * @singleton
6617  */
6618 Roo.EventObject = function(){
6619     
6620     var E = Roo.lib.Event;
6621     
6622     // safari keypress events for special keys return bad keycodes
6623     var safariKeys = {
6624         63234 : 37, // left
6625         63235 : 39, // right
6626         63232 : 38, // up
6627         63233 : 40, // down
6628         63276 : 33, // page up
6629         63277 : 34, // page down
6630         63272 : 46, // delete
6631         63273 : 36, // home
6632         63275 : 35  // end
6633     };
6634
6635     // normalize button clicks
6636     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6637                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6638
6639     Roo.EventObjectImpl = function(e){
6640         if(e){
6641             this.setEvent(e.browserEvent || e);
6642         }
6643     };
6644     Roo.EventObjectImpl.prototype = {
6645         /**
6646          * Used to fix doc tools.
6647          * @scope Roo.EventObject.prototype
6648          */
6649             
6650
6651         
6652         
6653         /** The normal browser event */
6654         browserEvent : null,
6655         /** The button pressed in a mouse event */
6656         button : -1,
6657         /** True if the shift key was down during the event */
6658         shiftKey : false,
6659         /** True if the control key was down during the event */
6660         ctrlKey : false,
6661         /** True if the alt key was down during the event */
6662         altKey : false,
6663
6664         /** Key constant 
6665         * @type Number */
6666         BACKSPACE : 8,
6667         /** Key constant 
6668         * @type Number */
6669         TAB : 9,
6670         /** Key constant 
6671         * @type Number */
6672         RETURN : 13,
6673         /** Key constant 
6674         * @type Number */
6675         ENTER : 13,
6676         /** Key constant 
6677         * @type Number */
6678         SHIFT : 16,
6679         /** Key constant 
6680         * @type Number */
6681         CONTROL : 17,
6682         /** Key constant 
6683         * @type Number */
6684         ESC : 27,
6685         /** Key constant 
6686         * @type Number */
6687         SPACE : 32,
6688         /** Key constant 
6689         * @type Number */
6690         PAGEUP : 33,
6691         /** Key constant 
6692         * @type Number */
6693         PAGEDOWN : 34,
6694         /** Key constant 
6695         * @type Number */
6696         END : 35,
6697         /** Key constant 
6698         * @type Number */
6699         HOME : 36,
6700         /** Key constant 
6701         * @type Number */
6702         LEFT : 37,
6703         /** Key constant 
6704         * @type Number */
6705         UP : 38,
6706         /** Key constant 
6707         * @type Number */
6708         RIGHT : 39,
6709         /** Key constant 
6710         * @type Number */
6711         DOWN : 40,
6712         /** Key constant 
6713         * @type Number */
6714         DELETE : 46,
6715         /** Key constant 
6716         * @type Number */
6717         F5 : 116,
6718
6719            /** @private */
6720         setEvent : function(e){
6721             if(e == this || (e && e.browserEvent)){ // already wrapped
6722                 return e;
6723             }
6724             this.browserEvent = e;
6725             if(e){
6726                 // normalize buttons
6727                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6728                 if(e.type == 'click' && this.button == -1){
6729                     this.button = 0;
6730                 }
6731                 this.type = e.type;
6732                 this.shiftKey = e.shiftKey;
6733                 // mac metaKey behaves like ctrlKey
6734                 this.ctrlKey = e.ctrlKey || e.metaKey;
6735                 this.altKey = e.altKey;
6736                 // in getKey these will be normalized for the mac
6737                 this.keyCode = e.keyCode;
6738                 // keyup warnings on firefox.
6739                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6740                 // cache the target for the delayed and or buffered events
6741                 this.target = E.getTarget(e);
6742                 // same for XY
6743                 this.xy = E.getXY(e);
6744             }else{
6745                 this.button = -1;
6746                 this.shiftKey = false;
6747                 this.ctrlKey = false;
6748                 this.altKey = false;
6749                 this.keyCode = 0;
6750                 this.charCode =0;
6751                 this.target = null;
6752                 this.xy = [0, 0];
6753             }
6754             return this;
6755         },
6756
6757         /**
6758          * Stop the event (preventDefault and stopPropagation)
6759          */
6760         stopEvent : function(){
6761             if(this.browserEvent){
6762                 if(this.browserEvent.type == 'mousedown'){
6763                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6764                 }
6765                 E.stopEvent(this.browserEvent);
6766             }
6767         },
6768
6769         /**
6770          * Prevents the browsers default handling of the event.
6771          */
6772         preventDefault : function(){
6773             if(this.browserEvent){
6774                 E.preventDefault(this.browserEvent);
6775             }
6776         },
6777
6778         /** @private */
6779         isNavKeyPress : function(){
6780             var k = this.keyCode;
6781             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6782             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6783         },
6784
6785         isSpecialKey : function(){
6786             var k = this.keyCode;
6787             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6788             (k == 16) || (k == 17) ||
6789             (k >= 18 && k <= 20) ||
6790             (k >= 33 && k <= 35) ||
6791             (k >= 36 && k <= 39) ||
6792             (k >= 44 && k <= 45);
6793         },
6794         /**
6795          * Cancels bubbling of the event.
6796          */
6797         stopPropagation : function(){
6798             if(this.browserEvent){
6799                 if(this.type == 'mousedown'){
6800                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6801                 }
6802                 E.stopPropagation(this.browserEvent);
6803             }
6804         },
6805
6806         /**
6807          * Gets the key code for the event.
6808          * @return {Number}
6809          */
6810         getCharCode : function(){
6811             return this.charCode || this.keyCode;
6812         },
6813
6814         /**
6815          * Returns a normalized keyCode for the event.
6816          * @return {Number} The key code
6817          */
6818         getKey : function(){
6819             var k = this.keyCode || this.charCode;
6820             return Roo.isSafari ? (safariKeys[k] || k) : k;
6821         },
6822
6823         /**
6824          * Gets the x coordinate of the event.
6825          * @return {Number}
6826          */
6827         getPageX : function(){
6828             return this.xy[0];
6829         },
6830
6831         /**
6832          * Gets the y coordinate of the event.
6833          * @return {Number}
6834          */
6835         getPageY : function(){
6836             return this.xy[1];
6837         },
6838
6839         /**
6840          * Gets the time of the event.
6841          * @return {Number}
6842          */
6843         getTime : function(){
6844             if(this.browserEvent){
6845                 return E.getTime(this.browserEvent);
6846             }
6847             return null;
6848         },
6849
6850         /**
6851          * Gets the page coordinates of the event.
6852          * @return {Array} The xy values like [x, y]
6853          */
6854         getXY : function(){
6855             return this.xy;
6856         },
6857
6858         /**
6859          * Gets the target for the event.
6860          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6861          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6862                 search as a number or element (defaults to 10 || document.body)
6863          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6864          * @return {HTMLelement}
6865          */
6866         getTarget : function(selector, maxDepth, returnEl){
6867             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6868         },
6869         /**
6870          * Gets the related target.
6871          * @return {HTMLElement}
6872          */
6873         getRelatedTarget : function(){
6874             if(this.browserEvent){
6875                 return E.getRelatedTarget(this.browserEvent);
6876             }
6877             return null;
6878         },
6879
6880         /**
6881          * Normalizes mouse wheel delta across browsers
6882          * @return {Number} The delta
6883          */
6884         getWheelDelta : function(){
6885             var e = this.browserEvent;
6886             var delta = 0;
6887             if(e.wheelDelta){ /* IE/Opera. */
6888                 delta = e.wheelDelta/120;
6889             }else if(e.detail){ /* Mozilla case. */
6890                 delta = -e.detail/3;
6891             }
6892             return delta;
6893         },
6894
6895         /**
6896          * Returns true if the control, meta, shift or alt key was pressed during this event.
6897          * @return {Boolean}
6898          */
6899         hasModifier : function(){
6900             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6901         },
6902
6903         /**
6904          * Returns true if the target of this event equals el or is a child of el
6905          * @param {String/HTMLElement/Element} el
6906          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6907          * @return {Boolean}
6908          */
6909         within : function(el, related){
6910             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6911             return t && Roo.fly(el).contains(t);
6912         },
6913
6914         getPoint : function(){
6915             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6916         }
6917     };
6918
6919     return new Roo.EventObjectImpl();
6920 }();
6921             
6922     /*
6923  * Based on:
6924  * Ext JS Library 1.1.1
6925  * Copyright(c) 2006-2007, Ext JS, LLC.
6926  *
6927  * Originally Released Under LGPL - original licence link has changed is not relivant.
6928  *
6929  * Fork - LGPL
6930  * <script type="text/javascript">
6931  */
6932
6933  
6934 // was in Composite Element!??!?!
6935  
6936 (function(){
6937     var D = Roo.lib.Dom;
6938     var E = Roo.lib.Event;
6939     var A = Roo.lib.Anim;
6940
6941     // local style camelizing for speed
6942     var propCache = {};
6943     var camelRe = /(-[a-z])/gi;
6944     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6945     var view = document.defaultView;
6946
6947 /**
6948  * @class Roo.Element
6949  * Represents an Element in the DOM.<br><br>
6950  * Usage:<br>
6951 <pre><code>
6952 var el = Roo.get("my-div");
6953
6954 // or with getEl
6955 var el = getEl("my-div");
6956
6957 // or with a DOM element
6958 var el = Roo.get(myDivElement);
6959 </code></pre>
6960  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6961  * each call instead of constructing a new one.<br><br>
6962  * <b>Animations</b><br />
6963  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6964  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6965 <pre>
6966 Option    Default   Description
6967 --------- --------  ---------------------------------------------
6968 duration  .35       The duration of the animation in seconds
6969 easing    easeOut   The YUI easing method
6970 callback  none      A function to execute when the anim completes
6971 scope     this      The scope (this) of the callback function
6972 </pre>
6973 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6974 * manipulate the animation. Here's an example:
6975 <pre><code>
6976 var el = Roo.get("my-div");
6977
6978 // no animation
6979 el.setWidth(100);
6980
6981 // default animation
6982 el.setWidth(100, true);
6983
6984 // animation with some options set
6985 el.setWidth(100, {
6986     duration: 1,
6987     callback: this.foo,
6988     scope: this
6989 });
6990
6991 // using the "anim" property to get the Anim object
6992 var opt = {
6993     duration: 1,
6994     callback: this.foo,
6995     scope: this
6996 };
6997 el.setWidth(100, opt);
6998 ...
6999 if(opt.anim.isAnimated()){
7000     opt.anim.stop();
7001 }
7002 </code></pre>
7003 * <b> Composite (Collections of) Elements</b><br />
7004  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7005  * @constructor Create a new Element directly.
7006  * @param {String/HTMLElement} element
7007  * @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).
7008  */
7009     Roo.Element = function(element, forceNew){
7010         var dom = typeof element == "string" ?
7011                 document.getElementById(element) : element;
7012         if(!dom){ // invalid id/element
7013             return null;
7014         }
7015         var id = dom.id;
7016         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7017             return Roo.Element.cache[id];
7018         }
7019
7020         /**
7021          * The DOM element
7022          * @type HTMLElement
7023          */
7024         this.dom = dom;
7025
7026         /**
7027          * The DOM element ID
7028          * @type String
7029          */
7030         this.id = id || Roo.id(dom);
7031     };
7032
7033     var El = Roo.Element;
7034
7035     El.prototype = {
7036         /**
7037          * The element's default display mode  (defaults to "")
7038          * @type String
7039          */
7040         originalDisplay : "",
7041
7042         visibilityMode : 1,
7043         /**
7044          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7045          * @type String
7046          */
7047         defaultUnit : "px",
7048         
7049         pxReg : /^\d+(?:\.\d*)?px$/i,
7050         /**
7051          * Sets the element's visibility mode. When setVisible() is called it
7052          * will use this to determine whether to set the visibility or the display property.
7053          * @param visMode Element.VISIBILITY or Element.DISPLAY
7054          * @return {Roo.Element} this
7055          */
7056         setVisibilityMode : function(visMode){
7057             this.visibilityMode = visMode;
7058             return this;
7059         },
7060         /**
7061          * Convenience method for setVisibilityMode(Element.DISPLAY)
7062          * @param {String} display (optional) What to set display to when visible
7063          * @return {Roo.Element} this
7064          */
7065         enableDisplayMode : function(display){
7066             this.setVisibilityMode(El.DISPLAY);
7067             if(typeof display != "undefined") this.originalDisplay = display;
7068             return this;
7069         },
7070
7071         /**
7072          * 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)
7073          * @param {String} selector The simple selector to test
7074          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7075                 search as a number or element (defaults to 10 || document.body)
7076          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7077          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7078          */
7079         findParent : function(simpleSelector, maxDepth, returnEl){
7080             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7081             maxDepth = maxDepth || 50;
7082             if(typeof maxDepth != "number"){
7083                 stopEl = Roo.getDom(maxDepth);
7084                 maxDepth = 10;
7085             }
7086             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7087                 if(dq.is(p, simpleSelector)){
7088                     return returnEl ? Roo.get(p) : p;
7089                 }
7090                 depth++;
7091                 p = p.parentNode;
7092             }
7093             return null;
7094         },
7095
7096
7097         /**
7098          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7099          * @param {String} selector The simple selector to test
7100          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7101                 search as a number or element (defaults to 10 || document.body)
7102          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7103          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7104          */
7105         findParentNode : function(simpleSelector, maxDepth, returnEl){
7106             var p = Roo.fly(this.dom.parentNode, '_internal');
7107             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7108         },
7109
7110         /**
7111          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7112          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7113          * @param {String} selector The simple selector to test
7114          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7115                 search as a number or element (defaults to 10 || document.body)
7116          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7117          */
7118         up : function(simpleSelector, maxDepth){
7119             return this.findParentNode(simpleSelector, maxDepth, true);
7120         },
7121
7122
7123
7124         /**
7125          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7126          * @param {String} selector The simple selector to test
7127          * @return {Boolean} True if this element matches the selector, else false
7128          */
7129         is : function(simpleSelector){
7130             return Roo.DomQuery.is(this.dom, simpleSelector);
7131         },
7132
7133         /**
7134          * Perform animation on this element.
7135          * @param {Object} args The YUI animation control args
7136          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7137          * @param {Function} onComplete (optional) Function to call when animation completes
7138          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7139          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7140          * @return {Roo.Element} this
7141          */
7142         animate : function(args, duration, onComplete, easing, animType){
7143             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7144             return this;
7145         },
7146
7147         /*
7148          * @private Internal animation call
7149          */
7150         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7151             animType = animType || 'run';
7152             opt = opt || {};
7153             var anim = Roo.lib.Anim[animType](
7154                 this.dom, args,
7155                 (opt.duration || defaultDur) || .35,
7156                 (opt.easing || defaultEase) || 'easeOut',
7157                 function(){
7158                     Roo.callback(cb, this);
7159                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7160                 },
7161                 this
7162             );
7163             opt.anim = anim;
7164             return anim;
7165         },
7166
7167         // private legacy anim prep
7168         preanim : function(a, i){
7169             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7170         },
7171
7172         /**
7173          * Removes worthless text nodes
7174          * @param {Boolean} forceReclean (optional) By default the element
7175          * keeps track if it has been cleaned already so
7176          * you can call this over and over. However, if you update the element and
7177          * need to force a reclean, you can pass true.
7178          */
7179         clean : function(forceReclean){
7180             if(this.isCleaned && forceReclean !== true){
7181                 return this;
7182             }
7183             var ns = /\S/;
7184             var d = this.dom, n = d.firstChild, ni = -1;
7185             while(n){
7186                 var nx = n.nextSibling;
7187                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7188                     d.removeChild(n);
7189                 }else{
7190                     n.nodeIndex = ++ni;
7191                 }
7192                 n = nx;
7193             }
7194             this.isCleaned = true;
7195             return this;
7196         },
7197
7198         // private
7199         calcOffsetsTo : function(el){
7200             el = Roo.get(el);
7201             var d = el.dom;
7202             var restorePos = false;
7203             if(el.getStyle('position') == 'static'){
7204                 el.position('relative');
7205                 restorePos = true;
7206             }
7207             var x = 0, y =0;
7208             var op = this.dom;
7209             while(op && op != d && op.tagName != 'HTML'){
7210                 x+= op.offsetLeft;
7211                 y+= op.offsetTop;
7212                 op = op.offsetParent;
7213             }
7214             if(restorePos){
7215                 el.position('static');
7216             }
7217             return [x, y];
7218         },
7219
7220         /**
7221          * Scrolls this element into view within the passed container.
7222          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7223          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7224          * @return {Roo.Element} this
7225          */
7226         scrollIntoView : function(container, hscroll){
7227             var c = Roo.getDom(container) || document.body;
7228             var el = this.dom;
7229
7230             var o = this.calcOffsetsTo(c),
7231                 l = o[0],
7232                 t = o[1],
7233                 b = t+el.offsetHeight,
7234                 r = l+el.offsetWidth;
7235
7236             var ch = c.clientHeight;
7237             var ct = parseInt(c.scrollTop, 10);
7238             var cl = parseInt(c.scrollLeft, 10);
7239             var cb = ct + ch;
7240             var cr = cl + c.clientWidth;
7241
7242             if(t < ct){
7243                 c.scrollTop = t;
7244             }else if(b > cb){
7245                 c.scrollTop = b-ch;
7246             }
7247
7248             if(hscroll !== false){
7249                 if(l < cl){
7250                     c.scrollLeft = l;
7251                 }else if(r > cr){
7252                     c.scrollLeft = r-c.clientWidth;
7253                 }
7254             }
7255             return this;
7256         },
7257
7258         // private
7259         scrollChildIntoView : function(child, hscroll){
7260             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7261         },
7262
7263         /**
7264          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7265          * the new height may not be available immediately.
7266          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7267          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7268          * @param {Function} onComplete (optional) Function to call when animation completes
7269          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7270          * @return {Roo.Element} this
7271          */
7272         autoHeight : function(animate, duration, onComplete, easing){
7273             var oldHeight = this.getHeight();
7274             this.clip();
7275             this.setHeight(1); // force clipping
7276             setTimeout(function(){
7277                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7278                 if(!animate){
7279                     this.setHeight(height);
7280                     this.unclip();
7281                     if(typeof onComplete == "function"){
7282                         onComplete();
7283                     }
7284                 }else{
7285                     this.setHeight(oldHeight); // restore original height
7286                     this.setHeight(height, animate, duration, function(){
7287                         this.unclip();
7288                         if(typeof onComplete == "function") onComplete();
7289                     }.createDelegate(this), easing);
7290                 }
7291             }.createDelegate(this), 0);
7292             return this;
7293         },
7294
7295         /**
7296          * Returns true if this element is an ancestor of the passed element
7297          * @param {HTMLElement/String} el The element to check
7298          * @return {Boolean} True if this element is an ancestor of el, else false
7299          */
7300         contains : function(el){
7301             if(!el){return false;}
7302             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7303         },
7304
7305         /**
7306          * Checks whether the element is currently visible using both visibility and display properties.
7307          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7308          * @return {Boolean} True if the element is currently visible, else false
7309          */
7310         isVisible : function(deep) {
7311             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7312             if(deep !== true || !vis){
7313                 return vis;
7314             }
7315             var p = this.dom.parentNode;
7316             while(p && p.tagName.toLowerCase() != "body"){
7317                 if(!Roo.fly(p, '_isVisible').isVisible()){
7318                     return false;
7319                 }
7320                 p = p.parentNode;
7321             }
7322             return true;
7323         },
7324
7325         /**
7326          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7327          * @param {String} selector The CSS selector
7328          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7329          * @return {CompositeElement/CompositeElementLite} The composite element
7330          */
7331         select : function(selector, unique){
7332             return El.select(selector, unique, this.dom);
7333         },
7334
7335         /**
7336          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7337          * @param {String} selector The CSS selector
7338          * @return {Array} An array of the matched nodes
7339          */
7340         query : function(selector, unique){
7341             return Roo.DomQuery.select(selector, this.dom);
7342         },
7343
7344         /**
7345          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7346          * @param {String} selector The CSS selector
7347          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7348          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7349          */
7350         child : function(selector, returnDom){
7351             var n = Roo.DomQuery.selectNode(selector, this.dom);
7352             return returnDom ? n : Roo.get(n);
7353         },
7354
7355         /**
7356          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7357          * @param {String} selector The CSS selector
7358          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7359          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7360          */
7361         down : function(selector, returnDom){
7362             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7363             return returnDom ? n : Roo.get(n);
7364         },
7365
7366         /**
7367          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7368          * @param {String} group The group the DD object is member of
7369          * @param {Object} config The DD config object
7370          * @param {Object} overrides An object containing methods to override/implement on the DD object
7371          * @return {Roo.dd.DD} The DD object
7372          */
7373         initDD : function(group, config, overrides){
7374             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7375             return Roo.apply(dd, overrides);
7376         },
7377
7378         /**
7379          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7380          * @param {String} group The group the DDProxy object is member of
7381          * @param {Object} config The DDProxy config object
7382          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7383          * @return {Roo.dd.DDProxy} The DDProxy object
7384          */
7385         initDDProxy : function(group, config, overrides){
7386             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7387             return Roo.apply(dd, overrides);
7388         },
7389
7390         /**
7391          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7392          * @param {String} group The group the DDTarget object is member of
7393          * @param {Object} config The DDTarget config object
7394          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7395          * @return {Roo.dd.DDTarget} The DDTarget object
7396          */
7397         initDDTarget : function(group, config, overrides){
7398             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7399             return Roo.apply(dd, overrides);
7400         },
7401
7402         /**
7403          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7404          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7405          * @param {Boolean} visible Whether the element is visible
7406          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7407          * @return {Roo.Element} this
7408          */
7409          setVisible : function(visible, animate){
7410             if(!animate || !A){
7411                 if(this.visibilityMode == El.DISPLAY){
7412                     this.setDisplayed(visible);
7413                 }else{
7414                     this.fixDisplay();
7415                     this.dom.style.visibility = visible ? "visible" : "hidden";
7416                 }
7417             }else{
7418                 // closure for composites
7419                 var dom = this.dom;
7420                 var visMode = this.visibilityMode;
7421                 if(visible){
7422                     this.setOpacity(.01);
7423                     this.setVisible(true);
7424                 }
7425                 this.anim({opacity: { to: (visible?1:0) }},
7426                       this.preanim(arguments, 1),
7427                       null, .35, 'easeIn', function(){
7428                          if(!visible){
7429                              if(visMode == El.DISPLAY){
7430                                  dom.style.display = "none";
7431                              }else{
7432                                  dom.style.visibility = "hidden";
7433                              }
7434                              Roo.get(dom).setOpacity(1);
7435                          }
7436                      });
7437             }
7438             return this;
7439         },
7440
7441         /**
7442          * Returns true if display is not "none"
7443          * @return {Boolean}
7444          */
7445         isDisplayed : function() {
7446             return this.getStyle("display") != "none";
7447         },
7448
7449         /**
7450          * Toggles the element's visibility or display, depending on visibility mode.
7451          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7452          * @return {Roo.Element} this
7453          */
7454         toggle : function(animate){
7455             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7456             return this;
7457         },
7458
7459         /**
7460          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7461          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7462          * @return {Roo.Element} this
7463          */
7464         setDisplayed : function(value) {
7465             if(typeof value == "boolean"){
7466                value = value ? this.originalDisplay : "none";
7467             }
7468             this.setStyle("display", value);
7469             return this;
7470         },
7471
7472         /**
7473          * Tries to focus the element. Any exceptions are caught and ignored.
7474          * @return {Roo.Element} this
7475          */
7476         focus : function() {
7477             try{
7478                 this.dom.focus();
7479             }catch(e){}
7480             return this;
7481         },
7482
7483         /**
7484          * Tries to blur the element. Any exceptions are caught and ignored.
7485          * @return {Roo.Element} this
7486          */
7487         blur : function() {
7488             try{
7489                 this.dom.blur();
7490             }catch(e){}
7491             return this;
7492         },
7493
7494         /**
7495          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7496          * @param {String/Array} className The CSS class to add, or an array of classes
7497          * @return {Roo.Element} this
7498          */
7499         addClass : function(className){
7500             if(className instanceof Array){
7501                 for(var i = 0, len = className.length; i < len; i++) {
7502                     this.addClass(className[i]);
7503                 }
7504             }else{
7505                 if(className && !this.hasClass(className)){
7506                     this.dom.className = this.dom.className + " " + className;
7507                 }
7508             }
7509             return this;
7510         },
7511
7512         /**
7513          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7514          * @param {String/Array} className The CSS class to add, or an array of classes
7515          * @return {Roo.Element} this
7516          */
7517         radioClass : function(className){
7518             var siblings = this.dom.parentNode.childNodes;
7519             for(var i = 0; i < siblings.length; i++) {
7520                 var s = siblings[i];
7521                 if(s.nodeType == 1){
7522                     Roo.get(s).removeClass(className);
7523                 }
7524             }
7525             this.addClass(className);
7526             return this;
7527         },
7528
7529         /**
7530          * Removes one or more CSS classes from the element.
7531          * @param {String/Array} className The CSS class to remove, or an array of classes
7532          * @return {Roo.Element} this
7533          */
7534         removeClass : function(className){
7535             if(!className || !this.dom.className){
7536                 return this;
7537             }
7538             if(className instanceof Array){
7539                 for(var i = 0, len = className.length; i < len; i++) {
7540                     this.removeClass(className[i]);
7541                 }
7542             }else{
7543                 if(this.hasClass(className)){
7544                     var re = this.classReCache[className];
7545                     if (!re) {
7546                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7547                        this.classReCache[className] = re;
7548                     }
7549                     this.dom.className =
7550                         this.dom.className.replace(re, " ");
7551                 }
7552             }
7553             return this;
7554         },
7555
7556         // private
7557         classReCache: {},
7558
7559         /**
7560          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7561          * @param {String} className The CSS class to toggle
7562          * @return {Roo.Element} this
7563          */
7564         toggleClass : function(className){
7565             if(this.hasClass(className)){
7566                 this.removeClass(className);
7567             }else{
7568                 this.addClass(className);
7569             }
7570             return this;
7571         },
7572
7573         /**
7574          * Checks if the specified CSS class exists on this element's DOM node.
7575          * @param {String} className The CSS class to check for
7576          * @return {Boolean} True if the class exists, else false
7577          */
7578         hasClass : function(className){
7579             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7580         },
7581
7582         /**
7583          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7584          * @param {String} oldClassName The CSS class to replace
7585          * @param {String} newClassName The replacement CSS class
7586          * @return {Roo.Element} this
7587          */
7588         replaceClass : function(oldClassName, newClassName){
7589             this.removeClass(oldClassName);
7590             this.addClass(newClassName);
7591             return this;
7592         },
7593
7594         /**
7595          * Returns an object with properties matching the styles requested.
7596          * For example, el.getStyles('color', 'font-size', 'width') might return
7597          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7598          * @param {String} style1 A style name
7599          * @param {String} style2 A style name
7600          * @param {String} etc.
7601          * @return {Object} The style object
7602          */
7603         getStyles : function(){
7604             var a = arguments, len = a.length, r = {};
7605             for(var i = 0; i < len; i++){
7606                 r[a[i]] = this.getStyle(a[i]);
7607             }
7608             return r;
7609         },
7610
7611         /**
7612          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7613          * @param {String} property The style property whose value is returned.
7614          * @return {String} The current value of the style property for this element.
7615          */
7616         getStyle : function(){
7617             return view && view.getComputedStyle ?
7618                 function(prop){
7619                     var el = this.dom, v, cs, camel;
7620                     if(prop == 'float'){
7621                         prop = "cssFloat";
7622                     }
7623                     if(el.style && (v = el.style[prop])){
7624                         return v;
7625                     }
7626                     if(cs = view.getComputedStyle(el, "")){
7627                         if(!(camel = propCache[prop])){
7628                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7629                         }
7630                         return cs[camel];
7631                     }
7632                     return null;
7633                 } :
7634                 function(prop){
7635                     var el = this.dom, v, cs, camel;
7636                     if(prop == 'opacity'){
7637                         if(typeof el.style.filter == 'string'){
7638                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7639                             if(m){
7640                                 var fv = parseFloat(m[1]);
7641                                 if(!isNaN(fv)){
7642                                     return fv ? fv / 100 : 0;
7643                                 }
7644                             }
7645                         }
7646                         return 1;
7647                     }else if(prop == 'float'){
7648                         prop = "styleFloat";
7649                     }
7650                     if(!(camel = propCache[prop])){
7651                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7652                     }
7653                     if(v = el.style[camel]){
7654                         return v;
7655                     }
7656                     if(cs = el.currentStyle){
7657                         return cs[camel];
7658                     }
7659                     return null;
7660                 };
7661         }(),
7662
7663         /**
7664          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7665          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7666          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7667          * @return {Roo.Element} this
7668          */
7669         setStyle : function(prop, value){
7670             if(typeof prop == "string"){
7671                 
7672                 if (prop == 'float') {
7673                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7674                     return this;
7675                 }
7676                 
7677                 var camel;
7678                 if(!(camel = propCache[prop])){
7679                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7680                 }
7681                 
7682                 if(camel == 'opacity') {
7683                     this.setOpacity(value);
7684                 }else{
7685                     this.dom.style[camel] = value;
7686                 }
7687             }else{
7688                 for(var style in prop){
7689                     if(typeof prop[style] != "function"){
7690                        this.setStyle(style, prop[style]);
7691                     }
7692                 }
7693             }
7694             return this;
7695         },
7696
7697         /**
7698          * More flexible version of {@link #setStyle} for setting style properties.
7699          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7700          * a function which returns such a specification.
7701          * @return {Roo.Element} this
7702          */
7703         applyStyles : function(style){
7704             Roo.DomHelper.applyStyles(this.dom, style);
7705             return this;
7706         },
7707
7708         /**
7709           * 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).
7710           * @return {Number} The X position of the element
7711           */
7712         getX : function(){
7713             return D.getX(this.dom);
7714         },
7715
7716         /**
7717           * 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).
7718           * @return {Number} The Y position of the element
7719           */
7720         getY : function(){
7721             return D.getY(this.dom);
7722         },
7723
7724         /**
7725           * 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).
7726           * @return {Array} The XY position of the element
7727           */
7728         getXY : function(){
7729             return D.getXY(this.dom);
7730         },
7731
7732         /**
7733          * 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).
7734          * @param {Number} The X position of the element
7735          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7736          * @return {Roo.Element} this
7737          */
7738         setX : function(x, animate){
7739             if(!animate || !A){
7740                 D.setX(this.dom, x);
7741             }else{
7742                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7743             }
7744             return this;
7745         },
7746
7747         /**
7748          * 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).
7749          * @param {Number} The Y position of the element
7750          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7751          * @return {Roo.Element} this
7752          */
7753         setY : function(y, animate){
7754             if(!animate || !A){
7755                 D.setY(this.dom, y);
7756             }else{
7757                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7758             }
7759             return this;
7760         },
7761
7762         /**
7763          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7764          * @param {String} left The left CSS property value
7765          * @return {Roo.Element} this
7766          */
7767         setLeft : function(left){
7768             this.setStyle("left", this.addUnits(left));
7769             return this;
7770         },
7771
7772         /**
7773          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7774          * @param {String} top The top CSS property value
7775          * @return {Roo.Element} this
7776          */
7777         setTop : function(top){
7778             this.setStyle("top", this.addUnits(top));
7779             return this;
7780         },
7781
7782         /**
7783          * Sets the element's CSS right style.
7784          * @param {String} right The right CSS property value
7785          * @return {Roo.Element} this
7786          */
7787         setRight : function(right){
7788             this.setStyle("right", this.addUnits(right));
7789             return this;
7790         },
7791
7792         /**
7793          * Sets the element's CSS bottom style.
7794          * @param {String} bottom The bottom CSS property value
7795          * @return {Roo.Element} this
7796          */
7797         setBottom : function(bottom){
7798             this.setStyle("bottom", this.addUnits(bottom));
7799             return this;
7800         },
7801
7802         /**
7803          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7804          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7805          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7806          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7807          * @return {Roo.Element} this
7808          */
7809         setXY : function(pos, animate){
7810             if(!animate || !A){
7811                 D.setXY(this.dom, pos);
7812             }else{
7813                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7814             }
7815             return this;
7816         },
7817
7818         /**
7819          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7820          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7821          * @param {Number} x X value for new position (coordinates are page-based)
7822          * @param {Number} y Y value for new position (coordinates are page-based)
7823          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7824          * @return {Roo.Element} this
7825          */
7826         setLocation : function(x, y, animate){
7827             this.setXY([x, y], this.preanim(arguments, 2));
7828             return this;
7829         },
7830
7831         /**
7832          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7833          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7834          * @param {Number} x X value for new position (coordinates are page-based)
7835          * @param {Number} y Y value for new position (coordinates are page-based)
7836          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7837          * @return {Roo.Element} this
7838          */
7839         moveTo : function(x, y, animate){
7840             this.setXY([x, y], this.preanim(arguments, 2));
7841             return this;
7842         },
7843
7844         /**
7845          * Returns the region of the given element.
7846          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7847          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7848          */
7849         getRegion : function(){
7850             return D.getRegion(this.dom);
7851         },
7852
7853         /**
7854          * Returns the offset height of the element
7855          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7856          * @return {Number} The element's height
7857          */
7858         getHeight : function(contentHeight){
7859             var h = this.dom.offsetHeight || 0;
7860             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7861         },
7862
7863         /**
7864          * Returns the offset width of the element
7865          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7866          * @return {Number} The element's width
7867          */
7868         getWidth : function(contentWidth){
7869             var w = this.dom.offsetWidth || 0;
7870             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7871         },
7872
7873         /**
7874          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7875          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7876          * if a height has not been set using CSS.
7877          * @return {Number}
7878          */
7879         getComputedHeight : function(){
7880             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7881             if(!h){
7882                 h = parseInt(this.getStyle('height'), 10) || 0;
7883                 if(!this.isBorderBox()){
7884                     h += this.getFrameWidth('tb');
7885                 }
7886             }
7887             return h;
7888         },
7889
7890         /**
7891          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7892          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7893          * if a width has not been set using CSS.
7894          * @return {Number}
7895          */
7896         getComputedWidth : function(){
7897             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7898             if(!w){
7899                 w = parseInt(this.getStyle('width'), 10) || 0;
7900                 if(!this.isBorderBox()){
7901                     w += this.getFrameWidth('lr');
7902                 }
7903             }
7904             return w;
7905         },
7906
7907         /**
7908          * Returns the size of the element.
7909          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7910          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7911          */
7912         getSize : function(contentSize){
7913             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7914         },
7915
7916         /**
7917          * Returns the width and height of the viewport.
7918          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7919          */
7920         getViewSize : function(){
7921             var d = this.dom, doc = document, aw = 0, ah = 0;
7922             if(d == doc || d == doc.body){
7923                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7924             }else{
7925                 return {
7926                     width : d.clientWidth,
7927                     height: d.clientHeight
7928                 };
7929             }
7930         },
7931
7932         /**
7933          * Returns the value of the "value" attribute
7934          * @param {Boolean} asNumber true to parse the value as a number
7935          * @return {String/Number}
7936          */
7937         getValue : function(asNumber){
7938             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7939         },
7940
7941         // private
7942         adjustWidth : function(width){
7943             if(typeof width == "number"){
7944                 if(this.autoBoxAdjust && !this.isBorderBox()){
7945                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7946                 }
7947                 if(width < 0){
7948                     width = 0;
7949                 }
7950             }
7951             return width;
7952         },
7953
7954         // private
7955         adjustHeight : function(height){
7956             if(typeof height == "number"){
7957                if(this.autoBoxAdjust && !this.isBorderBox()){
7958                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7959                }
7960                if(height < 0){
7961                    height = 0;
7962                }
7963             }
7964             return height;
7965         },
7966
7967         /**
7968          * Set the width of the element
7969          * @param {Number} width The new width
7970          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7971          * @return {Roo.Element} this
7972          */
7973         setWidth : function(width, animate){
7974             width = this.adjustWidth(width);
7975             if(!animate || !A){
7976                 this.dom.style.width = this.addUnits(width);
7977             }else{
7978                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7979             }
7980             return this;
7981         },
7982
7983         /**
7984          * Set the height of the element
7985          * @param {Number} height The new height
7986          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7987          * @return {Roo.Element} this
7988          */
7989          setHeight : function(height, animate){
7990             height = this.adjustHeight(height);
7991             if(!animate || !A){
7992                 this.dom.style.height = this.addUnits(height);
7993             }else{
7994                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7995             }
7996             return this;
7997         },
7998
7999         /**
8000          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8001          * @param {Number} width The new width
8002          * @param {Number} height The new height
8003          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8004          * @return {Roo.Element} this
8005          */
8006          setSize : function(width, height, animate){
8007             if(typeof width == "object"){ // in case of object from getSize()
8008                 height = width.height; width = width.width;
8009             }
8010             width = this.adjustWidth(width); height = this.adjustHeight(height);
8011             if(!animate || !A){
8012                 this.dom.style.width = this.addUnits(width);
8013                 this.dom.style.height = this.addUnits(height);
8014             }else{
8015                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8016             }
8017             return this;
8018         },
8019
8020         /**
8021          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8022          * @param {Number} x X value for new position (coordinates are page-based)
8023          * @param {Number} y Y value for new position (coordinates are page-based)
8024          * @param {Number} width The new width
8025          * @param {Number} height The new height
8026          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8027          * @return {Roo.Element} this
8028          */
8029         setBounds : function(x, y, width, height, animate){
8030             if(!animate || !A){
8031                 this.setSize(width, height);
8032                 this.setLocation(x, y);
8033             }else{
8034                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8035                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8036                               this.preanim(arguments, 4), 'motion');
8037             }
8038             return this;
8039         },
8040
8041         /**
8042          * 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.
8043          * @param {Roo.lib.Region} region The region to fill
8044          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8045          * @return {Roo.Element} this
8046          */
8047         setRegion : function(region, animate){
8048             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8049             return this;
8050         },
8051
8052         /**
8053          * Appends an event handler
8054          *
8055          * @param {String}   eventName     The type of event to append
8056          * @param {Function} fn        The method the event invokes
8057          * @param {Object} scope       (optional) The scope (this object) of the fn
8058          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8059          */
8060         addListener : function(eventName, fn, scope, options){
8061             if (this.dom) {
8062                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8063             }
8064         },
8065
8066         /**
8067          * Removes an event handler from this element
8068          * @param {String} eventName the type of event to remove
8069          * @param {Function} fn the method the event invokes
8070          * @return {Roo.Element} this
8071          */
8072         removeListener : function(eventName, fn){
8073             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8074             return this;
8075         },
8076
8077         /**
8078          * Removes all previous added listeners from this element
8079          * @return {Roo.Element} this
8080          */
8081         removeAllListeners : function(){
8082             E.purgeElement(this.dom);
8083             return this;
8084         },
8085
8086         relayEvent : function(eventName, observable){
8087             this.on(eventName, function(e){
8088                 observable.fireEvent(eventName, e);
8089             });
8090         },
8091
8092         /**
8093          * Set the opacity of the element
8094          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8095          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8096          * @return {Roo.Element} this
8097          */
8098          setOpacity : function(opacity, animate){
8099             if(!animate || !A){
8100                 var s = this.dom.style;
8101                 if(Roo.isIE){
8102                     s.zoom = 1;
8103                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8104                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8105                 }else{
8106                     s.opacity = opacity;
8107                 }
8108             }else{
8109                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8110             }
8111             return this;
8112         },
8113
8114         /**
8115          * Gets the left X coordinate
8116          * @param {Boolean} local True to get the local css position instead of page coordinate
8117          * @return {Number}
8118          */
8119         getLeft : function(local){
8120             if(!local){
8121                 return this.getX();
8122             }else{
8123                 
8124                 return parseInt(this.getStyle("left"), 10) || 0;
8125                 
8126 //                var x = this.getStyle("left");
8127 //                
8128 //                if(!x || x === 'AUTO'){
8129 //                    return 0;
8130 //                }
8131 //                
8132 //                if(this.pxReg.test(x)){
8133 //                    return parseFloat(x);
8134 //                }
8135 //                
8136 //                x = this.getX();
8137 //                
8138 //                var  par = this.dom.offsetParent ? Roo.fly(this.dom.offsetParent) : false;
8139 //                
8140 //                 if (par !== false) {
8141 //                    x -= par.getX();
8142 //                }
8143 //
8144 //                return x;
8145             }
8146         },
8147
8148         /**
8149          * Gets the right X coordinate of the element (element X position + element width)
8150          * @param {Boolean} local True to get the local css position instead of page coordinate
8151          * @return {Number}
8152          */
8153         getRight : function(local){
8154             if(!local){
8155                 return this.getX() + this.getWidth();
8156             }else{
8157                 return (this.getLeft(true) + this.getWidth()) || 0;
8158             }
8159         },
8160
8161         /**
8162          * Gets the top Y coordinate
8163          * @param {Boolean} local True to get the local css position instead of page coordinate
8164          * @return {Number}
8165          */
8166         getTop : function(local) {
8167             if(!local){
8168                 return this.getY();
8169             }else{
8170                 return parseInt(this.getStyle("top"), 10) || 0;
8171             }
8172         },
8173
8174         /**
8175          * Gets the bottom Y coordinate of the element (element Y position + element height)
8176          * @param {Boolean} local True to get the local css position instead of page coordinate
8177          * @return {Number}
8178          */
8179         getBottom : function(local){
8180             if(!local){
8181                 return this.getY() + this.getHeight();
8182             }else{
8183                 return (this.getTop(true) + this.getHeight()) || 0;
8184             }
8185         },
8186
8187         /**
8188         * Initializes positioning on this element. If a desired position is not passed, it will make the
8189         * the element positioned relative IF it is not already positioned.
8190         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8191         * @param {Number} zIndex (optional) The zIndex to apply
8192         * @param {Number} x (optional) Set the page X position
8193         * @param {Number} y (optional) Set the page Y position
8194         */
8195         position : function(pos, zIndex, x, y){
8196             if(!pos){
8197                if(this.getStyle('position') == 'static'){
8198                    this.setStyle('position', 'relative');
8199                }
8200             }else{
8201                 this.setStyle("position", pos);
8202             }
8203             if(zIndex){
8204                 this.setStyle("z-index", zIndex);
8205             }
8206             if(x !== undefined && y !== undefined){
8207                 this.setXY([x, y]);
8208             }else if(x !== undefined){
8209                 this.setX(x);
8210             }else if(y !== undefined){
8211                 this.setY(y);
8212             }
8213         },
8214
8215         /**
8216         * Clear positioning back to the default when the document was loaded
8217         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8218         * @return {Roo.Element} this
8219          */
8220         clearPositioning : function(value){
8221             value = value ||'';
8222             this.setStyle({
8223                 "left": value,
8224                 "right": value,
8225                 "top": value,
8226                 "bottom": value,
8227                 "z-index": "",
8228                 "position" : "static"
8229             });
8230             return this;
8231         },
8232
8233         /**
8234         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8235         * snapshot before performing an update and then restoring the element.
8236         * @return {Object}
8237         */
8238         getPositioning : function(){
8239             var l = this.getStyle("left");
8240             var t = this.getStyle("top");
8241             return {
8242                 "position" : this.getStyle("position"),
8243                 "left" : l,
8244                 "right" : l ? "" : this.getStyle("right"),
8245                 "top" : t,
8246                 "bottom" : t ? "" : this.getStyle("bottom"),
8247                 "z-index" : this.getStyle("z-index")
8248             };
8249         },
8250
8251         /**
8252          * Gets the width of the border(s) for the specified side(s)
8253          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8254          * passing lr would get the border (l)eft width + the border (r)ight width.
8255          * @return {Number} The width of the sides passed added together
8256          */
8257         getBorderWidth : function(side){
8258             return this.addStyles(side, El.borders);
8259         },
8260
8261         /**
8262          * Gets the width of the padding(s) for the specified side(s)
8263          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8264          * passing lr would get the padding (l)eft + the padding (r)ight.
8265          * @return {Number} The padding of the sides passed added together
8266          */
8267         getPadding : function(side){
8268             return this.addStyles(side, El.paddings);
8269         },
8270
8271         /**
8272         * Set positioning with an object returned by getPositioning().
8273         * @param {Object} posCfg
8274         * @return {Roo.Element} this
8275          */
8276         setPositioning : function(pc){
8277             this.applyStyles(pc);
8278             if(pc.right == "auto"){
8279                 this.dom.style.right = "";
8280             }
8281             if(pc.bottom == "auto"){
8282                 this.dom.style.bottom = "";
8283             }
8284             return this;
8285         },
8286
8287         // private
8288         fixDisplay : function(){
8289             if(this.getStyle("display") == "none"){
8290                 this.setStyle("visibility", "hidden");
8291                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8292                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8293                     this.setStyle("display", "block");
8294                 }
8295             }
8296         },
8297
8298         /**
8299          * Quick set left and top adding default units
8300          * @param {String} left The left CSS property value
8301          * @param {String} top The top CSS property value
8302          * @return {Roo.Element} this
8303          */
8304          setLeftTop : function(left, top){
8305             this.dom.style.left = this.addUnits(left);
8306             this.dom.style.top = this.addUnits(top);
8307             return this;
8308         },
8309
8310         /**
8311          * Move this element relative to its current position.
8312          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8313          * @param {Number} distance How far to move the element in pixels
8314          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8315          * @return {Roo.Element} this
8316          */
8317          move : function(direction, distance, animate){
8318             var xy = this.getXY();
8319             direction = direction.toLowerCase();
8320             switch(direction){
8321                 case "l":
8322                 case "left":
8323                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8324                     break;
8325                case "r":
8326                case "right":
8327                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8328                     break;
8329                case "t":
8330                case "top":
8331                case "up":
8332                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8333                     break;
8334                case "b":
8335                case "bottom":
8336                case "down":
8337                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8338                     break;
8339             }
8340             return this;
8341         },
8342
8343         /**
8344          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8345          * @return {Roo.Element} this
8346          */
8347         clip : function(){
8348             if(!this.isClipped){
8349                this.isClipped = true;
8350                this.originalClip = {
8351                    "o": this.getStyle("overflow"),
8352                    "x": this.getStyle("overflow-x"),
8353                    "y": this.getStyle("overflow-y")
8354                };
8355                this.setStyle("overflow", "hidden");
8356                this.setStyle("overflow-x", "hidden");
8357                this.setStyle("overflow-y", "hidden");
8358             }
8359             return this;
8360         },
8361
8362         /**
8363          *  Return clipping (overflow) to original clipping before clip() was called
8364          * @return {Roo.Element} this
8365          */
8366         unclip : function(){
8367             if(this.isClipped){
8368                 this.isClipped = false;
8369                 var o = this.originalClip;
8370                 if(o.o){this.setStyle("overflow", o.o);}
8371                 if(o.x){this.setStyle("overflow-x", o.x);}
8372                 if(o.y){this.setStyle("overflow-y", o.y);}
8373             }
8374             return this;
8375         },
8376
8377
8378         /**
8379          * Gets the x,y coordinates specified by the anchor position on the element.
8380          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8381          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8382          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8383          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8384          * @return {Array} [x, y] An array containing the element's x and y coordinates
8385          */
8386         getAnchorXY : function(anchor, local, s){
8387             //Passing a different size is useful for pre-calculating anchors,
8388             //especially for anchored animations that change the el size.
8389
8390             var w, h, vp = false;
8391             if(!s){
8392                 var d = this.dom;
8393                 if(d == document.body || d == document){
8394                     vp = true;
8395                     w = D.getViewWidth(); h = D.getViewHeight();
8396                 }else{
8397                     w = this.getWidth(); h = this.getHeight();
8398                 }
8399             }else{
8400                 w = s.width;  h = s.height;
8401             }
8402             var x = 0, y = 0, r = Math.round;
8403             switch((anchor || "tl").toLowerCase()){
8404                 case "c":
8405                     x = r(w*.5);
8406                     y = r(h*.5);
8407                 break;
8408                 case "t":
8409                     x = r(w*.5);
8410                     y = 0;
8411                 break;
8412                 case "l":
8413                     x = 0;
8414                     y = r(h*.5);
8415                 break;
8416                 case "r":
8417                     x = w;
8418                     y = r(h*.5);
8419                 break;
8420                 case "b":
8421                     x = r(w*.5);
8422                     y = h;
8423                 break;
8424                 case "tl":
8425                     x = 0;
8426                     y = 0;
8427                 break;
8428                 case "bl":
8429                     x = 0;
8430                     y = h;
8431                 break;
8432                 case "br":
8433                     x = w;
8434                     y = h;
8435                 break;
8436                 case "tr":
8437                     x = w;
8438                     y = 0;
8439                 break;
8440             }
8441             if(local === true){
8442                 return [x, y];
8443             }
8444             if(vp){
8445                 var sc = this.getScroll();
8446                 return [x + sc.left, y + sc.top];
8447             }
8448             //Add the element's offset xy
8449             var o = this.getXY();
8450             return [x+o[0], y+o[1]];
8451         },
8452
8453         /**
8454          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8455          * supported position values.
8456          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8457          * @param {String} position The position to align to.
8458          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8459          * @return {Array} [x, y]
8460          */
8461         getAlignToXY : function(el, p, o){
8462             el = Roo.get(el);
8463             var d = this.dom;
8464             if(!el.dom){
8465                 throw "Element.alignTo with an element that doesn't exist";
8466             }
8467             var c = false; //constrain to viewport
8468             var p1 = "", p2 = "";
8469             o = o || [0,0];
8470
8471             if(!p){
8472                 p = "tl-bl";
8473             }else if(p == "?"){
8474                 p = "tl-bl?";
8475             }else if(p.indexOf("-") == -1){
8476                 p = "tl-" + p;
8477             }
8478             p = p.toLowerCase();
8479             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8480             if(!m){
8481                throw "Element.alignTo with an invalid alignment " + p;
8482             }
8483             p1 = m[1]; p2 = m[2]; c = !!m[3];
8484
8485             //Subtract the aligned el's internal xy from the target's offset xy
8486             //plus custom offset to get the aligned el's new offset xy
8487             var a1 = this.getAnchorXY(p1, true);
8488             var a2 = el.getAnchorXY(p2, false);
8489             var x = a2[0] - a1[0] + o[0];
8490             var y = a2[1] - a1[1] + o[1];
8491             if(c){
8492                 //constrain the aligned el to viewport if necessary
8493                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8494                 // 5px of margin for ie
8495                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8496
8497                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8498                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8499                 //otherwise swap the aligned el to the opposite border of the target.
8500                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8501                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8502                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8503                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8504
8505                var doc = document;
8506                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8507                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8508
8509                if((x+w) > dw + scrollX){
8510                     x = swapX ? r.left-w : dw+scrollX-w;
8511                 }
8512                if(x < scrollX){
8513                    x = swapX ? r.right : scrollX;
8514                }
8515                if((y+h) > dh + scrollY){
8516                     y = swapY ? r.top-h : dh+scrollY-h;
8517                 }
8518                if (y < scrollY){
8519                    y = swapY ? r.bottom : scrollY;
8520                }
8521             }
8522             return [x,y];
8523         },
8524
8525         // private
8526         getConstrainToXY : function(){
8527             var os = {top:0, left:0, bottom:0, right: 0};
8528
8529             return function(el, local, offsets, proposedXY){
8530                 el = Roo.get(el);
8531                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8532
8533                 var vw, vh, vx = 0, vy = 0;
8534                 if(el.dom == document.body || el.dom == document){
8535                     vw = Roo.lib.Dom.getViewWidth();
8536                     vh = Roo.lib.Dom.getViewHeight();
8537                 }else{
8538                     vw = el.dom.clientWidth;
8539                     vh = el.dom.clientHeight;
8540                     if(!local){
8541                         var vxy = el.getXY();
8542                         vx = vxy[0];
8543                         vy = vxy[1];
8544                     }
8545                 }
8546
8547                 var s = el.getScroll();
8548
8549                 vx += offsets.left + s.left;
8550                 vy += offsets.top + s.top;
8551
8552                 vw -= offsets.right;
8553                 vh -= offsets.bottom;
8554
8555                 var vr = vx+vw;
8556                 var vb = vy+vh;
8557
8558                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8559                 var x = xy[0], y = xy[1];
8560                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8561
8562                 // only move it if it needs it
8563                 var moved = false;
8564
8565                 // first validate right/bottom
8566                 if((x + w) > vr){
8567                     x = vr - w;
8568                     moved = true;
8569                 }
8570                 if((y + h) > vb){
8571                     y = vb - h;
8572                     moved = true;
8573                 }
8574                 // then make sure top/left isn't negative
8575                 if(x < vx){
8576                     x = vx;
8577                     moved = true;
8578                 }
8579                 if(y < vy){
8580                     y = vy;
8581                     moved = true;
8582                 }
8583                 return moved ? [x, y] : false;
8584             };
8585         }(),
8586
8587         // private
8588         adjustForConstraints : function(xy, parent, offsets){
8589             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8590         },
8591
8592         /**
8593          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8594          * document it aligns it to the viewport.
8595          * The position parameter is optional, and can be specified in any one of the following formats:
8596          * <ul>
8597          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8598          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8599          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8600          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8601          *   <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
8602          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8603          * </ul>
8604          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8605          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8606          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8607          * that specified in order to enforce the viewport constraints.
8608          * Following are all of the supported anchor positions:
8609     <pre>
8610     Value  Description
8611     -----  -----------------------------
8612     tl     The top left corner (default)
8613     t      The center of the top edge
8614     tr     The top right corner
8615     l      The center of the left edge
8616     c      In the center of the element
8617     r      The center of the right edge
8618     bl     The bottom left corner
8619     b      The center of the bottom edge
8620     br     The bottom right corner
8621     </pre>
8622     Example Usage:
8623     <pre><code>
8624     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8625     el.alignTo("other-el");
8626
8627     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8628     el.alignTo("other-el", "tr?");
8629
8630     // align the bottom right corner of el with the center left edge of other-el
8631     el.alignTo("other-el", "br-l?");
8632
8633     // align the center of el with the bottom left corner of other-el and
8634     // adjust the x position by -6 pixels (and the y position by 0)
8635     el.alignTo("other-el", "c-bl", [-6, 0]);
8636     </code></pre>
8637          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8638          * @param {String} position The position to align to.
8639          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8640          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8641          * @return {Roo.Element} this
8642          */
8643         alignTo : function(element, position, offsets, animate){
8644             var xy = this.getAlignToXY(element, position, offsets);
8645             this.setXY(xy, this.preanim(arguments, 3));
8646             return this;
8647         },
8648
8649         /**
8650          * Anchors an element to another element and realigns it when the window is resized.
8651          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8652          * @param {String} position The position to align to.
8653          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8654          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8655          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8656          * is a number, it is used as the buffer delay (defaults to 50ms).
8657          * @param {Function} callback The function to call after the animation finishes
8658          * @return {Roo.Element} this
8659          */
8660         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8661             var action = function(){
8662                 this.alignTo(el, alignment, offsets, animate);
8663                 Roo.callback(callback, this);
8664             };
8665             Roo.EventManager.onWindowResize(action, this);
8666             var tm = typeof monitorScroll;
8667             if(tm != 'undefined'){
8668                 Roo.EventManager.on(window, 'scroll', action, this,
8669                     {buffer: tm == 'number' ? monitorScroll : 50});
8670             }
8671             action.call(this); // align immediately
8672             return this;
8673         },
8674         /**
8675          * Clears any opacity settings from this element. Required in some cases for IE.
8676          * @return {Roo.Element} this
8677          */
8678         clearOpacity : function(){
8679             if (window.ActiveXObject) {
8680                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8681                     this.dom.style.filter = "";
8682                 }
8683             } else {
8684                 this.dom.style.opacity = "";
8685                 this.dom.style["-moz-opacity"] = "";
8686                 this.dom.style["-khtml-opacity"] = "";
8687             }
8688             return this;
8689         },
8690
8691         /**
8692          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8693          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8694          * @return {Roo.Element} this
8695          */
8696         hide : function(animate){
8697             this.setVisible(false, this.preanim(arguments, 0));
8698             return this;
8699         },
8700
8701         /**
8702         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8703         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8704          * @return {Roo.Element} this
8705          */
8706         show : function(animate){
8707             this.setVisible(true, this.preanim(arguments, 0));
8708             return this;
8709         },
8710
8711         /**
8712          * @private Test if size has a unit, otherwise appends the default
8713          */
8714         addUnits : function(size){
8715             return Roo.Element.addUnits(size, this.defaultUnit);
8716         },
8717
8718         /**
8719          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8720          * @return {Roo.Element} this
8721          */
8722         beginMeasure : function(){
8723             var el = this.dom;
8724             if(el.offsetWidth || el.offsetHeight){
8725                 return this; // offsets work already
8726             }
8727             var changed = [];
8728             var p = this.dom, b = document.body; // start with this element
8729             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8730                 var pe = Roo.get(p);
8731                 if(pe.getStyle('display') == 'none'){
8732                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8733                     p.style.visibility = "hidden";
8734                     p.style.display = "block";
8735                 }
8736                 p = p.parentNode;
8737             }
8738             this._measureChanged = changed;
8739             return this;
8740
8741         },
8742
8743         /**
8744          * Restores displays to before beginMeasure was called
8745          * @return {Roo.Element} this
8746          */
8747         endMeasure : function(){
8748             var changed = this._measureChanged;
8749             if(changed){
8750                 for(var i = 0, len = changed.length; i < len; i++) {
8751                     var r = changed[i];
8752                     r.el.style.visibility = r.visibility;
8753                     r.el.style.display = "none";
8754                 }
8755                 this._measureChanged = null;
8756             }
8757             return this;
8758         },
8759
8760         /**
8761         * Update the innerHTML of this element, optionally searching for and processing scripts
8762         * @param {String} html The new HTML
8763         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8764         * @param {Function} callback For async script loading you can be noticed when the update completes
8765         * @return {Roo.Element} this
8766          */
8767         update : function(html, loadScripts, callback){
8768             if(typeof html == "undefined"){
8769                 html = "";
8770             }
8771             if(loadScripts !== true){
8772                 this.dom.innerHTML = html;
8773                 if(typeof callback == "function"){
8774                     callback();
8775                 }
8776                 return this;
8777             }
8778             var id = Roo.id();
8779             var dom = this.dom;
8780
8781             html += '<span id="' + id + '"></span>';
8782
8783             E.onAvailable(id, function(){
8784                 var hd = document.getElementsByTagName("head")[0];
8785                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8786                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8787                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8788
8789                 var match;
8790                 while(match = re.exec(html)){
8791                     var attrs = match[1];
8792                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8793                     if(srcMatch && srcMatch[2]){
8794                        var s = document.createElement("script");
8795                        s.src = srcMatch[2];
8796                        var typeMatch = attrs.match(typeRe);
8797                        if(typeMatch && typeMatch[2]){
8798                            s.type = typeMatch[2];
8799                        }
8800                        hd.appendChild(s);
8801                     }else if(match[2] && match[2].length > 0){
8802                         if(window.execScript) {
8803                            window.execScript(match[2]);
8804                         } else {
8805                             /**
8806                              * eval:var:id
8807                              * eval:var:dom
8808                              * eval:var:html
8809                              * 
8810                              */
8811                            window.eval(match[2]);
8812                         }
8813                     }
8814                 }
8815                 var el = document.getElementById(id);
8816                 if(el){el.parentNode.removeChild(el);}
8817                 if(typeof callback == "function"){
8818                     callback();
8819                 }
8820             });
8821             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8822             return this;
8823         },
8824
8825         /**
8826          * Direct access to the UpdateManager update() method (takes the same parameters).
8827          * @param {String/Function} url The url for this request or a function to call to get the url
8828          * @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}
8829          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8830          * @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.
8831          * @return {Roo.Element} this
8832          */
8833         load : function(){
8834             var um = this.getUpdateManager();
8835             um.update.apply(um, arguments);
8836             return this;
8837         },
8838
8839         /**
8840         * Gets this element's UpdateManager
8841         * @return {Roo.UpdateManager} The UpdateManager
8842         */
8843         getUpdateManager : function(){
8844             if(!this.updateManager){
8845                 this.updateManager = new Roo.UpdateManager(this);
8846             }
8847             return this.updateManager;
8848         },
8849
8850         /**
8851          * Disables text selection for this element (normalized across browsers)
8852          * @return {Roo.Element} this
8853          */
8854         unselectable : function(){
8855             this.dom.unselectable = "on";
8856             this.swallowEvent("selectstart", true);
8857             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8858             this.addClass("x-unselectable");
8859             return this;
8860         },
8861
8862         /**
8863         * Calculates the x, y to center this element on the screen
8864         * @return {Array} The x, y values [x, y]
8865         */
8866         getCenterXY : function(){
8867             return this.getAlignToXY(document, 'c-c');
8868         },
8869
8870         /**
8871         * Centers the Element in either the viewport, or another Element.
8872         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8873         */
8874         center : function(centerIn){
8875             this.alignTo(centerIn || document, 'c-c');
8876             return this;
8877         },
8878
8879         /**
8880          * Tests various css rules/browsers to determine if this element uses a border box
8881          * @return {Boolean}
8882          */
8883         isBorderBox : function(){
8884             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8885         },
8886
8887         /**
8888          * Return a box {x, y, width, height} that can be used to set another elements
8889          * size/location to match this element.
8890          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8891          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8892          * @return {Object} box An object in the format {x, y, width, height}
8893          */
8894         getBox : function(contentBox, local){
8895             var xy;
8896             if(!local){
8897                 xy = this.getXY();
8898             }else{
8899                 var left = parseInt(this.getStyle("left"), 10) || 0;
8900                 var top = parseInt(this.getStyle("top"), 10) || 0;
8901                 xy = [left, top];
8902             }
8903             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8904             if(!contentBox){
8905                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8906             }else{
8907                 var l = this.getBorderWidth("l")+this.getPadding("l");
8908                 var r = this.getBorderWidth("r")+this.getPadding("r");
8909                 var t = this.getBorderWidth("t")+this.getPadding("t");
8910                 var b = this.getBorderWidth("b")+this.getPadding("b");
8911                 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)};
8912             }
8913             bx.right = bx.x + bx.width;
8914             bx.bottom = bx.y + bx.height;
8915             return bx;
8916         },
8917
8918         /**
8919          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8920          for more information about the sides.
8921          * @param {String} sides
8922          * @return {Number}
8923          */
8924         getFrameWidth : function(sides, onlyContentBox){
8925             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8926         },
8927
8928         /**
8929          * 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.
8930          * @param {Object} box The box to fill {x, y, width, height}
8931          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8932          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8933          * @return {Roo.Element} this
8934          */
8935         setBox : function(box, adjust, animate){
8936             var w = box.width, h = box.height;
8937             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8938                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8939                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8940             }
8941             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8942             return this;
8943         },
8944
8945         /**
8946          * Forces the browser to repaint this element
8947          * @return {Roo.Element} this
8948          */
8949          repaint : function(){
8950             var dom = this.dom;
8951             this.addClass("x-repaint");
8952             setTimeout(function(){
8953                 Roo.get(dom).removeClass("x-repaint");
8954             }, 1);
8955             return this;
8956         },
8957
8958         /**
8959          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8960          * then it returns the calculated width of the sides (see getPadding)
8961          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8962          * @return {Object/Number}
8963          */
8964         getMargins : function(side){
8965             if(!side){
8966                 return {
8967                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8968                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8969                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8970                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8971                 };
8972             }else{
8973                 return this.addStyles(side, El.margins);
8974              }
8975         },
8976
8977         // private
8978         addStyles : function(sides, styles){
8979             var val = 0, v, w;
8980             for(var i = 0, len = sides.length; i < len; i++){
8981                 v = this.getStyle(styles[sides.charAt(i)]);
8982                 if(v){
8983                      w = parseInt(v, 10);
8984                      if(w){ val += w; }
8985                 }
8986             }
8987             return val;
8988         },
8989
8990         /**
8991          * Creates a proxy element of this element
8992          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8993          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8994          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8995          * @return {Roo.Element} The new proxy element
8996          */
8997         createProxy : function(config, renderTo, matchBox){
8998             if(renderTo){
8999                 renderTo = Roo.getDom(renderTo);
9000             }else{
9001                 renderTo = document.body;
9002             }
9003             config = typeof config == "object" ?
9004                 config : {tag : "div", cls: config};
9005             var proxy = Roo.DomHelper.append(renderTo, config, true);
9006             if(matchBox){
9007                proxy.setBox(this.getBox());
9008             }
9009             return proxy;
9010         },
9011
9012         /**
9013          * Puts a mask over this element to disable user interaction. Requires core.css.
9014          * This method can only be applied to elements which accept child nodes.
9015          * @param {String} msg (optional) A message to display in the mask
9016          * @param {String} msgCls (optional) A css class to apply to the msg element
9017          * @return {Element} The mask  element
9018          */
9019         mask : function(msg, msgCls)
9020         {
9021             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9022                 this.setStyle("position", "relative");
9023             }
9024             if(!this._mask){
9025                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9026             }
9027             this.addClass("x-masked");
9028             this._mask.setDisplayed(true);
9029             
9030             // we wander
9031             var z = 0;
9032             var dom = this.dom
9033             while (dom && dom.style) {
9034                 if (!isNaN(parseInt(dom.style.zIndex))) {
9035                     z = Math.max(z, parseInt(dom.style.zIndex));
9036                 }
9037                 dom = dom.parentNode;
9038             }
9039             // if we are masking the body - then it hides everything..
9040             if (this.dom == document.body) {
9041                 z = 1000000;
9042                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9043                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9044             }
9045            
9046             if(typeof msg == 'string'){
9047                 if(!this._maskMsg){
9048                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9049                 }
9050                 var mm = this._maskMsg;
9051                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9052                 if (mm.dom.firstChild) { // weird IE issue?
9053                     mm.dom.firstChild.innerHTML = msg;
9054                 }
9055                 mm.setDisplayed(true);
9056                 mm.center(this);
9057                 mm.setStyle('z-index', z + 102);
9058             }
9059             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9060                 this._mask.setHeight(this.getHeight());
9061             }
9062             this._mask.setStyle('z-index', z + 100);
9063             
9064             return this._mask;
9065         },
9066
9067         /**
9068          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9069          * it is cached for reuse.
9070          */
9071         unmask : function(removeEl){
9072             if(this._mask){
9073                 if(removeEl === true){
9074                     this._mask.remove();
9075                     delete this._mask;
9076                     if(this._maskMsg){
9077                         this._maskMsg.remove();
9078                         delete this._maskMsg;
9079                     }
9080                 }else{
9081                     this._mask.setDisplayed(false);
9082                     if(this._maskMsg){
9083                         this._maskMsg.setDisplayed(false);
9084                     }
9085                 }
9086             }
9087             this.removeClass("x-masked");
9088         },
9089
9090         /**
9091          * Returns true if this element is masked
9092          * @return {Boolean}
9093          */
9094         isMasked : function(){
9095             return this._mask && this._mask.isVisible();
9096         },
9097
9098         /**
9099          * Creates an iframe shim for this element to keep selects and other windowed objects from
9100          * showing through.
9101          * @return {Roo.Element} The new shim element
9102          */
9103         createShim : function(){
9104             var el = document.createElement('iframe');
9105             el.frameBorder = 'no';
9106             el.className = 'roo-shim';
9107             if(Roo.isIE && Roo.isSecure){
9108                 el.src = Roo.SSL_SECURE_URL;
9109             }
9110             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9111             shim.autoBoxAdjust = false;
9112             return shim;
9113         },
9114
9115         /**
9116          * Removes this element from the DOM and deletes it from the cache
9117          */
9118         remove : function(){
9119             if(this.dom.parentNode){
9120                 this.dom.parentNode.removeChild(this.dom);
9121             }
9122             delete El.cache[this.dom.id];
9123         },
9124
9125         /**
9126          * Sets up event handlers to add and remove a css class when the mouse is over this element
9127          * @param {String} className
9128          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9129          * mouseout events for children elements
9130          * @return {Roo.Element} this
9131          */
9132         addClassOnOver : function(className, preventFlicker){
9133             this.on("mouseover", function(){
9134                 Roo.fly(this, '_internal').addClass(className);
9135             }, this.dom);
9136             var removeFn = function(e){
9137                 if(preventFlicker !== true || !e.within(this, true)){
9138                     Roo.fly(this, '_internal').removeClass(className);
9139                 }
9140             };
9141             this.on("mouseout", removeFn, this.dom);
9142             return this;
9143         },
9144
9145         /**
9146          * Sets up event handlers to add and remove a css class when this element has the focus
9147          * @param {String} className
9148          * @return {Roo.Element} this
9149          */
9150         addClassOnFocus : function(className){
9151             this.on("focus", function(){
9152                 Roo.fly(this, '_internal').addClass(className);
9153             }, this.dom);
9154             this.on("blur", function(){
9155                 Roo.fly(this, '_internal').removeClass(className);
9156             }, this.dom);
9157             return this;
9158         },
9159         /**
9160          * 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)
9161          * @param {String} className
9162          * @return {Roo.Element} this
9163          */
9164         addClassOnClick : function(className){
9165             var dom = this.dom;
9166             this.on("mousedown", function(){
9167                 Roo.fly(dom, '_internal').addClass(className);
9168                 var d = Roo.get(document);
9169                 var fn = function(){
9170                     Roo.fly(dom, '_internal').removeClass(className);
9171                     d.removeListener("mouseup", fn);
9172                 };
9173                 d.on("mouseup", fn);
9174             });
9175             return this;
9176         },
9177
9178         /**
9179          * Stops the specified event from bubbling and optionally prevents the default action
9180          * @param {String} eventName
9181          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9182          * @return {Roo.Element} this
9183          */
9184         swallowEvent : function(eventName, preventDefault){
9185             var fn = function(e){
9186                 e.stopPropagation();
9187                 if(preventDefault){
9188                     e.preventDefault();
9189                 }
9190             };
9191             if(eventName instanceof Array){
9192                 for(var i = 0, len = eventName.length; i < len; i++){
9193                      this.on(eventName[i], fn);
9194                 }
9195                 return this;
9196             }
9197             this.on(eventName, fn);
9198             return this;
9199         },
9200
9201         /**
9202          * @private
9203          */
9204       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9205
9206         /**
9207          * Sizes this element to its parent element's dimensions performing
9208          * neccessary box adjustments.
9209          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9210          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9211          * @return {Roo.Element} this
9212          */
9213         fitToParent : function(monitorResize, targetParent) {
9214           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9215           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9216           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9217             return;
9218           }
9219           var p = Roo.get(targetParent || this.dom.parentNode);
9220           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9221           if (monitorResize === true) {
9222             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9223             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9224           }
9225           return this;
9226         },
9227
9228         /**
9229          * Gets the next sibling, skipping text nodes
9230          * @return {HTMLElement} The next sibling or null
9231          */
9232         getNextSibling : function(){
9233             var n = this.dom.nextSibling;
9234             while(n && n.nodeType != 1){
9235                 n = n.nextSibling;
9236             }
9237             return n;
9238         },
9239
9240         /**
9241          * Gets the previous sibling, skipping text nodes
9242          * @return {HTMLElement} The previous sibling or null
9243          */
9244         getPrevSibling : function(){
9245             var n = this.dom.previousSibling;
9246             while(n && n.nodeType != 1){
9247                 n = n.previousSibling;
9248             }
9249             return n;
9250         },
9251
9252
9253         /**
9254          * Appends the passed element(s) to this element
9255          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9256          * @return {Roo.Element} this
9257          */
9258         appendChild: function(el){
9259             el = Roo.get(el);
9260             el.appendTo(this);
9261             return this;
9262         },
9263
9264         /**
9265          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9266          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9267          * automatically generated with the specified attributes.
9268          * @param {HTMLElement} insertBefore (optional) a child element of this element
9269          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9270          * @return {Roo.Element} The new child element
9271          */
9272         createChild: function(config, insertBefore, returnDom){
9273             config = config || {tag:'div'};
9274             if(insertBefore){
9275                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9276             }
9277             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9278         },
9279
9280         /**
9281          * Appends this element to the passed element
9282          * @param {String/HTMLElement/Element} el The new parent element
9283          * @return {Roo.Element} this
9284          */
9285         appendTo: function(el){
9286             el = Roo.getDom(el);
9287             el.appendChild(this.dom);
9288             return this;
9289         },
9290
9291         /**
9292          * Inserts this element before the passed element in the DOM
9293          * @param {String/HTMLElement/Element} el The element to insert before
9294          * @return {Roo.Element} this
9295          */
9296         insertBefore: function(el){
9297             el = Roo.getDom(el);
9298             el.parentNode.insertBefore(this.dom, el);
9299             return this;
9300         },
9301
9302         /**
9303          * Inserts this element after the passed element in the DOM
9304          * @param {String/HTMLElement/Element} el The element to insert after
9305          * @return {Roo.Element} this
9306          */
9307         insertAfter: function(el){
9308             el = Roo.getDom(el);
9309             el.parentNode.insertBefore(this.dom, el.nextSibling);
9310             return this;
9311         },
9312
9313         /**
9314          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9315          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9316          * @return {Roo.Element} The new child
9317          */
9318         insertFirst: function(el, returnDom){
9319             el = el || {};
9320             if(typeof el == 'object' && !el.nodeType){ // dh config
9321                 return this.createChild(el, this.dom.firstChild, returnDom);
9322             }else{
9323                 el = Roo.getDom(el);
9324                 this.dom.insertBefore(el, this.dom.firstChild);
9325                 return !returnDom ? Roo.get(el) : el;
9326             }
9327         },
9328
9329         /**
9330          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9331          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9332          * @param {String} where (optional) 'before' or 'after' defaults to before
9333          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9334          * @return {Roo.Element} the inserted Element
9335          */
9336         insertSibling: function(el, where, returnDom){
9337             where = where ? where.toLowerCase() : 'before';
9338             el = el || {};
9339             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9340
9341             if(typeof el == 'object' && !el.nodeType){ // dh config
9342                 if(where == 'after' && !this.dom.nextSibling){
9343                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9344                 }else{
9345                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9346                 }
9347
9348             }else{
9349                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9350                             where == 'before' ? this.dom : this.dom.nextSibling);
9351                 if(!returnDom){
9352                     rt = Roo.get(rt);
9353                 }
9354             }
9355             return rt;
9356         },
9357
9358         /**
9359          * Creates and wraps this element with another element
9360          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9361          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9362          * @return {HTMLElement/Element} The newly created wrapper element
9363          */
9364         wrap: function(config, returnDom){
9365             if(!config){
9366                 config = {tag: "div"};
9367             }
9368             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9369             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9370             return newEl;
9371         },
9372
9373         /**
9374          * Replaces the passed element with this element
9375          * @param {String/HTMLElement/Element} el The element to replace
9376          * @return {Roo.Element} this
9377          */
9378         replace: function(el){
9379             el = Roo.get(el);
9380             this.insertBefore(el);
9381             el.remove();
9382             return this;
9383         },
9384
9385         /**
9386          * Inserts an html fragment into this element
9387          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9388          * @param {String} html The HTML fragment
9389          * @param {Boolean} returnEl True to return an Roo.Element
9390          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9391          */
9392         insertHtml : function(where, html, returnEl){
9393             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9394             return returnEl ? Roo.get(el) : el;
9395         },
9396
9397         /**
9398          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9399          * @param {Object} o The object with the attributes
9400          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9401          * @return {Roo.Element} this
9402          */
9403         set : function(o, useSet){
9404             var el = this.dom;
9405             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9406             for(var attr in o){
9407                 if(attr == "style" || typeof o[attr] == "function") continue;
9408                 if(attr=="cls"){
9409                     el.className = o["cls"];
9410                 }else{
9411                     if(useSet) el.setAttribute(attr, o[attr]);
9412                     else el[attr] = o[attr];
9413                 }
9414             }
9415             if(o.style){
9416                 Roo.DomHelper.applyStyles(el, o.style);
9417             }
9418             return this;
9419         },
9420
9421         /**
9422          * Convenience method for constructing a KeyMap
9423          * @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:
9424          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9425          * @param {Function} fn The function to call
9426          * @param {Object} scope (optional) The scope of the function
9427          * @return {Roo.KeyMap} The KeyMap created
9428          */
9429         addKeyListener : function(key, fn, scope){
9430             var config;
9431             if(typeof key != "object" || key instanceof Array){
9432                 config = {
9433                     key: key,
9434                     fn: fn,
9435                     scope: scope
9436                 };
9437             }else{
9438                 config = {
9439                     key : key.key,
9440                     shift : key.shift,
9441                     ctrl : key.ctrl,
9442                     alt : key.alt,
9443                     fn: fn,
9444                     scope: scope
9445                 };
9446             }
9447             return new Roo.KeyMap(this, config);
9448         },
9449
9450         /**
9451          * Creates a KeyMap for this element
9452          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9453          * @return {Roo.KeyMap} The KeyMap created
9454          */
9455         addKeyMap : function(config){
9456             return new Roo.KeyMap(this, config);
9457         },
9458
9459         /**
9460          * Returns true if this element is scrollable.
9461          * @return {Boolean}
9462          */
9463          isScrollable : function(){
9464             var dom = this.dom;
9465             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9466         },
9467
9468         /**
9469          * 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().
9470          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9471          * @param {Number} value The new scroll value
9472          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9473          * @return {Element} this
9474          */
9475
9476         scrollTo : function(side, value, animate){
9477             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9478             if(!animate || !A){
9479                 this.dom[prop] = value;
9480             }else{
9481                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9482                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9483             }
9484             return this;
9485         },
9486
9487         /**
9488          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9489          * within this element's scrollable range.
9490          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9491          * @param {Number} distance How far to scroll the element in pixels
9492          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9493          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9494          * was scrolled as far as it could go.
9495          */
9496          scroll : function(direction, distance, animate){
9497              if(!this.isScrollable()){
9498                  return;
9499              }
9500              var el = this.dom;
9501              var l = el.scrollLeft, t = el.scrollTop;
9502              var w = el.scrollWidth, h = el.scrollHeight;
9503              var cw = el.clientWidth, ch = el.clientHeight;
9504              direction = direction.toLowerCase();
9505              var scrolled = false;
9506              var a = this.preanim(arguments, 2);
9507              switch(direction){
9508                  case "l":
9509                  case "left":
9510                      if(w - l > cw){
9511                          var v = Math.min(l + distance, w-cw);
9512                          this.scrollTo("left", v, a);
9513                          scrolled = true;
9514                      }
9515                      break;
9516                 case "r":
9517                 case "right":
9518                      if(l > 0){
9519                          var v = Math.max(l - distance, 0);
9520                          this.scrollTo("left", v, a);
9521                          scrolled = true;
9522                      }
9523                      break;
9524                 case "t":
9525                 case "top":
9526                 case "up":
9527                      if(t > 0){
9528                          var v = Math.max(t - distance, 0);
9529                          this.scrollTo("top", v, a);
9530                          scrolled = true;
9531                      }
9532                      break;
9533                 case "b":
9534                 case "bottom":
9535                 case "down":
9536                      if(h - t > ch){
9537                          var v = Math.min(t + distance, h-ch);
9538                          this.scrollTo("top", v, a);
9539                          scrolled = true;
9540                      }
9541                      break;
9542              }
9543              return scrolled;
9544         },
9545
9546         /**
9547          * Translates the passed page coordinates into left/top css values for this element
9548          * @param {Number/Array} x The page x or an array containing [x, y]
9549          * @param {Number} y The page y
9550          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9551          */
9552         translatePoints : function(x, y){
9553             if(typeof x == 'object' || x instanceof Array){
9554                 y = x[1]; x = x[0];
9555             }
9556             var p = this.getStyle('position');
9557             var o = this.getXY();
9558
9559             var l = parseInt(this.getStyle('left'), 10);
9560             var t = parseInt(this.getStyle('top'), 10);
9561
9562             if(isNaN(l)){
9563                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9564             }
9565             if(isNaN(t)){
9566                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9567             }
9568
9569             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9570         },
9571
9572         /**
9573          * Returns the current scroll position of the element.
9574          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9575          */
9576         getScroll : function(){
9577             var d = this.dom, doc = document;
9578             if(d == doc || d == doc.body){
9579                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9580                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9581                 return {left: l, top: t};
9582             }else{
9583                 return {left: d.scrollLeft, top: d.scrollTop};
9584             }
9585         },
9586
9587         /**
9588          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9589          * are convert to standard 6 digit hex color.
9590          * @param {String} attr The css attribute
9591          * @param {String} defaultValue The default value to use when a valid color isn't found
9592          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9593          * YUI color anims.
9594          */
9595         getColor : function(attr, defaultValue, prefix){
9596             var v = this.getStyle(attr);
9597             if(!v || v == "transparent" || v == "inherit") {
9598                 return defaultValue;
9599             }
9600             var color = typeof prefix == "undefined" ? "#" : prefix;
9601             if(v.substr(0, 4) == "rgb("){
9602                 var rvs = v.slice(4, v.length -1).split(",");
9603                 for(var i = 0; i < 3; i++){
9604                     var h = parseInt(rvs[i]).toString(16);
9605                     if(h < 16){
9606                         h = "0" + h;
9607                     }
9608                     color += h;
9609                 }
9610             } else {
9611                 if(v.substr(0, 1) == "#"){
9612                     if(v.length == 4) {
9613                         for(var i = 1; i < 4; i++){
9614                             var c = v.charAt(i);
9615                             color +=  c + c;
9616                         }
9617                     }else if(v.length == 7){
9618                         color += v.substr(1);
9619                     }
9620                 }
9621             }
9622             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9623         },
9624
9625         /**
9626          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9627          * gradient background, rounded corners and a 4-way shadow.
9628          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9629          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9630          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9631          * @return {Roo.Element} this
9632          */
9633         boxWrap : function(cls){
9634             cls = cls || 'x-box';
9635             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9636             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9637             return el;
9638         },
9639
9640         /**
9641          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9642          * @param {String} namespace The namespace in which to look for the attribute
9643          * @param {String} name The attribute name
9644          * @return {String} The attribute value
9645          */
9646         getAttributeNS : Roo.isIE ? function(ns, name){
9647             var d = this.dom;
9648             var type = typeof d[ns+":"+name];
9649             if(type != 'undefined' && type != 'unknown'){
9650                 return d[ns+":"+name];
9651             }
9652             return d[name];
9653         } : function(ns, name){
9654             var d = this.dom;
9655             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9656         },
9657         
9658         
9659         /**
9660          * Sets or Returns the value the dom attribute value
9661          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9662          * @param {String} value (optional) The value to set the attribute to
9663          * @return {String} The attribute value
9664          */
9665         attr : function(name){
9666             if (arguments.length > 1) {
9667                 this.dom.setAttribute(name, arguments[1]);
9668                 return arguments[1];
9669             }
9670             if (typeof(name) == 'object') {
9671                 for(var i in name) {
9672                     this.attr(i, name[i]);
9673                 }
9674                 return name;
9675             }
9676             
9677             
9678             if (!this.dom.hasAttribute(name)) {
9679                 return undefined;
9680             }
9681             return this.dom.getAttribute(name);
9682         }
9683         
9684         
9685         
9686     };
9687
9688     var ep = El.prototype;
9689
9690     /**
9691      * Appends an event handler (Shorthand for addListener)
9692      * @param {String}   eventName     The type of event to append
9693      * @param {Function} fn        The method the event invokes
9694      * @param {Object} scope       (optional) The scope (this object) of the fn
9695      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9696      * @method
9697      */
9698     ep.on = ep.addListener;
9699         // backwards compat
9700     ep.mon = ep.addListener;
9701
9702     /**
9703      * Removes an event handler from this element (shorthand for removeListener)
9704      * @param {String} eventName the type of event to remove
9705      * @param {Function} fn the method the event invokes
9706      * @return {Roo.Element} this
9707      * @method
9708      */
9709     ep.un = ep.removeListener;
9710
9711     /**
9712      * true to automatically adjust width and height settings for box-model issues (default to true)
9713      */
9714     ep.autoBoxAdjust = true;
9715
9716     // private
9717     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9718
9719     // private
9720     El.addUnits = function(v, defaultUnit){
9721         if(v === "" || v == "auto"){
9722             return v;
9723         }
9724         if(v === undefined){
9725             return '';
9726         }
9727         if(typeof v == "number" || !El.unitPattern.test(v)){
9728             return v + (defaultUnit || 'px');
9729         }
9730         return v;
9731     };
9732
9733     // special markup used throughout Roo when box wrapping elements
9734     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>';
9735     /**
9736      * Visibility mode constant - Use visibility to hide element
9737      * @static
9738      * @type Number
9739      */
9740     El.VISIBILITY = 1;
9741     /**
9742      * Visibility mode constant - Use display to hide element
9743      * @static
9744      * @type Number
9745      */
9746     El.DISPLAY = 2;
9747
9748     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9749     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9750     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9751
9752
9753
9754     /**
9755      * @private
9756      */
9757     El.cache = {};
9758
9759     var docEl;
9760
9761     /**
9762      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9763      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9764      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9765      * @return {Element} The Element object
9766      * @static
9767      */
9768     El.get = function(el){
9769         var ex, elm, id;
9770         if(!el){ return null; }
9771         if(typeof el == "string"){ // element id
9772             if(!(elm = document.getElementById(el))){
9773                 return null;
9774             }
9775             if(ex = El.cache[el]){
9776                 ex.dom = elm;
9777             }else{
9778                 ex = El.cache[el] = new El(elm);
9779             }
9780             return ex;
9781         }else if(el.tagName){ // dom element
9782             if(!(id = el.id)){
9783                 id = Roo.id(el);
9784             }
9785             if(ex = El.cache[id]){
9786                 ex.dom = el;
9787             }else{
9788                 ex = El.cache[id] = new El(el);
9789             }
9790             return ex;
9791         }else if(el instanceof El){
9792             if(el != docEl){
9793                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9794                                                               // catch case where it hasn't been appended
9795                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9796             }
9797             return el;
9798         }else if(el.isComposite){
9799             return el;
9800         }else if(el instanceof Array){
9801             return El.select(el);
9802         }else if(el == document){
9803             // create a bogus element object representing the document object
9804             if(!docEl){
9805                 var f = function(){};
9806                 f.prototype = El.prototype;
9807                 docEl = new f();
9808                 docEl.dom = document;
9809             }
9810             return docEl;
9811         }
9812         return null;
9813     };
9814
9815     // private
9816     El.uncache = function(el){
9817         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9818             if(a[i]){
9819                 delete El.cache[a[i].id || a[i]];
9820             }
9821         }
9822     };
9823
9824     // private
9825     // Garbage collection - uncache elements/purge listeners on orphaned elements
9826     // so we don't hold a reference and cause the browser to retain them
9827     El.garbageCollect = function(){
9828         if(!Roo.enableGarbageCollector){
9829             clearInterval(El.collectorThread);
9830             return;
9831         }
9832         for(var eid in El.cache){
9833             var el = El.cache[eid], d = el.dom;
9834             // -------------------------------------------------------
9835             // Determining what is garbage:
9836             // -------------------------------------------------------
9837             // !d
9838             // dom node is null, definitely garbage
9839             // -------------------------------------------------------
9840             // !d.parentNode
9841             // no parentNode == direct orphan, definitely garbage
9842             // -------------------------------------------------------
9843             // !d.offsetParent && !document.getElementById(eid)
9844             // display none elements have no offsetParent so we will
9845             // also try to look it up by it's id. However, check
9846             // offsetParent first so we don't do unneeded lookups.
9847             // This enables collection of elements that are not orphans
9848             // directly, but somewhere up the line they have an orphan
9849             // parent.
9850             // -------------------------------------------------------
9851             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9852                 delete El.cache[eid];
9853                 if(d && Roo.enableListenerCollection){
9854                     E.purgeElement(d);
9855                 }
9856             }
9857         }
9858     }
9859     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9860
9861
9862     // dom is optional
9863     El.Flyweight = function(dom){
9864         this.dom = dom;
9865     };
9866     El.Flyweight.prototype = El.prototype;
9867
9868     El._flyweights = {};
9869     /**
9870      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9871      * the dom node can be overwritten by other code.
9872      * @param {String/HTMLElement} el The dom node or id
9873      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9874      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9875      * @static
9876      * @return {Element} The shared Element object
9877      */
9878     El.fly = function(el, named){
9879         named = named || '_global';
9880         el = Roo.getDom(el);
9881         if(!el){
9882             return null;
9883         }
9884         if(!El._flyweights[named]){
9885             El._flyweights[named] = new El.Flyweight();
9886         }
9887         El._flyweights[named].dom = el;
9888         return El._flyweights[named];
9889     };
9890
9891     /**
9892      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9893      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9894      * Shorthand of {@link Roo.Element#get}
9895      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9896      * @return {Element} The Element object
9897      * @member Roo
9898      * @method get
9899      */
9900     Roo.get = El.get;
9901     /**
9902      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9903      * the dom node can be overwritten by other code.
9904      * Shorthand of {@link Roo.Element#fly}
9905      * @param {String/HTMLElement} el The dom node or id
9906      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9907      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9908      * @static
9909      * @return {Element} The shared Element object
9910      * @member Roo
9911      * @method fly
9912      */
9913     Roo.fly = El.fly;
9914
9915     // speedy lookup for elements never to box adjust
9916     var noBoxAdjust = Roo.isStrict ? {
9917         select:1
9918     } : {
9919         input:1, select:1, textarea:1
9920     };
9921     if(Roo.isIE || Roo.isGecko){
9922         noBoxAdjust['button'] = 1;
9923     }
9924
9925
9926     Roo.EventManager.on(window, 'unload', function(){
9927         delete El.cache;
9928         delete El._flyweights;
9929     });
9930 })();
9931
9932
9933
9934
9935 if(Roo.DomQuery){
9936     Roo.Element.selectorFunction = Roo.DomQuery.select;
9937 }
9938
9939 Roo.Element.select = function(selector, unique, root){
9940     var els;
9941     if(typeof selector == "string"){
9942         els = Roo.Element.selectorFunction(selector, root);
9943     }else if(selector.length !== undefined){
9944         els = selector;
9945     }else{
9946         throw "Invalid selector";
9947     }
9948     if(unique === true){
9949         return new Roo.CompositeElement(els);
9950     }else{
9951         return new Roo.CompositeElementLite(els);
9952     }
9953 };
9954 /**
9955  * Selects elements based on the passed CSS selector to enable working on them as 1.
9956  * @param {String/Array} selector The CSS selector or an array of elements
9957  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9958  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9959  * @return {CompositeElementLite/CompositeElement}
9960  * @member Roo
9961  * @method select
9962  */
9963 Roo.select = Roo.Element.select;
9964
9965
9966
9967
9968
9969
9970
9971
9972
9973
9974
9975
9976
9977
9978 /*
9979  * Based on:
9980  * Ext JS Library 1.1.1
9981  * Copyright(c) 2006-2007, Ext JS, LLC.
9982  *
9983  * Originally Released Under LGPL - original licence link has changed is not relivant.
9984  *
9985  * Fork - LGPL
9986  * <script type="text/javascript">
9987  */
9988
9989
9990
9991 //Notifies Element that fx methods are available
9992 Roo.enableFx = true;
9993
9994 /**
9995  * @class Roo.Fx
9996  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9997  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9998  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9999  * Element effects to work.</p><br/>
10000  *
10001  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10002  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10003  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10004  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10005  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10006  * expected results and should be done with care.</p><br/>
10007  *
10008  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10009  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10010 <pre>
10011 Value  Description
10012 -----  -----------------------------
10013 tl     The top left corner
10014 t      The center of the top edge
10015 tr     The top right corner
10016 l      The center of the left edge
10017 r      The center of the right edge
10018 bl     The bottom left corner
10019 b      The center of the bottom edge
10020 br     The bottom right corner
10021 </pre>
10022  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10023  * below are common options that can be passed to any Fx method.</b>
10024  * @cfg {Function} callback A function called when the effect is finished
10025  * @cfg {Object} scope The scope of the effect function
10026  * @cfg {String} easing A valid Easing value for the effect
10027  * @cfg {String} afterCls A css class to apply after the effect
10028  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10029  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10030  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10031  * effects that end with the element being visually hidden, ignored otherwise)
10032  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10033  * a function which returns such a specification that will be applied to the Element after the effect finishes
10034  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10035  * @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
10036  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10037  */
10038 Roo.Fx = {
10039         /**
10040          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10041          * origin for the slide effect.  This function automatically handles wrapping the element with
10042          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10043          * Usage:
10044          *<pre><code>
10045 // default: slide the element in from the top
10046 el.slideIn();
10047
10048 // custom: slide the element in from the right with a 2-second duration
10049 el.slideIn('r', { duration: 2 });
10050
10051 // common config options shown with default values
10052 el.slideIn('t', {
10053     easing: 'easeOut',
10054     duration: .5
10055 });
10056 </code></pre>
10057          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10058          * @param {Object} options (optional) Object literal with any of the Fx config options
10059          * @return {Roo.Element} The Element
10060          */
10061     slideIn : function(anchor, o){
10062         var el = this.getFxEl();
10063         o = o || {};
10064
10065         el.queueFx(o, function(){
10066
10067             anchor = anchor || "t";
10068
10069             // fix display to visibility
10070             this.fixDisplay();
10071
10072             // restore values after effect
10073             var r = this.getFxRestore();
10074             var b = this.getBox();
10075             // fixed size for slide
10076             this.setSize(b);
10077
10078             // wrap if needed
10079             var wrap = this.fxWrap(r.pos, o, "hidden");
10080
10081             var st = this.dom.style;
10082             st.visibility = "visible";
10083             st.position = "absolute";
10084
10085             // clear out temp styles after slide and unwrap
10086             var after = function(){
10087                 el.fxUnwrap(wrap, r.pos, o);
10088                 st.width = r.width;
10089                 st.height = r.height;
10090                 el.afterFx(o);
10091             };
10092             // time to calc the positions
10093             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10094
10095             switch(anchor.toLowerCase()){
10096                 case "t":
10097                     wrap.setSize(b.width, 0);
10098                     st.left = st.bottom = "0";
10099                     a = {height: bh};
10100                 break;
10101                 case "l":
10102                     wrap.setSize(0, b.height);
10103                     st.right = st.top = "0";
10104                     a = {width: bw};
10105                 break;
10106                 case "r":
10107                     wrap.setSize(0, b.height);
10108                     wrap.setX(b.right);
10109                     st.left = st.top = "0";
10110                     a = {width: bw, points: pt};
10111                 break;
10112                 case "b":
10113                     wrap.setSize(b.width, 0);
10114                     wrap.setY(b.bottom);
10115                     st.left = st.top = "0";
10116                     a = {height: bh, points: pt};
10117                 break;
10118                 case "tl":
10119                     wrap.setSize(0, 0);
10120                     st.right = st.bottom = "0";
10121                     a = {width: bw, height: bh};
10122                 break;
10123                 case "bl":
10124                     wrap.setSize(0, 0);
10125                     wrap.setY(b.y+b.height);
10126                     st.right = st.top = "0";
10127                     a = {width: bw, height: bh, points: pt};
10128                 break;
10129                 case "br":
10130                     wrap.setSize(0, 0);
10131                     wrap.setXY([b.right, b.bottom]);
10132                     st.left = st.top = "0";
10133                     a = {width: bw, height: bh, points: pt};
10134                 break;
10135                 case "tr":
10136                     wrap.setSize(0, 0);
10137                     wrap.setX(b.x+b.width);
10138                     st.left = st.bottom = "0";
10139                     a = {width: bw, height: bh, points: pt};
10140                 break;
10141             }
10142             this.dom.style.visibility = "visible";
10143             wrap.show();
10144
10145             arguments.callee.anim = wrap.fxanim(a,
10146                 o,
10147                 'motion',
10148                 .5,
10149                 'easeOut', after);
10150         });
10151         return this;
10152     },
10153     
10154         /**
10155          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10156          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10157          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10158          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10159          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10160          * Usage:
10161          *<pre><code>
10162 // default: slide the element out to the top
10163 el.slideOut();
10164
10165 // custom: slide the element out to the right with a 2-second duration
10166 el.slideOut('r', { duration: 2 });
10167
10168 // common config options shown with default values
10169 el.slideOut('t', {
10170     easing: 'easeOut',
10171     duration: .5,
10172     remove: false,
10173     useDisplay: false
10174 });
10175 </code></pre>
10176          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10177          * @param {Object} options (optional) Object literal with any of the Fx config options
10178          * @return {Roo.Element} The Element
10179          */
10180     slideOut : function(anchor, o){
10181         var el = this.getFxEl();
10182         o = o || {};
10183
10184         el.queueFx(o, function(){
10185
10186             anchor = anchor || "t";
10187
10188             // restore values after effect
10189             var r = this.getFxRestore();
10190             
10191             var b = this.getBox();
10192             // fixed size for slide
10193             this.setSize(b);
10194
10195             // wrap if needed
10196             var wrap = this.fxWrap(r.pos, o, "visible");
10197
10198             var st = this.dom.style;
10199             st.visibility = "visible";
10200             st.position = "absolute";
10201
10202             wrap.setSize(b);
10203
10204             var after = function(){
10205                 if(o.useDisplay){
10206                     el.setDisplayed(false);
10207                 }else{
10208                     el.hide();
10209                 }
10210
10211                 el.fxUnwrap(wrap, r.pos, o);
10212
10213                 st.width = r.width;
10214                 st.height = r.height;
10215
10216                 el.afterFx(o);
10217             };
10218
10219             var a, zero = {to: 0};
10220             switch(anchor.toLowerCase()){
10221                 case "t":
10222                     st.left = st.bottom = "0";
10223                     a = {height: zero};
10224                 break;
10225                 case "l":
10226                     st.right = st.top = "0";
10227                     a = {width: zero};
10228                 break;
10229                 case "r":
10230                     st.left = st.top = "0";
10231                     a = {width: zero, points: {to:[b.right, b.y]}};
10232                 break;
10233                 case "b":
10234                     st.left = st.top = "0";
10235                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10236                 break;
10237                 case "tl":
10238                     st.right = st.bottom = "0";
10239                     a = {width: zero, height: zero};
10240                 break;
10241                 case "bl":
10242                     st.right = st.top = "0";
10243                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10244                 break;
10245                 case "br":
10246                     st.left = st.top = "0";
10247                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10248                 break;
10249                 case "tr":
10250                     st.left = st.bottom = "0";
10251                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10252                 break;
10253             }
10254
10255             arguments.callee.anim = wrap.fxanim(a,
10256                 o,
10257                 'motion',
10258                 .5,
10259                 "easeOut", after);
10260         });
10261         return this;
10262     },
10263
10264         /**
10265          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10266          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10267          * The element must be removed from the DOM using the 'remove' config option if desired.
10268          * Usage:
10269          *<pre><code>
10270 // default
10271 el.puff();
10272
10273 // common config options shown with default values
10274 el.puff({
10275     easing: 'easeOut',
10276     duration: .5,
10277     remove: false,
10278     useDisplay: false
10279 });
10280 </code></pre>
10281          * @param {Object} options (optional) Object literal with any of the Fx config options
10282          * @return {Roo.Element} The Element
10283          */
10284     puff : function(o){
10285         var el = this.getFxEl();
10286         o = o || {};
10287
10288         el.queueFx(o, function(){
10289             this.clearOpacity();
10290             this.show();
10291
10292             // restore values after effect
10293             var r = this.getFxRestore();
10294             var st = this.dom.style;
10295
10296             var after = function(){
10297                 if(o.useDisplay){
10298                     el.setDisplayed(false);
10299                 }else{
10300                     el.hide();
10301                 }
10302
10303                 el.clearOpacity();
10304
10305                 el.setPositioning(r.pos);
10306                 st.width = r.width;
10307                 st.height = r.height;
10308                 st.fontSize = '';
10309                 el.afterFx(o);
10310             };
10311
10312             var width = this.getWidth();
10313             var height = this.getHeight();
10314
10315             arguments.callee.anim = this.fxanim({
10316                     width : {to: this.adjustWidth(width * 2)},
10317                     height : {to: this.adjustHeight(height * 2)},
10318                     points : {by: [-(width * .5), -(height * .5)]},
10319                     opacity : {to: 0},
10320                     fontSize: {to:200, unit: "%"}
10321                 },
10322                 o,
10323                 'motion',
10324                 .5,
10325                 "easeOut", after);
10326         });
10327         return this;
10328     },
10329
10330         /**
10331          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10332          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10333          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10334          * Usage:
10335          *<pre><code>
10336 // default
10337 el.switchOff();
10338
10339 // all config options shown with default values
10340 el.switchOff({
10341     easing: 'easeIn',
10342     duration: .3,
10343     remove: false,
10344     useDisplay: false
10345 });
10346 </code></pre>
10347          * @param {Object} options (optional) Object literal with any of the Fx config options
10348          * @return {Roo.Element} The Element
10349          */
10350     switchOff : function(o){
10351         var el = this.getFxEl();
10352         o = o || {};
10353
10354         el.queueFx(o, function(){
10355             this.clearOpacity();
10356             this.clip();
10357
10358             // restore values after effect
10359             var r = this.getFxRestore();
10360             var st = this.dom.style;
10361
10362             var after = function(){
10363                 if(o.useDisplay){
10364                     el.setDisplayed(false);
10365                 }else{
10366                     el.hide();
10367                 }
10368
10369                 el.clearOpacity();
10370                 el.setPositioning(r.pos);
10371                 st.width = r.width;
10372                 st.height = r.height;
10373
10374                 el.afterFx(o);
10375             };
10376
10377             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10378                 this.clearOpacity();
10379                 (function(){
10380                     this.fxanim({
10381                         height:{to:1},
10382                         points:{by:[0, this.getHeight() * .5]}
10383                     }, o, 'motion', 0.3, 'easeIn', after);
10384                 }).defer(100, this);
10385             });
10386         });
10387         return this;
10388     },
10389
10390     /**
10391      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10392      * changed using the "attr" config option) and then fading back to the original color. If no original
10393      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10394      * Usage:
10395 <pre><code>
10396 // default: highlight background to yellow
10397 el.highlight();
10398
10399 // custom: highlight foreground text to blue for 2 seconds
10400 el.highlight("0000ff", { attr: 'color', duration: 2 });
10401
10402 // common config options shown with default values
10403 el.highlight("ffff9c", {
10404     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10405     endColor: (current color) or "ffffff",
10406     easing: 'easeIn',
10407     duration: 1
10408 });
10409 </code></pre>
10410      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10411      * @param {Object} options (optional) Object literal with any of the Fx config options
10412      * @return {Roo.Element} The Element
10413      */ 
10414     highlight : function(color, o){
10415         var el = this.getFxEl();
10416         o = o || {};
10417
10418         el.queueFx(o, function(){
10419             color = color || "ffff9c";
10420             attr = o.attr || "backgroundColor";
10421
10422             this.clearOpacity();
10423             this.show();
10424
10425             var origColor = this.getColor(attr);
10426             var restoreColor = this.dom.style[attr];
10427             endColor = (o.endColor || origColor) || "ffffff";
10428
10429             var after = function(){
10430                 el.dom.style[attr] = restoreColor;
10431                 el.afterFx(o);
10432             };
10433
10434             var a = {};
10435             a[attr] = {from: color, to: endColor};
10436             arguments.callee.anim = this.fxanim(a,
10437                 o,
10438                 'color',
10439                 1,
10440                 'easeIn', after);
10441         });
10442         return this;
10443     },
10444
10445    /**
10446     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10447     * Usage:
10448 <pre><code>
10449 // default: a single light blue ripple
10450 el.frame();
10451
10452 // custom: 3 red ripples lasting 3 seconds total
10453 el.frame("ff0000", 3, { duration: 3 });
10454
10455 // common config options shown with default values
10456 el.frame("C3DAF9", 1, {
10457     duration: 1 //duration of entire animation (not each individual ripple)
10458     // Note: Easing is not configurable and will be ignored if included
10459 });
10460 </code></pre>
10461     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10462     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10463     * @param {Object} options (optional) Object literal with any of the Fx config options
10464     * @return {Roo.Element} The Element
10465     */
10466     frame : function(color, count, o){
10467         var el = this.getFxEl();
10468         o = o || {};
10469
10470         el.queueFx(o, function(){
10471             color = color || "#C3DAF9";
10472             if(color.length == 6){
10473                 color = "#" + color;
10474             }
10475             count = count || 1;
10476             duration = o.duration || 1;
10477             this.show();
10478
10479             var b = this.getBox();
10480             var animFn = function(){
10481                 var proxy = this.createProxy({
10482
10483                      style:{
10484                         visbility:"hidden",
10485                         position:"absolute",
10486                         "z-index":"35000", // yee haw
10487                         border:"0px solid " + color
10488                      }
10489                   });
10490                 var scale = Roo.isBorderBox ? 2 : 1;
10491                 proxy.animate({
10492                     top:{from:b.y, to:b.y - 20},
10493                     left:{from:b.x, to:b.x - 20},
10494                     borderWidth:{from:0, to:10},
10495                     opacity:{from:1, to:0},
10496                     height:{from:b.height, to:(b.height + (20*scale))},
10497                     width:{from:b.width, to:(b.width + (20*scale))}
10498                 }, duration, function(){
10499                     proxy.remove();
10500                 });
10501                 if(--count > 0){
10502                      animFn.defer((duration/2)*1000, this);
10503                 }else{
10504                     el.afterFx(o);
10505                 }
10506             };
10507             animFn.call(this);
10508         });
10509         return this;
10510     },
10511
10512    /**
10513     * Creates a pause before any subsequent queued effects begin.  If there are
10514     * no effects queued after the pause it will have no effect.
10515     * Usage:
10516 <pre><code>
10517 el.pause(1);
10518 </code></pre>
10519     * @param {Number} seconds The length of time to pause (in seconds)
10520     * @return {Roo.Element} The Element
10521     */
10522     pause : function(seconds){
10523         var el = this.getFxEl();
10524         var o = {};
10525
10526         el.queueFx(o, function(){
10527             setTimeout(function(){
10528                 el.afterFx(o);
10529             }, seconds * 1000);
10530         });
10531         return this;
10532     },
10533
10534    /**
10535     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10536     * using the "endOpacity" config option.
10537     * Usage:
10538 <pre><code>
10539 // default: fade in from opacity 0 to 100%
10540 el.fadeIn();
10541
10542 // custom: fade in from opacity 0 to 75% over 2 seconds
10543 el.fadeIn({ endOpacity: .75, duration: 2});
10544
10545 // common config options shown with default values
10546 el.fadeIn({
10547     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10548     easing: 'easeOut',
10549     duration: .5
10550 });
10551 </code></pre>
10552     * @param {Object} options (optional) Object literal with any of the Fx config options
10553     * @return {Roo.Element} The Element
10554     */
10555     fadeIn : function(o){
10556         var el = this.getFxEl();
10557         o = o || {};
10558         el.queueFx(o, function(){
10559             this.setOpacity(0);
10560             this.fixDisplay();
10561             this.dom.style.visibility = 'visible';
10562             var to = o.endOpacity || 1;
10563             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10564                 o, null, .5, "easeOut", function(){
10565                 if(to == 1){
10566                     this.clearOpacity();
10567                 }
10568                 el.afterFx(o);
10569             });
10570         });
10571         return this;
10572     },
10573
10574    /**
10575     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10576     * using the "endOpacity" config option.
10577     * Usage:
10578 <pre><code>
10579 // default: fade out from the element's current opacity to 0
10580 el.fadeOut();
10581
10582 // custom: fade out from the element's current opacity to 25% over 2 seconds
10583 el.fadeOut({ endOpacity: .25, duration: 2});
10584
10585 // common config options shown with default values
10586 el.fadeOut({
10587     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10588     easing: 'easeOut',
10589     duration: .5
10590     remove: false,
10591     useDisplay: false
10592 });
10593 </code></pre>
10594     * @param {Object} options (optional) Object literal with any of the Fx config options
10595     * @return {Roo.Element} The Element
10596     */
10597     fadeOut : function(o){
10598         var el = this.getFxEl();
10599         o = o || {};
10600         el.queueFx(o, function(){
10601             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10602                 o, null, .5, "easeOut", function(){
10603                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10604                      this.dom.style.display = "none";
10605                 }else{
10606                      this.dom.style.visibility = "hidden";
10607                 }
10608                 this.clearOpacity();
10609                 el.afterFx(o);
10610             });
10611         });
10612         return this;
10613     },
10614
10615    /**
10616     * Animates the transition of an element's dimensions from a starting height/width
10617     * to an ending height/width.
10618     * Usage:
10619 <pre><code>
10620 // change height and width to 100x100 pixels
10621 el.scale(100, 100);
10622
10623 // common config options shown with default values.  The height and width will default to
10624 // the element's existing values if passed as null.
10625 el.scale(
10626     [element's width],
10627     [element's height], {
10628     easing: 'easeOut',
10629     duration: .35
10630 });
10631 </code></pre>
10632     * @param {Number} width  The new width (pass undefined to keep the original width)
10633     * @param {Number} height  The new height (pass undefined to keep the original height)
10634     * @param {Object} options (optional) Object literal with any of the Fx config options
10635     * @return {Roo.Element} The Element
10636     */
10637     scale : function(w, h, o){
10638         this.shift(Roo.apply({}, o, {
10639             width: w,
10640             height: h
10641         }));
10642         return this;
10643     },
10644
10645    /**
10646     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10647     * Any of these properties not specified in the config object will not be changed.  This effect 
10648     * requires that at least one new dimension, position or opacity setting must be passed in on
10649     * the config object in order for the function to have any effect.
10650     * Usage:
10651 <pre><code>
10652 // slide the element horizontally to x position 200 while changing the height and opacity
10653 el.shift({ x: 200, height: 50, opacity: .8 });
10654
10655 // common config options shown with default values.
10656 el.shift({
10657     width: [element's width],
10658     height: [element's height],
10659     x: [element's x position],
10660     y: [element's y position],
10661     opacity: [element's opacity],
10662     easing: 'easeOut',
10663     duration: .35
10664 });
10665 </code></pre>
10666     * @param {Object} options  Object literal with any of the Fx config options
10667     * @return {Roo.Element} The Element
10668     */
10669     shift : function(o){
10670         var el = this.getFxEl();
10671         o = o || {};
10672         el.queueFx(o, function(){
10673             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10674             if(w !== undefined){
10675                 a.width = {to: this.adjustWidth(w)};
10676             }
10677             if(h !== undefined){
10678                 a.height = {to: this.adjustHeight(h)};
10679             }
10680             if(x !== undefined || y !== undefined){
10681                 a.points = {to: [
10682                     x !== undefined ? x : this.getX(),
10683                     y !== undefined ? y : this.getY()
10684                 ]};
10685             }
10686             if(op !== undefined){
10687                 a.opacity = {to: op};
10688             }
10689             if(o.xy !== undefined){
10690                 a.points = {to: o.xy};
10691             }
10692             arguments.callee.anim = this.fxanim(a,
10693                 o, 'motion', .35, "easeOut", function(){
10694                 el.afterFx(o);
10695             });
10696         });
10697         return this;
10698     },
10699
10700         /**
10701          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10702          * ending point of the effect.
10703          * Usage:
10704          *<pre><code>
10705 // default: slide the element downward while fading out
10706 el.ghost();
10707
10708 // custom: slide the element out to the right with a 2-second duration
10709 el.ghost('r', { duration: 2 });
10710
10711 // common config options shown with default values
10712 el.ghost('b', {
10713     easing: 'easeOut',
10714     duration: .5
10715     remove: false,
10716     useDisplay: false
10717 });
10718 </code></pre>
10719          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10720          * @param {Object} options (optional) Object literal with any of the Fx config options
10721          * @return {Roo.Element} The Element
10722          */
10723     ghost : function(anchor, o){
10724         var el = this.getFxEl();
10725         o = o || {};
10726
10727         el.queueFx(o, function(){
10728             anchor = anchor || "b";
10729
10730             // restore values after effect
10731             var r = this.getFxRestore();
10732             var w = this.getWidth(),
10733                 h = this.getHeight();
10734
10735             var st = this.dom.style;
10736
10737             var after = function(){
10738                 if(o.useDisplay){
10739                     el.setDisplayed(false);
10740                 }else{
10741                     el.hide();
10742                 }
10743
10744                 el.clearOpacity();
10745                 el.setPositioning(r.pos);
10746                 st.width = r.width;
10747                 st.height = r.height;
10748
10749                 el.afterFx(o);
10750             };
10751
10752             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10753             switch(anchor.toLowerCase()){
10754                 case "t":
10755                     pt.by = [0, -h];
10756                 break;
10757                 case "l":
10758                     pt.by = [-w, 0];
10759                 break;
10760                 case "r":
10761                     pt.by = [w, 0];
10762                 break;
10763                 case "b":
10764                     pt.by = [0, h];
10765                 break;
10766                 case "tl":
10767                     pt.by = [-w, -h];
10768                 break;
10769                 case "bl":
10770                     pt.by = [-w, h];
10771                 break;
10772                 case "br":
10773                     pt.by = [w, h];
10774                 break;
10775                 case "tr":
10776                     pt.by = [w, -h];
10777                 break;
10778             }
10779
10780             arguments.callee.anim = this.fxanim(a,
10781                 o,
10782                 'motion',
10783                 .5,
10784                 "easeOut", after);
10785         });
10786         return this;
10787     },
10788
10789         /**
10790          * Ensures that all effects queued after syncFx is called on the element are
10791          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10792          * @return {Roo.Element} The Element
10793          */
10794     syncFx : function(){
10795         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10796             block : false,
10797             concurrent : true,
10798             stopFx : false
10799         });
10800         return this;
10801     },
10802
10803         /**
10804          * Ensures that all effects queued after sequenceFx is called on the element are
10805          * run in sequence.  This is the opposite of {@link #syncFx}.
10806          * @return {Roo.Element} The Element
10807          */
10808     sequenceFx : function(){
10809         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10810             block : false,
10811             concurrent : false,
10812             stopFx : false
10813         });
10814         return this;
10815     },
10816
10817         /* @private */
10818     nextFx : function(){
10819         var ef = this.fxQueue[0];
10820         if(ef){
10821             ef.call(this);
10822         }
10823     },
10824
10825         /**
10826          * Returns true if the element has any effects actively running or queued, else returns false.
10827          * @return {Boolean} True if element has active effects, else false
10828          */
10829     hasActiveFx : function(){
10830         return this.fxQueue && this.fxQueue[0];
10831     },
10832
10833         /**
10834          * Stops any running effects and clears the element's internal effects queue if it contains
10835          * any additional effects that haven't started yet.
10836          * @return {Roo.Element} The Element
10837          */
10838     stopFx : function(){
10839         if(this.hasActiveFx()){
10840             var cur = this.fxQueue[0];
10841             if(cur && cur.anim && cur.anim.isAnimated()){
10842                 this.fxQueue = [cur]; // clear out others
10843                 cur.anim.stop(true);
10844             }
10845         }
10846         return this;
10847     },
10848
10849         /* @private */
10850     beforeFx : function(o){
10851         if(this.hasActiveFx() && !o.concurrent){
10852            if(o.stopFx){
10853                this.stopFx();
10854                return true;
10855            }
10856            return false;
10857         }
10858         return true;
10859     },
10860
10861         /**
10862          * Returns true if the element is currently blocking so that no other effect can be queued
10863          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10864          * used to ensure that an effect initiated by a user action runs to completion prior to the
10865          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10866          * @return {Boolean} True if blocking, else false
10867          */
10868     hasFxBlock : function(){
10869         var q = this.fxQueue;
10870         return q && q[0] && q[0].block;
10871     },
10872
10873         /* @private */
10874     queueFx : function(o, fn){
10875         if(!this.fxQueue){
10876             this.fxQueue = [];
10877         }
10878         if(!this.hasFxBlock()){
10879             Roo.applyIf(o, this.fxDefaults);
10880             if(!o.concurrent){
10881                 var run = this.beforeFx(o);
10882                 fn.block = o.block;
10883                 this.fxQueue.push(fn);
10884                 if(run){
10885                     this.nextFx();
10886                 }
10887             }else{
10888                 fn.call(this);
10889             }
10890         }
10891         return this;
10892     },
10893
10894         /* @private */
10895     fxWrap : function(pos, o, vis){
10896         var wrap;
10897         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10898             var wrapXY;
10899             if(o.fixPosition){
10900                 wrapXY = this.getXY();
10901             }
10902             var div = document.createElement("div");
10903             div.style.visibility = vis;
10904             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10905             wrap.setPositioning(pos);
10906             if(wrap.getStyle("position") == "static"){
10907                 wrap.position("relative");
10908             }
10909             this.clearPositioning('auto');
10910             wrap.clip();
10911             wrap.dom.appendChild(this.dom);
10912             if(wrapXY){
10913                 wrap.setXY(wrapXY);
10914             }
10915         }
10916         return wrap;
10917     },
10918
10919         /* @private */
10920     fxUnwrap : function(wrap, pos, o){
10921         this.clearPositioning();
10922         this.setPositioning(pos);
10923         if(!o.wrap){
10924             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10925             wrap.remove();
10926         }
10927     },
10928
10929         /* @private */
10930     getFxRestore : function(){
10931         var st = this.dom.style;
10932         return {pos: this.getPositioning(), width: st.width, height : st.height};
10933     },
10934
10935         /* @private */
10936     afterFx : function(o){
10937         if(o.afterStyle){
10938             this.applyStyles(o.afterStyle);
10939         }
10940         if(o.afterCls){
10941             this.addClass(o.afterCls);
10942         }
10943         if(o.remove === true){
10944             this.remove();
10945         }
10946         Roo.callback(o.callback, o.scope, [this]);
10947         if(!o.concurrent){
10948             this.fxQueue.shift();
10949             this.nextFx();
10950         }
10951     },
10952
10953         /* @private */
10954     getFxEl : function(){ // support for composite element fx
10955         return Roo.get(this.dom);
10956     },
10957
10958         /* @private */
10959     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10960         animType = animType || 'run';
10961         opt = opt || {};
10962         var anim = Roo.lib.Anim[animType](
10963             this.dom, args,
10964             (opt.duration || defaultDur) || .35,
10965             (opt.easing || defaultEase) || 'easeOut',
10966             function(){
10967                 Roo.callback(cb, this);
10968             },
10969             this
10970         );
10971         opt.anim = anim;
10972         return anim;
10973     }
10974 };
10975
10976 // backwords compat
10977 Roo.Fx.resize = Roo.Fx.scale;
10978
10979 //When included, Roo.Fx is automatically applied to Element so that all basic
10980 //effects are available directly via the Element API
10981 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10982  * Based on:
10983  * Ext JS Library 1.1.1
10984  * Copyright(c) 2006-2007, Ext JS, LLC.
10985  *
10986  * Originally Released Under LGPL - original licence link has changed is not relivant.
10987  *
10988  * Fork - LGPL
10989  * <script type="text/javascript">
10990  */
10991
10992
10993 /**
10994  * @class Roo.CompositeElement
10995  * Standard composite class. Creates a Roo.Element for every element in the collection.
10996  * <br><br>
10997  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10998  * actions will be performed on all the elements in this collection.</b>
10999  * <br><br>
11000  * All methods return <i>this</i> and can be chained.
11001  <pre><code>
11002  var els = Roo.select("#some-el div.some-class", true);
11003  // or select directly from an existing element
11004  var el = Roo.get('some-el');
11005  el.select('div.some-class', true);
11006
11007  els.setWidth(100); // all elements become 100 width
11008  els.hide(true); // all elements fade out and hide
11009  // or
11010  els.setWidth(100).hide(true);
11011  </code></pre>
11012  */
11013 Roo.CompositeElement = function(els){
11014     this.elements = [];
11015     this.addElements(els);
11016 };
11017 Roo.CompositeElement.prototype = {
11018     isComposite: true,
11019     addElements : function(els){
11020         if(!els) return this;
11021         if(typeof els == "string"){
11022             els = Roo.Element.selectorFunction(els);
11023         }
11024         var yels = this.elements;
11025         var index = yels.length-1;
11026         for(var i = 0, len = els.length; i < len; i++) {
11027                 yels[++index] = Roo.get(els[i]);
11028         }
11029         return this;
11030     },
11031
11032     /**
11033     * Clears this composite and adds the elements returned by the passed selector.
11034     * @param {String/Array} els A string CSS selector, an array of elements or an element
11035     * @return {CompositeElement} this
11036     */
11037     fill : function(els){
11038         this.elements = [];
11039         this.add(els);
11040         return this;
11041     },
11042
11043     /**
11044     * Filters this composite to only elements that match the passed selector.
11045     * @param {String} selector A string CSS selector
11046     * @param {Boolean} inverse return inverse filter (not matches)
11047     * @return {CompositeElement} this
11048     */
11049     filter : function(selector, inverse){
11050         var els = [];
11051         inverse = inverse || false;
11052         this.each(function(el){
11053             var match = inverse ? !el.is(selector) : el.is(selector);
11054             if(match){
11055                 els[els.length] = el.dom;
11056             }
11057         });
11058         this.fill(els);
11059         return this;
11060     },
11061
11062     invoke : function(fn, args){
11063         var els = this.elements;
11064         for(var i = 0, len = els.length; i < len; i++) {
11065                 Roo.Element.prototype[fn].apply(els[i], args);
11066         }
11067         return this;
11068     },
11069     /**
11070     * Adds elements to this composite.
11071     * @param {String/Array} els A string CSS selector, an array of elements or an element
11072     * @return {CompositeElement} this
11073     */
11074     add : function(els){
11075         if(typeof els == "string"){
11076             this.addElements(Roo.Element.selectorFunction(els));
11077         }else if(els.length !== undefined){
11078             this.addElements(els);
11079         }else{
11080             this.addElements([els]);
11081         }
11082         return this;
11083     },
11084     /**
11085     * Calls the passed function passing (el, this, index) for each element in this composite.
11086     * @param {Function} fn The function to call
11087     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11088     * @return {CompositeElement} this
11089     */
11090     each : function(fn, scope){
11091         var els = this.elements;
11092         for(var i = 0, len = els.length; i < len; i++){
11093             if(fn.call(scope || els[i], els[i], this, i) === false) {
11094                 break;
11095             }
11096         }
11097         return this;
11098     },
11099
11100     /**
11101      * Returns the Element object at the specified index
11102      * @param {Number} index
11103      * @return {Roo.Element}
11104      */
11105     item : function(index){
11106         return this.elements[index] || null;
11107     },
11108
11109     /**
11110      * Returns the first Element
11111      * @return {Roo.Element}
11112      */
11113     first : function(){
11114         return this.item(0);
11115     },
11116
11117     /**
11118      * Returns the last Element
11119      * @return {Roo.Element}
11120      */
11121     last : function(){
11122         return this.item(this.elements.length-1);
11123     },
11124
11125     /**
11126      * Returns the number of elements in this composite
11127      * @return Number
11128      */
11129     getCount : function(){
11130         return this.elements.length;
11131     },
11132
11133     /**
11134      * Returns true if this composite contains the passed element
11135      * @return Boolean
11136      */
11137     contains : function(el){
11138         return this.indexOf(el) !== -1;
11139     },
11140
11141     /**
11142      * Returns true if this composite contains the passed element
11143      * @return Boolean
11144      */
11145     indexOf : function(el){
11146         return this.elements.indexOf(Roo.get(el));
11147     },
11148
11149
11150     /**
11151     * Removes the specified element(s).
11152     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11153     * or an array of any of those.
11154     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11155     * @return {CompositeElement} this
11156     */
11157     removeElement : function(el, removeDom){
11158         if(el instanceof Array){
11159             for(var i = 0, len = el.length; i < len; i++){
11160                 this.removeElement(el[i]);
11161             }
11162             return this;
11163         }
11164         var index = typeof el == 'number' ? el : this.indexOf(el);
11165         if(index !== -1){
11166             if(removeDom){
11167                 var d = this.elements[index];
11168                 if(d.dom){
11169                     d.remove();
11170                 }else{
11171                     d.parentNode.removeChild(d);
11172                 }
11173             }
11174             this.elements.splice(index, 1);
11175         }
11176         return this;
11177     },
11178
11179     /**
11180     * Replaces the specified element with the passed element.
11181     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11182     * to replace.
11183     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11184     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11185     * @return {CompositeElement} this
11186     */
11187     replaceElement : function(el, replacement, domReplace){
11188         var index = typeof el == 'number' ? el : this.indexOf(el);
11189         if(index !== -1){
11190             if(domReplace){
11191                 this.elements[index].replaceWith(replacement);
11192             }else{
11193                 this.elements.splice(index, 1, Roo.get(replacement))
11194             }
11195         }
11196         return this;
11197     },
11198
11199     /**
11200      * Removes all elements.
11201      */
11202     clear : function(){
11203         this.elements = [];
11204     }
11205 };
11206 (function(){
11207     Roo.CompositeElement.createCall = function(proto, fnName){
11208         if(!proto[fnName]){
11209             proto[fnName] = function(){
11210                 return this.invoke(fnName, arguments);
11211             };
11212         }
11213     };
11214     for(var fnName in Roo.Element.prototype){
11215         if(typeof Roo.Element.prototype[fnName] == "function"){
11216             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11217         }
11218     };
11219 })();
11220 /*
11221  * Based on:
11222  * Ext JS Library 1.1.1
11223  * Copyright(c) 2006-2007, Ext JS, LLC.
11224  *
11225  * Originally Released Under LGPL - original licence link has changed is not relivant.
11226  *
11227  * Fork - LGPL
11228  * <script type="text/javascript">
11229  */
11230
11231 /**
11232  * @class Roo.CompositeElementLite
11233  * @extends Roo.CompositeElement
11234  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11235  <pre><code>
11236  var els = Roo.select("#some-el div.some-class");
11237  // or select directly from an existing element
11238  var el = Roo.get('some-el');
11239  el.select('div.some-class');
11240
11241  els.setWidth(100); // all elements become 100 width
11242  els.hide(true); // all elements fade out and hide
11243  // or
11244  els.setWidth(100).hide(true);
11245  </code></pre><br><br>
11246  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11247  * actions will be performed on all the elements in this collection.</b>
11248  */
11249 Roo.CompositeElementLite = function(els){
11250     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11251     this.el = new Roo.Element.Flyweight();
11252 };
11253 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11254     addElements : function(els){
11255         if(els){
11256             if(els instanceof Array){
11257                 this.elements = this.elements.concat(els);
11258             }else{
11259                 var yels = this.elements;
11260                 var index = yels.length-1;
11261                 for(var i = 0, len = els.length; i < len; i++) {
11262                     yels[++index] = els[i];
11263                 }
11264             }
11265         }
11266         return this;
11267     },
11268     invoke : function(fn, args){
11269         var els = this.elements;
11270         var el = this.el;
11271         for(var i = 0, len = els.length; i < len; i++) {
11272             el.dom = els[i];
11273                 Roo.Element.prototype[fn].apply(el, args);
11274         }
11275         return this;
11276     },
11277     /**
11278      * Returns a flyweight Element of the dom element object at the specified index
11279      * @param {Number} index
11280      * @return {Roo.Element}
11281      */
11282     item : function(index){
11283         if(!this.elements[index]){
11284             return null;
11285         }
11286         this.el.dom = this.elements[index];
11287         return this.el;
11288     },
11289
11290     // fixes scope with flyweight
11291     addListener : function(eventName, handler, scope, opt){
11292         var els = this.elements;
11293         for(var i = 0, len = els.length; i < len; i++) {
11294             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11295         }
11296         return this;
11297     },
11298
11299     /**
11300     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11301     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11302     * a reference to the dom node, use el.dom.</b>
11303     * @param {Function} fn The function to call
11304     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11305     * @return {CompositeElement} this
11306     */
11307     each : function(fn, scope){
11308         var els = this.elements;
11309         var el = this.el;
11310         for(var i = 0, len = els.length; i < len; i++){
11311             el.dom = els[i];
11312                 if(fn.call(scope || el, el, this, i) === false){
11313                 break;
11314             }
11315         }
11316         return this;
11317     },
11318
11319     indexOf : function(el){
11320         return this.elements.indexOf(Roo.getDom(el));
11321     },
11322
11323     replaceElement : function(el, replacement, domReplace){
11324         var index = typeof el == 'number' ? el : this.indexOf(el);
11325         if(index !== -1){
11326             replacement = Roo.getDom(replacement);
11327             if(domReplace){
11328                 var d = this.elements[index];
11329                 d.parentNode.insertBefore(replacement, d);
11330                 d.parentNode.removeChild(d);
11331             }
11332             this.elements.splice(index, 1, replacement);
11333         }
11334         return this;
11335     }
11336 });
11337 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11338
11339 /*
11340  * Based on:
11341  * Ext JS Library 1.1.1
11342  * Copyright(c) 2006-2007, Ext JS, LLC.
11343  *
11344  * Originally Released Under LGPL - original licence link has changed is not relivant.
11345  *
11346  * Fork - LGPL
11347  * <script type="text/javascript">
11348  */
11349
11350  
11351
11352 /**
11353  * @class Roo.data.Connection
11354  * @extends Roo.util.Observable
11355  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11356  * either to a configured URL, or to a URL specified at request time.<br><br>
11357  * <p>
11358  * Requests made by this class are asynchronous, and will return immediately. No data from
11359  * the server will be available to the statement immediately following the {@link #request} call.
11360  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11361  * <p>
11362  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11363  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11364  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11365  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11366  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11367  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11368  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11369  * standard DOM methods.
11370  * @constructor
11371  * @param {Object} config a configuration object.
11372  */
11373 Roo.data.Connection = function(config){
11374     Roo.apply(this, config);
11375     this.addEvents({
11376         /**
11377          * @event beforerequest
11378          * Fires before a network request is made to retrieve a data object.
11379          * @param {Connection} conn This Connection object.
11380          * @param {Object} options The options config object passed to the {@link #request} method.
11381          */
11382         "beforerequest" : true,
11383         /**
11384          * @event requestcomplete
11385          * Fires if the request was successfully completed.
11386          * @param {Connection} conn This Connection object.
11387          * @param {Object} response The XHR object containing the response data.
11388          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11389          * @param {Object} options The options config object passed to the {@link #request} method.
11390          */
11391         "requestcomplete" : true,
11392         /**
11393          * @event requestexception
11394          * Fires if an error HTTP status was returned from the server.
11395          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11396          * @param {Connection} conn This Connection object.
11397          * @param {Object} response The XHR object containing the response data.
11398          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11399          * @param {Object} options The options config object passed to the {@link #request} method.
11400          */
11401         "requestexception" : true
11402     });
11403     Roo.data.Connection.superclass.constructor.call(this);
11404 };
11405
11406 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11407     /**
11408      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11409      */
11410     /**
11411      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11412      * extra parameters to each request made by this object. (defaults to undefined)
11413      */
11414     /**
11415      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11416      *  to each request made by this object. (defaults to undefined)
11417      */
11418     /**
11419      * @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)
11420      */
11421     /**
11422      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11423      */
11424     timeout : 30000,
11425     /**
11426      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11427      * @type Boolean
11428      */
11429     autoAbort:false,
11430
11431     /**
11432      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11433      * @type Boolean
11434      */
11435     disableCaching: true,
11436
11437     /**
11438      * Sends an HTTP request to a remote server.
11439      * @param {Object} options An object which may contain the following properties:<ul>
11440      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11441      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11442      * request, a url encoded string or a function to call to get either.</li>
11443      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11444      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11445      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11446      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11447      * <li>options {Object} The parameter to the request call.</li>
11448      * <li>success {Boolean} True if the request succeeded.</li>
11449      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11450      * </ul></li>
11451      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11452      * The callback is passed the following parameters:<ul>
11453      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11454      * <li>options {Object} The parameter to the request call.</li>
11455      * </ul></li>
11456      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11457      * The callback is passed the following parameters:<ul>
11458      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11459      * <li>options {Object} The parameter to the request call.</li>
11460      * </ul></li>
11461      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11462      * for the callback function. Defaults to the browser window.</li>
11463      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11464      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11465      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11466      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11467      * params for the post data. Any params will be appended to the URL.</li>
11468      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11469      * </ul>
11470      * @return {Number} transactionId
11471      */
11472     request : function(o){
11473         if(this.fireEvent("beforerequest", this, o) !== false){
11474             var p = o.params;
11475
11476             if(typeof p == "function"){
11477                 p = p.call(o.scope||window, o);
11478             }
11479             if(typeof p == "object"){
11480                 p = Roo.urlEncode(o.params);
11481             }
11482             if(this.extraParams){
11483                 var extras = Roo.urlEncode(this.extraParams);
11484                 p = p ? (p + '&' + extras) : extras;
11485             }
11486
11487             var url = o.url || this.url;
11488             if(typeof url == 'function'){
11489                 url = url.call(o.scope||window, o);
11490             }
11491
11492             if(o.form){
11493                 var form = Roo.getDom(o.form);
11494                 url = url || form.action;
11495
11496                 var enctype = form.getAttribute("enctype");
11497                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11498                     return this.doFormUpload(o, p, url);
11499                 }
11500                 var f = Roo.lib.Ajax.serializeForm(form);
11501                 p = p ? (p + '&' + f) : f;
11502             }
11503
11504             var hs = o.headers;
11505             if(this.defaultHeaders){
11506                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11507                 if(!o.headers){
11508                     o.headers = hs;
11509                 }
11510             }
11511
11512             var cb = {
11513                 success: this.handleResponse,
11514                 failure: this.handleFailure,
11515                 scope: this,
11516                 argument: {options: o},
11517                 timeout : o.timeout || this.timeout
11518             };
11519
11520             var method = o.method||this.method||(p ? "POST" : "GET");
11521
11522             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11523                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11524             }
11525
11526             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11527                 if(o.autoAbort){
11528                     this.abort();
11529                 }
11530             }else if(this.autoAbort !== false){
11531                 this.abort();
11532             }
11533
11534             if((method == 'GET' && p) || o.xmlData){
11535                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11536                 p = '';
11537             }
11538             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11539             return this.transId;
11540         }else{
11541             Roo.callback(o.callback, o.scope, [o, null, null]);
11542             return null;
11543         }
11544     },
11545
11546     /**
11547      * Determine whether this object has a request outstanding.
11548      * @param {Number} transactionId (Optional) defaults to the last transaction
11549      * @return {Boolean} True if there is an outstanding request.
11550      */
11551     isLoading : function(transId){
11552         if(transId){
11553             return Roo.lib.Ajax.isCallInProgress(transId);
11554         }else{
11555             return this.transId ? true : false;
11556         }
11557     },
11558
11559     /**
11560      * Aborts any outstanding request.
11561      * @param {Number} transactionId (Optional) defaults to the last transaction
11562      */
11563     abort : function(transId){
11564         if(transId || this.isLoading()){
11565             Roo.lib.Ajax.abort(transId || this.transId);
11566         }
11567     },
11568
11569     // private
11570     handleResponse : function(response){
11571         this.transId = false;
11572         var options = response.argument.options;
11573         response.argument = options ? options.argument : null;
11574         this.fireEvent("requestcomplete", this, response, options);
11575         Roo.callback(options.success, options.scope, [response, options]);
11576         Roo.callback(options.callback, options.scope, [options, true, response]);
11577     },
11578
11579     // private
11580     handleFailure : function(response, e){
11581         this.transId = false;
11582         var options = response.argument.options;
11583         response.argument = options ? options.argument : null;
11584         this.fireEvent("requestexception", this, response, options, e);
11585         Roo.callback(options.failure, options.scope, [response, options]);
11586         Roo.callback(options.callback, options.scope, [options, false, response]);
11587     },
11588
11589     // private
11590     doFormUpload : function(o, ps, url){
11591         var id = Roo.id();
11592         var frame = document.createElement('iframe');
11593         frame.id = id;
11594         frame.name = id;
11595         frame.className = 'x-hidden';
11596         if(Roo.isIE){
11597             frame.src = Roo.SSL_SECURE_URL;
11598         }
11599         document.body.appendChild(frame);
11600
11601         if(Roo.isIE){
11602            document.frames[id].name = id;
11603         }
11604
11605         var form = Roo.getDom(o.form);
11606         form.target = id;
11607         form.method = 'POST';
11608         form.enctype = form.encoding = 'multipart/form-data';
11609         if(url){
11610             form.action = url;
11611         }
11612
11613         var hiddens, hd;
11614         if(ps){ // add dynamic params
11615             hiddens = [];
11616             ps = Roo.urlDecode(ps, false);
11617             for(var k in ps){
11618                 if(ps.hasOwnProperty(k)){
11619                     hd = document.createElement('input');
11620                     hd.type = 'hidden';
11621                     hd.name = k;
11622                     hd.value = ps[k];
11623                     form.appendChild(hd);
11624                     hiddens.push(hd);
11625                 }
11626             }
11627         }
11628
11629         function cb(){
11630             var r = {  // bogus response object
11631                 responseText : '',
11632                 responseXML : null
11633             };
11634
11635             r.argument = o ? o.argument : null;
11636
11637             try { //
11638                 var doc;
11639                 if(Roo.isIE){
11640                     doc = frame.contentWindow.document;
11641                 }else {
11642                     doc = (frame.contentDocument || window.frames[id].document);
11643                 }
11644                 if(doc && doc.body){
11645                     r.responseText = doc.body.innerHTML;
11646                 }
11647                 if(doc && doc.XMLDocument){
11648                     r.responseXML = doc.XMLDocument;
11649                 }else {
11650                     r.responseXML = doc;
11651                 }
11652             }
11653             catch(e) {
11654                 // ignore
11655             }
11656
11657             Roo.EventManager.removeListener(frame, 'load', cb, this);
11658
11659             this.fireEvent("requestcomplete", this, r, o);
11660             Roo.callback(o.success, o.scope, [r, o]);
11661             Roo.callback(o.callback, o.scope, [o, true, r]);
11662
11663             setTimeout(function(){document.body.removeChild(frame);}, 100);
11664         }
11665
11666         Roo.EventManager.on(frame, 'load', cb, this);
11667         form.submit();
11668
11669         if(hiddens){ // remove dynamic params
11670             for(var i = 0, len = hiddens.length; i < len; i++){
11671                 form.removeChild(hiddens[i]);
11672             }
11673         }
11674     }
11675 });
11676 /*
11677  * Based on:
11678  * Ext JS Library 1.1.1
11679  * Copyright(c) 2006-2007, Ext JS, LLC.
11680  *
11681  * Originally Released Under LGPL - original licence link has changed is not relivant.
11682  *
11683  * Fork - LGPL
11684  * <script type="text/javascript">
11685  */
11686  
11687 /**
11688  * Global Ajax request class.
11689  * 
11690  * @class Roo.Ajax
11691  * @extends Roo.data.Connection
11692  * @static
11693  * 
11694  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11695  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11696  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11697  * @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)
11698  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11699  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11700  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11701  */
11702 Roo.Ajax = new Roo.data.Connection({
11703     // fix up the docs
11704     /**
11705      * @scope Roo.Ajax
11706      * @type {Boolear} 
11707      */
11708     autoAbort : false,
11709
11710     /**
11711      * Serialize the passed form into a url encoded string
11712      * @scope Roo.Ajax
11713      * @param {String/HTMLElement} form
11714      * @return {String}
11715      */
11716     serializeForm : function(form){
11717         return Roo.lib.Ajax.serializeForm(form);
11718     }
11719 });/*
11720  * Based on:
11721  * Ext JS Library 1.1.1
11722  * Copyright(c) 2006-2007, Ext JS, LLC.
11723  *
11724  * Originally Released Under LGPL - original licence link has changed is not relivant.
11725  *
11726  * Fork - LGPL
11727  * <script type="text/javascript">
11728  */
11729
11730  
11731 /**
11732  * @class Roo.UpdateManager
11733  * @extends Roo.util.Observable
11734  * Provides AJAX-style update for Element object.<br><br>
11735  * Usage:<br>
11736  * <pre><code>
11737  * // Get it from a Roo.Element object
11738  * var el = Roo.get("foo");
11739  * var mgr = el.getUpdateManager();
11740  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11741  * ...
11742  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11743  * <br>
11744  * // or directly (returns the same UpdateManager instance)
11745  * var mgr = new Roo.UpdateManager("myElementId");
11746  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11747  * mgr.on("update", myFcnNeedsToKnow);
11748  * <br>
11749    // short handed call directly from the element object
11750    Roo.get("foo").load({
11751         url: "bar.php",
11752         scripts:true,
11753         params: "for=bar",
11754         text: "Loading Foo..."
11755    });
11756  * </code></pre>
11757  * @constructor
11758  * Create new UpdateManager directly.
11759  * @param {String/HTMLElement/Roo.Element} el The element to update
11760  * @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).
11761  */
11762 Roo.UpdateManager = function(el, forceNew){
11763     el = Roo.get(el);
11764     if(!forceNew && el.updateManager){
11765         return el.updateManager;
11766     }
11767     /**
11768      * The Element object
11769      * @type Roo.Element
11770      */
11771     this.el = el;
11772     /**
11773      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11774      * @type String
11775      */
11776     this.defaultUrl = null;
11777
11778     this.addEvents({
11779         /**
11780          * @event beforeupdate
11781          * Fired before an update is made, return false from your handler and the update is cancelled.
11782          * @param {Roo.Element} el
11783          * @param {String/Object/Function} url
11784          * @param {String/Object} params
11785          */
11786         "beforeupdate": true,
11787         /**
11788          * @event update
11789          * Fired after successful update is made.
11790          * @param {Roo.Element} el
11791          * @param {Object} oResponseObject The response Object
11792          */
11793         "update": true,
11794         /**
11795          * @event failure
11796          * Fired on update failure.
11797          * @param {Roo.Element} el
11798          * @param {Object} oResponseObject The response Object
11799          */
11800         "failure": true
11801     });
11802     var d = Roo.UpdateManager.defaults;
11803     /**
11804      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11805      * @type String
11806      */
11807     this.sslBlankUrl = d.sslBlankUrl;
11808     /**
11809      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11810      * @type Boolean
11811      */
11812     this.disableCaching = d.disableCaching;
11813     /**
11814      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11815      * @type String
11816      */
11817     this.indicatorText = d.indicatorText;
11818     /**
11819      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11820      * @type String
11821      */
11822     this.showLoadIndicator = d.showLoadIndicator;
11823     /**
11824      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11825      * @type Number
11826      */
11827     this.timeout = d.timeout;
11828
11829     /**
11830      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11831      * @type Boolean
11832      */
11833     this.loadScripts = d.loadScripts;
11834
11835     /**
11836      * Transaction object of current executing transaction
11837      */
11838     this.transaction = null;
11839
11840     /**
11841      * @private
11842      */
11843     this.autoRefreshProcId = null;
11844     /**
11845      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11846      * @type Function
11847      */
11848     this.refreshDelegate = this.refresh.createDelegate(this);
11849     /**
11850      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11851      * @type Function
11852      */
11853     this.updateDelegate = this.update.createDelegate(this);
11854     /**
11855      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11856      * @type Function
11857      */
11858     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11859     /**
11860      * @private
11861      */
11862     this.successDelegate = this.processSuccess.createDelegate(this);
11863     /**
11864      * @private
11865      */
11866     this.failureDelegate = this.processFailure.createDelegate(this);
11867
11868     if(!this.renderer){
11869      /**
11870       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11871       */
11872     this.renderer = new Roo.UpdateManager.BasicRenderer();
11873     }
11874     
11875     Roo.UpdateManager.superclass.constructor.call(this);
11876 };
11877
11878 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11879     /**
11880      * Get the Element this UpdateManager is bound to
11881      * @return {Roo.Element} The element
11882      */
11883     getEl : function(){
11884         return this.el;
11885     },
11886     /**
11887      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11888      * @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:
11889 <pre><code>
11890 um.update({<br/>
11891     url: "your-url.php",<br/>
11892     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11893     callback: yourFunction,<br/>
11894     scope: yourObject, //(optional scope)  <br/>
11895     discardUrl: false, <br/>
11896     nocache: false,<br/>
11897     text: "Loading...",<br/>
11898     timeout: 30,<br/>
11899     scripts: false<br/>
11900 });
11901 </code></pre>
11902      * The only required property is url. The optional properties nocache, text and scripts
11903      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11904      * @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}
11905      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11906      * @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.
11907      */
11908     update : function(url, params, callback, discardUrl){
11909         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11910             var method = this.method,
11911                 cfg;
11912             if(typeof url == "object"){ // must be config object
11913                 cfg = url;
11914                 url = cfg.url;
11915                 params = params || cfg.params;
11916                 callback = callback || cfg.callback;
11917                 discardUrl = discardUrl || cfg.discardUrl;
11918                 if(callback && cfg.scope){
11919                     callback = callback.createDelegate(cfg.scope);
11920                 }
11921                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11922                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11923                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11924                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11925                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11926             }
11927             this.showLoading();
11928             if(!discardUrl){
11929                 this.defaultUrl = url;
11930             }
11931             if(typeof url == "function"){
11932                 url = url.call(this);
11933             }
11934
11935             method = method || (params ? "POST" : "GET");
11936             if(method == "GET"){
11937                 url = this.prepareUrl(url);
11938             }
11939
11940             var o = Roo.apply(cfg ||{}, {
11941                 url : url,
11942                 params: params,
11943                 success: this.successDelegate,
11944                 failure: this.failureDelegate,
11945                 callback: undefined,
11946                 timeout: (this.timeout*1000),
11947                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11948             });
11949             Roo.log("updated manager called with timeout of " + o.timeout);
11950             this.transaction = Roo.Ajax.request(o);
11951         }
11952     },
11953
11954     /**
11955      * 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.
11956      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11957      * @param {String/HTMLElement} form The form Id or form element
11958      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11959      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11960      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11961      */
11962     formUpdate : function(form, url, reset, callback){
11963         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11964             if(typeof url == "function"){
11965                 url = url.call(this);
11966             }
11967             form = Roo.getDom(form);
11968             this.transaction = Roo.Ajax.request({
11969                 form: form,
11970                 url:url,
11971                 success: this.successDelegate,
11972                 failure: this.failureDelegate,
11973                 timeout: (this.timeout*1000),
11974                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11975             });
11976             this.showLoading.defer(1, this);
11977         }
11978     },
11979
11980     /**
11981      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11982      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11983      */
11984     refresh : function(callback){
11985         if(this.defaultUrl == null){
11986             return;
11987         }
11988         this.update(this.defaultUrl, null, callback, true);
11989     },
11990
11991     /**
11992      * Set this element to auto refresh.
11993      * @param {Number} interval How often to update (in seconds).
11994      * @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)
11995      * @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}
11996      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11997      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11998      */
11999     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12000         if(refreshNow){
12001             this.update(url || this.defaultUrl, params, callback, true);
12002         }
12003         if(this.autoRefreshProcId){
12004             clearInterval(this.autoRefreshProcId);
12005         }
12006         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12007     },
12008
12009     /**
12010      * Stop auto refresh on this element.
12011      */
12012      stopAutoRefresh : function(){
12013         if(this.autoRefreshProcId){
12014             clearInterval(this.autoRefreshProcId);
12015             delete this.autoRefreshProcId;
12016         }
12017     },
12018
12019     isAutoRefreshing : function(){
12020        return this.autoRefreshProcId ? true : false;
12021     },
12022     /**
12023      * Called to update the element to "Loading" state. Override to perform custom action.
12024      */
12025     showLoading : function(){
12026         if(this.showLoadIndicator){
12027             this.el.update(this.indicatorText);
12028         }
12029     },
12030
12031     /**
12032      * Adds unique parameter to query string if disableCaching = true
12033      * @private
12034      */
12035     prepareUrl : function(url){
12036         if(this.disableCaching){
12037             var append = "_dc=" + (new Date().getTime());
12038             if(url.indexOf("?") !== -1){
12039                 url += "&" + append;
12040             }else{
12041                 url += "?" + append;
12042             }
12043         }
12044         return url;
12045     },
12046
12047     /**
12048      * @private
12049      */
12050     processSuccess : function(response){
12051         this.transaction = null;
12052         if(response.argument.form && response.argument.reset){
12053             try{ // put in try/catch since some older FF releases had problems with this
12054                 response.argument.form.reset();
12055             }catch(e){}
12056         }
12057         if(this.loadScripts){
12058             this.renderer.render(this.el, response, this,
12059                 this.updateComplete.createDelegate(this, [response]));
12060         }else{
12061             this.renderer.render(this.el, response, this);
12062             this.updateComplete(response);
12063         }
12064     },
12065
12066     updateComplete : function(response){
12067         this.fireEvent("update", this.el, response);
12068         if(typeof response.argument.callback == "function"){
12069             response.argument.callback(this.el, true, response);
12070         }
12071     },
12072
12073     /**
12074      * @private
12075      */
12076     processFailure : function(response){
12077         this.transaction = null;
12078         this.fireEvent("failure", this.el, response);
12079         if(typeof response.argument.callback == "function"){
12080             response.argument.callback(this.el, false, response);
12081         }
12082     },
12083
12084     /**
12085      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12086      * @param {Object} renderer The object implementing the render() method
12087      */
12088     setRenderer : function(renderer){
12089         this.renderer = renderer;
12090     },
12091
12092     getRenderer : function(){
12093        return this.renderer;
12094     },
12095
12096     /**
12097      * Set the defaultUrl used for updates
12098      * @param {String/Function} defaultUrl The url or a function to call to get the url
12099      */
12100     setDefaultUrl : function(defaultUrl){
12101         this.defaultUrl = defaultUrl;
12102     },
12103
12104     /**
12105      * Aborts the executing transaction
12106      */
12107     abort : function(){
12108         if(this.transaction){
12109             Roo.Ajax.abort(this.transaction);
12110         }
12111     },
12112
12113     /**
12114      * Returns true if an update is in progress
12115      * @return {Boolean}
12116      */
12117     isUpdating : function(){
12118         if(this.transaction){
12119             return Roo.Ajax.isLoading(this.transaction);
12120         }
12121         return false;
12122     }
12123 });
12124
12125 /**
12126  * @class Roo.UpdateManager.defaults
12127  * @static (not really - but it helps the doc tool)
12128  * The defaults collection enables customizing the default properties of UpdateManager
12129  */
12130    Roo.UpdateManager.defaults = {
12131        /**
12132          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12133          * @type Number
12134          */
12135          timeout : 30,
12136
12137          /**
12138          * True to process scripts by default (Defaults to false).
12139          * @type Boolean
12140          */
12141         loadScripts : false,
12142
12143         /**
12144         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12145         * @type String
12146         */
12147         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12148         /**
12149          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12150          * @type Boolean
12151          */
12152         disableCaching : false,
12153         /**
12154          * Whether to show indicatorText when loading (Defaults to true).
12155          * @type Boolean
12156          */
12157         showLoadIndicator : true,
12158         /**
12159          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12160          * @type String
12161          */
12162         indicatorText : '<div class="loading-indicator">Loading...</div>'
12163    };
12164
12165 /**
12166  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12167  *Usage:
12168  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12169  * @param {String/HTMLElement/Roo.Element} el The element to update
12170  * @param {String} url The url
12171  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12172  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12173  * @static
12174  * @deprecated
12175  * @member Roo.UpdateManager
12176  */
12177 Roo.UpdateManager.updateElement = function(el, url, params, options){
12178     var um = Roo.get(el, true).getUpdateManager();
12179     Roo.apply(um, options);
12180     um.update(url, params, options ? options.callback : null);
12181 };
12182 // alias for backwards compat
12183 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12184 /**
12185  * @class Roo.UpdateManager.BasicRenderer
12186  * Default Content renderer. Updates the elements innerHTML with the responseText.
12187  */
12188 Roo.UpdateManager.BasicRenderer = function(){};
12189
12190 Roo.UpdateManager.BasicRenderer.prototype = {
12191     /**
12192      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12193      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12194      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12195      * @param {Roo.Element} el The element being rendered
12196      * @param {Object} response The YUI Connect response object
12197      * @param {UpdateManager} updateManager The calling update manager
12198      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12199      */
12200      render : function(el, response, updateManager, callback){
12201         el.update(response.responseText, updateManager.loadScripts, callback);
12202     }
12203 };
12204 /*
12205  * Based on:
12206  * Roo JS
12207  * (c)) Alan Knowles
12208  * Licence : LGPL
12209  */
12210
12211
12212 /**
12213  * @class Roo.DomTemplate
12214  * @extends Roo.Template
12215  * An effort at a dom based template engine..
12216  *
12217  * Similar to XTemplate, except it uses dom parsing to create the template..
12218  *
12219  * Supported features:
12220  *
12221  *  Tags:
12222
12223 <pre><code>
12224       {a_variable} - output encoded.
12225       {a_variable.format:("Y-m-d")} - call a method on the variable
12226       {a_variable:raw} - unencoded output
12227       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12228       {a_variable:this.method_on_template(...)} - call a method on the template object.
12229  
12230 </code></pre>
12231  *  The tpl tag:
12232 <pre><code>
12233         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12234         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12235         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12236         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12237   
12238 </code></pre>
12239  *      
12240  */
12241 Roo.DomTemplate = function()
12242 {
12243      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12244      if (this.html) {
12245         this.compile();
12246      }
12247 };
12248
12249
12250 Roo.extend(Roo.DomTemplate, Roo.Template, {
12251     /**
12252      * id counter for sub templates.
12253      */
12254     id : 0,
12255     /**
12256      * flag to indicate if dom parser is inside a pre,
12257      * it will strip whitespace if not.
12258      */
12259     inPre : false,
12260     
12261     /**
12262      * The various sub templates
12263      */
12264     tpls : false,
12265     
12266     
12267     
12268     /**
12269      *
12270      * basic tag replacing syntax
12271      * WORD:WORD()
12272      *
12273      * // you can fake an object call by doing this
12274      *  x.t:(test,tesT) 
12275      * 
12276      */
12277     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12278     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12279     
12280     iterChild : function (node, method) {
12281         
12282         var oldPre = this.inPre;
12283         if (node.tagName == 'PRE') {
12284             this.inPre = true;
12285         }
12286         for( var i = 0; i < node.childNodes.length; i++) {
12287             method.call(this, node.childNodes[i]);
12288         }
12289         this.inPre = oldPre;
12290     },
12291     
12292     
12293     
12294     /**
12295      * compile the template
12296      *
12297      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12298      *
12299      */
12300     compile: function()
12301     {
12302         var s = this.html;
12303         
12304         // covert the html into DOM...
12305         var doc = false;
12306         var div =false;
12307         try {
12308             doc = document.implementation.createHTMLDocument("");
12309             doc.documentElement.innerHTML =   this.html  ;
12310             div = doc.documentElement;
12311         } catch (e) {
12312             // old IE... - nasty -- it causes all sorts of issues.. with
12313             // images getting pulled from server..
12314             div = document.createElement('div');
12315             div.innerHTML = this.html;
12316         }
12317         //doc.documentElement.innerHTML = htmlBody
12318          
12319         
12320         
12321         this.tpls = [];
12322         var _t = this;
12323         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12324         
12325         var tpls = this.tpls;
12326         
12327         // create a top level template from the snippet..
12328         
12329         //Roo.log(div.innerHTML);
12330         
12331         var tpl = {
12332             uid : 'master',
12333             id : this.id++,
12334             attr : false,
12335             value : false,
12336             body : div.innerHTML,
12337             
12338             forCall : false,
12339             execCall : false,
12340             dom : div,
12341             isTop : true
12342             
12343         };
12344         tpls.unshift(tpl);
12345         
12346         
12347         // compile them...
12348         this.tpls = [];
12349         Roo.each(tpls, function(tp){
12350             this.compileTpl(tp);
12351             this.tpls[tp.id] = tp;
12352         }, this);
12353         
12354         this.master = tpls[0];
12355         return this;
12356         
12357         
12358     },
12359     
12360     compileNode : function(node, istop) {
12361         // test for
12362         //Roo.log(node);
12363         
12364         
12365         // skip anything not a tag..
12366         if (node.nodeType != 1) {
12367             if (node.nodeType == 3 && !this.inPre) {
12368                 // reduce white space..
12369                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12370                 
12371             }
12372             return;
12373         }
12374         
12375         var tpl = {
12376             uid : false,
12377             id : false,
12378             attr : false,
12379             value : false,
12380             body : '',
12381             
12382             forCall : false,
12383             execCall : false,
12384             dom : false,
12385             isTop : istop
12386             
12387             
12388         };
12389         
12390         
12391         switch(true) {
12392             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12393             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12394             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12395             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12396             // no default..
12397         }
12398         
12399         
12400         if (!tpl.attr) {
12401             // just itterate children..
12402             this.iterChild(node,this.compileNode);
12403             return;
12404         }
12405         tpl.uid = this.id++;
12406         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12407         node.removeAttribute('roo-'+ tpl.attr);
12408         if (tpl.attr != 'name') {
12409             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12410             node.parentNode.replaceChild(placeholder,  node);
12411         } else {
12412             
12413             var placeholder =  document.createElement('span');
12414             placeholder.className = 'roo-tpl-' + tpl.value;
12415             node.parentNode.replaceChild(placeholder,  node);
12416         }
12417         
12418         // parent now sees '{domtplXXXX}
12419         this.iterChild(node,this.compileNode);
12420         
12421         // we should now have node body...
12422         var div = document.createElement('div');
12423         div.appendChild(node);
12424         tpl.dom = node;
12425         // this has the unfortunate side effect of converting tagged attributes
12426         // eg. href="{...}" into %7C...%7D
12427         // this has been fixed by searching for those combo's although it's a bit hacky..
12428         
12429         
12430         tpl.body = div.innerHTML;
12431         
12432         
12433          
12434         tpl.id = tpl.uid;
12435         switch(tpl.attr) {
12436             case 'for' :
12437                 switch (tpl.value) {
12438                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12439                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12440                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12441                 }
12442                 break;
12443             
12444             case 'exec':
12445                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12446                 break;
12447             
12448             case 'if':     
12449                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12450                 break;
12451             
12452             case 'name':
12453                 tpl.id  = tpl.value; // replace non characters???
12454                 break;
12455             
12456         }
12457         
12458         
12459         this.tpls.push(tpl);
12460         
12461         
12462         
12463     },
12464     
12465     
12466     
12467     
12468     /**
12469      * Compile a segment of the template into a 'sub-template'
12470      *
12471      * 
12472      * 
12473      *
12474      */
12475     compileTpl : function(tpl)
12476     {
12477         var fm = Roo.util.Format;
12478         var useF = this.disableFormats !== true;
12479         
12480         var sep = Roo.isGecko ? "+\n" : ",\n";
12481         
12482         var undef = function(str) {
12483             Roo.debug && Roo.log("Property not found :"  + str);
12484             return '';
12485         };
12486           
12487         //Roo.log(tpl.body);
12488         
12489         
12490         
12491         var fn = function(m, lbrace, name, format, args)
12492         {
12493             //Roo.log("ARGS");
12494             //Roo.log(arguments);
12495             args = args ? args.replace(/\\'/g,"'") : args;
12496             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12497             if (typeof(format) == 'undefined') {
12498                 format =  'htmlEncode'; 
12499             }
12500             if (format == 'raw' ) {
12501                 format = false;
12502             }
12503             
12504             if(name.substr(0, 6) == 'domtpl'){
12505                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12506             }
12507             
12508             // build an array of options to determine if value is undefined..
12509             
12510             // basically get 'xxxx.yyyy' then do
12511             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12512             //    (function () { Roo.log("Property not found"); return ''; })() :
12513             //    ......
12514             
12515             var udef_ar = [];
12516             var lookfor = '';
12517             Roo.each(name.split('.'), function(st) {
12518                 lookfor += (lookfor.length ? '.': '') + st;
12519                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12520             });
12521             
12522             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12523             
12524             
12525             if(format && useF){
12526                 
12527                 args = args ? ',' + args : "";
12528                  
12529                 if(format.substr(0, 5) != "this."){
12530                     format = "fm." + format + '(';
12531                 }else{
12532                     format = 'this.call("'+ format.substr(5) + '", ';
12533                     args = ", values";
12534                 }
12535                 
12536                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12537             }
12538              
12539             if (args && args.length) {
12540                 // called with xxyx.yuu:(test,test)
12541                 // change to ()
12542                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12543             }
12544             // raw.. - :raw modifier..
12545             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12546             
12547         };
12548         var body;
12549         // branched to use + in gecko and [].join() in others
12550         if(Roo.isGecko){
12551             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12552                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12553                     "';};};";
12554         }else{
12555             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12556             body.push(tpl.body.replace(/(\r\n|\n)/g,
12557                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12558             body.push("'].join('');};};");
12559             body = body.join('');
12560         }
12561         
12562         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12563        
12564         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12565         eval(body);
12566         
12567         return this;
12568     },
12569      
12570     /**
12571      * same as applyTemplate, except it's done to one of the subTemplates
12572      * when using named templates, you can do:
12573      *
12574      * var str = pl.applySubTemplate('your-name', values);
12575      *
12576      * 
12577      * @param {Number} id of the template
12578      * @param {Object} values to apply to template
12579      * @param {Object} parent (normaly the instance of this object)
12580      */
12581     applySubTemplate : function(id, values, parent)
12582     {
12583         
12584         
12585         var t = this.tpls[id];
12586         
12587         
12588         try { 
12589             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12590                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12591                 return '';
12592             }
12593         } catch(e) {
12594             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12595             Roo.log(values);
12596           
12597             return '';
12598         }
12599         try { 
12600             
12601             if(t.execCall && t.execCall.call(this, values, parent)){
12602                 return '';
12603             }
12604         } catch(e) {
12605             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12606             Roo.log(values);
12607             return '';
12608         }
12609         
12610         try {
12611             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12612             parent = t.target ? values : parent;
12613             if(t.forCall && vs instanceof Array){
12614                 var buf = [];
12615                 for(var i = 0, len = vs.length; i < len; i++){
12616                     try {
12617                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12618                     } catch (e) {
12619                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12620                         Roo.log(e.body);
12621                         //Roo.log(t.compiled);
12622                         Roo.log(vs[i]);
12623                     }   
12624                 }
12625                 return buf.join('');
12626             }
12627         } catch (e) {
12628             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12629             Roo.log(values);
12630             return '';
12631         }
12632         try {
12633             return t.compiled.call(this, vs, parent);
12634         } catch (e) {
12635             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12636             Roo.log(e.body);
12637             //Roo.log(t.compiled);
12638             Roo.log(values);
12639             return '';
12640         }
12641     },
12642
12643    
12644
12645     applyTemplate : function(values){
12646         return this.master.compiled.call(this, values, {});
12647         //var s = this.subs;
12648     },
12649
12650     apply : function(){
12651         return this.applyTemplate.apply(this, arguments);
12652     }
12653
12654  });
12655
12656 Roo.DomTemplate.from = function(el){
12657     el = Roo.getDom(el);
12658     return new Roo.Domtemplate(el.value || el.innerHTML);
12659 };/*
12660  * Based on:
12661  * Ext JS Library 1.1.1
12662  * Copyright(c) 2006-2007, Ext JS, LLC.
12663  *
12664  * Originally Released Under LGPL - original licence link has changed is not relivant.
12665  *
12666  * Fork - LGPL
12667  * <script type="text/javascript">
12668  */
12669
12670 /**
12671  * @class Roo.util.DelayedTask
12672  * Provides a convenient method of performing setTimeout where a new
12673  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12674  * You can use this class to buffer
12675  * the keypress events for a certain number of milliseconds, and perform only if they stop
12676  * for that amount of time.
12677  * @constructor The parameters to this constructor serve as defaults and are not required.
12678  * @param {Function} fn (optional) The default function to timeout
12679  * @param {Object} scope (optional) The default scope of that timeout
12680  * @param {Array} args (optional) The default Array of arguments
12681  */
12682 Roo.util.DelayedTask = function(fn, scope, args){
12683     var id = null, d, t;
12684
12685     var call = function(){
12686         var now = new Date().getTime();
12687         if(now - t >= d){
12688             clearInterval(id);
12689             id = null;
12690             fn.apply(scope, args || []);
12691         }
12692     };
12693     /**
12694      * Cancels any pending timeout and queues a new one
12695      * @param {Number} delay The milliseconds to delay
12696      * @param {Function} newFn (optional) Overrides function passed to constructor
12697      * @param {Object} newScope (optional) Overrides scope passed to constructor
12698      * @param {Array} newArgs (optional) Overrides args passed to constructor
12699      */
12700     this.delay = function(delay, newFn, newScope, newArgs){
12701         if(id && delay != d){
12702             this.cancel();
12703         }
12704         d = delay;
12705         t = new Date().getTime();
12706         fn = newFn || fn;
12707         scope = newScope || scope;
12708         args = newArgs || args;
12709         if(!id){
12710             id = setInterval(call, d);
12711         }
12712     };
12713
12714     /**
12715      * Cancel the last queued timeout
12716      */
12717     this.cancel = function(){
12718         if(id){
12719             clearInterval(id);
12720             id = null;
12721         }
12722     };
12723 };/*
12724  * Based on:
12725  * Ext JS Library 1.1.1
12726  * Copyright(c) 2006-2007, Ext JS, LLC.
12727  *
12728  * Originally Released Under LGPL - original licence link has changed is not relivant.
12729  *
12730  * Fork - LGPL
12731  * <script type="text/javascript">
12732  */
12733  
12734  
12735 Roo.util.TaskRunner = function(interval){
12736     interval = interval || 10;
12737     var tasks = [], removeQueue = [];
12738     var id = 0;
12739     var running = false;
12740
12741     var stopThread = function(){
12742         running = false;
12743         clearInterval(id);
12744         id = 0;
12745     };
12746
12747     var startThread = function(){
12748         if(!running){
12749             running = true;
12750             id = setInterval(runTasks, interval);
12751         }
12752     };
12753
12754     var removeTask = function(task){
12755         removeQueue.push(task);
12756         if(task.onStop){
12757             task.onStop();
12758         }
12759     };
12760
12761     var runTasks = function(){
12762         if(removeQueue.length > 0){
12763             for(var i = 0, len = removeQueue.length; i < len; i++){
12764                 tasks.remove(removeQueue[i]);
12765             }
12766             removeQueue = [];
12767             if(tasks.length < 1){
12768                 stopThread();
12769                 return;
12770             }
12771         }
12772         var now = new Date().getTime();
12773         for(var i = 0, len = tasks.length; i < len; ++i){
12774             var t = tasks[i];
12775             var itime = now - t.taskRunTime;
12776             if(t.interval <= itime){
12777                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12778                 t.taskRunTime = now;
12779                 if(rt === false || t.taskRunCount === t.repeat){
12780                     removeTask(t);
12781                     return;
12782                 }
12783             }
12784             if(t.duration && t.duration <= (now - t.taskStartTime)){
12785                 removeTask(t);
12786             }
12787         }
12788     };
12789
12790     /**
12791      * Queues a new task.
12792      * @param {Object} task
12793      */
12794     this.start = function(task){
12795         tasks.push(task);
12796         task.taskStartTime = new Date().getTime();
12797         task.taskRunTime = 0;
12798         task.taskRunCount = 0;
12799         startThread();
12800         return task;
12801     };
12802
12803     this.stop = function(task){
12804         removeTask(task);
12805         return task;
12806     };
12807
12808     this.stopAll = function(){
12809         stopThread();
12810         for(var i = 0, len = tasks.length; i < len; i++){
12811             if(tasks[i].onStop){
12812                 tasks[i].onStop();
12813             }
12814         }
12815         tasks = [];
12816         removeQueue = [];
12817     };
12818 };
12819
12820 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12821  * Based on:
12822  * Ext JS Library 1.1.1
12823  * Copyright(c) 2006-2007, Ext JS, LLC.
12824  *
12825  * Originally Released Under LGPL - original licence link has changed is not relivant.
12826  *
12827  * Fork - LGPL
12828  * <script type="text/javascript">
12829  */
12830
12831  
12832 /**
12833  * @class Roo.util.MixedCollection
12834  * @extends Roo.util.Observable
12835  * A Collection class that maintains both numeric indexes and keys and exposes events.
12836  * @constructor
12837  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12838  * collection (defaults to false)
12839  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12840  * and return the key value for that item.  This is used when available to look up the key on items that
12841  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12842  * equivalent to providing an implementation for the {@link #getKey} method.
12843  */
12844 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12845     this.items = [];
12846     this.map = {};
12847     this.keys = [];
12848     this.length = 0;
12849     this.addEvents({
12850         /**
12851          * @event clear
12852          * Fires when the collection is cleared.
12853          */
12854         "clear" : true,
12855         /**
12856          * @event add
12857          * Fires when an item is added to the collection.
12858          * @param {Number} index The index at which the item was added.
12859          * @param {Object} o The item added.
12860          * @param {String} key The key associated with the added item.
12861          */
12862         "add" : true,
12863         /**
12864          * @event replace
12865          * Fires when an item is replaced in the collection.
12866          * @param {String} key he key associated with the new added.
12867          * @param {Object} old The item being replaced.
12868          * @param {Object} new The new item.
12869          */
12870         "replace" : true,
12871         /**
12872          * @event remove
12873          * Fires when an item is removed from the collection.
12874          * @param {Object} o The item being removed.
12875          * @param {String} key (optional) The key associated with the removed item.
12876          */
12877         "remove" : true,
12878         "sort" : true
12879     });
12880     this.allowFunctions = allowFunctions === true;
12881     if(keyFn){
12882         this.getKey = keyFn;
12883     }
12884     Roo.util.MixedCollection.superclass.constructor.call(this);
12885 };
12886
12887 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12888     allowFunctions : false,
12889     
12890 /**
12891  * Adds an item to the collection.
12892  * @param {String} key The key to associate with the item
12893  * @param {Object} o The item to add.
12894  * @return {Object} The item added.
12895  */
12896     add : function(key, o){
12897         if(arguments.length == 1){
12898             o = arguments[0];
12899             key = this.getKey(o);
12900         }
12901         if(typeof key == "undefined" || key === null){
12902             this.length++;
12903             this.items.push(o);
12904             this.keys.push(null);
12905         }else{
12906             var old = this.map[key];
12907             if(old){
12908                 return this.replace(key, o);
12909             }
12910             this.length++;
12911             this.items.push(o);
12912             this.map[key] = o;
12913             this.keys.push(key);
12914         }
12915         this.fireEvent("add", this.length-1, o, key);
12916         return o;
12917     },
12918        
12919 /**
12920   * MixedCollection has a generic way to fetch keys if you implement getKey.
12921 <pre><code>
12922 // normal way
12923 var mc = new Roo.util.MixedCollection();
12924 mc.add(someEl.dom.id, someEl);
12925 mc.add(otherEl.dom.id, otherEl);
12926 //and so on
12927
12928 // using getKey
12929 var mc = new Roo.util.MixedCollection();
12930 mc.getKey = function(el){
12931    return el.dom.id;
12932 };
12933 mc.add(someEl);
12934 mc.add(otherEl);
12935
12936 // or via the constructor
12937 var mc = new Roo.util.MixedCollection(false, function(el){
12938    return el.dom.id;
12939 });
12940 mc.add(someEl);
12941 mc.add(otherEl);
12942 </code></pre>
12943  * @param o {Object} The item for which to find the key.
12944  * @return {Object} The key for the passed item.
12945  */
12946     getKey : function(o){
12947          return o.id; 
12948     },
12949    
12950 /**
12951  * Replaces an item in the collection.
12952  * @param {String} key The key associated with the item to replace, or the item to replace.
12953  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12954  * @return {Object}  The new item.
12955  */
12956     replace : function(key, o){
12957         if(arguments.length == 1){
12958             o = arguments[0];
12959             key = this.getKey(o);
12960         }
12961         var old = this.item(key);
12962         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12963              return this.add(key, o);
12964         }
12965         var index = this.indexOfKey(key);
12966         this.items[index] = o;
12967         this.map[key] = o;
12968         this.fireEvent("replace", key, old, o);
12969         return o;
12970     },
12971    
12972 /**
12973  * Adds all elements of an Array or an Object to the collection.
12974  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12975  * an Array of values, each of which are added to the collection.
12976  */
12977     addAll : function(objs){
12978         if(arguments.length > 1 || objs instanceof Array){
12979             var args = arguments.length > 1 ? arguments : objs;
12980             for(var i = 0, len = args.length; i < len; i++){
12981                 this.add(args[i]);
12982             }
12983         }else{
12984             for(var key in objs){
12985                 if(this.allowFunctions || typeof objs[key] != "function"){
12986                     this.add(key, objs[key]);
12987                 }
12988             }
12989         }
12990     },
12991    
12992 /**
12993  * Executes the specified function once for every item in the collection, passing each
12994  * item as the first and only parameter. returning false from the function will stop the iteration.
12995  * @param {Function} fn The function to execute for each item.
12996  * @param {Object} scope (optional) The scope in which to execute the function.
12997  */
12998     each : function(fn, scope){
12999         var items = [].concat(this.items); // each safe for removal
13000         for(var i = 0, len = items.length; i < len; i++){
13001             if(fn.call(scope || items[i], items[i], i, len) === false){
13002                 break;
13003             }
13004         }
13005     },
13006    
13007 /**
13008  * Executes the specified function once for every key in the collection, passing each
13009  * key, and its associated item as the first two parameters.
13010  * @param {Function} fn The function to execute for each item.
13011  * @param {Object} scope (optional) The scope in which to execute the function.
13012  */
13013     eachKey : function(fn, scope){
13014         for(var i = 0, len = this.keys.length; i < len; i++){
13015             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13016         }
13017     },
13018    
13019 /**
13020  * Returns the first item in the collection which elicits a true return value from the
13021  * passed selection function.
13022  * @param {Function} fn The selection function to execute for each item.
13023  * @param {Object} scope (optional) The scope in which to execute the function.
13024  * @return {Object} The first item in the collection which returned true from the selection function.
13025  */
13026     find : function(fn, scope){
13027         for(var i = 0, len = this.items.length; i < len; i++){
13028             if(fn.call(scope || window, this.items[i], this.keys[i])){
13029                 return this.items[i];
13030             }
13031         }
13032         return null;
13033     },
13034    
13035 /**
13036  * Inserts an item at the specified index in the collection.
13037  * @param {Number} index The index to insert the item at.
13038  * @param {String} key The key to associate with the new item, or the item itself.
13039  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13040  * @return {Object} The item inserted.
13041  */
13042     insert : function(index, key, o){
13043         if(arguments.length == 2){
13044             o = arguments[1];
13045             key = this.getKey(o);
13046         }
13047         if(index >= this.length){
13048             return this.add(key, o);
13049         }
13050         this.length++;
13051         this.items.splice(index, 0, o);
13052         if(typeof key != "undefined" && key != null){
13053             this.map[key] = o;
13054         }
13055         this.keys.splice(index, 0, key);
13056         this.fireEvent("add", index, o, key);
13057         return o;
13058     },
13059    
13060 /**
13061  * Removed an item from the collection.
13062  * @param {Object} o The item to remove.
13063  * @return {Object} The item removed.
13064  */
13065     remove : function(o){
13066         return this.removeAt(this.indexOf(o));
13067     },
13068    
13069 /**
13070  * Remove an item from a specified index in the collection.
13071  * @param {Number} index The index within the collection of the item to remove.
13072  */
13073     removeAt : function(index){
13074         if(index < this.length && index >= 0){
13075             this.length--;
13076             var o = this.items[index];
13077             this.items.splice(index, 1);
13078             var key = this.keys[index];
13079             if(typeof key != "undefined"){
13080                 delete this.map[key];
13081             }
13082             this.keys.splice(index, 1);
13083             this.fireEvent("remove", o, key);
13084         }
13085     },
13086    
13087 /**
13088  * Removed an item associated with the passed key fom the collection.
13089  * @param {String} key The key of the item to remove.
13090  */
13091     removeKey : function(key){
13092         return this.removeAt(this.indexOfKey(key));
13093     },
13094    
13095 /**
13096  * Returns the number of items in the collection.
13097  * @return {Number} the number of items in the collection.
13098  */
13099     getCount : function(){
13100         return this.length; 
13101     },
13102    
13103 /**
13104  * Returns index within the collection of the passed Object.
13105  * @param {Object} o The item to find the index of.
13106  * @return {Number} index of the item.
13107  */
13108     indexOf : function(o){
13109         if(!this.items.indexOf){
13110             for(var i = 0, len = this.items.length; i < len; i++){
13111                 if(this.items[i] == o) return i;
13112             }
13113             return -1;
13114         }else{
13115             return this.items.indexOf(o);
13116         }
13117     },
13118    
13119 /**
13120  * Returns index within the collection of the passed key.
13121  * @param {String} key The key to find the index of.
13122  * @return {Number} index of the key.
13123  */
13124     indexOfKey : function(key){
13125         if(!this.keys.indexOf){
13126             for(var i = 0, len = this.keys.length; i < len; i++){
13127                 if(this.keys[i] == key) return i;
13128             }
13129             return -1;
13130         }else{
13131             return this.keys.indexOf(key);
13132         }
13133     },
13134    
13135 /**
13136  * Returns the item associated with the passed key OR index. Key has priority over index.
13137  * @param {String/Number} key The key or index of the item.
13138  * @return {Object} The item associated with the passed key.
13139  */
13140     item : function(key){
13141         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13142         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13143     },
13144     
13145 /**
13146  * Returns the item at the specified index.
13147  * @param {Number} index The index of the item.
13148  * @return {Object}
13149  */
13150     itemAt : function(index){
13151         return this.items[index];
13152     },
13153     
13154 /**
13155  * Returns the item associated with the passed key.
13156  * @param {String/Number} key The key of the item.
13157  * @return {Object} The item associated with the passed key.
13158  */
13159     key : function(key){
13160         return this.map[key];
13161     },
13162    
13163 /**
13164  * Returns true if the collection contains the passed Object as an item.
13165  * @param {Object} o  The Object to look for in the collection.
13166  * @return {Boolean} True if the collection contains the Object as an item.
13167  */
13168     contains : function(o){
13169         return this.indexOf(o) != -1;
13170     },
13171    
13172 /**
13173  * Returns true if the collection contains the passed Object as a key.
13174  * @param {String} key The key to look for in the collection.
13175  * @return {Boolean} True if the collection contains the Object as a key.
13176  */
13177     containsKey : function(key){
13178         return typeof this.map[key] != "undefined";
13179     },
13180    
13181 /**
13182  * Removes all items from the collection.
13183  */
13184     clear : function(){
13185         this.length = 0;
13186         this.items = [];
13187         this.keys = [];
13188         this.map = {};
13189         this.fireEvent("clear");
13190     },
13191    
13192 /**
13193  * Returns the first item in the collection.
13194  * @return {Object} the first item in the collection..
13195  */
13196     first : function(){
13197         return this.items[0]; 
13198     },
13199    
13200 /**
13201  * Returns the last item in the collection.
13202  * @return {Object} the last item in the collection..
13203  */
13204     last : function(){
13205         return this.items[this.length-1];   
13206     },
13207     
13208     _sort : function(property, dir, fn){
13209         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13210         fn = fn || function(a, b){
13211             return a-b;
13212         };
13213         var c = [], k = this.keys, items = this.items;
13214         for(var i = 0, len = items.length; i < len; i++){
13215             c[c.length] = {key: k[i], value: items[i], index: i};
13216         }
13217         c.sort(function(a, b){
13218             var v = fn(a[property], b[property]) * dsc;
13219             if(v == 0){
13220                 v = (a.index < b.index ? -1 : 1);
13221             }
13222             return v;
13223         });
13224         for(var i = 0, len = c.length; i < len; i++){
13225             items[i] = c[i].value;
13226             k[i] = c[i].key;
13227         }
13228         this.fireEvent("sort", this);
13229     },
13230     
13231     /**
13232      * Sorts this collection with the passed comparison function
13233      * @param {String} direction (optional) "ASC" or "DESC"
13234      * @param {Function} fn (optional) comparison function
13235      */
13236     sort : function(dir, fn){
13237         this._sort("value", dir, fn);
13238     },
13239     
13240     /**
13241      * Sorts this collection by keys
13242      * @param {String} direction (optional) "ASC" or "DESC"
13243      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13244      */
13245     keySort : function(dir, fn){
13246         this._sort("key", dir, fn || function(a, b){
13247             return String(a).toUpperCase()-String(b).toUpperCase();
13248         });
13249     },
13250     
13251     /**
13252      * Returns a range of items in this collection
13253      * @param {Number} startIndex (optional) defaults to 0
13254      * @param {Number} endIndex (optional) default to the last item
13255      * @return {Array} An array of items
13256      */
13257     getRange : function(start, end){
13258         var items = this.items;
13259         if(items.length < 1){
13260             return [];
13261         }
13262         start = start || 0;
13263         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13264         var r = [];
13265         if(start <= end){
13266             for(var i = start; i <= end; i++) {
13267                     r[r.length] = items[i];
13268             }
13269         }else{
13270             for(var i = start; i >= end; i--) {
13271                     r[r.length] = items[i];
13272             }
13273         }
13274         return r;
13275     },
13276         
13277     /**
13278      * Filter the <i>objects</i> in this collection by a specific property. 
13279      * Returns a new collection that has been filtered.
13280      * @param {String} property A property on your objects
13281      * @param {String/RegExp} value Either string that the property values 
13282      * should start with or a RegExp to test against the property
13283      * @return {MixedCollection} The new filtered collection
13284      */
13285     filter : function(property, value){
13286         if(!value.exec){ // not a regex
13287             value = String(value);
13288             if(value.length == 0){
13289                 return this.clone();
13290             }
13291             value = new RegExp("^" + Roo.escapeRe(value), "i");
13292         }
13293         return this.filterBy(function(o){
13294             return o && value.test(o[property]);
13295         });
13296         },
13297     
13298     /**
13299      * Filter by a function. * Returns a new collection that has been filtered.
13300      * The passed function will be called with each 
13301      * object in the collection. If the function returns true, the value is included 
13302      * otherwise it is filtered.
13303      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13304      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13305      * @return {MixedCollection} The new filtered collection
13306      */
13307     filterBy : function(fn, scope){
13308         var r = new Roo.util.MixedCollection();
13309         r.getKey = this.getKey;
13310         var k = this.keys, it = this.items;
13311         for(var i = 0, len = it.length; i < len; i++){
13312             if(fn.call(scope||this, it[i], k[i])){
13313                                 r.add(k[i], it[i]);
13314                         }
13315         }
13316         return r;
13317     },
13318     
13319     /**
13320      * Creates a duplicate of this collection
13321      * @return {MixedCollection}
13322      */
13323     clone : function(){
13324         var r = new Roo.util.MixedCollection();
13325         var k = this.keys, it = this.items;
13326         for(var i = 0, len = it.length; i < len; i++){
13327             r.add(k[i], it[i]);
13328         }
13329         r.getKey = this.getKey;
13330         return r;
13331     }
13332 });
13333 /**
13334  * Returns the item associated with the passed key or index.
13335  * @method
13336  * @param {String/Number} key The key or index of the item.
13337  * @return {Object} The item associated with the passed key.
13338  */
13339 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13340  * Based on:
13341  * Ext JS Library 1.1.1
13342  * Copyright(c) 2006-2007, Ext JS, LLC.
13343  *
13344  * Originally Released Under LGPL - original licence link has changed is not relivant.
13345  *
13346  * Fork - LGPL
13347  * <script type="text/javascript">
13348  */
13349 /**
13350  * @class Roo.util.JSON
13351  * Modified version of Douglas Crockford"s json.js that doesn"t
13352  * mess with the Object prototype 
13353  * http://www.json.org/js.html
13354  * @singleton
13355  */
13356 Roo.util.JSON = new (function(){
13357     var useHasOwn = {}.hasOwnProperty ? true : false;
13358     
13359     // crashes Safari in some instances
13360     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13361     
13362     var pad = function(n) {
13363         return n < 10 ? "0" + n : n;
13364     };
13365     
13366     var m = {
13367         "\b": '\\b',
13368         "\t": '\\t',
13369         "\n": '\\n',
13370         "\f": '\\f',
13371         "\r": '\\r',
13372         '"' : '\\"',
13373         "\\": '\\\\'
13374     };
13375
13376     var encodeString = function(s){
13377         if (/["\\\x00-\x1f]/.test(s)) {
13378             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13379                 var c = m[b];
13380                 if(c){
13381                     return c;
13382                 }
13383                 c = b.charCodeAt();
13384                 return "\\u00" +
13385                     Math.floor(c / 16).toString(16) +
13386                     (c % 16).toString(16);
13387             }) + '"';
13388         }
13389         return '"' + s + '"';
13390     };
13391     
13392     var encodeArray = function(o){
13393         var a = ["["], b, i, l = o.length, v;
13394             for (i = 0; i < l; i += 1) {
13395                 v = o[i];
13396                 switch (typeof v) {
13397                     case "undefined":
13398                     case "function":
13399                     case "unknown":
13400                         break;
13401                     default:
13402                         if (b) {
13403                             a.push(',');
13404                         }
13405                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13406                         b = true;
13407                 }
13408             }
13409             a.push("]");
13410             return a.join("");
13411     };
13412     
13413     var encodeDate = function(o){
13414         return '"' + o.getFullYear() + "-" +
13415                 pad(o.getMonth() + 1) + "-" +
13416                 pad(o.getDate()) + "T" +
13417                 pad(o.getHours()) + ":" +
13418                 pad(o.getMinutes()) + ":" +
13419                 pad(o.getSeconds()) + '"';
13420     };
13421     
13422     /**
13423      * Encodes an Object, Array or other value
13424      * @param {Mixed} o The variable to encode
13425      * @return {String} The JSON string
13426      */
13427     this.encode = function(o)
13428     {
13429         // should this be extended to fully wrap stringify..
13430         
13431         if(typeof o == "undefined" || o === null){
13432             return "null";
13433         }else if(o instanceof Array){
13434             return encodeArray(o);
13435         }else if(o instanceof Date){
13436             return encodeDate(o);
13437         }else if(typeof o == "string"){
13438             return encodeString(o);
13439         }else if(typeof o == "number"){
13440             return isFinite(o) ? String(o) : "null";
13441         }else if(typeof o == "boolean"){
13442             return String(o);
13443         }else {
13444             var a = ["{"], b, i, v;
13445             for (i in o) {
13446                 if(!useHasOwn || o.hasOwnProperty(i)) {
13447                     v = o[i];
13448                     switch (typeof v) {
13449                     case "undefined":
13450                     case "function":
13451                     case "unknown":
13452                         break;
13453                     default:
13454                         if(b){
13455                             a.push(',');
13456                         }
13457                         a.push(this.encode(i), ":",
13458                                 v === null ? "null" : this.encode(v));
13459                         b = true;
13460                     }
13461                 }
13462             }
13463             a.push("}");
13464             return a.join("");
13465         }
13466     };
13467     
13468     /**
13469      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13470      * @param {String} json The JSON string
13471      * @return {Object} The resulting object
13472      */
13473     this.decode = function(json){
13474         
13475         return  /** eval:var:json */ eval("(" + json + ')');
13476     };
13477 })();
13478 /** 
13479  * Shorthand for {@link Roo.util.JSON#encode}
13480  * @member Roo encode 
13481  * @method */
13482 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13483 /** 
13484  * Shorthand for {@link Roo.util.JSON#decode}
13485  * @member Roo decode 
13486  * @method */
13487 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13488 /*
13489  * Based on:
13490  * Ext JS Library 1.1.1
13491  * Copyright(c) 2006-2007, Ext JS, LLC.
13492  *
13493  * Originally Released Under LGPL - original licence link has changed is not relivant.
13494  *
13495  * Fork - LGPL
13496  * <script type="text/javascript">
13497  */
13498  
13499 /**
13500  * @class Roo.util.Format
13501  * Reusable data formatting functions
13502  * @singleton
13503  */
13504 Roo.util.Format = function(){
13505     var trimRe = /^\s+|\s+$/g;
13506     return {
13507         /**
13508          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13509          * @param {String} value The string to truncate
13510          * @param {Number} length The maximum length to allow before truncating
13511          * @return {String} The converted text
13512          */
13513         ellipsis : function(value, len){
13514             if(value && value.length > len){
13515                 return value.substr(0, len-3)+"...";
13516             }
13517             return value;
13518         },
13519
13520         /**
13521          * Checks a reference and converts it to empty string if it is undefined
13522          * @param {Mixed} value Reference to check
13523          * @return {Mixed} Empty string if converted, otherwise the original value
13524          */
13525         undef : function(value){
13526             return typeof value != "undefined" ? value : "";
13527         },
13528
13529         /**
13530          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13531          * @param {String} value The string to encode
13532          * @return {String} The encoded text
13533          */
13534         htmlEncode : function(value){
13535             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13536         },
13537
13538         /**
13539          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13540          * @param {String} value The string to decode
13541          * @return {String} The decoded text
13542          */
13543         htmlDecode : function(value){
13544             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13545         },
13546
13547         /**
13548          * Trims any whitespace from either side of a string
13549          * @param {String} value The text to trim
13550          * @return {String} The trimmed text
13551          */
13552         trim : function(value){
13553             return String(value).replace(trimRe, "");
13554         },
13555
13556         /**
13557          * Returns a substring from within an original string
13558          * @param {String} value The original text
13559          * @param {Number} start The start index of the substring
13560          * @param {Number} length The length of the substring
13561          * @return {String} The substring
13562          */
13563         substr : function(value, start, length){
13564             return String(value).substr(start, length);
13565         },
13566
13567         /**
13568          * Converts a string to all lower case letters
13569          * @param {String} value The text to convert
13570          * @return {String} The converted text
13571          */
13572         lowercase : function(value){
13573             return String(value).toLowerCase();
13574         },
13575
13576         /**
13577          * Converts a string to all upper case letters
13578          * @param {String} value The text to convert
13579          * @return {String} The converted text
13580          */
13581         uppercase : function(value){
13582             return String(value).toUpperCase();
13583         },
13584
13585         /**
13586          * Converts the first character only of a string to upper case
13587          * @param {String} value The text to convert
13588          * @return {String} The converted text
13589          */
13590         capitalize : function(value){
13591             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13592         },
13593
13594         // private
13595         call : function(value, fn){
13596             if(arguments.length > 2){
13597                 var args = Array.prototype.slice.call(arguments, 2);
13598                 args.unshift(value);
13599                  
13600                 return /** eval:var:value */  eval(fn).apply(window, args);
13601             }else{
13602                 /** eval:var:value */
13603                 return /** eval:var:value */ eval(fn).call(window, value);
13604             }
13605         },
13606
13607        
13608         /**
13609          * safer version of Math.toFixed..??/
13610          * @param {Number/String} value The numeric value to format
13611          * @param {Number/String} value Decimal places 
13612          * @return {String} The formatted currency string
13613          */
13614         toFixed : function(v, n)
13615         {
13616             // why not use to fixed - precision is buggered???
13617             if (!n) {
13618                 return Math.round(v-0);
13619             }
13620             var fact = Math.pow(10,n+1);
13621             v = (Math.round((v-0)*fact))/fact;
13622             var z = (''+fact).substring(2);
13623             if (v == Math.floor(v)) {
13624                 return Math.floor(v) + '.' + z;
13625             }
13626             
13627             // now just padd decimals..
13628             var ps = String(v).split('.');
13629             var fd = (ps[1] + z);
13630             var r = fd.substring(0,n); 
13631             var rm = fd.substring(n); 
13632             if (rm < 5) {
13633                 return ps[0] + '.' + r;
13634             }
13635             r*=1; // turn it into a number;
13636             r++;
13637             if (String(r).length != n) {
13638                 ps[0]*=1;
13639                 ps[0]++;
13640                 r = String(r).substring(1); // chop the end off.
13641             }
13642             
13643             return ps[0] + '.' + r;
13644              
13645         },
13646         
13647         /**
13648          * Format a number as US currency
13649          * @param {Number/String} value The numeric value to format
13650          * @return {String} The formatted currency string
13651          */
13652         usMoney : function(v){
13653             return '$' + Roo.util.Format.number(v);
13654         },
13655         
13656         /**
13657          * Format a number
13658          * eventually this should probably emulate php's number_format
13659          * @param {Number/String} value The numeric value to format
13660          * @param {Number} decimals number of decimal places
13661          * @return {String} The formatted currency string
13662          */
13663         number : function(v,decimals)
13664         {
13665             // multiply and round.
13666             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13667             var mul = Math.pow(10, decimals);
13668             var zero = String(mul).substring(1);
13669             v = (Math.round((v-0)*mul))/mul;
13670             
13671             // if it's '0' number.. then
13672             
13673             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13674             v = String(v);
13675             var ps = v.split('.');
13676             var whole = ps[0];
13677             
13678             
13679             var r = /(\d+)(\d{3})/;
13680             // add comma's
13681             while (r.test(whole)) {
13682                 whole = whole.replace(r, '$1' + ',' + '$2');
13683             }
13684             
13685             
13686             var sub = ps[1] ?
13687                     // has decimals..
13688                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13689                     // does not have decimals
13690                     (decimals ? ('.' + zero) : '');
13691             
13692             
13693             return whole + sub ;
13694         },
13695         
13696         /**
13697          * Parse a value into a formatted date using the specified format pattern.
13698          * @param {Mixed} value The value to format
13699          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13700          * @return {String} The formatted date string
13701          */
13702         date : function(v, format){
13703             if(!v){
13704                 return "";
13705             }
13706             if(!(v instanceof Date)){
13707                 v = new Date(Date.parse(v));
13708             }
13709             return v.dateFormat(format || Roo.util.Format.defaults.date);
13710         },
13711
13712         /**
13713          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13714          * @param {String} format Any valid date format string
13715          * @return {Function} The date formatting function
13716          */
13717         dateRenderer : function(format){
13718             return function(v){
13719                 return Roo.util.Format.date(v, format);  
13720             };
13721         },
13722
13723         // private
13724         stripTagsRE : /<\/?[^>]+>/gi,
13725         
13726         /**
13727          * Strips all HTML tags
13728          * @param {Mixed} value The text from which to strip tags
13729          * @return {String} The stripped text
13730          */
13731         stripTags : function(v){
13732             return !v ? v : String(v).replace(this.stripTagsRE, "");
13733         }
13734     };
13735 }();
13736 Roo.util.Format.defaults = {
13737     date : 'd/M/Y'
13738 };/*
13739  * Based on:
13740  * Ext JS Library 1.1.1
13741  * Copyright(c) 2006-2007, Ext JS, LLC.
13742  *
13743  * Originally Released Under LGPL - original licence link has changed is not relivant.
13744  *
13745  * Fork - LGPL
13746  * <script type="text/javascript">
13747  */
13748
13749
13750  
13751
13752 /**
13753  * @class Roo.MasterTemplate
13754  * @extends Roo.Template
13755  * Provides a template that can have child templates. The syntax is:
13756 <pre><code>
13757 var t = new Roo.MasterTemplate(
13758         '&lt;select name="{name}"&gt;',
13759                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13760         '&lt;/select&gt;'
13761 );
13762 t.add('options', {value: 'foo', text: 'bar'});
13763 // or you can add multiple child elements in one shot
13764 t.addAll('options', [
13765     {value: 'foo', text: 'bar'},
13766     {value: 'foo2', text: 'bar2'},
13767     {value: 'foo3', text: 'bar3'}
13768 ]);
13769 // then append, applying the master template values
13770 t.append('my-form', {name: 'my-select'});
13771 </code></pre>
13772 * A name attribute for the child template is not required if you have only one child
13773 * template or you want to refer to them by index.
13774  */
13775 Roo.MasterTemplate = function(){
13776     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13777     this.originalHtml = this.html;
13778     var st = {};
13779     var m, re = this.subTemplateRe;
13780     re.lastIndex = 0;
13781     var subIndex = 0;
13782     while(m = re.exec(this.html)){
13783         var name = m[1], content = m[2];
13784         st[subIndex] = {
13785             name: name,
13786             index: subIndex,
13787             buffer: [],
13788             tpl : new Roo.Template(content)
13789         };
13790         if(name){
13791             st[name] = st[subIndex];
13792         }
13793         st[subIndex].tpl.compile();
13794         st[subIndex].tpl.call = this.call.createDelegate(this);
13795         subIndex++;
13796     }
13797     this.subCount = subIndex;
13798     this.subs = st;
13799 };
13800 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13801     /**
13802     * The regular expression used to match sub templates
13803     * @type RegExp
13804     * @property
13805     */
13806     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13807
13808     /**
13809      * Applies the passed values to a child template.
13810      * @param {String/Number} name (optional) The name or index of the child template
13811      * @param {Array/Object} values The values to be applied to the template
13812      * @return {MasterTemplate} this
13813      */
13814      add : function(name, values){
13815         if(arguments.length == 1){
13816             values = arguments[0];
13817             name = 0;
13818         }
13819         var s = this.subs[name];
13820         s.buffer[s.buffer.length] = s.tpl.apply(values);
13821         return this;
13822     },
13823
13824     /**
13825      * Applies all the passed values to a child template.
13826      * @param {String/Number} name (optional) The name or index of the child template
13827      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13828      * @param {Boolean} reset (optional) True to reset the template first
13829      * @return {MasterTemplate} this
13830      */
13831     fill : function(name, values, reset){
13832         var a = arguments;
13833         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13834             values = a[0];
13835             name = 0;
13836             reset = a[1];
13837         }
13838         if(reset){
13839             this.reset();
13840         }
13841         for(var i = 0, len = values.length; i < len; i++){
13842             this.add(name, values[i]);
13843         }
13844         return this;
13845     },
13846
13847     /**
13848      * Resets the template for reuse
13849      * @return {MasterTemplate} this
13850      */
13851      reset : function(){
13852         var s = this.subs;
13853         for(var i = 0; i < this.subCount; i++){
13854             s[i].buffer = [];
13855         }
13856         return this;
13857     },
13858
13859     applyTemplate : function(values){
13860         var s = this.subs;
13861         var replaceIndex = -1;
13862         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13863             return s[++replaceIndex].buffer.join("");
13864         });
13865         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13866     },
13867
13868     apply : function(){
13869         return this.applyTemplate.apply(this, arguments);
13870     },
13871
13872     compile : function(){return this;}
13873 });
13874
13875 /**
13876  * Alias for fill().
13877  * @method
13878  */
13879 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13880  /**
13881  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13882  * var tpl = Roo.MasterTemplate.from('element-id');
13883  * @param {String/HTMLElement} el
13884  * @param {Object} config
13885  * @static
13886  */
13887 Roo.MasterTemplate.from = function(el, config){
13888     el = Roo.getDom(el);
13889     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13890 };/*
13891  * Based on:
13892  * Ext JS Library 1.1.1
13893  * Copyright(c) 2006-2007, Ext JS, LLC.
13894  *
13895  * Originally Released Under LGPL - original licence link has changed is not relivant.
13896  *
13897  * Fork - LGPL
13898  * <script type="text/javascript">
13899  */
13900
13901  
13902 /**
13903  * @class Roo.util.CSS
13904  * Utility class for manipulating CSS rules
13905  * @singleton
13906  */
13907 Roo.util.CSS = function(){
13908         var rules = null;
13909         var doc = document;
13910
13911     var camelRe = /(-[a-z])/gi;
13912     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13913
13914    return {
13915    /**
13916     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13917     * tag and appended to the HEAD of the document.
13918     * @param {String|Object} cssText The text containing the css rules
13919     * @param {String} id An id to add to the stylesheet for later removal
13920     * @return {StyleSheet}
13921     */
13922     createStyleSheet : function(cssText, id){
13923         var ss;
13924         var head = doc.getElementsByTagName("head")[0];
13925         var nrules = doc.createElement("style");
13926         nrules.setAttribute("type", "text/css");
13927         if(id){
13928             nrules.setAttribute("id", id);
13929         }
13930         if (typeof(cssText) != 'string') {
13931             // support object maps..
13932             // not sure if this a good idea.. 
13933             // perhaps it should be merged with the general css handling
13934             // and handle js style props.
13935             var cssTextNew = [];
13936             for(var n in cssText) {
13937                 var citems = [];
13938                 for(var k in cssText[n]) {
13939                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13940                 }
13941                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13942                 
13943             }
13944             cssText = cssTextNew.join("\n");
13945             
13946         }
13947        
13948        
13949        if(Roo.isIE){
13950            head.appendChild(nrules);
13951            ss = nrules.styleSheet;
13952            ss.cssText = cssText;
13953        }else{
13954            try{
13955                 nrules.appendChild(doc.createTextNode(cssText));
13956            }catch(e){
13957                nrules.cssText = cssText; 
13958            }
13959            head.appendChild(nrules);
13960            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13961        }
13962        this.cacheStyleSheet(ss);
13963        return ss;
13964    },
13965
13966    /**
13967     * Removes a style or link tag by id
13968     * @param {String} id The id of the tag
13969     */
13970    removeStyleSheet : function(id){
13971        var existing = doc.getElementById(id);
13972        if(existing){
13973            existing.parentNode.removeChild(existing);
13974        }
13975    },
13976
13977    /**
13978     * Dynamically swaps an existing stylesheet reference for a new one
13979     * @param {String} id The id of an existing link tag to remove
13980     * @param {String} url The href of the new stylesheet to include
13981     */
13982    swapStyleSheet : function(id, url){
13983        this.removeStyleSheet(id);
13984        var ss = doc.createElement("link");
13985        ss.setAttribute("rel", "stylesheet");
13986        ss.setAttribute("type", "text/css");
13987        ss.setAttribute("id", id);
13988        ss.setAttribute("href", url);
13989        doc.getElementsByTagName("head")[0].appendChild(ss);
13990    },
13991    
13992    /**
13993     * Refresh the rule cache if you have dynamically added stylesheets
13994     * @return {Object} An object (hash) of rules indexed by selector
13995     */
13996    refreshCache : function(){
13997        return this.getRules(true);
13998    },
13999
14000    // private
14001    cacheStyleSheet : function(stylesheet){
14002        if(!rules){
14003            rules = {};
14004        }
14005        try{// try catch for cross domain access issue
14006            var ssRules = stylesheet.cssRules || stylesheet.rules;
14007            for(var j = ssRules.length-1; j >= 0; --j){
14008                rules[ssRules[j].selectorText] = ssRules[j];
14009            }
14010        }catch(e){}
14011    },
14012    
14013    /**
14014     * Gets all css rules for the document
14015     * @param {Boolean} refreshCache true to refresh the internal cache
14016     * @return {Object} An object (hash) of rules indexed by selector
14017     */
14018    getRules : function(refreshCache){
14019                 if(rules == null || refreshCache){
14020                         rules = {};
14021                         var ds = doc.styleSheets;
14022                         for(var i =0, len = ds.length; i < len; i++){
14023                             try{
14024                         this.cacheStyleSheet(ds[i]);
14025                     }catch(e){} 
14026                 }
14027                 }
14028                 return rules;
14029         },
14030         
14031         /**
14032     * Gets an an individual CSS rule by selector(s)
14033     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14034     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14035     * @return {CSSRule} The CSS rule or null if one is not found
14036     */
14037    getRule : function(selector, refreshCache){
14038                 var rs = this.getRules(refreshCache);
14039                 if(!(selector instanceof Array)){
14040                     return rs[selector];
14041                 }
14042                 for(var i = 0; i < selector.length; i++){
14043                         if(rs[selector[i]]){
14044                                 return rs[selector[i]];
14045                         }
14046                 }
14047                 return null;
14048         },
14049         
14050         
14051         /**
14052     * Updates a rule property
14053     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14054     * @param {String} property The css property
14055     * @param {String} value The new value for the property
14056     * @return {Boolean} true If a rule was found and updated
14057     */
14058    updateRule : function(selector, property, value){
14059                 if(!(selector instanceof Array)){
14060                         var rule = this.getRule(selector);
14061                         if(rule){
14062                                 rule.style[property.replace(camelRe, camelFn)] = value;
14063                                 return true;
14064                         }
14065                 }else{
14066                         for(var i = 0; i < selector.length; i++){
14067                                 if(this.updateRule(selector[i], property, value)){
14068                                         return true;
14069                                 }
14070                         }
14071                 }
14072                 return false;
14073         }
14074    };   
14075 }();/*
14076  * Based on:
14077  * Ext JS Library 1.1.1
14078  * Copyright(c) 2006-2007, Ext JS, LLC.
14079  *
14080  * Originally Released Under LGPL - original licence link has changed is not relivant.
14081  *
14082  * Fork - LGPL
14083  * <script type="text/javascript">
14084  */
14085
14086  
14087
14088 /**
14089  * @class Roo.util.ClickRepeater
14090  * @extends Roo.util.Observable
14091  * 
14092  * A wrapper class which can be applied to any element. Fires a "click" event while the
14093  * mouse is pressed. The interval between firings may be specified in the config but
14094  * defaults to 10 milliseconds.
14095  * 
14096  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14097  * 
14098  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14099  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14100  * Similar to an autorepeat key delay.
14101  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14102  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14103  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14104  *           "interval" and "delay" are ignored. "immediate" is honored.
14105  * @cfg {Boolean} preventDefault True to prevent the default click event
14106  * @cfg {Boolean} stopDefault True to stop the default click event
14107  * 
14108  * @history
14109  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14110  *     2007-02-02 jvs Renamed to ClickRepeater
14111  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14112  *
14113  *  @constructor
14114  * @param {String/HTMLElement/Element} el The element to listen on
14115  * @param {Object} config
14116  **/
14117 Roo.util.ClickRepeater = function(el, config)
14118 {
14119     this.el = Roo.get(el);
14120     this.el.unselectable();
14121
14122     Roo.apply(this, config);
14123
14124     this.addEvents({
14125     /**
14126      * @event mousedown
14127      * Fires when the mouse button is depressed.
14128      * @param {Roo.util.ClickRepeater} this
14129      */
14130         "mousedown" : true,
14131     /**
14132      * @event click
14133      * Fires on a specified interval during the time the element is pressed.
14134      * @param {Roo.util.ClickRepeater} this
14135      */
14136         "click" : true,
14137     /**
14138      * @event mouseup
14139      * Fires when the mouse key is released.
14140      * @param {Roo.util.ClickRepeater} this
14141      */
14142         "mouseup" : true
14143     });
14144
14145     this.el.on("mousedown", this.handleMouseDown, this);
14146     if(this.preventDefault || this.stopDefault){
14147         this.el.on("click", function(e){
14148             if(this.preventDefault){
14149                 e.preventDefault();
14150             }
14151             if(this.stopDefault){
14152                 e.stopEvent();
14153             }
14154         }, this);
14155     }
14156
14157     // allow inline handler
14158     if(this.handler){
14159         this.on("click", this.handler,  this.scope || this);
14160     }
14161
14162     Roo.util.ClickRepeater.superclass.constructor.call(this);
14163 };
14164
14165 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14166     interval : 20,
14167     delay: 250,
14168     preventDefault : true,
14169     stopDefault : false,
14170     timer : 0,
14171
14172     // private
14173     handleMouseDown : function(){
14174         clearTimeout(this.timer);
14175         this.el.blur();
14176         if(this.pressClass){
14177             this.el.addClass(this.pressClass);
14178         }
14179         this.mousedownTime = new Date();
14180
14181         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14182         this.el.on("mouseout", this.handleMouseOut, this);
14183
14184         this.fireEvent("mousedown", this);
14185         this.fireEvent("click", this);
14186         
14187         this.timer = this.click.defer(this.delay || this.interval, this);
14188     },
14189
14190     // private
14191     click : function(){
14192         this.fireEvent("click", this);
14193         this.timer = this.click.defer(this.getInterval(), this);
14194     },
14195
14196     // private
14197     getInterval: function(){
14198         if(!this.accelerate){
14199             return this.interval;
14200         }
14201         var pressTime = this.mousedownTime.getElapsed();
14202         if(pressTime < 500){
14203             return 400;
14204         }else if(pressTime < 1700){
14205             return 320;
14206         }else if(pressTime < 2600){
14207             return 250;
14208         }else if(pressTime < 3500){
14209             return 180;
14210         }else if(pressTime < 4400){
14211             return 140;
14212         }else if(pressTime < 5300){
14213             return 80;
14214         }else if(pressTime < 6200){
14215             return 50;
14216         }else{
14217             return 10;
14218         }
14219     },
14220
14221     // private
14222     handleMouseOut : function(){
14223         clearTimeout(this.timer);
14224         if(this.pressClass){
14225             this.el.removeClass(this.pressClass);
14226         }
14227         this.el.on("mouseover", this.handleMouseReturn, this);
14228     },
14229
14230     // private
14231     handleMouseReturn : function(){
14232         this.el.un("mouseover", this.handleMouseReturn);
14233         if(this.pressClass){
14234             this.el.addClass(this.pressClass);
14235         }
14236         this.click();
14237     },
14238
14239     // private
14240     handleMouseUp : function(){
14241         clearTimeout(this.timer);
14242         this.el.un("mouseover", this.handleMouseReturn);
14243         this.el.un("mouseout", this.handleMouseOut);
14244         Roo.get(document).un("mouseup", this.handleMouseUp);
14245         this.el.removeClass(this.pressClass);
14246         this.fireEvent("mouseup", this);
14247     }
14248 });/*
14249  * Based on:
14250  * Ext JS Library 1.1.1
14251  * Copyright(c) 2006-2007, Ext JS, LLC.
14252  *
14253  * Originally Released Under LGPL - original licence link has changed is not relivant.
14254  *
14255  * Fork - LGPL
14256  * <script type="text/javascript">
14257  */
14258
14259  
14260 /**
14261  * @class Roo.KeyNav
14262  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14263  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14264  * way to implement custom navigation schemes for any UI component.</p>
14265  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14266  * pageUp, pageDown, del, home, end.  Usage:</p>
14267  <pre><code>
14268 var nav = new Roo.KeyNav("my-element", {
14269     "left" : function(e){
14270         this.moveLeft(e.ctrlKey);
14271     },
14272     "right" : function(e){
14273         this.moveRight(e.ctrlKey);
14274     },
14275     "enter" : function(e){
14276         this.save();
14277     },
14278     scope : this
14279 });
14280 </code></pre>
14281  * @constructor
14282  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14283  * @param {Object} config The config
14284  */
14285 Roo.KeyNav = function(el, config){
14286     this.el = Roo.get(el);
14287     Roo.apply(this, config);
14288     if(!this.disabled){
14289         this.disabled = true;
14290         this.enable();
14291     }
14292 };
14293
14294 Roo.KeyNav.prototype = {
14295     /**
14296      * @cfg {Boolean} disabled
14297      * True to disable this KeyNav instance (defaults to false)
14298      */
14299     disabled : false,
14300     /**
14301      * @cfg {String} defaultEventAction
14302      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14303      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14304      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14305      */
14306     defaultEventAction: "stopEvent",
14307     /**
14308      * @cfg {Boolean} forceKeyDown
14309      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14310      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14311      * handle keydown instead of keypress.
14312      */
14313     forceKeyDown : false,
14314
14315     // private
14316     prepareEvent : function(e){
14317         var k = e.getKey();
14318         var h = this.keyToHandler[k];
14319         //if(h && this[h]){
14320         //    e.stopPropagation();
14321         //}
14322         if(Roo.isSafari && h && k >= 37 && k <= 40){
14323             e.stopEvent();
14324         }
14325     },
14326
14327     // private
14328     relay : function(e){
14329         var k = e.getKey();
14330         var h = this.keyToHandler[k];
14331         if(h && this[h]){
14332             if(this.doRelay(e, this[h], h) !== true){
14333                 e[this.defaultEventAction]();
14334             }
14335         }
14336     },
14337
14338     // private
14339     doRelay : function(e, h, hname){
14340         return h.call(this.scope || this, e);
14341     },
14342
14343     // possible handlers
14344     enter : false,
14345     left : false,
14346     right : false,
14347     up : false,
14348     down : false,
14349     tab : false,
14350     esc : false,
14351     pageUp : false,
14352     pageDown : false,
14353     del : false,
14354     home : false,
14355     end : false,
14356
14357     // quick lookup hash
14358     keyToHandler : {
14359         37 : "left",
14360         39 : "right",
14361         38 : "up",
14362         40 : "down",
14363         33 : "pageUp",
14364         34 : "pageDown",
14365         46 : "del",
14366         36 : "home",
14367         35 : "end",
14368         13 : "enter",
14369         27 : "esc",
14370         9  : "tab"
14371     },
14372
14373         /**
14374          * Enable this KeyNav
14375          */
14376         enable: function(){
14377                 if(this.disabled){
14378             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14379             // the EventObject will normalize Safari automatically
14380             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14381                 this.el.on("keydown", this.relay,  this);
14382             }else{
14383                 this.el.on("keydown", this.prepareEvent,  this);
14384                 this.el.on("keypress", this.relay,  this);
14385             }
14386                     this.disabled = false;
14387                 }
14388         },
14389
14390         /**
14391          * Disable this KeyNav
14392          */
14393         disable: function(){
14394                 if(!this.disabled){
14395                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14396                 this.el.un("keydown", this.relay);
14397             }else{
14398                 this.el.un("keydown", this.prepareEvent);
14399                 this.el.un("keypress", this.relay);
14400             }
14401                     this.disabled = true;
14402                 }
14403         }
14404 };/*
14405  * Based on:
14406  * Ext JS Library 1.1.1
14407  * Copyright(c) 2006-2007, Ext JS, LLC.
14408  *
14409  * Originally Released Under LGPL - original licence link has changed is not relivant.
14410  *
14411  * Fork - LGPL
14412  * <script type="text/javascript">
14413  */
14414
14415  
14416 /**
14417  * @class Roo.KeyMap
14418  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14419  * The constructor accepts the same config object as defined by {@link #addBinding}.
14420  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14421  * combination it will call the function with this signature (if the match is a multi-key
14422  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14423  * A KeyMap can also handle a string representation of keys.<br />
14424  * Usage:
14425  <pre><code>
14426 // map one key by key code
14427 var map = new Roo.KeyMap("my-element", {
14428     key: 13, // or Roo.EventObject.ENTER
14429     fn: myHandler,
14430     scope: myObject
14431 });
14432
14433 // map multiple keys to one action by string
14434 var map = new Roo.KeyMap("my-element", {
14435     key: "a\r\n\t",
14436     fn: myHandler,
14437     scope: myObject
14438 });
14439
14440 // map multiple keys to multiple actions by strings and array of codes
14441 var map = new Roo.KeyMap("my-element", [
14442     {
14443         key: [10,13],
14444         fn: function(){ alert("Return was pressed"); }
14445     }, {
14446         key: "abc",
14447         fn: function(){ alert('a, b or c was pressed'); }
14448     }, {
14449         key: "\t",
14450         ctrl:true,
14451         shift:true,
14452         fn: function(){ alert('Control + shift + tab was pressed.'); }
14453     }
14454 ]);
14455 </code></pre>
14456  * <b>Note: A KeyMap starts enabled</b>
14457  * @constructor
14458  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14459  * @param {Object} config The config (see {@link #addBinding})
14460  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14461  */
14462 Roo.KeyMap = function(el, config, eventName){
14463     this.el  = Roo.get(el);
14464     this.eventName = eventName || "keydown";
14465     this.bindings = [];
14466     if(config){
14467         this.addBinding(config);
14468     }
14469     this.enable();
14470 };
14471
14472 Roo.KeyMap.prototype = {
14473     /**
14474      * True to stop the event from bubbling and prevent the default browser action if the
14475      * key was handled by the KeyMap (defaults to false)
14476      * @type Boolean
14477      */
14478     stopEvent : false,
14479
14480     /**
14481      * Add a new binding to this KeyMap. The following config object properties are supported:
14482      * <pre>
14483 Property    Type             Description
14484 ----------  ---------------  ----------------------------------------------------------------------
14485 key         String/Array     A single keycode or an array of keycodes to handle
14486 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14487 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14488 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14489 fn          Function         The function to call when KeyMap finds the expected key combination
14490 scope       Object           The scope of the callback function
14491 </pre>
14492      *
14493      * Usage:
14494      * <pre><code>
14495 // Create a KeyMap
14496 var map = new Roo.KeyMap(document, {
14497     key: Roo.EventObject.ENTER,
14498     fn: handleKey,
14499     scope: this
14500 });
14501
14502 //Add a new binding to the existing KeyMap later
14503 map.addBinding({
14504     key: 'abc',
14505     shift: true,
14506     fn: handleKey,
14507     scope: this
14508 });
14509 </code></pre>
14510      * @param {Object/Array} config A single KeyMap config or an array of configs
14511      */
14512         addBinding : function(config){
14513         if(config instanceof Array){
14514             for(var i = 0, len = config.length; i < len; i++){
14515                 this.addBinding(config[i]);
14516             }
14517             return;
14518         }
14519         var keyCode = config.key,
14520             shift = config.shift, 
14521             ctrl = config.ctrl, 
14522             alt = config.alt,
14523             fn = config.fn,
14524             scope = config.scope;
14525         if(typeof keyCode == "string"){
14526             var ks = [];
14527             var keyString = keyCode.toUpperCase();
14528             for(var j = 0, len = keyString.length; j < len; j++){
14529                 ks.push(keyString.charCodeAt(j));
14530             }
14531             keyCode = ks;
14532         }
14533         var keyArray = keyCode instanceof Array;
14534         var handler = function(e){
14535             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14536                 var k = e.getKey();
14537                 if(keyArray){
14538                     for(var i = 0, len = keyCode.length; i < len; i++){
14539                         if(keyCode[i] == k){
14540                           if(this.stopEvent){
14541                               e.stopEvent();
14542                           }
14543                           fn.call(scope || window, k, e);
14544                           return;
14545                         }
14546                     }
14547                 }else{
14548                     if(k == keyCode){
14549                         if(this.stopEvent){
14550                            e.stopEvent();
14551                         }
14552                         fn.call(scope || window, k, e);
14553                     }
14554                 }
14555             }
14556         };
14557         this.bindings.push(handler);  
14558         },
14559
14560     /**
14561      * Shorthand for adding a single key listener
14562      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14563      * following options:
14564      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14565      * @param {Function} fn The function to call
14566      * @param {Object} scope (optional) The scope of the function
14567      */
14568     on : function(key, fn, scope){
14569         var keyCode, shift, ctrl, alt;
14570         if(typeof key == "object" && !(key instanceof Array)){
14571             keyCode = key.key;
14572             shift = key.shift;
14573             ctrl = key.ctrl;
14574             alt = key.alt;
14575         }else{
14576             keyCode = key;
14577         }
14578         this.addBinding({
14579             key: keyCode,
14580             shift: shift,
14581             ctrl: ctrl,
14582             alt: alt,
14583             fn: fn,
14584             scope: scope
14585         })
14586     },
14587
14588     // private
14589     handleKeyDown : function(e){
14590             if(this.enabled){ //just in case
14591             var b = this.bindings;
14592             for(var i = 0, len = b.length; i < len; i++){
14593                 b[i].call(this, e);
14594             }
14595             }
14596         },
14597         
14598         /**
14599          * Returns true if this KeyMap is enabled
14600          * @return {Boolean} 
14601          */
14602         isEnabled : function(){
14603             return this.enabled;  
14604         },
14605         
14606         /**
14607          * Enables this KeyMap
14608          */
14609         enable: function(){
14610                 if(!this.enabled){
14611                     this.el.on(this.eventName, this.handleKeyDown, this);
14612                     this.enabled = true;
14613                 }
14614         },
14615
14616         /**
14617          * Disable this KeyMap
14618          */
14619         disable: function(){
14620                 if(this.enabled){
14621                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14622                     this.enabled = false;
14623                 }
14624         }
14625 };/*
14626  * Based on:
14627  * Ext JS Library 1.1.1
14628  * Copyright(c) 2006-2007, Ext JS, LLC.
14629  *
14630  * Originally Released Under LGPL - original licence link has changed is not relivant.
14631  *
14632  * Fork - LGPL
14633  * <script type="text/javascript">
14634  */
14635
14636  
14637 /**
14638  * @class Roo.util.TextMetrics
14639  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14640  * wide, in pixels, a given block of text will be.
14641  * @singleton
14642  */
14643 Roo.util.TextMetrics = function(){
14644     var shared;
14645     return {
14646         /**
14647          * Measures the size of the specified text
14648          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14649          * that can affect the size of the rendered text
14650          * @param {String} text The text to measure
14651          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14652          * in order to accurately measure the text height
14653          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14654          */
14655         measure : function(el, text, fixedWidth){
14656             if(!shared){
14657                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14658             }
14659             shared.bind(el);
14660             shared.setFixedWidth(fixedWidth || 'auto');
14661             return shared.getSize(text);
14662         },
14663
14664         /**
14665          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14666          * the overhead of multiple calls to initialize the style properties on each measurement.
14667          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14668          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14669          * in order to accurately measure the text height
14670          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14671          */
14672         createInstance : function(el, fixedWidth){
14673             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14674         }
14675     };
14676 }();
14677
14678  
14679
14680 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14681     var ml = new Roo.Element(document.createElement('div'));
14682     document.body.appendChild(ml.dom);
14683     ml.position('absolute');
14684     ml.setLeftTop(-1000, -1000);
14685     ml.hide();
14686
14687     if(fixedWidth){
14688         ml.setWidth(fixedWidth);
14689     }
14690      
14691     var instance = {
14692         /**
14693          * Returns the size of the specified text based on the internal element's style and width properties
14694          * @memberOf Roo.util.TextMetrics.Instance#
14695          * @param {String} text The text to measure
14696          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14697          */
14698         getSize : function(text){
14699             ml.update(text);
14700             var s = ml.getSize();
14701             ml.update('');
14702             return s;
14703         },
14704
14705         /**
14706          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14707          * that can affect the size of the rendered text
14708          * @memberOf Roo.util.TextMetrics.Instance#
14709          * @param {String/HTMLElement} el The element, dom node or id
14710          */
14711         bind : function(el){
14712             ml.setStyle(
14713                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14714             );
14715         },
14716
14717         /**
14718          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14719          * to set a fixed width in order to accurately measure the text height.
14720          * @memberOf Roo.util.TextMetrics.Instance#
14721          * @param {Number} width The width to set on the element
14722          */
14723         setFixedWidth : function(width){
14724             ml.setWidth(width);
14725         },
14726
14727         /**
14728          * Returns the measured width of the specified text
14729          * @memberOf Roo.util.TextMetrics.Instance#
14730          * @param {String} text The text to measure
14731          * @return {Number} width The width in pixels
14732          */
14733         getWidth : function(text){
14734             ml.dom.style.width = 'auto';
14735             return this.getSize(text).width;
14736         },
14737
14738         /**
14739          * Returns the measured height of the specified text.  For multiline text, be sure to call
14740          * {@link #setFixedWidth} if necessary.
14741          * @memberOf Roo.util.TextMetrics.Instance#
14742          * @param {String} text The text to measure
14743          * @return {Number} height The height in pixels
14744          */
14745         getHeight : function(text){
14746             return this.getSize(text).height;
14747         }
14748     };
14749
14750     instance.bind(bindTo);
14751
14752     return instance;
14753 };
14754
14755 // backwards compat
14756 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14757  * Based on:
14758  * Ext JS Library 1.1.1
14759  * Copyright(c) 2006-2007, Ext JS, LLC.
14760  *
14761  * Originally Released Under LGPL - original licence link has changed is not relivant.
14762  *
14763  * Fork - LGPL
14764  * <script type="text/javascript">
14765  */
14766
14767 /**
14768  * @class Roo.state.Provider
14769  * Abstract base class for state provider implementations. This class provides methods
14770  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14771  * Provider interface.
14772  */
14773 Roo.state.Provider = function(){
14774     /**
14775      * @event statechange
14776      * Fires when a state change occurs.
14777      * @param {Provider} this This state provider
14778      * @param {String} key The state key which was changed
14779      * @param {String} value The encoded value for the state
14780      */
14781     this.addEvents({
14782         "statechange": true
14783     });
14784     this.state = {};
14785     Roo.state.Provider.superclass.constructor.call(this);
14786 };
14787 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14788     /**
14789      * Returns the current value for a key
14790      * @param {String} name The key name
14791      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14792      * @return {Mixed} The state data
14793      */
14794     get : function(name, defaultValue){
14795         return typeof this.state[name] == "undefined" ?
14796             defaultValue : this.state[name];
14797     },
14798     
14799     /**
14800      * Clears a value from the state
14801      * @param {String} name The key name
14802      */
14803     clear : function(name){
14804         delete this.state[name];
14805         this.fireEvent("statechange", this, name, null);
14806     },
14807     
14808     /**
14809      * Sets the value for a key
14810      * @param {String} name The key name
14811      * @param {Mixed} value The value to set
14812      */
14813     set : function(name, value){
14814         this.state[name] = value;
14815         this.fireEvent("statechange", this, name, value);
14816     },
14817     
14818     /**
14819      * Decodes a string previously encoded with {@link #encodeValue}.
14820      * @param {String} value The value to decode
14821      * @return {Mixed} The decoded value
14822      */
14823     decodeValue : function(cookie){
14824         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14825         var matches = re.exec(unescape(cookie));
14826         if(!matches || !matches[1]) return; // non state cookie
14827         var type = matches[1];
14828         var v = matches[2];
14829         switch(type){
14830             case "n":
14831                 return parseFloat(v);
14832             case "d":
14833                 return new Date(Date.parse(v));
14834             case "b":
14835                 return (v == "1");
14836             case "a":
14837                 var all = [];
14838                 var values = v.split("^");
14839                 for(var i = 0, len = values.length; i < len; i++){
14840                     all.push(this.decodeValue(values[i]));
14841                 }
14842                 return all;
14843            case "o":
14844                 var all = {};
14845                 var values = v.split("^");
14846                 for(var i = 0, len = values.length; i < len; i++){
14847                     var kv = values[i].split("=");
14848                     all[kv[0]] = this.decodeValue(kv[1]);
14849                 }
14850                 return all;
14851            default:
14852                 return v;
14853         }
14854     },
14855     
14856     /**
14857      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14858      * @param {Mixed} value The value to encode
14859      * @return {String} The encoded value
14860      */
14861     encodeValue : function(v){
14862         var enc;
14863         if(typeof v == "number"){
14864             enc = "n:" + v;
14865         }else if(typeof v == "boolean"){
14866             enc = "b:" + (v ? "1" : "0");
14867         }else if(v instanceof Date){
14868             enc = "d:" + v.toGMTString();
14869         }else if(v instanceof Array){
14870             var flat = "";
14871             for(var i = 0, len = v.length; i < len; i++){
14872                 flat += this.encodeValue(v[i]);
14873                 if(i != len-1) flat += "^";
14874             }
14875             enc = "a:" + flat;
14876         }else if(typeof v == "object"){
14877             var flat = "";
14878             for(var key in v){
14879                 if(typeof v[key] != "function"){
14880                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14881                 }
14882             }
14883             enc = "o:" + flat.substring(0, flat.length-1);
14884         }else{
14885             enc = "s:" + v;
14886         }
14887         return escape(enc);        
14888     }
14889 });
14890
14891 /*
14892  * Based on:
14893  * Ext JS Library 1.1.1
14894  * Copyright(c) 2006-2007, Ext JS, LLC.
14895  *
14896  * Originally Released Under LGPL - original licence link has changed is not relivant.
14897  *
14898  * Fork - LGPL
14899  * <script type="text/javascript">
14900  */
14901 /**
14902  * @class Roo.state.Manager
14903  * This is the global state manager. By default all components that are "state aware" check this class
14904  * for state information if you don't pass them a custom state provider. In order for this class
14905  * to be useful, it must be initialized with a provider when your application initializes.
14906  <pre><code>
14907 // in your initialization function
14908 init : function(){
14909    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14910    ...
14911    // supposed you have a {@link Roo.BorderLayout}
14912    var layout = new Roo.BorderLayout(...);
14913    layout.restoreState();
14914    // or a {Roo.BasicDialog}
14915    var dialog = new Roo.BasicDialog(...);
14916    dialog.restoreState();
14917  </code></pre>
14918  * @singleton
14919  */
14920 Roo.state.Manager = function(){
14921     var provider = new Roo.state.Provider();
14922     
14923     return {
14924         /**
14925          * Configures the default state provider for your application
14926          * @param {Provider} stateProvider The state provider to set
14927          */
14928         setProvider : function(stateProvider){
14929             provider = stateProvider;
14930         },
14931         
14932         /**
14933          * Returns the current value for a key
14934          * @param {String} name The key name
14935          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14936          * @return {Mixed} The state data
14937          */
14938         get : function(key, defaultValue){
14939             return provider.get(key, defaultValue);
14940         },
14941         
14942         /**
14943          * Sets the value for a key
14944          * @param {String} name The key name
14945          * @param {Mixed} value The state data
14946          */
14947          set : function(key, value){
14948             provider.set(key, value);
14949         },
14950         
14951         /**
14952          * Clears a value from the state
14953          * @param {String} name The key name
14954          */
14955         clear : function(key){
14956             provider.clear(key);
14957         },
14958         
14959         /**
14960          * Gets the currently configured state provider
14961          * @return {Provider} The state provider
14962          */
14963         getProvider : function(){
14964             return provider;
14965         }
14966     };
14967 }();
14968 /*
14969  * Based on:
14970  * Ext JS Library 1.1.1
14971  * Copyright(c) 2006-2007, Ext JS, LLC.
14972  *
14973  * Originally Released Under LGPL - original licence link has changed is not relivant.
14974  *
14975  * Fork - LGPL
14976  * <script type="text/javascript">
14977  */
14978 /**
14979  * @class Roo.state.CookieProvider
14980  * @extends Roo.state.Provider
14981  * The default Provider implementation which saves state via cookies.
14982  * <br />Usage:
14983  <pre><code>
14984    var cp = new Roo.state.CookieProvider({
14985        path: "/cgi-bin/",
14986        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14987        domain: "roojs.com"
14988    })
14989    Roo.state.Manager.setProvider(cp);
14990  </code></pre>
14991  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14992  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14993  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14994  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14995  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14996  * domain the page is running on including the 'www' like 'www.roojs.com')
14997  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14998  * @constructor
14999  * Create a new CookieProvider
15000  * @param {Object} config The configuration object
15001  */
15002 Roo.state.CookieProvider = function(config){
15003     Roo.state.CookieProvider.superclass.constructor.call(this);
15004     this.path = "/";
15005     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15006     this.domain = null;
15007     this.secure = false;
15008     Roo.apply(this, config);
15009     this.state = this.readCookies();
15010 };
15011
15012 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15013     // private
15014     set : function(name, value){
15015         if(typeof value == "undefined" || value === null){
15016             this.clear(name);
15017             return;
15018         }
15019         this.setCookie(name, value);
15020         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15021     },
15022
15023     // private
15024     clear : function(name){
15025         this.clearCookie(name);
15026         Roo.state.CookieProvider.superclass.clear.call(this, name);
15027     },
15028
15029     // private
15030     readCookies : function(){
15031         var cookies = {};
15032         var c = document.cookie + ";";
15033         var re = /\s?(.*?)=(.*?);/g;
15034         var matches;
15035         while((matches = re.exec(c)) != null){
15036             var name = matches[1];
15037             var value = matches[2];
15038             if(name && name.substring(0,3) == "ys-"){
15039                 cookies[name.substr(3)] = this.decodeValue(value);
15040             }
15041         }
15042         return cookies;
15043     },
15044
15045     // private
15046     setCookie : function(name, value){
15047         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15048            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15049            ((this.path == null) ? "" : ("; path=" + this.path)) +
15050            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15051            ((this.secure == true) ? "; secure" : "");
15052     },
15053
15054     // private
15055     clearCookie : function(name){
15056         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15057            ((this.path == null) ? "" : ("; path=" + this.path)) +
15058            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15059            ((this.secure == true) ? "; secure" : "");
15060     }
15061 });/*
15062  * Based on:
15063  * Ext JS Library 1.1.1
15064  * Copyright(c) 2006-2007, Ext JS, LLC.
15065  *
15066  * Originally Released Under LGPL - original licence link has changed is not relivant.
15067  *
15068  * Fork - LGPL
15069  * <script type="text/javascript">
15070  */
15071  
15072
15073 /**
15074  * @class Roo.ComponentMgr
15075  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15076  * @singleton
15077  */
15078 Roo.ComponentMgr = function(){
15079     var all = new Roo.util.MixedCollection();
15080
15081     return {
15082         /**
15083          * Registers a component.
15084          * @param {Roo.Component} c The component
15085          */
15086         register : function(c){
15087             all.add(c);
15088         },
15089
15090         /**
15091          * Unregisters a component.
15092          * @param {Roo.Component} c The component
15093          */
15094         unregister : function(c){
15095             all.remove(c);
15096         },
15097
15098         /**
15099          * Returns a component by id
15100          * @param {String} id The component id
15101          */
15102         get : function(id){
15103             return all.get(id);
15104         },
15105
15106         /**
15107          * Registers a function that will be called when a specified component is added to ComponentMgr
15108          * @param {String} id The component id
15109          * @param {Funtction} fn The callback function
15110          * @param {Object} scope The scope of the callback
15111          */
15112         onAvailable : function(id, fn, scope){
15113             all.on("add", function(index, o){
15114                 if(o.id == id){
15115                     fn.call(scope || o, o);
15116                     all.un("add", fn, scope);
15117                 }
15118             });
15119         }
15120     };
15121 }();/*
15122  * Based on:
15123  * Ext JS Library 1.1.1
15124  * Copyright(c) 2006-2007, Ext JS, LLC.
15125  *
15126  * Originally Released Under LGPL - original licence link has changed is not relivant.
15127  *
15128  * Fork - LGPL
15129  * <script type="text/javascript">
15130  */
15131  
15132 /**
15133  * @class Roo.Component
15134  * @extends Roo.util.Observable
15135  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15136  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15137  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15138  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15139  * All visual components (widgets) that require rendering into a layout should subclass Component.
15140  * @constructor
15141  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15142  * 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
15143  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15144  */
15145 Roo.Component = function(config){
15146     config = config || {};
15147     if(config.tagName || config.dom || typeof config == "string"){ // element object
15148         config = {el: config, id: config.id || config};
15149     }
15150     this.initialConfig = config;
15151
15152     Roo.apply(this, config);
15153     this.addEvents({
15154         /**
15155          * @event disable
15156          * Fires after the component is disabled.
15157              * @param {Roo.Component} this
15158              */
15159         disable : true,
15160         /**
15161          * @event enable
15162          * Fires after the component is enabled.
15163              * @param {Roo.Component} this
15164              */
15165         enable : true,
15166         /**
15167          * @event beforeshow
15168          * Fires before the component is shown.  Return false to stop the show.
15169              * @param {Roo.Component} this
15170              */
15171         beforeshow : true,
15172         /**
15173          * @event show
15174          * Fires after the component is shown.
15175              * @param {Roo.Component} this
15176              */
15177         show : true,
15178         /**
15179          * @event beforehide
15180          * Fires before the component is hidden. Return false to stop the hide.
15181              * @param {Roo.Component} this
15182              */
15183         beforehide : true,
15184         /**
15185          * @event hide
15186          * Fires after the component is hidden.
15187              * @param {Roo.Component} this
15188              */
15189         hide : true,
15190         /**
15191          * @event beforerender
15192          * Fires before the component is rendered. Return false to stop the render.
15193              * @param {Roo.Component} this
15194              */
15195         beforerender : true,
15196         /**
15197          * @event render
15198          * Fires after the component is rendered.
15199              * @param {Roo.Component} this
15200              */
15201         render : true,
15202         /**
15203          * @event beforedestroy
15204          * Fires before the component is destroyed. Return false to stop the destroy.
15205              * @param {Roo.Component} this
15206              */
15207         beforedestroy : true,
15208         /**
15209          * @event destroy
15210          * Fires after the component is destroyed.
15211              * @param {Roo.Component} this
15212              */
15213         destroy : true
15214     });
15215     if(!this.id){
15216         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15217     }
15218     Roo.ComponentMgr.register(this);
15219     Roo.Component.superclass.constructor.call(this);
15220     this.initComponent();
15221     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15222         this.render(this.renderTo);
15223         delete this.renderTo;
15224     }
15225 };
15226
15227 /** @private */
15228 Roo.Component.AUTO_ID = 1000;
15229
15230 Roo.extend(Roo.Component, Roo.util.Observable, {
15231     /**
15232      * @scope Roo.Component.prototype
15233      * @type {Boolean}
15234      * true if this component is hidden. Read-only.
15235      */
15236     hidden : false,
15237     /**
15238      * @type {Boolean}
15239      * true if this component is disabled. Read-only.
15240      */
15241     disabled : false,
15242     /**
15243      * @type {Boolean}
15244      * true if this component has been rendered. Read-only.
15245      */
15246     rendered : false,
15247     
15248     /** @cfg {String} disableClass
15249      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15250      */
15251     disabledClass : "x-item-disabled",
15252         /** @cfg {Boolean} allowDomMove
15253          * Whether the component can move the Dom node when rendering (defaults to true).
15254          */
15255     allowDomMove : true,
15256     /** @cfg {String} hideMode (display|visibility)
15257      * How this component should hidden. Supported values are
15258      * "visibility" (css visibility), "offsets" (negative offset position) and
15259      * "display" (css display) - defaults to "display".
15260      */
15261     hideMode: 'display',
15262
15263     /** @private */
15264     ctype : "Roo.Component",
15265
15266     /**
15267      * @cfg {String} actionMode 
15268      * which property holds the element that used for  hide() / show() / disable() / enable()
15269      * default is 'el' 
15270      */
15271     actionMode : "el",
15272
15273     /** @private */
15274     getActionEl : function(){
15275         return this[this.actionMode];
15276     },
15277
15278     initComponent : Roo.emptyFn,
15279     /**
15280      * If this is a lazy rendering component, render it to its container element.
15281      * @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.
15282      */
15283     render : function(container, position){
15284         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15285             if(!container && this.el){
15286                 this.el = Roo.get(this.el);
15287                 container = this.el.dom.parentNode;
15288                 this.allowDomMove = false;
15289             }
15290             this.container = Roo.get(container);
15291             this.rendered = true;
15292             if(position !== undefined){
15293                 if(typeof position == 'number'){
15294                     position = this.container.dom.childNodes[position];
15295                 }else{
15296                     position = Roo.getDom(position);
15297                 }
15298             }
15299             this.onRender(this.container, position || null);
15300             if(this.cls){
15301                 this.el.addClass(this.cls);
15302                 delete this.cls;
15303             }
15304             if(this.style){
15305                 this.el.applyStyles(this.style);
15306                 delete this.style;
15307             }
15308             this.fireEvent("render", this);
15309             this.afterRender(this.container);
15310             if(this.hidden){
15311                 this.hide();
15312             }
15313             if(this.disabled){
15314                 this.disable();
15315             }
15316         }
15317         return this;
15318     },
15319
15320     /** @private */
15321     // default function is not really useful
15322     onRender : function(ct, position){
15323         if(this.el){
15324             this.el = Roo.get(this.el);
15325             if(this.allowDomMove !== false){
15326                 ct.dom.insertBefore(this.el.dom, position);
15327             }
15328         }
15329     },
15330
15331     /** @private */
15332     getAutoCreate : function(){
15333         var cfg = typeof this.autoCreate == "object" ?
15334                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15335         if(this.id && !cfg.id){
15336             cfg.id = this.id;
15337         }
15338         return cfg;
15339     },
15340
15341     /** @private */
15342     afterRender : Roo.emptyFn,
15343
15344     /**
15345      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15346      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15347      */
15348     destroy : function(){
15349         if(this.fireEvent("beforedestroy", this) !== false){
15350             this.purgeListeners();
15351             this.beforeDestroy();
15352             if(this.rendered){
15353                 this.el.removeAllListeners();
15354                 this.el.remove();
15355                 if(this.actionMode == "container"){
15356                     this.container.remove();
15357                 }
15358             }
15359             this.onDestroy();
15360             Roo.ComponentMgr.unregister(this);
15361             this.fireEvent("destroy", this);
15362         }
15363     },
15364
15365         /** @private */
15366     beforeDestroy : function(){
15367
15368     },
15369
15370         /** @private */
15371         onDestroy : function(){
15372
15373     },
15374
15375     /**
15376      * Returns the underlying {@link Roo.Element}.
15377      * @return {Roo.Element} The element
15378      */
15379     getEl : function(){
15380         return this.el;
15381     },
15382
15383     /**
15384      * Returns the id of this component.
15385      * @return {String}
15386      */
15387     getId : function(){
15388         return this.id;
15389     },
15390
15391     /**
15392      * Try to focus this component.
15393      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15394      * @return {Roo.Component} this
15395      */
15396     focus : function(selectText){
15397         if(this.rendered){
15398             this.el.focus();
15399             if(selectText === true){
15400                 this.el.dom.select();
15401             }
15402         }
15403         return this;
15404     },
15405
15406     /** @private */
15407     blur : function(){
15408         if(this.rendered){
15409             this.el.blur();
15410         }
15411         return this;
15412     },
15413
15414     /**
15415      * Disable this component.
15416      * @return {Roo.Component} this
15417      */
15418     disable : function(){
15419         if(this.rendered){
15420             this.onDisable();
15421         }
15422         this.disabled = true;
15423         this.fireEvent("disable", this);
15424         return this;
15425     },
15426
15427         // private
15428     onDisable : function(){
15429         this.getActionEl().addClass(this.disabledClass);
15430         this.el.dom.disabled = true;
15431     },
15432
15433     /**
15434      * Enable this component.
15435      * @return {Roo.Component} this
15436      */
15437     enable : function(){
15438         if(this.rendered){
15439             this.onEnable();
15440         }
15441         this.disabled = false;
15442         this.fireEvent("enable", this);
15443         return this;
15444     },
15445
15446         // private
15447     onEnable : function(){
15448         this.getActionEl().removeClass(this.disabledClass);
15449         this.el.dom.disabled = false;
15450     },
15451
15452     /**
15453      * Convenience function for setting disabled/enabled by boolean.
15454      * @param {Boolean} disabled
15455      */
15456     setDisabled : function(disabled){
15457         this[disabled ? "disable" : "enable"]();
15458     },
15459
15460     /**
15461      * Show this component.
15462      * @return {Roo.Component} this
15463      */
15464     show: function(){
15465         if(this.fireEvent("beforeshow", this) !== false){
15466             this.hidden = false;
15467             if(this.rendered){
15468                 this.onShow();
15469             }
15470             this.fireEvent("show", this);
15471         }
15472         return this;
15473     },
15474
15475     // private
15476     onShow : function(){
15477         var ae = this.getActionEl();
15478         if(this.hideMode == 'visibility'){
15479             ae.dom.style.visibility = "visible";
15480         }else if(this.hideMode == 'offsets'){
15481             ae.removeClass('x-hidden');
15482         }else{
15483             ae.dom.style.display = "";
15484         }
15485     },
15486
15487     /**
15488      * Hide this component.
15489      * @return {Roo.Component} this
15490      */
15491     hide: function(){
15492         if(this.fireEvent("beforehide", this) !== false){
15493             this.hidden = true;
15494             if(this.rendered){
15495                 this.onHide();
15496             }
15497             this.fireEvent("hide", this);
15498         }
15499         return this;
15500     },
15501
15502     // private
15503     onHide : function(){
15504         var ae = this.getActionEl();
15505         if(this.hideMode == 'visibility'){
15506             ae.dom.style.visibility = "hidden";
15507         }else if(this.hideMode == 'offsets'){
15508             ae.addClass('x-hidden');
15509         }else{
15510             ae.dom.style.display = "none";
15511         }
15512     },
15513
15514     /**
15515      * Convenience function to hide or show this component by boolean.
15516      * @param {Boolean} visible True to show, false to hide
15517      * @return {Roo.Component} this
15518      */
15519     setVisible: function(visible){
15520         if(visible) {
15521             this.show();
15522         }else{
15523             this.hide();
15524         }
15525         return this;
15526     },
15527
15528     /**
15529      * Returns true if this component is visible.
15530      */
15531     isVisible : function(){
15532         return this.getActionEl().isVisible();
15533     },
15534
15535     cloneConfig : function(overrides){
15536         overrides = overrides || {};
15537         var id = overrides.id || Roo.id();
15538         var cfg = Roo.applyIf(overrides, this.initialConfig);
15539         cfg.id = id; // prevent dup id
15540         return new this.constructor(cfg);
15541     }
15542 });/*
15543  * Based on:
15544  * Ext JS Library 1.1.1
15545  * Copyright(c) 2006-2007, Ext JS, LLC.
15546  *
15547  * Originally Released Under LGPL - original licence link has changed is not relivant.
15548  *
15549  * Fork - LGPL
15550  * <script type="text/javascript">
15551  */
15552
15553 /**
15554  * @class Roo.BoxComponent
15555  * @extends Roo.Component
15556  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15557  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15558  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15559  * layout containers.
15560  * @constructor
15561  * @param {Roo.Element/String/Object} config The configuration options.
15562  */
15563 Roo.BoxComponent = function(config){
15564     Roo.Component.call(this, config);
15565     this.addEvents({
15566         /**
15567          * @event resize
15568          * Fires after the component is resized.
15569              * @param {Roo.Component} this
15570              * @param {Number} adjWidth The box-adjusted width that was set
15571              * @param {Number} adjHeight The box-adjusted height that was set
15572              * @param {Number} rawWidth The width that was originally specified
15573              * @param {Number} rawHeight The height that was originally specified
15574              */
15575         resize : true,
15576         /**
15577          * @event move
15578          * Fires after the component is moved.
15579              * @param {Roo.Component} this
15580              * @param {Number} x The new x position
15581              * @param {Number} y The new y position
15582              */
15583         move : true
15584     });
15585 };
15586
15587 Roo.extend(Roo.BoxComponent, Roo.Component, {
15588     // private, set in afterRender to signify that the component has been rendered
15589     boxReady : false,
15590     // private, used to defer height settings to subclasses
15591     deferHeight: false,
15592     /** @cfg {Number} width
15593      * width (optional) size of component
15594      */
15595      /** @cfg {Number} height
15596      * height (optional) size of component
15597      */
15598      
15599     /**
15600      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15601      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15602      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15603      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15604      * @return {Roo.BoxComponent} this
15605      */
15606     setSize : function(w, h){
15607         // support for standard size objects
15608         if(typeof w == 'object'){
15609             h = w.height;
15610             w = w.width;
15611         }
15612         // not rendered
15613         if(!this.boxReady){
15614             this.width = w;
15615             this.height = h;
15616             return this;
15617         }
15618
15619         // prevent recalcs when not needed
15620         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15621             return this;
15622         }
15623         this.lastSize = {width: w, height: h};
15624
15625         var adj = this.adjustSize(w, h);
15626         var aw = adj.width, ah = adj.height;
15627         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15628             var rz = this.getResizeEl();
15629             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15630                 rz.setSize(aw, ah);
15631             }else if(!this.deferHeight && ah !== undefined){
15632                 rz.setHeight(ah);
15633             }else if(aw !== undefined){
15634                 rz.setWidth(aw);
15635             }
15636             this.onResize(aw, ah, w, h);
15637             this.fireEvent('resize', this, aw, ah, w, h);
15638         }
15639         return this;
15640     },
15641
15642     /**
15643      * Gets the current size of the component's underlying element.
15644      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15645      */
15646     getSize : function(){
15647         return this.el.getSize();
15648     },
15649
15650     /**
15651      * Gets the current XY position of the component's underlying element.
15652      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15653      * @return {Array} The XY position of the element (e.g., [100, 200])
15654      */
15655     getPosition : function(local){
15656         if(local === true){
15657             return [this.el.getLeft(true), this.el.getTop(true)];
15658         }
15659         return this.xy || this.el.getXY();
15660     },
15661
15662     /**
15663      * Gets the current box measurements of the component's underlying element.
15664      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15665      * @returns {Object} box An object in the format {x, y, width, height}
15666      */
15667     getBox : function(local){
15668         var s = this.el.getSize();
15669         if(local){
15670             s.x = this.el.getLeft(true);
15671             s.y = this.el.getTop(true);
15672         }else{
15673             var xy = this.xy || this.el.getXY();
15674             s.x = xy[0];
15675             s.y = xy[1];
15676         }
15677         return s;
15678     },
15679
15680     /**
15681      * Sets the current box measurements of the component's underlying element.
15682      * @param {Object} box An object in the format {x, y, width, height}
15683      * @returns {Roo.BoxComponent} this
15684      */
15685     updateBox : function(box){
15686         this.setSize(box.width, box.height);
15687         this.setPagePosition(box.x, box.y);
15688         return this;
15689     },
15690
15691     // protected
15692     getResizeEl : function(){
15693         return this.resizeEl || this.el;
15694     },
15695
15696     // protected
15697     getPositionEl : function(){
15698         return this.positionEl || this.el;
15699     },
15700
15701     /**
15702      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15703      * This method fires the move event.
15704      * @param {Number} left The new left
15705      * @param {Number} top The new top
15706      * @returns {Roo.BoxComponent} this
15707      */
15708     setPosition : function(x, y){
15709         this.x = x;
15710         this.y = y;
15711         if(!this.boxReady){
15712             return this;
15713         }
15714         var adj = this.adjustPosition(x, y);
15715         var ax = adj.x, ay = adj.y;
15716
15717         var el = this.getPositionEl();
15718         if(ax !== undefined || ay !== undefined){
15719             if(ax !== undefined && ay !== undefined){
15720                 el.setLeftTop(ax, ay);
15721             }else if(ax !== undefined){
15722                 el.setLeft(ax);
15723             }else if(ay !== undefined){
15724                 el.setTop(ay);
15725             }
15726             this.onPosition(ax, ay);
15727             this.fireEvent('move', this, ax, ay);
15728         }
15729         return this;
15730     },
15731
15732     /**
15733      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15734      * This method fires the move event.
15735      * @param {Number} x The new x position
15736      * @param {Number} y The new y position
15737      * @returns {Roo.BoxComponent} this
15738      */
15739     setPagePosition : function(x, y){
15740         this.pageX = x;
15741         this.pageY = y;
15742         if(!this.boxReady){
15743             return;
15744         }
15745         if(x === undefined || y === undefined){ // cannot translate undefined points
15746             return;
15747         }
15748         var p = this.el.translatePoints(x, y);
15749         this.setPosition(p.left, p.top);
15750         return this;
15751     },
15752
15753     // private
15754     onRender : function(ct, position){
15755         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15756         if(this.resizeEl){
15757             this.resizeEl = Roo.get(this.resizeEl);
15758         }
15759         if(this.positionEl){
15760             this.positionEl = Roo.get(this.positionEl);
15761         }
15762     },
15763
15764     // private
15765     afterRender : function(){
15766         Roo.BoxComponent.superclass.afterRender.call(this);
15767         this.boxReady = true;
15768         this.setSize(this.width, this.height);
15769         if(this.x || this.y){
15770             this.setPosition(this.x, this.y);
15771         }
15772         if(this.pageX || this.pageY){
15773             this.setPagePosition(this.pageX, this.pageY);
15774         }
15775     },
15776
15777     /**
15778      * Force the component's size to recalculate based on the underlying element's current height and width.
15779      * @returns {Roo.BoxComponent} this
15780      */
15781     syncSize : function(){
15782         delete this.lastSize;
15783         this.setSize(this.el.getWidth(), this.el.getHeight());
15784         return this;
15785     },
15786
15787     /**
15788      * Called after the component is resized, this method is empty by default but can be implemented by any
15789      * subclass that needs to perform custom logic after a resize occurs.
15790      * @param {Number} adjWidth The box-adjusted width that was set
15791      * @param {Number} adjHeight The box-adjusted height that was set
15792      * @param {Number} rawWidth The width that was originally specified
15793      * @param {Number} rawHeight The height that was originally specified
15794      */
15795     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15796
15797     },
15798
15799     /**
15800      * Called after the component is moved, this method is empty by default but can be implemented by any
15801      * subclass that needs to perform custom logic after a move occurs.
15802      * @param {Number} x The new x position
15803      * @param {Number} y The new y position
15804      */
15805     onPosition : function(x, y){
15806
15807     },
15808
15809     // private
15810     adjustSize : function(w, h){
15811         if(this.autoWidth){
15812             w = 'auto';
15813         }
15814         if(this.autoHeight){
15815             h = 'auto';
15816         }
15817         return {width : w, height: h};
15818     },
15819
15820     // private
15821     adjustPosition : function(x, y){
15822         return {x : x, y: y};
15823     }
15824 });/*
15825  * Original code for Roojs - LGPL
15826  * <script type="text/javascript">
15827  */
15828  
15829 /**
15830  * @class Roo.XComponent
15831  * A delayed Element creator...
15832  * Or a way to group chunks of interface together.
15833  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15834  *  used in conjunction with XComponent.build() it will create an instance of each element,
15835  *  then call addxtype() to build the User interface.
15836  * 
15837  * Mypart.xyx = new Roo.XComponent({
15838
15839     parent : 'Mypart.xyz', // empty == document.element.!!
15840     order : '001',
15841     name : 'xxxx'
15842     region : 'xxxx'
15843     disabled : function() {} 
15844      
15845     tree : function() { // return an tree of xtype declared components
15846         var MODULE = this;
15847         return 
15848         {
15849             xtype : 'NestedLayoutPanel',
15850             // technicall
15851         }
15852      ]
15853  *})
15854  *
15855  *
15856  * It can be used to build a big heiracy, with parent etc.
15857  * or you can just use this to render a single compoent to a dom element
15858  * MYPART.render(Roo.Element | String(id) | dom_element )
15859  *
15860  *
15861  * Usage patterns.
15862  *
15863  * Classic Roo
15864  *
15865  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15866  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15867  *
15868  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15869  *
15870  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15871  * - if mulitple topModules exist, the last one is defined as the top module.
15872  *
15873  * Embeded Roo
15874  * 
15875  * When the top level or multiple modules are to embedded into a existing HTML page,
15876  * the parent element can container '#id' of the element where the module will be drawn.
15877  *
15878  * Bootstrap Roo
15879  *
15880  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15881  * it relies more on a include mechanism, where sub modules are included into an outer page.
15882  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15883  * 
15884  * Bootstrap Roo Included elements
15885  *
15886  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15887  * hence confusing the component builder as it thinks there are multiple top level elements. 
15888  *
15889  * 
15890  * 
15891  * @extends Roo.util.Observable
15892  * @constructor
15893  * @param cfg {Object} configuration of component
15894  * 
15895  */
15896 Roo.XComponent = function(cfg) {
15897     Roo.apply(this, cfg);
15898     this.addEvents({ 
15899         /**
15900              * @event built
15901              * Fires when this the componnt is built
15902              * @param {Roo.XComponent} c the component
15903              */
15904         'built' : true
15905         
15906     });
15907     this.region = this.region || 'center'; // default..
15908     Roo.XComponent.register(this);
15909     this.modules = false;
15910     this.el = false; // where the layout goes..
15911     
15912     
15913 }
15914 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15915     /**
15916      * @property el
15917      * The created element (with Roo.factory())
15918      * @type {Roo.Layout}
15919      */
15920     el  : false,
15921     
15922     /**
15923      * @property el
15924      * for BC  - use el in new code
15925      * @type {Roo.Layout}
15926      */
15927     panel : false,
15928     
15929     /**
15930      * @property layout
15931      * for BC  - use el in new code
15932      * @type {Roo.Layout}
15933      */
15934     layout : false,
15935     
15936      /**
15937      * @cfg {Function|boolean} disabled
15938      * If this module is disabled by some rule, return true from the funtion
15939      */
15940     disabled : false,
15941     
15942     /**
15943      * @cfg {String} parent 
15944      * Name of parent element which it get xtype added to..
15945      */
15946     parent: false,
15947     
15948     /**
15949      * @cfg {String} order
15950      * Used to set the order in which elements are created (usefull for multiple tabs)
15951      */
15952     
15953     order : false,
15954     /**
15955      * @cfg {String} name
15956      * String to display while loading.
15957      */
15958     name : false,
15959     /**
15960      * @cfg {String} region
15961      * Region to render component to (defaults to center)
15962      */
15963     region : 'center',
15964     
15965     /**
15966      * @cfg {Array} items
15967      * A single item array - the first element is the root of the tree..
15968      * It's done this way to stay compatible with the Xtype system...
15969      */
15970     items : false,
15971     
15972     /**
15973      * @property _tree
15974      * The method that retuns the tree of parts that make up this compoennt 
15975      * @type {function}
15976      */
15977     _tree  : false,
15978     
15979      /**
15980      * render
15981      * render element to dom or tree
15982      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15983      */
15984     
15985     render : function(el)
15986     {
15987         
15988         el = el || false;
15989         var hp = this.parent ? 1 : 0;
15990         Roo.debug &&  Roo.log(this);
15991         
15992         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15993             // if parent is a '#.....' string, then let's use that..
15994             var ename = this.parent.substr(1);
15995             this.parent = false;
15996             Roo.debug && Roo.log(ename);
15997             switch (ename) {
15998                 case 'bootstrap-body' :
15999                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
16000                         this.parent = { el :  new  Roo.bootstrap.Body() };
16001                         Roo.debug && Roo.log("setting el to doc body");
16002                          
16003                     } else {
16004                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16005                     }
16006                     break;
16007                 case 'bootstrap':
16008                     this.parent = { el : true};
16009                     // fall through
16010                 default:
16011                     el = Roo.get(ename);
16012                     break;
16013             }
16014                 
16015             
16016             if (!el && !this.parent) {
16017                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16018                 return;
16019             }
16020         }
16021         Roo.debug && Roo.log("EL:");
16022         Roo.debug && Roo.log(el);
16023         Roo.debug && Roo.log("this.parent.el:");
16024         Roo.debug && Roo.log(this.parent.el);
16025         
16026         var tree = this._tree ? this._tree() : this.tree();
16027
16028         // altertive root elements ??? - we need a better way to indicate these.
16029         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16030                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16031         
16032         if (!this.parent && is_alt) {
16033             //el = Roo.get(document.body);
16034             this.parent = { el : true };
16035         }
16036             
16037             
16038         
16039         if (!this.parent) {
16040             
16041             Roo.debug && Roo.log("no parent - creating one");
16042             
16043             el = el ? Roo.get(el) : false;      
16044             
16045             // it's a top level one..
16046             this.parent =  {
16047                 el : new Roo.BorderLayout(el || document.body, {
16048                 
16049                      center: {
16050                          titlebar: false,
16051                          autoScroll:false,
16052                          closeOnTab: true,
16053                          tabPosition: 'top',
16054                           //resizeTabs: true,
16055                          alwaysShowTabs: el && hp? false :  true,
16056                          hideTabs: el || !hp ? true :  false,
16057                          minTabWidth: 140
16058                      }
16059                  })
16060             }
16061         }
16062         
16063         if (!this.parent.el) {
16064                 // probably an old style ctor, which has been disabled.
16065                 return;
16066
16067         }
16068                 // The 'tree' method is  '_tree now' 
16069             
16070         tree.region = tree.region || this.region;
16071         
16072         if (this.parent.el === true) {
16073             // bootstrap... - body..
16074             this.parent.el = Roo.factory(tree);
16075         }
16076         
16077         this.el = this.parent.el.addxtype(tree);
16078         this.fireEvent('built', this);
16079         
16080         this.panel = this.el;
16081         this.layout = this.panel.layout;
16082         this.parentLayout = this.parent.layout  || false;  
16083          
16084     }
16085     
16086 });
16087
16088 Roo.apply(Roo.XComponent, {
16089     /**
16090      * @property  hideProgress
16091      * true to disable the building progress bar.. usefull on single page renders.
16092      * @type Boolean
16093      */
16094     hideProgress : false,
16095     /**
16096      * @property  buildCompleted
16097      * True when the builder has completed building the interface.
16098      * @type Boolean
16099      */
16100     buildCompleted : false,
16101      
16102     /**
16103      * @property  topModule
16104      * the upper most module - uses document.element as it's constructor.
16105      * @type Object
16106      */
16107      
16108     topModule  : false,
16109       
16110     /**
16111      * @property  modules
16112      * array of modules to be created by registration system.
16113      * @type {Array} of Roo.XComponent
16114      */
16115     
16116     modules : [],
16117     /**
16118      * @property  elmodules
16119      * array of modules to be created by which use #ID 
16120      * @type {Array} of Roo.XComponent
16121      */
16122      
16123     elmodules : [],
16124
16125      /**
16126      * @property  build_from_html
16127      * Build elements from html - used by bootstrap HTML stuff 
16128      *    - this is cleared after build is completed
16129      * @type {boolean} true  (default false)
16130      */
16131      
16132     build_from_html : false,
16133
16134     /**
16135      * Register components to be built later.
16136      *
16137      * This solves the following issues
16138      * - Building is not done on page load, but after an authentication process has occured.
16139      * - Interface elements are registered on page load
16140      * - Parent Interface elements may not be loaded before child, so this handles that..
16141      * 
16142      *
16143      * example:
16144      * 
16145      * MyApp.register({
16146           order : '000001',
16147           module : 'Pman.Tab.projectMgr',
16148           region : 'center',
16149           parent : 'Pman.layout',
16150           disabled : false,  // or use a function..
16151         })
16152      
16153      * * @param {Object} details about module
16154      */
16155     register : function(obj) {
16156                 
16157         Roo.XComponent.event.fireEvent('register', obj);
16158         switch(typeof(obj.disabled) ) {
16159                 
16160             case 'undefined':
16161                 break;
16162             
16163             case 'function':
16164                 if ( obj.disabled() ) {
16165                         return;
16166                 }
16167                 break;
16168             
16169             default:
16170                 if (obj.disabled) {
16171                         return;
16172                 }
16173                 break;
16174         }
16175                 
16176         this.modules.push(obj);
16177          
16178     },
16179     /**
16180      * convert a string to an object..
16181      * eg. 'AAA.BBB' -> finds AAA.BBB
16182
16183      */
16184     
16185     toObject : function(str)
16186     {
16187         if (!str || typeof(str) == 'object') {
16188             return str;
16189         }
16190         if (str.substring(0,1) == '#') {
16191             return str;
16192         }
16193
16194         var ar = str.split('.');
16195         var rt, o;
16196         rt = ar.shift();
16197             /** eval:var:o */
16198         try {
16199             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16200         } catch (e) {
16201             throw "Module not found : " + str;
16202         }
16203         
16204         if (o === false) {
16205             throw "Module not found : " + str;
16206         }
16207         Roo.each(ar, function(e) {
16208             if (typeof(o[e]) == 'undefined') {
16209                 throw "Module not found : " + str;
16210             }
16211             o = o[e];
16212         });
16213         
16214         return o;
16215         
16216     },
16217     
16218     
16219     /**
16220      * move modules into their correct place in the tree..
16221      * 
16222      */
16223     preBuild : function ()
16224     {
16225         var _t = this;
16226         Roo.each(this.modules , function (obj)
16227         {
16228             Roo.XComponent.event.fireEvent('beforebuild', obj);
16229             
16230             var opar = obj.parent;
16231             try { 
16232                 obj.parent = this.toObject(opar);
16233             } catch(e) {
16234                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16235                 return;
16236             }
16237             
16238             if (!obj.parent) {
16239                 Roo.debug && Roo.log("GOT top level module");
16240                 Roo.debug && Roo.log(obj);
16241                 obj.modules = new Roo.util.MixedCollection(false, 
16242                     function(o) { return o.order + '' }
16243                 );
16244                 this.topModule = obj;
16245                 return;
16246             }
16247                         // parent is a string (usually a dom element name..)
16248             if (typeof(obj.parent) == 'string') {
16249                 this.elmodules.push(obj);
16250                 return;
16251             }
16252             if (obj.parent.constructor != Roo.XComponent) {
16253                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16254             }
16255             if (!obj.parent.modules) {
16256                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16257                     function(o) { return o.order + '' }
16258                 );
16259             }
16260             if (obj.parent.disabled) {
16261                 obj.disabled = true;
16262             }
16263             obj.parent.modules.add(obj);
16264         }, this);
16265     },
16266     
16267      /**
16268      * make a list of modules to build.
16269      * @return {Array} list of modules. 
16270      */ 
16271     
16272     buildOrder : function()
16273     {
16274         var _this = this;
16275         var cmp = function(a,b) {   
16276             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16277         };
16278         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16279             throw "No top level modules to build";
16280         }
16281         
16282         // make a flat list in order of modules to build.
16283         var mods = this.topModule ? [ this.topModule ] : [];
16284                 
16285         
16286         // elmodules (is a list of DOM based modules )
16287         Roo.each(this.elmodules, function(e) {
16288             mods.push(e);
16289             if (!this.topModule &&
16290                 typeof(e.parent) == 'string' &&
16291                 e.parent.substring(0,1) == '#' &&
16292                 Roo.get(e.parent.substr(1))
16293                ) {
16294                 
16295                 _this.topModule = e;
16296             }
16297             
16298         });
16299
16300         
16301         // add modules to their parents..
16302         var addMod = function(m) {
16303             Roo.debug && Roo.log("build Order: add: " + m.name);
16304                 
16305             mods.push(m);
16306             if (m.modules && !m.disabled) {
16307                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16308                 m.modules.keySort('ASC',  cmp );
16309                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16310     
16311                 m.modules.each(addMod);
16312             } else {
16313                 Roo.debug && Roo.log("build Order: no child modules");
16314             }
16315             // not sure if this is used any more..
16316             if (m.finalize) {
16317                 m.finalize.name = m.name + " (clean up) ";
16318                 mods.push(m.finalize);
16319             }
16320             
16321         }
16322         if (this.topModule && this.topModule.modules) { 
16323             this.topModule.modules.keySort('ASC',  cmp );
16324             this.topModule.modules.each(addMod);
16325         } 
16326         return mods;
16327     },
16328     
16329      /**
16330      * Build the registered modules.
16331      * @param {Object} parent element.
16332      * @param {Function} optional method to call after module has been added.
16333      * 
16334      */ 
16335    
16336     build : function(opts) 
16337     {
16338         
16339         if (typeof(opts) != 'undefined') {
16340             Roo.apply(this,opts);
16341         }
16342         
16343         this.preBuild();
16344         var mods = this.buildOrder();
16345       
16346         //this.allmods = mods;
16347         //Roo.debug && Roo.log(mods);
16348         //return;
16349         if (!mods.length) { // should not happen
16350             throw "NO modules!!!";
16351         }
16352         
16353         
16354         var msg = "Building Interface...";
16355         // flash it up as modal - so we store the mask!?
16356         if (!this.hideProgress && Roo.MessageBox) {
16357             Roo.MessageBox.show({ title: 'loading' });
16358             Roo.MessageBox.show({
16359                title: "Please wait...",
16360                msg: msg,
16361                width:450,
16362                progress:true,
16363                closable:false,
16364                modal: false
16365               
16366             });
16367         }
16368         var total = mods.length;
16369         
16370         var _this = this;
16371         var progressRun = function() {
16372             if (!mods.length) {
16373                 Roo.debug && Roo.log('hide?');
16374                 if (!this.hideProgress && Roo.MessageBox) {
16375                     Roo.MessageBox.hide();
16376                 }
16377                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16378                 
16379                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16380                 
16381                 // THE END...
16382                 return false;   
16383             }
16384             
16385             var m = mods.shift();
16386             
16387             
16388             Roo.debug && Roo.log(m);
16389             // not sure if this is supported any more.. - modules that are are just function
16390             if (typeof(m) == 'function') { 
16391                 m.call(this);
16392                 return progressRun.defer(10, _this);
16393             } 
16394             
16395             
16396             msg = "Building Interface " + (total  - mods.length) + 
16397                     " of " + total + 
16398                     (m.name ? (' - ' + m.name) : '');
16399                         Roo.debug && Roo.log(msg);
16400             if (!this.hideProgress &&  Roo.MessageBox) { 
16401                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16402             }
16403             
16404          
16405             // is the module disabled?
16406             var disabled = (typeof(m.disabled) == 'function') ?
16407                 m.disabled.call(m.module.disabled) : m.disabled;    
16408             
16409             
16410             if (disabled) {
16411                 return progressRun(); // we do not update the display!
16412             }
16413             
16414             // now build 
16415             
16416                         
16417                         
16418             m.render();
16419             // it's 10 on top level, and 1 on others??? why...
16420             return progressRun.defer(10, _this);
16421              
16422         }
16423         progressRun.defer(1, _this);
16424      
16425         
16426         
16427     },
16428         
16429         
16430         /**
16431          * Event Object.
16432          *
16433          *
16434          */
16435         event: false, 
16436     /**
16437          * wrapper for event.on - aliased later..  
16438          * Typically use to register a event handler for register:
16439          *
16440          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16441          *
16442          */
16443     on : false
16444    
16445     
16446     
16447 });
16448
16449 Roo.XComponent.event = new Roo.util.Observable({
16450                 events : { 
16451                         /**
16452                          * @event register
16453                          * Fires when an Component is registered,
16454                          * set the disable property on the Component to stop registration.
16455                          * @param {Roo.XComponent} c the component being registerd.
16456                          * 
16457                          */
16458                         'register' : true,
16459             /**
16460                          * @event beforebuild
16461                          * Fires before each Component is built
16462                          * can be used to apply permissions.
16463                          * @param {Roo.XComponent} c the component being registerd.
16464                          * 
16465                          */
16466                         'beforebuild' : true,
16467                         /**
16468                          * @event buildcomplete
16469                          * Fires on the top level element when all elements have been built
16470                          * @param {Roo.XComponent} the top level component.
16471                          */
16472                         'buildcomplete' : true
16473                         
16474                 }
16475 });
16476
16477 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16478  /*
16479  * Based on:
16480  * Ext JS Library 1.1.1
16481  * Copyright(c) 2006-2007, Ext JS, LLC.
16482  *
16483  * Originally Released Under LGPL - original licence link has changed is not relivant.
16484  *
16485  * Fork - LGPL
16486  * <script type="text/javascript">
16487  */
16488
16489
16490
16491 /*
16492  * These classes are derivatives of the similarly named classes in the YUI Library.
16493  * The original license:
16494  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16495  * Code licensed under the BSD License:
16496  * http://developer.yahoo.net/yui/license.txt
16497  */
16498
16499 (function() {
16500
16501 var Event=Roo.EventManager;
16502 var Dom=Roo.lib.Dom;
16503
16504 /**
16505  * @class Roo.dd.DragDrop
16506  * @extends Roo.util.Observable
16507  * Defines the interface and base operation of items that that can be
16508  * dragged or can be drop targets.  It was designed to be extended, overriding
16509  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16510  * Up to three html elements can be associated with a DragDrop instance:
16511  * <ul>
16512  * <li>linked element: the element that is passed into the constructor.
16513  * This is the element which defines the boundaries for interaction with
16514  * other DragDrop objects.</li>
16515  * <li>handle element(s): The drag operation only occurs if the element that
16516  * was clicked matches a handle element.  By default this is the linked
16517  * element, but there are times that you will want only a portion of the
16518  * linked element to initiate the drag operation, and the setHandleElId()
16519  * method provides a way to define this.</li>
16520  * <li>drag element: this represents the element that would be moved along
16521  * with the cursor during a drag operation.  By default, this is the linked
16522  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16523  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16524  * </li>
16525  * </ul>
16526  * This class should not be instantiated until the onload event to ensure that
16527  * the associated elements are available.
16528  * The following would define a DragDrop obj that would interact with any
16529  * other DragDrop obj in the "group1" group:
16530  * <pre>
16531  *  dd = new Roo.dd.DragDrop("div1", "group1");
16532  * </pre>
16533  * Since none of the event handlers have been implemented, nothing would
16534  * actually happen if you were to run the code above.  Normally you would
16535  * override this class or one of the default implementations, but you can
16536  * also override the methods you want on an instance of the class...
16537  * <pre>
16538  *  dd.onDragDrop = function(e, id) {
16539  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16540  *  }
16541  * </pre>
16542  * @constructor
16543  * @param {String} id of the element that is linked to this instance
16544  * @param {String} sGroup the group of related DragDrop objects
16545  * @param {object} config an object containing configurable attributes
16546  *                Valid properties for DragDrop:
16547  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16548  */
16549 Roo.dd.DragDrop = function(id, sGroup, config) {
16550     if (id) {
16551         this.init(id, sGroup, config);
16552     }
16553     
16554 };
16555
16556 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16557
16558     /**
16559      * The id of the element associated with this object.  This is what we
16560      * refer to as the "linked element" because the size and position of
16561      * this element is used to determine when the drag and drop objects have
16562      * interacted.
16563      * @property id
16564      * @type String
16565      */
16566     id: null,
16567
16568     /**
16569      * Configuration attributes passed into the constructor
16570      * @property config
16571      * @type object
16572      */
16573     config: null,
16574
16575     /**
16576      * The id of the element that will be dragged.  By default this is same
16577      * as the linked element , but could be changed to another element. Ex:
16578      * Roo.dd.DDProxy
16579      * @property dragElId
16580      * @type String
16581      * @private
16582      */
16583     dragElId: null,
16584
16585     /**
16586      * the id of the element that initiates the drag operation.  By default
16587      * this is the linked element, but could be changed to be a child of this
16588      * element.  This lets us do things like only starting the drag when the
16589      * header element within the linked html element is clicked.
16590      * @property handleElId
16591      * @type String
16592      * @private
16593      */
16594     handleElId: null,
16595
16596     /**
16597      * An associative array of HTML tags that will be ignored if clicked.
16598      * @property invalidHandleTypes
16599      * @type {string: string}
16600      */
16601     invalidHandleTypes: null,
16602
16603     /**
16604      * An associative array of ids for elements that will be ignored if clicked
16605      * @property invalidHandleIds
16606      * @type {string: string}
16607      */
16608     invalidHandleIds: null,
16609
16610     /**
16611      * An indexted array of css class names for elements that will be ignored
16612      * if clicked.
16613      * @property invalidHandleClasses
16614      * @type string[]
16615      */
16616     invalidHandleClasses: null,
16617
16618     /**
16619      * The linked element's absolute X position at the time the drag was
16620      * started
16621      * @property startPageX
16622      * @type int
16623      * @private
16624      */
16625     startPageX: 0,
16626
16627     /**
16628      * The linked element's absolute X position at the time the drag was
16629      * started
16630      * @property startPageY
16631      * @type int
16632      * @private
16633      */
16634     startPageY: 0,
16635
16636     /**
16637      * The group defines a logical collection of DragDrop objects that are
16638      * related.  Instances only get events when interacting with other
16639      * DragDrop object in the same group.  This lets us define multiple
16640      * groups using a single DragDrop subclass if we want.
16641      * @property groups
16642      * @type {string: string}
16643      */
16644     groups: null,
16645
16646     /**
16647      * Individual drag/drop instances can be locked.  This will prevent
16648      * onmousedown start drag.
16649      * @property locked
16650      * @type boolean
16651      * @private
16652      */
16653     locked: false,
16654
16655     /**
16656      * Lock this instance
16657      * @method lock
16658      */
16659     lock: function() { this.locked = true; },
16660
16661     /**
16662      * Unlock this instace
16663      * @method unlock
16664      */
16665     unlock: function() { this.locked = false; },
16666
16667     /**
16668      * By default, all insances can be a drop target.  This can be disabled by
16669      * setting isTarget to false.
16670      * @method isTarget
16671      * @type boolean
16672      */
16673     isTarget: true,
16674
16675     /**
16676      * The padding configured for this drag and drop object for calculating
16677      * the drop zone intersection with this object.
16678      * @method padding
16679      * @type int[]
16680      */
16681     padding: null,
16682
16683     /**
16684      * Cached reference to the linked element
16685      * @property _domRef
16686      * @private
16687      */
16688     _domRef: null,
16689
16690     /**
16691      * Internal typeof flag
16692      * @property __ygDragDrop
16693      * @private
16694      */
16695     __ygDragDrop: true,
16696
16697     /**
16698      * Set to true when horizontal contraints are applied
16699      * @property constrainX
16700      * @type boolean
16701      * @private
16702      */
16703     constrainX: false,
16704
16705     /**
16706      * Set to true when vertical contraints are applied
16707      * @property constrainY
16708      * @type boolean
16709      * @private
16710      */
16711     constrainY: false,
16712
16713     /**
16714      * The left constraint
16715      * @property minX
16716      * @type int
16717      * @private
16718      */
16719     minX: 0,
16720
16721     /**
16722      * The right constraint
16723      * @property maxX
16724      * @type int
16725      * @private
16726      */
16727     maxX: 0,
16728
16729     /**
16730      * The up constraint
16731      * @property minY
16732      * @type int
16733      * @type int
16734      * @private
16735      */
16736     minY: 0,
16737
16738     /**
16739      * The down constraint
16740      * @property maxY
16741      * @type int
16742      * @private
16743      */
16744     maxY: 0,
16745
16746     /**
16747      * Maintain offsets when we resetconstraints.  Set to true when you want
16748      * the position of the element relative to its parent to stay the same
16749      * when the page changes
16750      *
16751      * @property maintainOffset
16752      * @type boolean
16753      */
16754     maintainOffset: false,
16755
16756     /**
16757      * Array of pixel locations the element will snap to if we specified a
16758      * horizontal graduation/interval.  This array is generated automatically
16759      * when you define a tick interval.
16760      * @property xTicks
16761      * @type int[]
16762      */
16763     xTicks: null,
16764
16765     /**
16766      * Array of pixel locations the element will snap to if we specified a
16767      * vertical graduation/interval.  This array is generated automatically
16768      * when you define a tick interval.
16769      * @property yTicks
16770      * @type int[]
16771      */
16772     yTicks: null,
16773
16774     /**
16775      * By default the drag and drop instance will only respond to the primary
16776      * button click (left button for a right-handed mouse).  Set to true to
16777      * allow drag and drop to start with any mouse click that is propogated
16778      * by the browser
16779      * @property primaryButtonOnly
16780      * @type boolean
16781      */
16782     primaryButtonOnly: true,
16783
16784     /**
16785      * The availabe property is false until the linked dom element is accessible.
16786      * @property available
16787      * @type boolean
16788      */
16789     available: false,
16790
16791     /**
16792      * By default, drags can only be initiated if the mousedown occurs in the
16793      * region the linked element is.  This is done in part to work around a
16794      * bug in some browsers that mis-report the mousedown if the previous
16795      * mouseup happened outside of the window.  This property is set to true
16796      * if outer handles are defined.
16797      *
16798      * @property hasOuterHandles
16799      * @type boolean
16800      * @default false
16801      */
16802     hasOuterHandles: false,
16803
16804     /**
16805      * Code that executes immediately before the startDrag event
16806      * @method b4StartDrag
16807      * @private
16808      */
16809     b4StartDrag: function(x, y) { },
16810
16811     /**
16812      * Abstract method called after a drag/drop object is clicked
16813      * and the drag or mousedown time thresholds have beeen met.
16814      * @method startDrag
16815      * @param {int} X click location
16816      * @param {int} Y click location
16817      */
16818     startDrag: function(x, y) { /* override this */ },
16819
16820     /**
16821      * Code that executes immediately before the onDrag event
16822      * @method b4Drag
16823      * @private
16824      */
16825     b4Drag: function(e) { },
16826
16827     /**
16828      * Abstract method called during the onMouseMove event while dragging an
16829      * object.
16830      * @method onDrag
16831      * @param {Event} e the mousemove event
16832      */
16833     onDrag: function(e) { /* override this */ },
16834
16835     /**
16836      * Abstract method called when this element fist begins hovering over
16837      * another DragDrop obj
16838      * @method onDragEnter
16839      * @param {Event} e the mousemove event
16840      * @param {String|DragDrop[]} id In POINT mode, the element
16841      * id this is hovering over.  In INTERSECT mode, an array of one or more
16842      * dragdrop items being hovered over.
16843      */
16844     onDragEnter: function(e, id) { /* override this */ },
16845
16846     /**
16847      * Code that executes immediately before the onDragOver event
16848      * @method b4DragOver
16849      * @private
16850      */
16851     b4DragOver: function(e) { },
16852
16853     /**
16854      * Abstract method called when this element is hovering over another
16855      * DragDrop obj
16856      * @method onDragOver
16857      * @param {Event} e the mousemove event
16858      * @param {String|DragDrop[]} id In POINT mode, the element
16859      * id this is hovering over.  In INTERSECT mode, an array of dd items
16860      * being hovered over.
16861      */
16862     onDragOver: function(e, id) { /* override this */ },
16863
16864     /**
16865      * Code that executes immediately before the onDragOut event
16866      * @method b4DragOut
16867      * @private
16868      */
16869     b4DragOut: function(e) { },
16870
16871     /**
16872      * Abstract method called when we are no longer hovering over an element
16873      * @method onDragOut
16874      * @param {Event} e the mousemove event
16875      * @param {String|DragDrop[]} id In POINT mode, the element
16876      * id this was hovering over.  In INTERSECT mode, an array of dd items
16877      * that the mouse is no longer over.
16878      */
16879     onDragOut: function(e, id) { /* override this */ },
16880
16881     /**
16882      * Code that executes immediately before the onDragDrop event
16883      * @method b4DragDrop
16884      * @private
16885      */
16886     b4DragDrop: function(e) { },
16887
16888     /**
16889      * Abstract method called when this item is dropped on another DragDrop
16890      * obj
16891      * @method onDragDrop
16892      * @param {Event} e the mouseup event
16893      * @param {String|DragDrop[]} id In POINT mode, the element
16894      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16895      * was dropped on.
16896      */
16897     onDragDrop: function(e, id) { /* override this */ },
16898
16899     /**
16900      * Abstract method called when this item is dropped on an area with no
16901      * drop target
16902      * @method onInvalidDrop
16903      * @param {Event} e the mouseup event
16904      */
16905     onInvalidDrop: function(e) { /* override this */ },
16906
16907     /**
16908      * Code that executes immediately before the endDrag event
16909      * @method b4EndDrag
16910      * @private
16911      */
16912     b4EndDrag: function(e) { },
16913
16914     /**
16915      * Fired when we are done dragging the object
16916      * @method endDrag
16917      * @param {Event} e the mouseup event
16918      */
16919     endDrag: function(e) { /* override this */ },
16920
16921     /**
16922      * Code executed immediately before the onMouseDown event
16923      * @method b4MouseDown
16924      * @param {Event} e the mousedown event
16925      * @private
16926      */
16927     b4MouseDown: function(e) {  },
16928
16929     /**
16930      * Event handler that fires when a drag/drop obj gets a mousedown
16931      * @method onMouseDown
16932      * @param {Event} e the mousedown event
16933      */
16934     onMouseDown: function(e) { /* override this */ },
16935
16936     /**
16937      * Event handler that fires when a drag/drop obj gets a mouseup
16938      * @method onMouseUp
16939      * @param {Event} e the mouseup event
16940      */
16941     onMouseUp: function(e) { /* override this */ },
16942
16943     /**
16944      * Override the onAvailable method to do what is needed after the initial
16945      * position was determined.
16946      * @method onAvailable
16947      */
16948     onAvailable: function () {
16949     },
16950
16951     /*
16952      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16953      * @type Object
16954      */
16955     defaultPadding : {left:0, right:0, top:0, bottom:0},
16956
16957     /*
16958      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16959  *
16960  * Usage:
16961  <pre><code>
16962  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16963                 { dragElId: "existingProxyDiv" });
16964  dd.startDrag = function(){
16965      this.constrainTo("parent-id");
16966  };
16967  </code></pre>
16968  * Or you can initalize it using the {@link Roo.Element} object:
16969  <pre><code>
16970  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16971      startDrag : function(){
16972          this.constrainTo("parent-id");
16973      }
16974  });
16975  </code></pre>
16976      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16977      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16978      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16979      * an object containing the sides to pad. For example: {right:10, bottom:10}
16980      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16981      */
16982     constrainTo : function(constrainTo, pad, inContent){
16983         if(typeof pad == "number"){
16984             pad = {left: pad, right:pad, top:pad, bottom:pad};
16985         }
16986         pad = pad || this.defaultPadding;
16987         var b = Roo.get(this.getEl()).getBox();
16988         var ce = Roo.get(constrainTo);
16989         var s = ce.getScroll();
16990         var c, cd = ce.dom;
16991         if(cd == document.body){
16992             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16993         }else{
16994             xy = ce.getXY();
16995             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16996         }
16997
16998
16999         var topSpace = b.y - c.y;
17000         var leftSpace = b.x - c.x;
17001
17002         this.resetConstraints();
17003         this.setXConstraint(leftSpace - (pad.left||0), // left
17004                 c.width - leftSpace - b.width - (pad.right||0) //right
17005         );
17006         this.setYConstraint(topSpace - (pad.top||0), //top
17007                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
17008         );
17009     },
17010
17011     /**
17012      * Returns a reference to the linked element
17013      * @method getEl
17014      * @return {HTMLElement} the html element
17015      */
17016     getEl: function() {
17017         if (!this._domRef) {
17018             this._domRef = Roo.getDom(this.id);
17019         }
17020
17021         return this._domRef;
17022     },
17023
17024     /**
17025      * Returns a reference to the actual element to drag.  By default this is
17026      * the same as the html element, but it can be assigned to another
17027      * element. An example of this can be found in Roo.dd.DDProxy
17028      * @method getDragEl
17029      * @return {HTMLElement} the html element
17030      */
17031     getDragEl: function() {
17032         return Roo.getDom(this.dragElId);
17033     },
17034
17035     /**
17036      * Sets up the DragDrop object.  Must be called in the constructor of any
17037      * Roo.dd.DragDrop subclass
17038      * @method init
17039      * @param id the id of the linked element
17040      * @param {String} sGroup the group of related items
17041      * @param {object} config configuration attributes
17042      */
17043     init: function(id, sGroup, config) {
17044         this.initTarget(id, sGroup, config);
17045         if (!Roo.isTouch) {
17046             Event.on(this.id, "mousedown", this.handleMouseDown, this);
17047         }
17048         Event.on(this.id, "touchstart", this.handleMouseDown, this);
17049         // Event.on(this.id, "selectstart", Event.preventDefault);
17050     },
17051
17052     /**
17053      * Initializes Targeting functionality only... the object does not
17054      * get a mousedown handler.
17055      * @method initTarget
17056      * @param id the id of the linked element
17057      * @param {String} sGroup the group of related items
17058      * @param {object} config configuration attributes
17059      */
17060     initTarget: function(id, sGroup, config) {
17061
17062         // configuration attributes
17063         this.config = config || {};
17064
17065         // create a local reference to the drag and drop manager
17066         this.DDM = Roo.dd.DDM;
17067         // initialize the groups array
17068         this.groups = {};
17069
17070         // assume that we have an element reference instead of an id if the
17071         // parameter is not a string
17072         if (typeof id !== "string") {
17073             id = Roo.id(id);
17074         }
17075
17076         // set the id
17077         this.id = id;
17078
17079         // add to an interaction group
17080         this.addToGroup((sGroup) ? sGroup : "default");
17081
17082         // We don't want to register this as the handle with the manager
17083         // so we just set the id rather than calling the setter.
17084         this.handleElId = id;
17085
17086         // the linked element is the element that gets dragged by default
17087         this.setDragElId(id);
17088
17089         // by default, clicked anchors will not start drag operations.
17090         this.invalidHandleTypes = { A: "A" };
17091         this.invalidHandleIds = {};
17092         this.invalidHandleClasses = [];
17093
17094         this.applyConfig();
17095
17096         this.handleOnAvailable();
17097     },
17098
17099     /**
17100      * Applies the configuration parameters that were passed into the constructor.
17101      * This is supposed to happen at each level through the inheritance chain.  So
17102      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17103      * DragDrop in order to get all of the parameters that are available in
17104      * each object.
17105      * @method applyConfig
17106      */
17107     applyConfig: function() {
17108
17109         // configurable properties:
17110         //    padding, isTarget, maintainOffset, primaryButtonOnly
17111         this.padding           = this.config.padding || [0, 0, 0, 0];
17112         this.isTarget          = (this.config.isTarget !== false);
17113         this.maintainOffset    = (this.config.maintainOffset);
17114         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17115
17116     },
17117
17118     /**
17119      * Executed when the linked element is available
17120      * @method handleOnAvailable
17121      * @private
17122      */
17123     handleOnAvailable: function() {
17124         this.available = true;
17125         this.resetConstraints();
17126         this.onAvailable();
17127     },
17128
17129      /**
17130      * Configures the padding for the target zone in px.  Effectively expands
17131      * (or reduces) the virtual object size for targeting calculations.
17132      * Supports css-style shorthand; if only one parameter is passed, all sides
17133      * will have that padding, and if only two are passed, the top and bottom
17134      * will have the first param, the left and right the second.
17135      * @method setPadding
17136      * @param {int} iTop    Top pad
17137      * @param {int} iRight  Right pad
17138      * @param {int} iBot    Bot pad
17139      * @param {int} iLeft   Left pad
17140      */
17141     setPadding: function(iTop, iRight, iBot, iLeft) {
17142         // this.padding = [iLeft, iRight, iTop, iBot];
17143         if (!iRight && 0 !== iRight) {
17144             this.padding = [iTop, iTop, iTop, iTop];
17145         } else if (!iBot && 0 !== iBot) {
17146             this.padding = [iTop, iRight, iTop, iRight];
17147         } else {
17148             this.padding = [iTop, iRight, iBot, iLeft];
17149         }
17150     },
17151
17152     /**
17153      * Stores the initial placement of the linked element.
17154      * @method setInitialPosition
17155      * @param {int} diffX   the X offset, default 0
17156      * @param {int} diffY   the Y offset, default 0
17157      */
17158     setInitPosition: function(diffX, diffY) {
17159         var el = this.getEl();
17160
17161         if (!this.DDM.verifyEl(el)) {
17162             return;
17163         }
17164
17165         var dx = diffX || 0;
17166         var dy = diffY || 0;
17167
17168         var p = Dom.getXY( el );
17169
17170         this.initPageX = p[0] - dx;
17171         this.initPageY = p[1] - dy;
17172
17173         this.lastPageX = p[0];
17174         this.lastPageY = p[1];
17175
17176
17177         this.setStartPosition(p);
17178     },
17179
17180     /**
17181      * Sets the start position of the element.  This is set when the obj
17182      * is initialized, the reset when a drag is started.
17183      * @method setStartPosition
17184      * @param pos current position (from previous lookup)
17185      * @private
17186      */
17187     setStartPosition: function(pos) {
17188         var p = pos || Dom.getXY( this.getEl() );
17189         this.deltaSetXY = null;
17190
17191         this.startPageX = p[0];
17192         this.startPageY = p[1];
17193     },
17194
17195     /**
17196      * Add this instance to a group of related drag/drop objects.  All
17197      * instances belong to at least one group, and can belong to as many
17198      * groups as needed.
17199      * @method addToGroup
17200      * @param sGroup {string} the name of the group
17201      */
17202     addToGroup: function(sGroup) {
17203         this.groups[sGroup] = true;
17204         this.DDM.regDragDrop(this, sGroup);
17205     },
17206
17207     /**
17208      * Remove's this instance from the supplied interaction group
17209      * @method removeFromGroup
17210      * @param {string}  sGroup  The group to drop
17211      */
17212     removeFromGroup: function(sGroup) {
17213         if (this.groups[sGroup]) {
17214             delete this.groups[sGroup];
17215         }
17216
17217         this.DDM.removeDDFromGroup(this, sGroup);
17218     },
17219
17220     /**
17221      * Allows you to specify that an element other than the linked element
17222      * will be moved with the cursor during a drag
17223      * @method setDragElId
17224      * @param id {string} the id of the element that will be used to initiate the drag
17225      */
17226     setDragElId: function(id) {
17227         this.dragElId = id;
17228     },
17229
17230     /**
17231      * Allows you to specify a child of the linked element that should be
17232      * used to initiate the drag operation.  An example of this would be if
17233      * you have a content div with text and links.  Clicking anywhere in the
17234      * content area would normally start the drag operation.  Use this method
17235      * to specify that an element inside of the content div is the element
17236      * that starts the drag operation.
17237      * @method setHandleElId
17238      * @param id {string} the id of the element that will be used to
17239      * initiate the drag.
17240      */
17241     setHandleElId: function(id) {
17242         if (typeof id !== "string") {
17243             id = Roo.id(id);
17244         }
17245         this.handleElId = id;
17246         this.DDM.regHandle(this.id, id);
17247     },
17248
17249     /**
17250      * Allows you to set an element outside of the linked element as a drag
17251      * handle
17252      * @method setOuterHandleElId
17253      * @param id the id of the element that will be used to initiate the drag
17254      */
17255     setOuterHandleElId: function(id) {
17256         if (typeof id !== "string") {
17257             id = Roo.id(id);
17258         }
17259         Event.on(id, "mousedown",
17260                 this.handleMouseDown, this);
17261         this.setHandleElId(id);
17262
17263         this.hasOuterHandles = true;
17264     },
17265
17266     /**
17267      * Remove all drag and drop hooks for this element
17268      * @method unreg
17269      */
17270     unreg: function() {
17271         Event.un(this.id, "mousedown",
17272                 this.handleMouseDown);
17273         Event.un(this.id, "touchstart",
17274                 this.handleMouseDown);
17275         this._domRef = null;
17276         this.DDM._remove(this);
17277     },
17278
17279     destroy : function(){
17280         this.unreg();
17281     },
17282
17283     /**
17284      * Returns true if this instance is locked, or the drag drop mgr is locked
17285      * (meaning that all drag/drop is disabled on the page.)
17286      * @method isLocked
17287      * @return {boolean} true if this obj or all drag/drop is locked, else
17288      * false
17289      */
17290     isLocked: function() {
17291         return (this.DDM.isLocked() || this.locked);
17292     },
17293
17294     /**
17295      * Fired when this object is clicked
17296      * @method handleMouseDown
17297      * @param {Event} e
17298      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17299      * @private
17300      */
17301     handleMouseDown: function(e, oDD){
17302      
17303         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17304             //Roo.log('not touch/ button !=0');
17305             return;
17306         }
17307         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17308             return; // double touch..
17309         }
17310         
17311
17312         if (this.isLocked()) {
17313             //Roo.log('locked');
17314             return;
17315         }
17316
17317         this.DDM.refreshCache(this.groups);
17318 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17319         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17320         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17321             //Roo.log('no outer handes or not over target');
17322                 // do nothing.
17323         } else {
17324 //            Roo.log('check validator');
17325             if (this.clickValidator(e)) {
17326 //                Roo.log('validate success');
17327                 // set the initial element position
17328                 this.setStartPosition();
17329
17330
17331                 this.b4MouseDown(e);
17332                 this.onMouseDown(e);
17333
17334                 this.DDM.handleMouseDown(e, this);
17335
17336                 this.DDM.stopEvent(e);
17337             } else {
17338
17339
17340             }
17341         }
17342     },
17343
17344     clickValidator: function(e) {
17345         var target = e.getTarget();
17346         return ( this.isValidHandleChild(target) &&
17347                     (this.id == this.handleElId ||
17348                         this.DDM.handleWasClicked(target, this.id)) );
17349     },
17350
17351     /**
17352      * Allows you to specify a tag name that should not start a drag operation
17353      * when clicked.  This is designed to facilitate embedding links within a
17354      * drag handle that do something other than start the drag.
17355      * @method addInvalidHandleType
17356      * @param {string} tagName the type of element to exclude
17357      */
17358     addInvalidHandleType: function(tagName) {
17359         var type = tagName.toUpperCase();
17360         this.invalidHandleTypes[type] = type;
17361     },
17362
17363     /**
17364      * Lets you to specify an element id for a child of a drag handle
17365      * that should not initiate a drag
17366      * @method addInvalidHandleId
17367      * @param {string} id the element id of the element you wish to ignore
17368      */
17369     addInvalidHandleId: function(id) {
17370         if (typeof id !== "string") {
17371             id = Roo.id(id);
17372         }
17373         this.invalidHandleIds[id] = id;
17374     },
17375
17376     /**
17377      * Lets you specify a css class of elements that will not initiate a drag
17378      * @method addInvalidHandleClass
17379      * @param {string} cssClass the class of the elements you wish to ignore
17380      */
17381     addInvalidHandleClass: function(cssClass) {
17382         this.invalidHandleClasses.push(cssClass);
17383     },
17384
17385     /**
17386      * Unsets an excluded tag name set by addInvalidHandleType
17387      * @method removeInvalidHandleType
17388      * @param {string} tagName the type of element to unexclude
17389      */
17390     removeInvalidHandleType: function(tagName) {
17391         var type = tagName.toUpperCase();
17392         // this.invalidHandleTypes[type] = null;
17393         delete this.invalidHandleTypes[type];
17394     },
17395
17396     /**
17397      * Unsets an invalid handle id
17398      * @method removeInvalidHandleId
17399      * @param {string} id the id of the element to re-enable
17400      */
17401     removeInvalidHandleId: function(id) {
17402         if (typeof id !== "string") {
17403             id = Roo.id(id);
17404         }
17405         delete this.invalidHandleIds[id];
17406     },
17407
17408     /**
17409      * Unsets an invalid css class
17410      * @method removeInvalidHandleClass
17411      * @param {string} cssClass the class of the element(s) you wish to
17412      * re-enable
17413      */
17414     removeInvalidHandleClass: function(cssClass) {
17415         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17416             if (this.invalidHandleClasses[i] == cssClass) {
17417                 delete this.invalidHandleClasses[i];
17418             }
17419         }
17420     },
17421
17422     /**
17423      * Checks the tag exclusion list to see if this click should be ignored
17424      * @method isValidHandleChild
17425      * @param {HTMLElement} node the HTMLElement to evaluate
17426      * @return {boolean} true if this is a valid tag type, false if not
17427      */
17428     isValidHandleChild: function(node) {
17429
17430         var valid = true;
17431         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17432         var nodeName;
17433         try {
17434             nodeName = node.nodeName.toUpperCase();
17435         } catch(e) {
17436             nodeName = node.nodeName;
17437         }
17438         valid = valid && !this.invalidHandleTypes[nodeName];
17439         valid = valid && !this.invalidHandleIds[node.id];
17440
17441         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17442             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17443         }
17444
17445
17446         return valid;
17447
17448     },
17449
17450     /**
17451      * Create the array of horizontal tick marks if an interval was specified
17452      * in setXConstraint().
17453      * @method setXTicks
17454      * @private
17455      */
17456     setXTicks: function(iStartX, iTickSize) {
17457         this.xTicks = [];
17458         this.xTickSize = iTickSize;
17459
17460         var tickMap = {};
17461
17462         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17463             if (!tickMap[i]) {
17464                 this.xTicks[this.xTicks.length] = i;
17465                 tickMap[i] = true;
17466             }
17467         }
17468
17469         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17470             if (!tickMap[i]) {
17471                 this.xTicks[this.xTicks.length] = i;
17472                 tickMap[i] = true;
17473             }
17474         }
17475
17476         this.xTicks.sort(this.DDM.numericSort) ;
17477     },
17478
17479     /**
17480      * Create the array of vertical tick marks if an interval was specified in
17481      * setYConstraint().
17482      * @method setYTicks
17483      * @private
17484      */
17485     setYTicks: function(iStartY, iTickSize) {
17486         this.yTicks = [];
17487         this.yTickSize = iTickSize;
17488
17489         var tickMap = {};
17490
17491         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17492             if (!tickMap[i]) {
17493                 this.yTicks[this.yTicks.length] = i;
17494                 tickMap[i] = true;
17495             }
17496         }
17497
17498         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17499             if (!tickMap[i]) {
17500                 this.yTicks[this.yTicks.length] = i;
17501                 tickMap[i] = true;
17502             }
17503         }
17504
17505         this.yTicks.sort(this.DDM.numericSort) ;
17506     },
17507
17508     /**
17509      * By default, the element can be dragged any place on the screen.  Use
17510      * this method to limit the horizontal travel of the element.  Pass in
17511      * 0,0 for the parameters if you want to lock the drag to the y axis.
17512      * @method setXConstraint
17513      * @param {int} iLeft the number of pixels the element can move to the left
17514      * @param {int} iRight the number of pixels the element can move to the
17515      * right
17516      * @param {int} iTickSize optional parameter for specifying that the
17517      * element
17518      * should move iTickSize pixels at a time.
17519      */
17520     setXConstraint: function(iLeft, iRight, iTickSize) {
17521         this.leftConstraint = iLeft;
17522         this.rightConstraint = iRight;
17523
17524         this.minX = this.initPageX - iLeft;
17525         this.maxX = this.initPageX + iRight;
17526         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17527
17528         this.constrainX = true;
17529     },
17530
17531     /**
17532      * Clears any constraints applied to this instance.  Also clears ticks
17533      * since they can't exist independent of a constraint at this time.
17534      * @method clearConstraints
17535      */
17536     clearConstraints: function() {
17537         this.constrainX = false;
17538         this.constrainY = false;
17539         this.clearTicks();
17540     },
17541
17542     /**
17543      * Clears any tick interval defined for this instance
17544      * @method clearTicks
17545      */
17546     clearTicks: function() {
17547         this.xTicks = null;
17548         this.yTicks = null;
17549         this.xTickSize = 0;
17550         this.yTickSize = 0;
17551     },
17552
17553     /**
17554      * By default, the element can be dragged any place on the screen.  Set
17555      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17556      * parameters if you want to lock the drag to the x axis.
17557      * @method setYConstraint
17558      * @param {int} iUp the number of pixels the element can move up
17559      * @param {int} iDown the number of pixels the element can move down
17560      * @param {int} iTickSize optional parameter for specifying that the
17561      * element should move iTickSize pixels at a time.
17562      */
17563     setYConstraint: function(iUp, iDown, iTickSize) {
17564         this.topConstraint = iUp;
17565         this.bottomConstraint = iDown;
17566
17567         this.minY = this.initPageY - iUp;
17568         this.maxY = this.initPageY + iDown;
17569         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17570
17571         this.constrainY = true;
17572
17573     },
17574
17575     /**
17576      * resetConstraints must be called if you manually reposition a dd element.
17577      * @method resetConstraints
17578      * @param {boolean} maintainOffset
17579      */
17580     resetConstraints: function() {
17581
17582
17583         // Maintain offsets if necessary
17584         if (this.initPageX || this.initPageX === 0) {
17585             // figure out how much this thing has moved
17586             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17587             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17588
17589             this.setInitPosition(dx, dy);
17590
17591         // This is the first time we have detected the element's position
17592         } else {
17593             this.setInitPosition();
17594         }
17595
17596         if (this.constrainX) {
17597             this.setXConstraint( this.leftConstraint,
17598                                  this.rightConstraint,
17599                                  this.xTickSize        );
17600         }
17601
17602         if (this.constrainY) {
17603             this.setYConstraint( this.topConstraint,
17604                                  this.bottomConstraint,
17605                                  this.yTickSize         );
17606         }
17607     },
17608
17609     /**
17610      * Normally the drag element is moved pixel by pixel, but we can specify
17611      * that it move a number of pixels at a time.  This method resolves the
17612      * location when we have it set up like this.
17613      * @method getTick
17614      * @param {int} val where we want to place the object
17615      * @param {int[]} tickArray sorted array of valid points
17616      * @return {int} the closest tick
17617      * @private
17618      */
17619     getTick: function(val, tickArray) {
17620
17621         if (!tickArray) {
17622             // If tick interval is not defined, it is effectively 1 pixel,
17623             // so we return the value passed to us.
17624             return val;
17625         } else if (tickArray[0] >= val) {
17626             // The value is lower than the first tick, so we return the first
17627             // tick.
17628             return tickArray[0];
17629         } else {
17630             for (var i=0, len=tickArray.length; i<len; ++i) {
17631                 var next = i + 1;
17632                 if (tickArray[next] && tickArray[next] >= val) {
17633                     var diff1 = val - tickArray[i];
17634                     var diff2 = tickArray[next] - val;
17635                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17636                 }
17637             }
17638
17639             // The value is larger than the last tick, so we return the last
17640             // tick.
17641             return tickArray[tickArray.length - 1];
17642         }
17643     },
17644
17645     /**
17646      * toString method
17647      * @method toString
17648      * @return {string} string representation of the dd obj
17649      */
17650     toString: function() {
17651         return ("DragDrop " + this.id);
17652     }
17653
17654 });
17655
17656 })();
17657 /*
17658  * Based on:
17659  * Ext JS Library 1.1.1
17660  * Copyright(c) 2006-2007, Ext JS, LLC.
17661  *
17662  * Originally Released Under LGPL - original licence link has changed is not relivant.
17663  *
17664  * Fork - LGPL
17665  * <script type="text/javascript">
17666  */
17667
17668
17669 /**
17670  * The drag and drop utility provides a framework for building drag and drop
17671  * applications.  In addition to enabling drag and drop for specific elements,
17672  * the drag and drop elements are tracked by the manager class, and the
17673  * interactions between the various elements are tracked during the drag and
17674  * the implementing code is notified about these important moments.
17675  */
17676
17677 // Only load the library once.  Rewriting the manager class would orphan
17678 // existing drag and drop instances.
17679 if (!Roo.dd.DragDropMgr) {
17680
17681 /**
17682  * @class Roo.dd.DragDropMgr
17683  * DragDropMgr is a singleton that tracks the element interaction for
17684  * all DragDrop items in the window.  Generally, you will not call
17685  * this class directly, but it does have helper methods that could
17686  * be useful in your DragDrop implementations.
17687  * @singleton
17688  */
17689 Roo.dd.DragDropMgr = function() {
17690
17691     var Event = Roo.EventManager;
17692
17693     return {
17694
17695         /**
17696          * Two dimensional Array of registered DragDrop objects.  The first
17697          * dimension is the DragDrop item group, the second the DragDrop
17698          * object.
17699          * @property ids
17700          * @type {string: string}
17701          * @private
17702          * @static
17703          */
17704         ids: {},
17705
17706         /**
17707          * Array of element ids defined as drag handles.  Used to determine
17708          * if the element that generated the mousedown event is actually the
17709          * handle and not the html element itself.
17710          * @property handleIds
17711          * @type {string: string}
17712          * @private
17713          * @static
17714          */
17715         handleIds: {},
17716
17717         /**
17718          * the DragDrop object that is currently being dragged
17719          * @property dragCurrent
17720          * @type DragDrop
17721          * @private
17722          * @static
17723          **/
17724         dragCurrent: null,
17725
17726         /**
17727          * the DragDrop object(s) that are being hovered over
17728          * @property dragOvers
17729          * @type Array
17730          * @private
17731          * @static
17732          */
17733         dragOvers: {},
17734
17735         /**
17736          * the X distance between the cursor and the object being dragged
17737          * @property deltaX
17738          * @type int
17739          * @private
17740          * @static
17741          */
17742         deltaX: 0,
17743
17744         /**
17745          * the Y distance between the cursor and the object being dragged
17746          * @property deltaY
17747          * @type int
17748          * @private
17749          * @static
17750          */
17751         deltaY: 0,
17752
17753         /**
17754          * Flag to determine if we should prevent the default behavior of the
17755          * events we define. By default this is true, but this can be set to
17756          * false if you need the default behavior (not recommended)
17757          * @property preventDefault
17758          * @type boolean
17759          * @static
17760          */
17761         preventDefault: true,
17762
17763         /**
17764          * Flag to determine if we should stop the propagation of the events
17765          * we generate. This is true by default but you may want to set it to
17766          * false if the html element contains other features that require the
17767          * mouse click.
17768          * @property stopPropagation
17769          * @type boolean
17770          * @static
17771          */
17772         stopPropagation: true,
17773
17774         /**
17775          * Internal flag that is set to true when drag and drop has been
17776          * intialized
17777          * @property initialized
17778          * @private
17779          * @static
17780          */
17781         initalized: false,
17782
17783         /**
17784          * All drag and drop can be disabled.
17785          * @property locked
17786          * @private
17787          * @static
17788          */
17789         locked: false,
17790
17791         /**
17792          * Called the first time an element is registered.
17793          * @method init
17794          * @private
17795          * @static
17796          */
17797         init: function() {
17798             this.initialized = true;
17799         },
17800
17801         /**
17802          * In point mode, drag and drop interaction is defined by the
17803          * location of the cursor during the drag/drop
17804          * @property POINT
17805          * @type int
17806          * @static
17807          */
17808         POINT: 0,
17809
17810         /**
17811          * In intersect mode, drag and drop interactio nis defined by the
17812          * overlap of two or more drag and drop objects.
17813          * @property INTERSECT
17814          * @type int
17815          * @static
17816          */
17817         INTERSECT: 1,
17818
17819         /**
17820          * The current drag and drop mode.  Default: POINT
17821          * @property mode
17822          * @type int
17823          * @static
17824          */
17825         mode: 0,
17826
17827         /**
17828          * Runs method on all drag and drop objects
17829          * @method _execOnAll
17830          * @private
17831          * @static
17832          */
17833         _execOnAll: function(sMethod, args) {
17834             for (var i in this.ids) {
17835                 for (var j in this.ids[i]) {
17836                     var oDD = this.ids[i][j];
17837                     if (! this.isTypeOfDD(oDD)) {
17838                         continue;
17839                     }
17840                     oDD[sMethod].apply(oDD, args);
17841                 }
17842             }
17843         },
17844
17845         /**
17846          * Drag and drop initialization.  Sets up the global event handlers
17847          * @method _onLoad
17848          * @private
17849          * @static
17850          */
17851         _onLoad: function() {
17852
17853             this.init();
17854
17855             if (!Roo.isTouch) {
17856                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17857                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17858             }
17859             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17860             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17861             
17862             Event.on(window,   "unload",    this._onUnload, this, true);
17863             Event.on(window,   "resize",    this._onResize, this, true);
17864             // Event.on(window,   "mouseout",    this._test);
17865
17866         },
17867
17868         /**
17869          * Reset constraints on all drag and drop objs
17870          * @method _onResize
17871          * @private
17872          * @static
17873          */
17874         _onResize: function(e) {
17875             this._execOnAll("resetConstraints", []);
17876         },
17877
17878         /**
17879          * Lock all drag and drop functionality
17880          * @method lock
17881          * @static
17882          */
17883         lock: function() { this.locked = true; },
17884
17885         /**
17886          * Unlock all drag and drop functionality
17887          * @method unlock
17888          * @static
17889          */
17890         unlock: function() { this.locked = false; },
17891
17892         /**
17893          * Is drag and drop locked?
17894          * @method isLocked
17895          * @return {boolean} True if drag and drop is locked, false otherwise.
17896          * @static
17897          */
17898         isLocked: function() { return this.locked; },
17899
17900         /**
17901          * Location cache that is set for all drag drop objects when a drag is
17902          * initiated, cleared when the drag is finished.
17903          * @property locationCache
17904          * @private
17905          * @static
17906          */
17907         locationCache: {},
17908
17909         /**
17910          * Set useCache to false if you want to force object the lookup of each
17911          * drag and drop linked element constantly during a drag.
17912          * @property useCache
17913          * @type boolean
17914          * @static
17915          */
17916         useCache: true,
17917
17918         /**
17919          * The number of pixels that the mouse needs to move after the
17920          * mousedown before the drag is initiated.  Default=3;
17921          * @property clickPixelThresh
17922          * @type int
17923          * @static
17924          */
17925         clickPixelThresh: 3,
17926
17927         /**
17928          * The number of milliseconds after the mousedown event to initiate the
17929          * drag if we don't get a mouseup event. Default=1000
17930          * @property clickTimeThresh
17931          * @type int
17932          * @static
17933          */
17934         clickTimeThresh: 350,
17935
17936         /**
17937          * Flag that indicates that either the drag pixel threshold or the
17938          * mousdown time threshold has been met
17939          * @property dragThreshMet
17940          * @type boolean
17941          * @private
17942          * @static
17943          */
17944         dragThreshMet: false,
17945
17946         /**
17947          * Timeout used for the click time threshold
17948          * @property clickTimeout
17949          * @type Object
17950          * @private
17951          * @static
17952          */
17953         clickTimeout: null,
17954
17955         /**
17956          * The X position of the mousedown event stored for later use when a
17957          * drag threshold is met.
17958          * @property startX
17959          * @type int
17960          * @private
17961          * @static
17962          */
17963         startX: 0,
17964
17965         /**
17966          * The Y position of the mousedown event stored for later use when a
17967          * drag threshold is met.
17968          * @property startY
17969          * @type int
17970          * @private
17971          * @static
17972          */
17973         startY: 0,
17974
17975         /**
17976          * Each DragDrop instance must be registered with the DragDropMgr.
17977          * This is executed in DragDrop.init()
17978          * @method regDragDrop
17979          * @param {DragDrop} oDD the DragDrop object to register
17980          * @param {String} sGroup the name of the group this element belongs to
17981          * @static
17982          */
17983         regDragDrop: function(oDD, sGroup) {
17984             if (!this.initialized) { this.init(); }
17985
17986             if (!this.ids[sGroup]) {
17987                 this.ids[sGroup] = {};
17988             }
17989             this.ids[sGroup][oDD.id] = oDD;
17990         },
17991
17992         /**
17993          * Removes the supplied dd instance from the supplied group. Executed
17994          * by DragDrop.removeFromGroup, so don't call this function directly.
17995          * @method removeDDFromGroup
17996          * @private
17997          * @static
17998          */
17999         removeDDFromGroup: function(oDD, sGroup) {
18000             if (!this.ids[sGroup]) {
18001                 this.ids[sGroup] = {};
18002             }
18003
18004             var obj = this.ids[sGroup];
18005             if (obj && obj[oDD.id]) {
18006                 delete obj[oDD.id];
18007             }
18008         },
18009
18010         /**
18011          * Unregisters a drag and drop item.  This is executed in
18012          * DragDrop.unreg, use that method instead of calling this directly.
18013          * @method _remove
18014          * @private
18015          * @static
18016          */
18017         _remove: function(oDD) {
18018             for (var g in oDD.groups) {
18019                 if (g && this.ids[g][oDD.id]) {
18020                     delete this.ids[g][oDD.id];
18021                 }
18022             }
18023             delete this.handleIds[oDD.id];
18024         },
18025
18026         /**
18027          * Each DragDrop handle element must be registered.  This is done
18028          * automatically when executing DragDrop.setHandleElId()
18029          * @method regHandle
18030          * @param {String} sDDId the DragDrop id this element is a handle for
18031          * @param {String} sHandleId the id of the element that is the drag
18032          * handle
18033          * @static
18034          */
18035         regHandle: function(sDDId, sHandleId) {
18036             if (!this.handleIds[sDDId]) {
18037                 this.handleIds[sDDId] = {};
18038             }
18039             this.handleIds[sDDId][sHandleId] = sHandleId;
18040         },
18041
18042         /**
18043          * Utility function to determine if a given element has been
18044          * registered as a drag drop item.
18045          * @method isDragDrop
18046          * @param {String} id the element id to check
18047          * @return {boolean} true if this element is a DragDrop item,
18048          * false otherwise
18049          * @static
18050          */
18051         isDragDrop: function(id) {
18052             return ( this.getDDById(id) ) ? true : false;
18053         },
18054
18055         /**
18056          * Returns the drag and drop instances that are in all groups the
18057          * passed in instance belongs to.
18058          * @method getRelated
18059          * @param {DragDrop} p_oDD the obj to get related data for
18060          * @param {boolean} bTargetsOnly if true, only return targetable objs
18061          * @return {DragDrop[]} the related instances
18062          * @static
18063          */
18064         getRelated: function(p_oDD, bTargetsOnly) {
18065             var oDDs = [];
18066             for (var i in p_oDD.groups) {
18067                 for (j in this.ids[i]) {
18068                     var dd = this.ids[i][j];
18069                     if (! this.isTypeOfDD(dd)) {
18070                         continue;
18071                     }
18072                     if (!bTargetsOnly || dd.isTarget) {
18073                         oDDs[oDDs.length] = dd;
18074                     }
18075                 }
18076             }
18077
18078             return oDDs;
18079         },
18080
18081         /**
18082          * Returns true if the specified dd target is a legal target for
18083          * the specifice drag obj
18084          * @method isLegalTarget
18085          * @param {DragDrop} the drag obj
18086          * @param {DragDrop} the target
18087          * @return {boolean} true if the target is a legal target for the
18088          * dd obj
18089          * @static
18090          */
18091         isLegalTarget: function (oDD, oTargetDD) {
18092             var targets = this.getRelated(oDD, true);
18093             for (var i=0, len=targets.length;i<len;++i) {
18094                 if (targets[i].id == oTargetDD.id) {
18095                     return true;
18096                 }
18097             }
18098
18099             return false;
18100         },
18101
18102         /**
18103          * My goal is to be able to transparently determine if an object is
18104          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18105          * returns "object", oDD.constructor.toString() always returns
18106          * "DragDrop" and not the name of the subclass.  So for now it just
18107          * evaluates a well-known variable in DragDrop.
18108          * @method isTypeOfDD
18109          * @param {Object} the object to evaluate
18110          * @return {boolean} true if typeof oDD = DragDrop
18111          * @static
18112          */
18113         isTypeOfDD: function (oDD) {
18114             return (oDD && oDD.__ygDragDrop);
18115         },
18116
18117         /**
18118          * Utility function to determine if a given element has been
18119          * registered as a drag drop handle for the given Drag Drop object.
18120          * @method isHandle
18121          * @param {String} id the element id to check
18122          * @return {boolean} true if this element is a DragDrop handle, false
18123          * otherwise
18124          * @static
18125          */
18126         isHandle: function(sDDId, sHandleId) {
18127             return ( this.handleIds[sDDId] &&
18128                             this.handleIds[sDDId][sHandleId] );
18129         },
18130
18131         /**
18132          * Returns the DragDrop instance for a given id
18133          * @method getDDById
18134          * @param {String} id the id of the DragDrop object
18135          * @return {DragDrop} the drag drop object, null if it is not found
18136          * @static
18137          */
18138         getDDById: function(id) {
18139             for (var i in this.ids) {
18140                 if (this.ids[i][id]) {
18141                     return this.ids[i][id];
18142                 }
18143             }
18144             return null;
18145         },
18146
18147         /**
18148          * Fired after a registered DragDrop object gets the mousedown event.
18149          * Sets up the events required to track the object being dragged
18150          * @method handleMouseDown
18151          * @param {Event} e the event
18152          * @param oDD the DragDrop object being dragged
18153          * @private
18154          * @static
18155          */
18156         handleMouseDown: function(e, oDD) {
18157             if(Roo.QuickTips){
18158                 Roo.QuickTips.disable();
18159             }
18160             this.currentTarget = e.getTarget();
18161
18162             this.dragCurrent = oDD;
18163
18164             var el = oDD.getEl();
18165
18166             // track start position
18167             this.startX = e.getPageX();
18168             this.startY = e.getPageY();
18169
18170             this.deltaX = this.startX - el.offsetLeft;
18171             this.deltaY = this.startY - el.offsetTop;
18172
18173             this.dragThreshMet = false;
18174
18175             this.clickTimeout = setTimeout(
18176                     function() {
18177                         var DDM = Roo.dd.DDM;
18178                         DDM.startDrag(DDM.startX, DDM.startY);
18179                     },
18180                     this.clickTimeThresh );
18181         },
18182
18183         /**
18184          * Fired when either the drag pixel threshol or the mousedown hold
18185          * time threshold has been met.
18186          * @method startDrag
18187          * @param x {int} the X position of the original mousedown
18188          * @param y {int} the Y position of the original mousedown
18189          * @static
18190          */
18191         startDrag: function(x, y) {
18192             clearTimeout(this.clickTimeout);
18193             if (this.dragCurrent) {
18194                 this.dragCurrent.b4StartDrag(x, y);
18195                 this.dragCurrent.startDrag(x, y);
18196             }
18197             this.dragThreshMet = true;
18198         },
18199
18200         /**
18201          * Internal function to handle the mouseup event.  Will be invoked
18202          * from the context of the document.
18203          * @method handleMouseUp
18204          * @param {Event} e the event
18205          * @private
18206          * @static
18207          */
18208         handleMouseUp: function(e) {
18209
18210             if(Roo.QuickTips){
18211                 Roo.QuickTips.enable();
18212             }
18213             if (! this.dragCurrent) {
18214                 return;
18215             }
18216
18217             clearTimeout(this.clickTimeout);
18218
18219             if (this.dragThreshMet) {
18220                 this.fireEvents(e, true);
18221             } else {
18222             }
18223
18224             this.stopDrag(e);
18225
18226             this.stopEvent(e);
18227         },
18228
18229         /**
18230          * Utility to stop event propagation and event default, if these
18231          * features are turned on.
18232          * @method stopEvent
18233          * @param {Event} e the event as returned by this.getEvent()
18234          * @static
18235          */
18236         stopEvent: function(e){
18237             if(this.stopPropagation) {
18238                 e.stopPropagation();
18239             }
18240
18241             if (this.preventDefault) {
18242                 e.preventDefault();
18243             }
18244         },
18245
18246         /**
18247          * Internal function to clean up event handlers after the drag
18248          * operation is complete
18249          * @method stopDrag
18250          * @param {Event} e the event
18251          * @private
18252          * @static
18253          */
18254         stopDrag: function(e) {
18255             // Fire the drag end event for the item that was dragged
18256             if (this.dragCurrent) {
18257                 if (this.dragThreshMet) {
18258                     this.dragCurrent.b4EndDrag(e);
18259                     this.dragCurrent.endDrag(e);
18260                 }
18261
18262                 this.dragCurrent.onMouseUp(e);
18263             }
18264
18265             this.dragCurrent = null;
18266             this.dragOvers = {};
18267         },
18268
18269         /**
18270          * Internal function to handle the mousemove event.  Will be invoked
18271          * from the context of the html element.
18272          *
18273          * @TODO figure out what we can do about mouse events lost when the
18274          * user drags objects beyond the window boundary.  Currently we can
18275          * detect this in internet explorer by verifying that the mouse is
18276          * down during the mousemove event.  Firefox doesn't give us the
18277          * button state on the mousemove event.
18278          * @method handleMouseMove
18279          * @param {Event} e the event
18280          * @private
18281          * @static
18282          */
18283         handleMouseMove: function(e) {
18284             if (! this.dragCurrent) {
18285                 return true;
18286             }
18287
18288             // var button = e.which || e.button;
18289
18290             // check for IE mouseup outside of page boundary
18291             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18292                 this.stopEvent(e);
18293                 return this.handleMouseUp(e);
18294             }
18295
18296             if (!this.dragThreshMet) {
18297                 var diffX = Math.abs(this.startX - e.getPageX());
18298                 var diffY = Math.abs(this.startY - e.getPageY());
18299                 if (diffX > this.clickPixelThresh ||
18300                             diffY > this.clickPixelThresh) {
18301                     this.startDrag(this.startX, this.startY);
18302                 }
18303             }
18304
18305             if (this.dragThreshMet) {
18306                 this.dragCurrent.b4Drag(e);
18307                 this.dragCurrent.onDrag(e);
18308                 if(!this.dragCurrent.moveOnly){
18309                     this.fireEvents(e, false);
18310                 }
18311             }
18312
18313             this.stopEvent(e);
18314
18315             return true;
18316         },
18317
18318         /**
18319          * Iterates over all of the DragDrop elements to find ones we are
18320          * hovering over or dropping on
18321          * @method fireEvents
18322          * @param {Event} e the event
18323          * @param {boolean} isDrop is this a drop op or a mouseover op?
18324          * @private
18325          * @static
18326          */
18327         fireEvents: function(e, isDrop) {
18328             var dc = this.dragCurrent;
18329
18330             // If the user did the mouse up outside of the window, we could
18331             // get here even though we have ended the drag.
18332             if (!dc || dc.isLocked()) {
18333                 return;
18334             }
18335
18336             var pt = e.getPoint();
18337
18338             // cache the previous dragOver array
18339             var oldOvers = [];
18340
18341             var outEvts   = [];
18342             var overEvts  = [];
18343             var dropEvts  = [];
18344             var enterEvts = [];
18345
18346             // Check to see if the object(s) we were hovering over is no longer
18347             // being hovered over so we can fire the onDragOut event
18348             for (var i in this.dragOvers) {
18349
18350                 var ddo = this.dragOvers[i];
18351
18352                 if (! this.isTypeOfDD(ddo)) {
18353                     continue;
18354                 }
18355
18356                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18357                     outEvts.push( ddo );
18358                 }
18359
18360                 oldOvers[i] = true;
18361                 delete this.dragOvers[i];
18362             }
18363
18364             for (var sGroup in dc.groups) {
18365
18366                 if ("string" != typeof sGroup) {
18367                     continue;
18368                 }
18369
18370                 for (i in this.ids[sGroup]) {
18371                     var oDD = this.ids[sGroup][i];
18372                     if (! this.isTypeOfDD(oDD)) {
18373                         continue;
18374                     }
18375
18376                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18377                         if (this.isOverTarget(pt, oDD, this.mode)) {
18378                             // look for drop interactions
18379                             if (isDrop) {
18380                                 dropEvts.push( oDD );
18381                             // look for drag enter and drag over interactions
18382                             } else {
18383
18384                                 // initial drag over: dragEnter fires
18385                                 if (!oldOvers[oDD.id]) {
18386                                     enterEvts.push( oDD );
18387                                 // subsequent drag overs: dragOver fires
18388                                 } else {
18389                                     overEvts.push( oDD );
18390                                 }
18391
18392                                 this.dragOvers[oDD.id] = oDD;
18393                             }
18394                         }
18395                     }
18396                 }
18397             }
18398
18399             if (this.mode) {
18400                 if (outEvts.length) {
18401                     dc.b4DragOut(e, outEvts);
18402                     dc.onDragOut(e, outEvts);
18403                 }
18404
18405                 if (enterEvts.length) {
18406                     dc.onDragEnter(e, enterEvts);
18407                 }
18408
18409                 if (overEvts.length) {
18410                     dc.b4DragOver(e, overEvts);
18411                     dc.onDragOver(e, overEvts);
18412                 }
18413
18414                 if (dropEvts.length) {
18415                     dc.b4DragDrop(e, dropEvts);
18416                     dc.onDragDrop(e, dropEvts);
18417                 }
18418
18419             } else {
18420                 // fire dragout events
18421                 var len = 0;
18422                 for (i=0, len=outEvts.length; i<len; ++i) {
18423                     dc.b4DragOut(e, outEvts[i].id);
18424                     dc.onDragOut(e, outEvts[i].id);
18425                 }
18426
18427                 // fire enter events
18428                 for (i=0,len=enterEvts.length; i<len; ++i) {
18429                     // dc.b4DragEnter(e, oDD.id);
18430                     dc.onDragEnter(e, enterEvts[i].id);
18431                 }
18432
18433                 // fire over events
18434                 for (i=0,len=overEvts.length; i<len; ++i) {
18435                     dc.b4DragOver(e, overEvts[i].id);
18436                     dc.onDragOver(e, overEvts[i].id);
18437                 }
18438
18439                 // fire drop events
18440                 for (i=0, len=dropEvts.length; i<len; ++i) {
18441                     dc.b4DragDrop(e, dropEvts[i].id);
18442                     dc.onDragDrop(e, dropEvts[i].id);
18443                 }
18444
18445             }
18446
18447             // notify about a drop that did not find a target
18448             if (isDrop && !dropEvts.length) {
18449                 dc.onInvalidDrop(e);
18450             }
18451
18452         },
18453
18454         /**
18455          * Helper function for getting the best match from the list of drag
18456          * and drop objects returned by the drag and drop events when we are
18457          * in INTERSECT mode.  It returns either the first object that the
18458          * cursor is over, or the object that has the greatest overlap with
18459          * the dragged element.
18460          * @method getBestMatch
18461          * @param  {DragDrop[]} dds The array of drag and drop objects
18462          * targeted
18463          * @return {DragDrop}       The best single match
18464          * @static
18465          */
18466         getBestMatch: function(dds) {
18467             var winner = null;
18468             // Return null if the input is not what we expect
18469             //if (!dds || !dds.length || dds.length == 0) {
18470                // winner = null;
18471             // If there is only one item, it wins
18472             //} else if (dds.length == 1) {
18473
18474             var len = dds.length;
18475
18476             if (len == 1) {
18477                 winner = dds[0];
18478             } else {
18479                 // Loop through the targeted items
18480                 for (var i=0; i<len; ++i) {
18481                     var dd = dds[i];
18482                     // If the cursor is over the object, it wins.  If the
18483                     // cursor is over multiple matches, the first one we come
18484                     // to wins.
18485                     if (dd.cursorIsOver) {
18486                         winner = dd;
18487                         break;
18488                     // Otherwise the object with the most overlap wins
18489                     } else {
18490                         if (!winner ||
18491                             winner.overlap.getArea() < dd.overlap.getArea()) {
18492                             winner = dd;
18493                         }
18494                     }
18495                 }
18496             }
18497
18498             return winner;
18499         },
18500
18501         /**
18502          * Refreshes the cache of the top-left and bottom-right points of the
18503          * drag and drop objects in the specified group(s).  This is in the
18504          * format that is stored in the drag and drop instance, so typical
18505          * usage is:
18506          * <code>
18507          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18508          * </code>
18509          * Alternatively:
18510          * <code>
18511          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18512          * </code>
18513          * @TODO this really should be an indexed array.  Alternatively this
18514          * method could accept both.
18515          * @method refreshCache
18516          * @param {Object} groups an associative array of groups to refresh
18517          * @static
18518          */
18519         refreshCache: function(groups) {
18520             for (var sGroup in groups) {
18521                 if ("string" != typeof sGroup) {
18522                     continue;
18523                 }
18524                 for (var i in this.ids[sGroup]) {
18525                     var oDD = this.ids[sGroup][i];
18526
18527                     if (this.isTypeOfDD(oDD)) {
18528                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18529                         var loc = this.getLocation(oDD);
18530                         if (loc) {
18531                             this.locationCache[oDD.id] = loc;
18532                         } else {
18533                             delete this.locationCache[oDD.id];
18534                             // this will unregister the drag and drop object if
18535                             // the element is not in a usable state
18536                             // oDD.unreg();
18537                         }
18538                     }
18539                 }
18540             }
18541         },
18542
18543         /**
18544          * This checks to make sure an element exists and is in the DOM.  The
18545          * main purpose is to handle cases where innerHTML is used to remove
18546          * drag and drop objects from the DOM.  IE provides an 'unspecified
18547          * error' when trying to access the offsetParent of such an element
18548          * @method verifyEl
18549          * @param {HTMLElement} el the element to check
18550          * @return {boolean} true if the element looks usable
18551          * @static
18552          */
18553         verifyEl: function(el) {
18554             if (el) {
18555                 var parent;
18556                 if(Roo.isIE){
18557                     try{
18558                         parent = el.offsetParent;
18559                     }catch(e){}
18560                 }else{
18561                     parent = el.offsetParent;
18562                 }
18563                 if (parent) {
18564                     return true;
18565                 }
18566             }
18567
18568             return false;
18569         },
18570
18571         /**
18572          * Returns a Region object containing the drag and drop element's position
18573          * and size, including the padding configured for it
18574          * @method getLocation
18575          * @param {DragDrop} oDD the drag and drop object to get the
18576          *                       location for
18577          * @return {Roo.lib.Region} a Region object representing the total area
18578          *                             the element occupies, including any padding
18579          *                             the instance is configured for.
18580          * @static
18581          */
18582         getLocation: function(oDD) {
18583             if (! this.isTypeOfDD(oDD)) {
18584                 return null;
18585             }
18586
18587             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18588
18589             try {
18590                 pos= Roo.lib.Dom.getXY(el);
18591             } catch (e) { }
18592
18593             if (!pos) {
18594                 return null;
18595             }
18596
18597             x1 = pos[0];
18598             x2 = x1 + el.offsetWidth;
18599             y1 = pos[1];
18600             y2 = y1 + el.offsetHeight;
18601
18602             t = y1 - oDD.padding[0];
18603             r = x2 + oDD.padding[1];
18604             b = y2 + oDD.padding[2];
18605             l = x1 - oDD.padding[3];
18606
18607             return new Roo.lib.Region( t, r, b, l );
18608         },
18609
18610         /**
18611          * Checks the cursor location to see if it over the target
18612          * @method isOverTarget
18613          * @param {Roo.lib.Point} pt The point to evaluate
18614          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18615          * @return {boolean} true if the mouse is over the target
18616          * @private
18617          * @static
18618          */
18619         isOverTarget: function(pt, oTarget, intersect) {
18620             // use cache if available
18621             var loc = this.locationCache[oTarget.id];
18622             if (!loc || !this.useCache) {
18623                 loc = this.getLocation(oTarget);
18624                 this.locationCache[oTarget.id] = loc;
18625
18626             }
18627
18628             if (!loc) {
18629                 return false;
18630             }
18631
18632             oTarget.cursorIsOver = loc.contains( pt );
18633
18634             // DragDrop is using this as a sanity check for the initial mousedown
18635             // in this case we are done.  In POINT mode, if the drag obj has no
18636             // contraints, we are also done. Otherwise we need to evaluate the
18637             // location of the target as related to the actual location of the
18638             // dragged element.
18639             var dc = this.dragCurrent;
18640             if (!dc || !dc.getTargetCoord ||
18641                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18642                 return oTarget.cursorIsOver;
18643             }
18644
18645             oTarget.overlap = null;
18646
18647             // Get the current location of the drag element, this is the
18648             // location of the mouse event less the delta that represents
18649             // where the original mousedown happened on the element.  We
18650             // need to consider constraints and ticks as well.
18651             var pos = dc.getTargetCoord(pt.x, pt.y);
18652
18653             var el = dc.getDragEl();
18654             var curRegion = new Roo.lib.Region( pos.y,
18655                                                    pos.x + el.offsetWidth,
18656                                                    pos.y + el.offsetHeight,
18657                                                    pos.x );
18658
18659             var overlap = curRegion.intersect(loc);
18660
18661             if (overlap) {
18662                 oTarget.overlap = overlap;
18663                 return (intersect) ? true : oTarget.cursorIsOver;
18664             } else {
18665                 return false;
18666             }
18667         },
18668
18669         /**
18670          * unload event handler
18671          * @method _onUnload
18672          * @private
18673          * @static
18674          */
18675         _onUnload: function(e, me) {
18676             Roo.dd.DragDropMgr.unregAll();
18677         },
18678
18679         /**
18680          * Cleans up the drag and drop events and objects.
18681          * @method unregAll
18682          * @private
18683          * @static
18684          */
18685         unregAll: function() {
18686
18687             if (this.dragCurrent) {
18688                 this.stopDrag();
18689                 this.dragCurrent = null;
18690             }
18691
18692             this._execOnAll("unreg", []);
18693
18694             for (i in this.elementCache) {
18695                 delete this.elementCache[i];
18696             }
18697
18698             this.elementCache = {};
18699             this.ids = {};
18700         },
18701
18702         /**
18703          * A cache of DOM elements
18704          * @property elementCache
18705          * @private
18706          * @static
18707          */
18708         elementCache: {},
18709
18710         /**
18711          * Get the wrapper for the DOM element specified
18712          * @method getElWrapper
18713          * @param {String} id the id of the element to get
18714          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18715          * @private
18716          * @deprecated This wrapper isn't that useful
18717          * @static
18718          */
18719         getElWrapper: function(id) {
18720             var oWrapper = this.elementCache[id];
18721             if (!oWrapper || !oWrapper.el) {
18722                 oWrapper = this.elementCache[id] =
18723                     new this.ElementWrapper(Roo.getDom(id));
18724             }
18725             return oWrapper;
18726         },
18727
18728         /**
18729          * Returns the actual DOM element
18730          * @method getElement
18731          * @param {String} id the id of the elment to get
18732          * @return {Object} The element
18733          * @deprecated use Roo.getDom instead
18734          * @static
18735          */
18736         getElement: function(id) {
18737             return Roo.getDom(id);
18738         },
18739
18740         /**
18741          * Returns the style property for the DOM element (i.e.,
18742          * document.getElById(id).style)
18743          * @method getCss
18744          * @param {String} id the id of the elment to get
18745          * @return {Object} The style property of the element
18746          * @deprecated use Roo.getDom instead
18747          * @static
18748          */
18749         getCss: function(id) {
18750             var el = Roo.getDom(id);
18751             return (el) ? el.style : null;
18752         },
18753
18754         /**
18755          * Inner class for cached elements
18756          * @class DragDropMgr.ElementWrapper
18757          * @for DragDropMgr
18758          * @private
18759          * @deprecated
18760          */
18761         ElementWrapper: function(el) {
18762                 /**
18763                  * The element
18764                  * @property el
18765                  */
18766                 this.el = el || null;
18767                 /**
18768                  * The element id
18769                  * @property id
18770                  */
18771                 this.id = this.el && el.id;
18772                 /**
18773                  * A reference to the style property
18774                  * @property css
18775                  */
18776                 this.css = this.el && el.style;
18777             },
18778
18779         /**
18780          * Returns the X position of an html element
18781          * @method getPosX
18782          * @param el the element for which to get the position
18783          * @return {int} the X coordinate
18784          * @for DragDropMgr
18785          * @deprecated use Roo.lib.Dom.getX instead
18786          * @static
18787          */
18788         getPosX: function(el) {
18789             return Roo.lib.Dom.getX(el);
18790         },
18791
18792         /**
18793          * Returns the Y position of an html element
18794          * @method getPosY
18795          * @param el the element for which to get the position
18796          * @return {int} the Y coordinate
18797          * @deprecated use Roo.lib.Dom.getY instead
18798          * @static
18799          */
18800         getPosY: function(el) {
18801             return Roo.lib.Dom.getY(el);
18802         },
18803
18804         /**
18805          * Swap two nodes.  In IE, we use the native method, for others we
18806          * emulate the IE behavior
18807          * @method swapNode
18808          * @param n1 the first node to swap
18809          * @param n2 the other node to swap
18810          * @static
18811          */
18812         swapNode: function(n1, n2) {
18813             if (n1.swapNode) {
18814                 n1.swapNode(n2);
18815             } else {
18816                 var p = n2.parentNode;
18817                 var s = n2.nextSibling;
18818
18819                 if (s == n1) {
18820                     p.insertBefore(n1, n2);
18821                 } else if (n2 == n1.nextSibling) {
18822                     p.insertBefore(n2, n1);
18823                 } else {
18824                     n1.parentNode.replaceChild(n2, n1);
18825                     p.insertBefore(n1, s);
18826                 }
18827             }
18828         },
18829
18830         /**
18831          * Returns the current scroll position
18832          * @method getScroll
18833          * @private
18834          * @static
18835          */
18836         getScroll: function () {
18837             var t, l, dde=document.documentElement, db=document.body;
18838             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18839                 t = dde.scrollTop;
18840                 l = dde.scrollLeft;
18841             } else if (db) {
18842                 t = db.scrollTop;
18843                 l = db.scrollLeft;
18844             } else {
18845
18846             }
18847             return { top: t, left: l };
18848         },
18849
18850         /**
18851          * Returns the specified element style property
18852          * @method getStyle
18853          * @param {HTMLElement} el          the element
18854          * @param {string}      styleProp   the style property
18855          * @return {string} The value of the style property
18856          * @deprecated use Roo.lib.Dom.getStyle
18857          * @static
18858          */
18859         getStyle: function(el, styleProp) {
18860             return Roo.fly(el).getStyle(styleProp);
18861         },
18862
18863         /**
18864          * Gets the scrollTop
18865          * @method getScrollTop
18866          * @return {int} the document's scrollTop
18867          * @static
18868          */
18869         getScrollTop: function () { return this.getScroll().top; },
18870
18871         /**
18872          * Gets the scrollLeft
18873          * @method getScrollLeft
18874          * @return {int} the document's scrollTop
18875          * @static
18876          */
18877         getScrollLeft: function () { return this.getScroll().left; },
18878
18879         /**
18880          * Sets the x/y position of an element to the location of the
18881          * target element.
18882          * @method moveToEl
18883          * @param {HTMLElement} moveEl      The element to move
18884          * @param {HTMLElement} targetEl    The position reference element
18885          * @static
18886          */
18887         moveToEl: function (moveEl, targetEl) {
18888             var aCoord = Roo.lib.Dom.getXY(targetEl);
18889             Roo.lib.Dom.setXY(moveEl, aCoord);
18890         },
18891
18892         /**
18893          * Numeric array sort function
18894          * @method numericSort
18895          * @static
18896          */
18897         numericSort: function(a, b) { return (a - b); },
18898
18899         /**
18900          * Internal counter
18901          * @property _timeoutCount
18902          * @private
18903          * @static
18904          */
18905         _timeoutCount: 0,
18906
18907         /**
18908          * Trying to make the load order less important.  Without this we get
18909          * an error if this file is loaded before the Event Utility.
18910          * @method _addListeners
18911          * @private
18912          * @static
18913          */
18914         _addListeners: function() {
18915             var DDM = Roo.dd.DDM;
18916             if ( Roo.lib.Event && document ) {
18917                 DDM._onLoad();
18918             } else {
18919                 if (DDM._timeoutCount > 2000) {
18920                 } else {
18921                     setTimeout(DDM._addListeners, 10);
18922                     if (document && document.body) {
18923                         DDM._timeoutCount += 1;
18924                     }
18925                 }
18926             }
18927         },
18928
18929         /**
18930          * Recursively searches the immediate parent and all child nodes for
18931          * the handle element in order to determine wheter or not it was
18932          * clicked.
18933          * @method handleWasClicked
18934          * @param node the html element to inspect
18935          * @static
18936          */
18937         handleWasClicked: function(node, id) {
18938             if (this.isHandle(id, node.id)) {
18939                 return true;
18940             } else {
18941                 // check to see if this is a text node child of the one we want
18942                 var p = node.parentNode;
18943
18944                 while (p) {
18945                     if (this.isHandle(id, p.id)) {
18946                         return true;
18947                     } else {
18948                         p = p.parentNode;
18949                     }
18950                 }
18951             }
18952
18953             return false;
18954         }
18955
18956     };
18957
18958 }();
18959
18960 // shorter alias, save a few bytes
18961 Roo.dd.DDM = Roo.dd.DragDropMgr;
18962 Roo.dd.DDM._addListeners();
18963
18964 }/*
18965  * Based on:
18966  * Ext JS Library 1.1.1
18967  * Copyright(c) 2006-2007, Ext JS, LLC.
18968  *
18969  * Originally Released Under LGPL - original licence link has changed is not relivant.
18970  *
18971  * Fork - LGPL
18972  * <script type="text/javascript">
18973  */
18974
18975 /**
18976  * @class Roo.dd.DD
18977  * A DragDrop implementation where the linked element follows the
18978  * mouse cursor during a drag.
18979  * @extends Roo.dd.DragDrop
18980  * @constructor
18981  * @param {String} id the id of the linked element
18982  * @param {String} sGroup the group of related DragDrop items
18983  * @param {object} config an object containing configurable attributes
18984  *                Valid properties for DD:
18985  *                    scroll
18986  */
18987 Roo.dd.DD = function(id, sGroup, config) {
18988     if (id) {
18989         this.init(id, sGroup, config);
18990     }
18991 };
18992
18993 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18994
18995     /**
18996      * When set to true, the utility automatically tries to scroll the browser
18997      * window wehn a drag and drop element is dragged near the viewport boundary.
18998      * Defaults to true.
18999      * @property scroll
19000      * @type boolean
19001      */
19002     scroll: true,
19003
19004     /**
19005      * Sets the pointer offset to the distance between the linked element's top
19006      * left corner and the location the element was clicked
19007      * @method autoOffset
19008      * @param {int} iPageX the X coordinate of the click
19009      * @param {int} iPageY the Y coordinate of the click
19010      */
19011     autoOffset: function(iPageX, iPageY) {
19012         var x = iPageX - this.startPageX;
19013         var y = iPageY - this.startPageY;
19014         this.setDelta(x, y);
19015     },
19016
19017     /**
19018      * Sets the pointer offset.  You can call this directly to force the
19019      * offset to be in a particular location (e.g., pass in 0,0 to set it
19020      * to the center of the object)
19021      * @method setDelta
19022      * @param {int} iDeltaX the distance from the left
19023      * @param {int} iDeltaY the distance from the top
19024      */
19025     setDelta: function(iDeltaX, iDeltaY) {
19026         this.deltaX = iDeltaX;
19027         this.deltaY = iDeltaY;
19028     },
19029
19030     /**
19031      * Sets the drag element to the location of the mousedown or click event,
19032      * maintaining the cursor location relative to the location on the element
19033      * that was clicked.  Override this if you want to place the element in a
19034      * location other than where the cursor is.
19035      * @method setDragElPos
19036      * @param {int} iPageX the X coordinate of the mousedown or drag event
19037      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19038      */
19039     setDragElPos: function(iPageX, iPageY) {
19040         // the first time we do this, we are going to check to make sure
19041         // the element has css positioning
19042
19043         var el = this.getDragEl();
19044         this.alignElWithMouse(el, iPageX, iPageY);
19045     },
19046
19047     /**
19048      * Sets the element to the location of the mousedown or click event,
19049      * maintaining the cursor location relative to the location on the element
19050      * that was clicked.  Override this if you want to place the element in a
19051      * location other than where the cursor is.
19052      * @method alignElWithMouse
19053      * @param {HTMLElement} el the element to move
19054      * @param {int} iPageX the X coordinate of the mousedown or drag event
19055      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19056      */
19057     alignElWithMouse: function(el, iPageX, iPageY) {
19058         var oCoord = this.getTargetCoord(iPageX, iPageY);
19059         var fly = el.dom ? el : Roo.fly(el);
19060         if (!this.deltaSetXY) {
19061             var aCoord = [oCoord.x, oCoord.y];
19062             fly.setXY(aCoord);
19063             var newLeft = fly.getLeft(true);
19064             var newTop  = fly.getTop(true);
19065             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
19066         } else {
19067             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
19068         }
19069
19070         this.cachePosition(oCoord.x, oCoord.y);
19071         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
19072         return oCoord;
19073     },
19074
19075     /**
19076      * Saves the most recent position so that we can reset the constraints and
19077      * tick marks on-demand.  We need to know this so that we can calculate the
19078      * number of pixels the element is offset from its original position.
19079      * @method cachePosition
19080      * @param iPageX the current x position (optional, this just makes it so we
19081      * don't have to look it up again)
19082      * @param iPageY the current y position (optional, this just makes it so we
19083      * don't have to look it up again)
19084      */
19085     cachePosition: function(iPageX, iPageY) {
19086         if (iPageX) {
19087             this.lastPageX = iPageX;
19088             this.lastPageY = iPageY;
19089         } else {
19090             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19091             this.lastPageX = aCoord[0];
19092             this.lastPageY = aCoord[1];
19093         }
19094     },
19095
19096     /**
19097      * Auto-scroll the window if the dragged object has been moved beyond the
19098      * visible window boundary.
19099      * @method autoScroll
19100      * @param {int} x the drag element's x position
19101      * @param {int} y the drag element's y position
19102      * @param {int} h the height of the drag element
19103      * @param {int} w the width of the drag element
19104      * @private
19105      */
19106     autoScroll: function(x, y, h, w) {
19107
19108         if (this.scroll) {
19109             // The client height
19110             var clientH = Roo.lib.Dom.getViewWidth();
19111
19112             // The client width
19113             var clientW = Roo.lib.Dom.getViewHeight();
19114
19115             // The amt scrolled down
19116             var st = this.DDM.getScrollTop();
19117
19118             // The amt scrolled right
19119             var sl = this.DDM.getScrollLeft();
19120
19121             // Location of the bottom of the element
19122             var bot = h + y;
19123
19124             // Location of the right of the element
19125             var right = w + x;
19126
19127             // The distance from the cursor to the bottom of the visible area,
19128             // adjusted so that we don't scroll if the cursor is beyond the
19129             // element drag constraints
19130             var toBot = (clientH + st - y - this.deltaY);
19131
19132             // The distance from the cursor to the right of the visible area
19133             var toRight = (clientW + sl - x - this.deltaX);
19134
19135
19136             // How close to the edge the cursor must be before we scroll
19137             // var thresh = (document.all) ? 100 : 40;
19138             var thresh = 40;
19139
19140             // How many pixels to scroll per autoscroll op.  This helps to reduce
19141             // clunky scrolling. IE is more sensitive about this ... it needs this
19142             // value to be higher.
19143             var scrAmt = (document.all) ? 80 : 30;
19144
19145             // Scroll down if we are near the bottom of the visible page and the
19146             // obj extends below the crease
19147             if ( bot > clientH && toBot < thresh ) {
19148                 window.scrollTo(sl, st + scrAmt);
19149             }
19150
19151             // Scroll up if the window is scrolled down and the top of the object
19152             // goes above the top border
19153             if ( y < st && st > 0 && y - st < thresh ) {
19154                 window.scrollTo(sl, st - scrAmt);
19155             }
19156
19157             // Scroll right if the obj is beyond the right border and the cursor is
19158             // near the border.
19159             if ( right > clientW && toRight < thresh ) {
19160                 window.scrollTo(sl + scrAmt, st);
19161             }
19162
19163             // Scroll left if the window has been scrolled to the right and the obj
19164             // extends past the left border
19165             if ( x < sl && sl > 0 && x - sl < thresh ) {
19166                 window.scrollTo(sl - scrAmt, st);
19167             }
19168         }
19169     },
19170
19171     /**
19172      * Finds the location the element should be placed if we want to move
19173      * it to where the mouse location less the click offset would place us.
19174      * @method getTargetCoord
19175      * @param {int} iPageX the X coordinate of the click
19176      * @param {int} iPageY the Y coordinate of the click
19177      * @return an object that contains the coordinates (Object.x and Object.y)
19178      * @private
19179      */
19180     getTargetCoord: function(iPageX, iPageY) {
19181
19182
19183         var x = iPageX - this.deltaX;
19184         var y = iPageY - this.deltaY;
19185
19186         if (this.constrainX) {
19187             if (x < this.minX) { x = this.minX; }
19188             if (x > this.maxX) { x = this.maxX; }
19189         }
19190
19191         if (this.constrainY) {
19192             if (y < this.minY) { y = this.minY; }
19193             if (y > this.maxY) { y = this.maxY; }
19194         }
19195
19196         x = this.getTick(x, this.xTicks);
19197         y = this.getTick(y, this.yTicks);
19198
19199
19200         return {x:x, y:y};
19201     },
19202
19203     /*
19204      * Sets up config options specific to this class. Overrides
19205      * Roo.dd.DragDrop, but all versions of this method through the
19206      * inheritance chain are called
19207      */
19208     applyConfig: function() {
19209         Roo.dd.DD.superclass.applyConfig.call(this);
19210         this.scroll = (this.config.scroll !== false);
19211     },
19212
19213     /*
19214      * Event that fires prior to the onMouseDown event.  Overrides
19215      * Roo.dd.DragDrop.
19216      */
19217     b4MouseDown: function(e) {
19218         // this.resetConstraints();
19219         this.autoOffset(e.getPageX(),
19220                             e.getPageY());
19221     },
19222
19223     /*
19224      * Event that fires prior to the onDrag event.  Overrides
19225      * Roo.dd.DragDrop.
19226      */
19227     b4Drag: function(e) {
19228         this.setDragElPos(e.getPageX(),
19229                             e.getPageY());
19230     },
19231
19232     toString: function() {
19233         return ("DD " + this.id);
19234     }
19235
19236     //////////////////////////////////////////////////////////////////////////
19237     // Debugging ygDragDrop events that can be overridden
19238     //////////////////////////////////////////////////////////////////////////
19239     /*
19240     startDrag: function(x, y) {
19241     },
19242
19243     onDrag: function(e) {
19244     },
19245
19246     onDragEnter: function(e, id) {
19247     },
19248
19249     onDragOver: function(e, id) {
19250     },
19251
19252     onDragOut: function(e, id) {
19253     },
19254
19255     onDragDrop: function(e, id) {
19256     },
19257
19258     endDrag: function(e) {
19259     }
19260
19261     */
19262
19263 });/*
19264  * Based on:
19265  * Ext JS Library 1.1.1
19266  * Copyright(c) 2006-2007, Ext JS, LLC.
19267  *
19268  * Originally Released Under LGPL - original licence link has changed is not relivant.
19269  *
19270  * Fork - LGPL
19271  * <script type="text/javascript">
19272  */
19273
19274 /**
19275  * @class Roo.dd.DDProxy
19276  * A DragDrop implementation that inserts an empty, bordered div into
19277  * the document that follows the cursor during drag operations.  At the time of
19278  * the click, the frame div is resized to the dimensions of the linked html
19279  * element, and moved to the exact location of the linked element.
19280  *
19281  * References to the "frame" element refer to the single proxy element that
19282  * was created to be dragged in place of all DDProxy elements on the
19283  * page.
19284  *
19285  * @extends Roo.dd.DD
19286  * @constructor
19287  * @param {String} id the id of the linked html element
19288  * @param {String} sGroup the group of related DragDrop objects
19289  * @param {object} config an object containing configurable attributes
19290  *                Valid properties for DDProxy in addition to those in DragDrop:
19291  *                   resizeFrame, centerFrame, dragElId
19292  */
19293 Roo.dd.DDProxy = function(id, sGroup, config) {
19294     if (id) {
19295         this.init(id, sGroup, config);
19296         this.initFrame();
19297     }
19298 };
19299
19300 /**
19301  * The default drag frame div id
19302  * @property Roo.dd.DDProxy.dragElId
19303  * @type String
19304  * @static
19305  */
19306 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19307
19308 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19309
19310     /**
19311      * By default we resize the drag frame to be the same size as the element
19312      * we want to drag (this is to get the frame effect).  We can turn it off
19313      * if we want a different behavior.
19314      * @property resizeFrame
19315      * @type boolean
19316      */
19317     resizeFrame: true,
19318
19319     /**
19320      * By default the frame is positioned exactly where the drag element is, so
19321      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19322      * you do not have constraints on the obj is to have the drag frame centered
19323      * around the cursor.  Set centerFrame to true for this effect.
19324      * @property centerFrame
19325      * @type boolean
19326      */
19327     centerFrame: false,
19328
19329     /**
19330      * Creates the proxy element if it does not yet exist
19331      * @method createFrame
19332      */
19333     createFrame: function() {
19334         var self = this;
19335         var body = document.body;
19336
19337         if (!body || !body.firstChild) {
19338             setTimeout( function() { self.createFrame(); }, 50 );
19339             return;
19340         }
19341
19342         var div = this.getDragEl();
19343
19344         if (!div) {
19345             div    = document.createElement("div");
19346             div.id = this.dragElId;
19347             var s  = div.style;
19348
19349             s.position   = "absolute";
19350             s.visibility = "hidden";
19351             s.cursor     = "move";
19352             s.border     = "2px solid #aaa";
19353             s.zIndex     = 999;
19354
19355             // appendChild can blow up IE if invoked prior to the window load event
19356             // while rendering a table.  It is possible there are other scenarios
19357             // that would cause this to happen as well.
19358             body.insertBefore(div, body.firstChild);
19359         }
19360     },
19361
19362     /**
19363      * Initialization for the drag frame element.  Must be called in the
19364      * constructor of all subclasses
19365      * @method initFrame
19366      */
19367     initFrame: function() {
19368         this.createFrame();
19369     },
19370
19371     applyConfig: function() {
19372         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19373
19374         this.resizeFrame = (this.config.resizeFrame !== false);
19375         this.centerFrame = (this.config.centerFrame);
19376         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19377     },
19378
19379     /**
19380      * Resizes the drag frame to the dimensions of the clicked object, positions
19381      * it over the object, and finally displays it
19382      * @method showFrame
19383      * @param {int} iPageX X click position
19384      * @param {int} iPageY Y click position
19385      * @private
19386      */
19387     showFrame: function(iPageX, iPageY) {
19388         var el = this.getEl();
19389         var dragEl = this.getDragEl();
19390         var s = dragEl.style;
19391
19392         this._resizeProxy();
19393
19394         if (this.centerFrame) {
19395             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19396                            Math.round(parseInt(s.height, 10)/2) );
19397         }
19398
19399         this.setDragElPos(iPageX, iPageY);
19400
19401         Roo.fly(dragEl).show();
19402     },
19403
19404     /**
19405      * The proxy is automatically resized to the dimensions of the linked
19406      * element when a drag is initiated, unless resizeFrame is set to false
19407      * @method _resizeProxy
19408      * @private
19409      */
19410     _resizeProxy: function() {
19411         if (this.resizeFrame) {
19412             var el = this.getEl();
19413             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19414         }
19415     },
19416
19417     // overrides Roo.dd.DragDrop
19418     b4MouseDown: function(e) {
19419         var x = e.getPageX();
19420         var y = e.getPageY();
19421         this.autoOffset(x, y);
19422         this.setDragElPos(x, y);
19423     },
19424
19425     // overrides Roo.dd.DragDrop
19426     b4StartDrag: function(x, y) {
19427         // show the drag frame
19428         this.showFrame(x, y);
19429     },
19430
19431     // overrides Roo.dd.DragDrop
19432     b4EndDrag: function(e) {
19433         Roo.fly(this.getDragEl()).hide();
19434     },
19435
19436     // overrides Roo.dd.DragDrop
19437     // By default we try to move the element to the last location of the frame.
19438     // This is so that the default behavior mirrors that of Roo.dd.DD.
19439     endDrag: function(e) {
19440
19441         var lel = this.getEl();
19442         var del = this.getDragEl();
19443
19444         // Show the drag frame briefly so we can get its position
19445         del.style.visibility = "";
19446
19447         this.beforeMove();
19448         // Hide the linked element before the move to get around a Safari
19449         // rendering bug.
19450         lel.style.visibility = "hidden";
19451         Roo.dd.DDM.moveToEl(lel, del);
19452         del.style.visibility = "hidden";
19453         lel.style.visibility = "";
19454
19455         this.afterDrag();
19456     },
19457
19458     beforeMove : function(){
19459
19460     },
19461
19462     afterDrag : function(){
19463
19464     },
19465
19466     toString: function() {
19467         return ("DDProxy " + this.id);
19468     }
19469
19470 });
19471 /*
19472  * Based on:
19473  * Ext JS Library 1.1.1
19474  * Copyright(c) 2006-2007, Ext JS, LLC.
19475  *
19476  * Originally Released Under LGPL - original licence link has changed is not relivant.
19477  *
19478  * Fork - LGPL
19479  * <script type="text/javascript">
19480  */
19481
19482  /**
19483  * @class Roo.dd.DDTarget
19484  * A DragDrop implementation that does not move, but can be a drop
19485  * target.  You would get the same result by simply omitting implementation
19486  * for the event callbacks, but this way we reduce the processing cost of the
19487  * event listener and the callbacks.
19488  * @extends Roo.dd.DragDrop
19489  * @constructor
19490  * @param {String} id the id of the element that is a drop target
19491  * @param {String} sGroup the group of related DragDrop objects
19492  * @param {object} config an object containing configurable attributes
19493  *                 Valid properties for DDTarget in addition to those in
19494  *                 DragDrop:
19495  *                    none
19496  */
19497 Roo.dd.DDTarget = function(id, sGroup, config) {
19498     if (id) {
19499         this.initTarget(id, sGroup, config);
19500     }
19501     if (config.listeners || config.events) { 
19502        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19503             listeners : config.listeners || {}, 
19504             events : config.events || {} 
19505         });    
19506     }
19507 };
19508
19509 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19510 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19511     toString: function() {
19512         return ("DDTarget " + this.id);
19513     }
19514 });
19515 /*
19516  * Based on:
19517  * Ext JS Library 1.1.1
19518  * Copyright(c) 2006-2007, Ext JS, LLC.
19519  *
19520  * Originally Released Under LGPL - original licence link has changed is not relivant.
19521  *
19522  * Fork - LGPL
19523  * <script type="text/javascript">
19524  */
19525  
19526
19527 /**
19528  * @class Roo.dd.ScrollManager
19529  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19530  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19531  * @singleton
19532  */
19533 Roo.dd.ScrollManager = function(){
19534     var ddm = Roo.dd.DragDropMgr;
19535     var els = {};
19536     var dragEl = null;
19537     var proc = {};
19538     
19539     
19540     
19541     var onStop = function(e){
19542         dragEl = null;
19543         clearProc();
19544     };
19545     
19546     var triggerRefresh = function(){
19547         if(ddm.dragCurrent){
19548              ddm.refreshCache(ddm.dragCurrent.groups);
19549         }
19550     };
19551     
19552     var doScroll = function(){
19553         if(ddm.dragCurrent){
19554             var dds = Roo.dd.ScrollManager;
19555             if(!dds.animate){
19556                 if(proc.el.scroll(proc.dir, dds.increment)){
19557                     triggerRefresh();
19558                 }
19559             }else{
19560                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19561             }
19562         }
19563     };
19564     
19565     var clearProc = function(){
19566         if(proc.id){
19567             clearInterval(proc.id);
19568         }
19569         proc.id = 0;
19570         proc.el = null;
19571         proc.dir = "";
19572     };
19573     
19574     var startProc = function(el, dir){
19575          Roo.log('scroll startproc');
19576         clearProc();
19577         proc.el = el;
19578         proc.dir = dir;
19579         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19580     };
19581     
19582     var onFire = function(e, isDrop){
19583        
19584         if(isDrop || !ddm.dragCurrent){ return; }
19585         var dds = Roo.dd.ScrollManager;
19586         if(!dragEl || dragEl != ddm.dragCurrent){
19587             dragEl = ddm.dragCurrent;
19588             // refresh regions on drag start
19589             dds.refreshCache();
19590         }
19591         
19592         var xy = Roo.lib.Event.getXY(e);
19593         var pt = new Roo.lib.Point(xy[0], xy[1]);
19594         for(var id in els){
19595             var el = els[id], r = el._region;
19596             if(r && r.contains(pt) && el.isScrollable()){
19597                 if(r.bottom - pt.y <= dds.thresh){
19598                     if(proc.el != el){
19599                         startProc(el, "down");
19600                     }
19601                     return;
19602                 }else if(r.right - pt.x <= dds.thresh){
19603                     if(proc.el != el){
19604                         startProc(el, "left");
19605                     }
19606                     return;
19607                 }else if(pt.y - r.top <= dds.thresh){
19608                     if(proc.el != el){
19609                         startProc(el, "up");
19610                     }
19611                     return;
19612                 }else if(pt.x - r.left <= dds.thresh){
19613                     if(proc.el != el){
19614                         startProc(el, "right");
19615                     }
19616                     return;
19617                 }
19618             }
19619         }
19620         clearProc();
19621     };
19622     
19623     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19624     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19625     
19626     return {
19627         /**
19628          * Registers new overflow element(s) to auto scroll
19629          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19630          */
19631         register : function(el){
19632             if(el instanceof Array){
19633                 for(var i = 0, len = el.length; i < len; i++) {
19634                         this.register(el[i]);
19635                 }
19636             }else{
19637                 el = Roo.get(el);
19638                 els[el.id] = el;
19639             }
19640             Roo.dd.ScrollManager.els = els;
19641         },
19642         
19643         /**
19644          * Unregisters overflow element(s) so they are no longer scrolled
19645          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19646          */
19647         unregister : function(el){
19648             if(el instanceof Array){
19649                 for(var i = 0, len = el.length; i < len; i++) {
19650                         this.unregister(el[i]);
19651                 }
19652             }else{
19653                 el = Roo.get(el);
19654                 delete els[el.id];
19655             }
19656         },
19657         
19658         /**
19659          * The number of pixels from the edge of a container the pointer needs to be to 
19660          * trigger scrolling (defaults to 25)
19661          * @type Number
19662          */
19663         thresh : 25,
19664         
19665         /**
19666          * The number of pixels to scroll in each scroll increment (defaults to 50)
19667          * @type Number
19668          */
19669         increment : 100,
19670         
19671         /**
19672          * The frequency of scrolls in milliseconds (defaults to 500)
19673          * @type Number
19674          */
19675         frequency : 500,
19676         
19677         /**
19678          * True to animate the scroll (defaults to true)
19679          * @type Boolean
19680          */
19681         animate: true,
19682         
19683         /**
19684          * The animation duration in seconds - 
19685          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19686          * @type Number
19687          */
19688         animDuration: .4,
19689         
19690         /**
19691          * Manually trigger a cache refresh.
19692          */
19693         refreshCache : function(){
19694             for(var id in els){
19695                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19696                     els[id]._region = els[id].getRegion();
19697                 }
19698             }
19699         }
19700     };
19701 }();/*
19702  * Based on:
19703  * Ext JS Library 1.1.1
19704  * Copyright(c) 2006-2007, Ext JS, LLC.
19705  *
19706  * Originally Released Under LGPL - original licence link has changed is not relivant.
19707  *
19708  * Fork - LGPL
19709  * <script type="text/javascript">
19710  */
19711  
19712
19713 /**
19714  * @class Roo.dd.Registry
19715  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19716  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19717  * @singleton
19718  */
19719 Roo.dd.Registry = function(){
19720     var elements = {}; 
19721     var handles = {}; 
19722     var autoIdSeed = 0;
19723
19724     var getId = function(el, autogen){
19725         if(typeof el == "string"){
19726             return el;
19727         }
19728         var id = el.id;
19729         if(!id && autogen !== false){
19730             id = "roodd-" + (++autoIdSeed);
19731             el.id = id;
19732         }
19733         return id;
19734     };
19735     
19736     return {
19737     /**
19738      * Register a drag drop element
19739      * @param {String|HTMLElement} element The id or DOM node to register
19740      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19741      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19742      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19743      * populated in the data object (if applicable):
19744      * <pre>
19745 Value      Description<br />
19746 ---------  ------------------------------------------<br />
19747 handles    Array of DOM nodes that trigger dragging<br />
19748            for the element being registered<br />
19749 isHandle   True if the element passed in triggers<br />
19750            dragging itself, else false
19751 </pre>
19752      */
19753         register : function(el, data){
19754             data = data || {};
19755             if(typeof el == "string"){
19756                 el = document.getElementById(el);
19757             }
19758             data.ddel = el;
19759             elements[getId(el)] = data;
19760             if(data.isHandle !== false){
19761                 handles[data.ddel.id] = data;
19762             }
19763             if(data.handles){
19764                 var hs = data.handles;
19765                 for(var i = 0, len = hs.length; i < len; i++){
19766                         handles[getId(hs[i])] = data;
19767                 }
19768             }
19769         },
19770
19771     /**
19772      * Unregister a drag drop element
19773      * @param {String|HTMLElement}  element The id or DOM node to unregister
19774      */
19775         unregister : function(el){
19776             var id = getId(el, false);
19777             var data = elements[id];
19778             if(data){
19779                 delete elements[id];
19780                 if(data.handles){
19781                     var hs = data.handles;
19782                     for(var i = 0, len = hs.length; i < len; i++){
19783                         delete handles[getId(hs[i], false)];
19784                     }
19785                 }
19786             }
19787         },
19788
19789     /**
19790      * Returns the handle registered for a DOM Node by id
19791      * @param {String|HTMLElement} id The DOM node or id to look up
19792      * @return {Object} handle The custom handle data
19793      */
19794         getHandle : function(id){
19795             if(typeof id != "string"){ // must be element?
19796                 id = id.id;
19797             }
19798             return handles[id];
19799         },
19800
19801     /**
19802      * Returns the handle that is registered for the DOM node that is the target of the event
19803      * @param {Event} e The event
19804      * @return {Object} handle The custom handle data
19805      */
19806         getHandleFromEvent : function(e){
19807             var t = Roo.lib.Event.getTarget(e);
19808             return t ? handles[t.id] : null;
19809         },
19810
19811     /**
19812      * Returns a custom data object that is registered for a DOM node by id
19813      * @param {String|HTMLElement} id The DOM node or id to look up
19814      * @return {Object} data The custom data
19815      */
19816         getTarget : function(id){
19817             if(typeof id != "string"){ // must be element?
19818                 id = id.id;
19819             }
19820             return elements[id];
19821         },
19822
19823     /**
19824      * Returns a custom data object that is registered for the DOM node that is the target of the event
19825      * @param {Event} e The event
19826      * @return {Object} data The custom data
19827      */
19828         getTargetFromEvent : function(e){
19829             var t = Roo.lib.Event.getTarget(e);
19830             return t ? elements[t.id] || handles[t.id] : null;
19831         }
19832     };
19833 }();/*
19834  * Based on:
19835  * Ext JS Library 1.1.1
19836  * Copyright(c) 2006-2007, Ext JS, LLC.
19837  *
19838  * Originally Released Under LGPL - original licence link has changed is not relivant.
19839  *
19840  * Fork - LGPL
19841  * <script type="text/javascript">
19842  */
19843  
19844
19845 /**
19846  * @class Roo.dd.StatusProxy
19847  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19848  * default drag proxy used by all Roo.dd components.
19849  * @constructor
19850  * @param {Object} config
19851  */
19852 Roo.dd.StatusProxy = function(config){
19853     Roo.apply(this, config);
19854     this.id = this.id || Roo.id();
19855     this.el = new Roo.Layer({
19856         dh: {
19857             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19858                 {tag: "div", cls: "x-dd-drop-icon"},
19859                 {tag: "div", cls: "x-dd-drag-ghost"}
19860             ]
19861         }, 
19862         shadow: !config || config.shadow !== false
19863     });
19864     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19865     this.dropStatus = this.dropNotAllowed;
19866 };
19867
19868 Roo.dd.StatusProxy.prototype = {
19869     /**
19870      * @cfg {String} dropAllowed
19871      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19872      */
19873     dropAllowed : "x-dd-drop-ok",
19874     /**
19875      * @cfg {String} dropNotAllowed
19876      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19877      */
19878     dropNotAllowed : "x-dd-drop-nodrop",
19879
19880     /**
19881      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19882      * over the current target element.
19883      * @param {String} cssClass The css class for the new drop status indicator image
19884      */
19885     setStatus : function(cssClass){
19886         cssClass = cssClass || this.dropNotAllowed;
19887         if(this.dropStatus != cssClass){
19888             this.el.replaceClass(this.dropStatus, cssClass);
19889             this.dropStatus = cssClass;
19890         }
19891     },
19892
19893     /**
19894      * Resets the status indicator to the default dropNotAllowed value
19895      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19896      */
19897     reset : function(clearGhost){
19898         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19899         this.dropStatus = this.dropNotAllowed;
19900         if(clearGhost){
19901             this.ghost.update("");
19902         }
19903     },
19904
19905     /**
19906      * Updates the contents of the ghost element
19907      * @param {String} html The html that will replace the current innerHTML of the ghost element
19908      */
19909     update : function(html){
19910         if(typeof html == "string"){
19911             this.ghost.update(html);
19912         }else{
19913             this.ghost.update("");
19914             html.style.margin = "0";
19915             this.ghost.dom.appendChild(html);
19916         }
19917         // ensure float = none set?? cant remember why though.
19918         var el = this.ghost.dom.firstChild;
19919                 if(el){
19920                         Roo.fly(el).setStyle('float', 'none');
19921                 }
19922     },
19923     
19924     /**
19925      * Returns the underlying proxy {@link Roo.Layer}
19926      * @return {Roo.Layer} el
19927     */
19928     getEl : function(){
19929         return this.el;
19930     },
19931
19932     /**
19933      * Returns the ghost element
19934      * @return {Roo.Element} el
19935      */
19936     getGhost : function(){
19937         return this.ghost;
19938     },
19939
19940     /**
19941      * Hides the proxy
19942      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19943      */
19944     hide : function(clear){
19945         this.el.hide();
19946         if(clear){
19947             this.reset(true);
19948         }
19949     },
19950
19951     /**
19952      * Stops the repair animation if it's currently running
19953      */
19954     stop : function(){
19955         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19956             this.anim.stop();
19957         }
19958     },
19959
19960     /**
19961      * Displays this proxy
19962      */
19963     show : function(){
19964         this.el.show();
19965     },
19966
19967     /**
19968      * Force the Layer to sync its shadow and shim positions to the element
19969      */
19970     sync : function(){
19971         this.el.sync();
19972     },
19973
19974     /**
19975      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19976      * invalid drop operation by the item being dragged.
19977      * @param {Array} xy The XY position of the element ([x, y])
19978      * @param {Function} callback The function to call after the repair is complete
19979      * @param {Object} scope The scope in which to execute the callback
19980      */
19981     repair : function(xy, callback, scope){
19982         this.callback = callback;
19983         this.scope = scope;
19984         if(xy && this.animRepair !== false){
19985             this.el.addClass("x-dd-drag-repair");
19986             this.el.hideUnders(true);
19987             this.anim = this.el.shift({
19988                 duration: this.repairDuration || .5,
19989                 easing: 'easeOut',
19990                 xy: xy,
19991                 stopFx: true,
19992                 callback: this.afterRepair,
19993                 scope: this
19994             });
19995         }else{
19996             this.afterRepair();
19997         }
19998     },
19999
20000     // private
20001     afterRepair : function(){
20002         this.hide(true);
20003         if(typeof this.callback == "function"){
20004             this.callback.call(this.scope || this);
20005         }
20006         this.callback = null;
20007         this.scope = null;
20008     }
20009 };/*
20010  * Based on:
20011  * Ext JS Library 1.1.1
20012  * Copyright(c) 2006-2007, Ext JS, LLC.
20013  *
20014  * Originally Released Under LGPL - original licence link has changed is not relivant.
20015  *
20016  * Fork - LGPL
20017  * <script type="text/javascript">
20018  */
20019
20020 /**
20021  * @class Roo.dd.DragSource
20022  * @extends Roo.dd.DDProxy
20023  * A simple class that provides the basic implementation needed to make any element draggable.
20024  * @constructor
20025  * @param {String/HTMLElement/Element} el The container element
20026  * @param {Object} config
20027  */
20028 Roo.dd.DragSource = function(el, config){
20029     this.el = Roo.get(el);
20030     this.dragData = {};
20031     
20032     Roo.apply(this, config);
20033     
20034     if(!this.proxy){
20035         this.proxy = new Roo.dd.StatusProxy();
20036     }
20037
20038     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
20039           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
20040     
20041     this.dragging = false;
20042 };
20043
20044 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
20045     /**
20046      * @cfg {String} dropAllowed
20047      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20048      */
20049     dropAllowed : "x-dd-drop-ok",
20050     /**
20051      * @cfg {String} dropNotAllowed
20052      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20053      */
20054     dropNotAllowed : "x-dd-drop-nodrop",
20055
20056     /**
20057      * Returns the data object associated with this drag source
20058      * @return {Object} data An object containing arbitrary data
20059      */
20060     getDragData : function(e){
20061         return this.dragData;
20062     },
20063
20064     // private
20065     onDragEnter : function(e, id){
20066         var target = Roo.dd.DragDropMgr.getDDById(id);
20067         this.cachedTarget = target;
20068         if(this.beforeDragEnter(target, e, id) !== false){
20069             if(target.isNotifyTarget){
20070                 var status = target.notifyEnter(this, e, this.dragData);
20071                 this.proxy.setStatus(status);
20072             }else{
20073                 this.proxy.setStatus(this.dropAllowed);
20074             }
20075             
20076             if(this.afterDragEnter){
20077                 /**
20078                  * An empty function by default, but provided so that you can perform a custom action
20079                  * when the dragged item enters the drop target by providing an implementation.
20080                  * @param {Roo.dd.DragDrop} target The drop target
20081                  * @param {Event} e The event object
20082                  * @param {String} id The id of the dragged element
20083                  * @method afterDragEnter
20084                  */
20085                 this.afterDragEnter(target, e, id);
20086             }
20087         }
20088     },
20089
20090     /**
20091      * An empty function by default, but provided so that you can perform a custom action
20092      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20093      * @param {Roo.dd.DragDrop} target The drop target
20094      * @param {Event} e The event object
20095      * @param {String} id The id of the dragged element
20096      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20097      */
20098     beforeDragEnter : function(target, e, id){
20099         return true;
20100     },
20101
20102     // private
20103     alignElWithMouse: function() {
20104         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20105         this.proxy.sync();
20106     },
20107
20108     // private
20109     onDragOver : function(e, id){
20110         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20111         if(this.beforeDragOver(target, e, id) !== false){
20112             if(target.isNotifyTarget){
20113                 var status = target.notifyOver(this, e, this.dragData);
20114                 this.proxy.setStatus(status);
20115             }
20116
20117             if(this.afterDragOver){
20118                 /**
20119                  * An empty function by default, but provided so that you can perform a custom action
20120                  * while the dragged item is over the drop target by providing an implementation.
20121                  * @param {Roo.dd.DragDrop} target The drop target
20122                  * @param {Event} e The event object
20123                  * @param {String} id The id of the dragged element
20124                  * @method afterDragOver
20125                  */
20126                 this.afterDragOver(target, e, id);
20127             }
20128         }
20129     },
20130
20131     /**
20132      * An empty function by default, but provided so that you can perform a custom action
20133      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20134      * @param {Roo.dd.DragDrop} target The drop target
20135      * @param {Event} e The event object
20136      * @param {String} id The id of the dragged element
20137      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20138      */
20139     beforeDragOver : function(target, e, id){
20140         return true;
20141     },
20142
20143     // private
20144     onDragOut : function(e, id){
20145         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20146         if(this.beforeDragOut(target, e, id) !== false){
20147             if(target.isNotifyTarget){
20148                 target.notifyOut(this, e, this.dragData);
20149             }
20150             this.proxy.reset();
20151             if(this.afterDragOut){
20152                 /**
20153                  * An empty function by default, but provided so that you can perform a custom action
20154                  * after the dragged item is dragged out of the target without dropping.
20155                  * @param {Roo.dd.DragDrop} target The drop target
20156                  * @param {Event} e The event object
20157                  * @param {String} id The id of the dragged element
20158                  * @method afterDragOut
20159                  */
20160                 this.afterDragOut(target, e, id);
20161             }
20162         }
20163         this.cachedTarget = null;
20164     },
20165
20166     /**
20167      * An empty function by default, but provided so that you can perform a custom action before the dragged
20168      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20169      * @param {Roo.dd.DragDrop} target The drop target
20170      * @param {Event} e The event object
20171      * @param {String} id The id of the dragged element
20172      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20173      */
20174     beforeDragOut : function(target, e, id){
20175         return true;
20176     },
20177     
20178     // private
20179     onDragDrop : function(e, id){
20180         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20181         if(this.beforeDragDrop(target, e, id) !== false){
20182             if(target.isNotifyTarget){
20183                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20184                     this.onValidDrop(target, e, id);
20185                 }else{
20186                     this.onInvalidDrop(target, e, id);
20187                 }
20188             }else{
20189                 this.onValidDrop(target, e, id);
20190             }
20191             
20192             if(this.afterDragDrop){
20193                 /**
20194                  * An empty function by default, but provided so that you can perform a custom action
20195                  * after a valid drag drop has occurred by providing an implementation.
20196                  * @param {Roo.dd.DragDrop} target The drop target
20197                  * @param {Event} e The event object
20198                  * @param {String} id The id of the dropped element
20199                  * @method afterDragDrop
20200                  */
20201                 this.afterDragDrop(target, e, id);
20202             }
20203         }
20204         delete this.cachedTarget;
20205     },
20206
20207     /**
20208      * An empty function by default, but provided so that you can perform a custom action before the dragged
20209      * item is dropped onto the target and optionally cancel the onDragDrop.
20210      * @param {Roo.dd.DragDrop} target The drop target
20211      * @param {Event} e The event object
20212      * @param {String} id The id of the dragged element
20213      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20214      */
20215     beforeDragDrop : function(target, e, id){
20216         return true;
20217     },
20218
20219     // private
20220     onValidDrop : function(target, e, id){
20221         this.hideProxy();
20222         if(this.afterValidDrop){
20223             /**
20224              * An empty function by default, but provided so that you can perform a custom action
20225              * after a valid drop has occurred by providing an implementation.
20226              * @param {Object} target The target DD 
20227              * @param {Event} e The event object
20228              * @param {String} id The id of the dropped element
20229              * @method afterInvalidDrop
20230              */
20231             this.afterValidDrop(target, e, id);
20232         }
20233     },
20234
20235     // private
20236     getRepairXY : function(e, data){
20237         return this.el.getXY();  
20238     },
20239
20240     // private
20241     onInvalidDrop : function(target, e, id){
20242         this.beforeInvalidDrop(target, e, id);
20243         if(this.cachedTarget){
20244             if(this.cachedTarget.isNotifyTarget){
20245                 this.cachedTarget.notifyOut(this, e, this.dragData);
20246             }
20247             this.cacheTarget = null;
20248         }
20249         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20250
20251         if(this.afterInvalidDrop){
20252             /**
20253              * An empty function by default, but provided so that you can perform a custom action
20254              * after an invalid drop has occurred by providing an implementation.
20255              * @param {Event} e The event object
20256              * @param {String} id The id of the dropped element
20257              * @method afterInvalidDrop
20258              */
20259             this.afterInvalidDrop(e, id);
20260         }
20261     },
20262
20263     // private
20264     afterRepair : function(){
20265         if(Roo.enableFx){
20266             this.el.highlight(this.hlColor || "c3daf9");
20267         }
20268         this.dragging = false;
20269     },
20270
20271     /**
20272      * An empty function by default, but provided so that you can perform a custom action after an invalid
20273      * drop has occurred.
20274      * @param {Roo.dd.DragDrop} target The drop target
20275      * @param {Event} e The event object
20276      * @param {String} id The id of the dragged element
20277      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20278      */
20279     beforeInvalidDrop : function(target, e, id){
20280         return true;
20281     },
20282
20283     // private
20284     handleMouseDown : function(e){
20285         if(this.dragging) {
20286             return;
20287         }
20288         var data = this.getDragData(e);
20289         if(data && this.onBeforeDrag(data, e) !== false){
20290             this.dragData = data;
20291             this.proxy.stop();
20292             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20293         } 
20294     },
20295
20296     /**
20297      * An empty function by default, but provided so that you can perform a custom action before the initial
20298      * drag event begins and optionally cancel it.
20299      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20300      * @param {Event} e The event object
20301      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20302      */
20303     onBeforeDrag : function(data, e){
20304         return true;
20305     },
20306
20307     /**
20308      * An empty function by default, but provided so that you can perform a custom action once the initial
20309      * drag event has begun.  The drag cannot be canceled from this function.
20310      * @param {Number} x The x position of the click on the dragged object
20311      * @param {Number} y The y position of the click on the dragged object
20312      */
20313     onStartDrag : Roo.emptyFn,
20314
20315     // private - YUI override
20316     startDrag : function(x, y){
20317         this.proxy.reset();
20318         this.dragging = true;
20319         this.proxy.update("");
20320         this.onInitDrag(x, y);
20321         this.proxy.show();
20322     },
20323
20324     // private
20325     onInitDrag : function(x, y){
20326         var clone = this.el.dom.cloneNode(true);
20327         clone.id = Roo.id(); // prevent duplicate ids
20328         this.proxy.update(clone);
20329         this.onStartDrag(x, y);
20330         return true;
20331     },
20332
20333     /**
20334      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20335      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20336      */
20337     getProxy : function(){
20338         return this.proxy;  
20339     },
20340
20341     /**
20342      * Hides the drag source's {@link Roo.dd.StatusProxy}
20343      */
20344     hideProxy : function(){
20345         this.proxy.hide();  
20346         this.proxy.reset(true);
20347         this.dragging = false;
20348     },
20349
20350     // private
20351     triggerCacheRefresh : function(){
20352         Roo.dd.DDM.refreshCache(this.groups);
20353     },
20354
20355     // private - override to prevent hiding
20356     b4EndDrag: function(e) {
20357     },
20358
20359     // private - override to prevent moving
20360     endDrag : function(e){
20361         this.onEndDrag(this.dragData, e);
20362     },
20363
20364     // private
20365     onEndDrag : function(data, e){
20366     },
20367     
20368     // private - pin to cursor
20369     autoOffset : function(x, y) {
20370         this.setDelta(-12, -20);
20371     }    
20372 });/*
20373  * Based on:
20374  * Ext JS Library 1.1.1
20375  * Copyright(c) 2006-2007, Ext JS, LLC.
20376  *
20377  * Originally Released Under LGPL - original licence link has changed is not relivant.
20378  *
20379  * Fork - LGPL
20380  * <script type="text/javascript">
20381  */
20382
20383
20384 /**
20385  * @class Roo.dd.DropTarget
20386  * @extends Roo.dd.DDTarget
20387  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20388  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20389  * @constructor
20390  * @param {String/HTMLElement/Element} el The container element
20391  * @param {Object} config
20392  */
20393 Roo.dd.DropTarget = function(el, config){
20394     this.el = Roo.get(el);
20395     
20396     var listeners = false; ;
20397     if (config && config.listeners) {
20398         listeners= config.listeners;
20399         delete config.listeners;
20400     }
20401     Roo.apply(this, config);
20402     
20403     if(this.containerScroll){
20404         Roo.dd.ScrollManager.register(this.el);
20405     }
20406     this.addEvents( {
20407          /**
20408          * @scope Roo.dd.DropTarget
20409          */
20410          
20411          /**
20412          * @event enter
20413          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20414          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20415          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20416          * 
20417          * IMPORTANT : it should set this.overClass and this.dropAllowed
20418          * 
20419          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20420          * @param {Event} e The event
20421          * @param {Object} data An object containing arbitrary data supplied by the drag source
20422          */
20423         "enter" : true,
20424         
20425          /**
20426          * @event over
20427          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20428          * This method will be called on every mouse movement while the drag source is over the drop target.
20429          * This default implementation simply returns the dropAllowed config value.
20430          * 
20431          * IMPORTANT : it should set this.dropAllowed
20432          * 
20433          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20434          * @param {Event} e The event
20435          * @param {Object} data An object containing arbitrary data supplied by the drag source
20436          
20437          */
20438         "over" : true,
20439         /**
20440          * @event out
20441          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20442          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20443          * overClass (if any) from the drop element.
20444          * 
20445          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20446          * @param {Event} e The event
20447          * @param {Object} data An object containing arbitrary data supplied by the drag source
20448          */
20449          "out" : true,
20450          
20451         /**
20452          * @event drop
20453          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20454          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20455          * implementation that does something to process the drop event and returns true so that the drag source's
20456          * repair action does not run.
20457          * 
20458          * IMPORTANT : it should set this.success
20459          * 
20460          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20461          * @param {Event} e The event
20462          * @param {Object} data An object containing arbitrary data supplied by the drag source
20463         */
20464          "drop" : true
20465     });
20466             
20467      
20468     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20469         this.el.dom, 
20470         this.ddGroup || this.group,
20471         {
20472             isTarget: true,
20473             listeners : listeners || {} 
20474            
20475         
20476         }
20477     );
20478
20479 };
20480
20481 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20482     /**
20483      * @cfg {String} overClass
20484      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20485      */
20486      /**
20487      * @cfg {String} ddGroup
20488      * The drag drop group to handle drop events for
20489      */
20490      
20491     /**
20492      * @cfg {String} dropAllowed
20493      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20494      */
20495     dropAllowed : "x-dd-drop-ok",
20496     /**
20497      * @cfg {String} dropNotAllowed
20498      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20499      */
20500     dropNotAllowed : "x-dd-drop-nodrop",
20501     /**
20502      * @cfg {boolean} success
20503      * set this after drop listener.. 
20504      */
20505     success : false,
20506     /**
20507      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20508      * if the drop point is valid for over/enter..
20509      */
20510     valid : false,
20511     // private
20512     isTarget : true,
20513
20514     // private
20515     isNotifyTarget : true,
20516     
20517     /**
20518      * @hide
20519      */
20520     notifyEnter : function(dd, e, data)
20521     {
20522         this.valid = true;
20523         this.fireEvent('enter', dd, e, data);
20524         if(this.overClass){
20525             this.el.addClass(this.overClass);
20526         }
20527         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20528             this.valid ? this.dropAllowed : this.dropNotAllowed
20529         );
20530     },
20531
20532     /**
20533      * @hide
20534      */
20535     notifyOver : function(dd, e, data)
20536     {
20537         this.valid = true;
20538         this.fireEvent('over', dd, e, data);
20539         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20540             this.valid ? this.dropAllowed : this.dropNotAllowed
20541         );
20542     },
20543
20544     /**
20545      * @hide
20546      */
20547     notifyOut : function(dd, e, data)
20548     {
20549         this.fireEvent('out', dd, e, data);
20550         if(this.overClass){
20551             this.el.removeClass(this.overClass);
20552         }
20553     },
20554
20555     /**
20556      * @hide
20557      */
20558     notifyDrop : function(dd, e, data)
20559     {
20560         this.success = false;
20561         this.fireEvent('drop', dd, e, data);
20562         return this.success;
20563     }
20564 });/*
20565  * Based on:
20566  * Ext JS Library 1.1.1
20567  * Copyright(c) 2006-2007, Ext JS, LLC.
20568  *
20569  * Originally Released Under LGPL - original licence link has changed is not relivant.
20570  *
20571  * Fork - LGPL
20572  * <script type="text/javascript">
20573  */
20574
20575
20576 /**
20577  * @class Roo.dd.DragZone
20578  * @extends Roo.dd.DragSource
20579  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20580  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20581  * @constructor
20582  * @param {String/HTMLElement/Element} el The container element
20583  * @param {Object} config
20584  */
20585 Roo.dd.DragZone = function(el, config){
20586     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20587     if(this.containerScroll){
20588         Roo.dd.ScrollManager.register(this.el);
20589     }
20590 };
20591
20592 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20593     /**
20594      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20595      * for auto scrolling during drag operations.
20596      */
20597     /**
20598      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20599      * method after a failed drop (defaults to "c3daf9" - light blue)
20600      */
20601
20602     /**
20603      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20604      * for a valid target to drag based on the mouse down. Override this method
20605      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20606      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20607      * @param {EventObject} e The mouse down event
20608      * @return {Object} The dragData
20609      */
20610     getDragData : function(e){
20611         return Roo.dd.Registry.getHandleFromEvent(e);
20612     },
20613     
20614     /**
20615      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20616      * this.dragData.ddel
20617      * @param {Number} x The x position of the click on the dragged object
20618      * @param {Number} y The y position of the click on the dragged object
20619      * @return {Boolean} true to continue the drag, false to cancel
20620      */
20621     onInitDrag : function(x, y){
20622         this.proxy.update(this.dragData.ddel.cloneNode(true));
20623         this.onStartDrag(x, y);
20624         return true;
20625     },
20626     
20627     /**
20628      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20629      */
20630     afterRepair : function(){
20631         if(Roo.enableFx){
20632             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20633         }
20634         this.dragging = false;
20635     },
20636
20637     /**
20638      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20639      * the XY of this.dragData.ddel
20640      * @param {EventObject} e The mouse up event
20641      * @return {Array} The xy location (e.g. [100, 200])
20642      */
20643     getRepairXY : function(e){
20644         return Roo.Element.fly(this.dragData.ddel).getXY();  
20645     }
20646 });/*
20647  * Based on:
20648  * Ext JS Library 1.1.1
20649  * Copyright(c) 2006-2007, Ext JS, LLC.
20650  *
20651  * Originally Released Under LGPL - original licence link has changed is not relivant.
20652  *
20653  * Fork - LGPL
20654  * <script type="text/javascript">
20655  */
20656 /**
20657  * @class Roo.dd.DropZone
20658  * @extends Roo.dd.DropTarget
20659  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20660  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20661  * @constructor
20662  * @param {String/HTMLElement/Element} el The container element
20663  * @param {Object} config
20664  */
20665 Roo.dd.DropZone = function(el, config){
20666     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20667 };
20668
20669 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20670     /**
20671      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20672      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20673      * provide your own custom lookup.
20674      * @param {Event} e The event
20675      * @return {Object} data The custom data
20676      */
20677     getTargetFromEvent : function(e){
20678         return Roo.dd.Registry.getTargetFromEvent(e);
20679     },
20680
20681     /**
20682      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20683      * that it has registered.  This method has no default implementation and should be overridden to provide
20684      * node-specific processing if necessary.
20685      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20686      * {@link #getTargetFromEvent} for this node)
20687      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20688      * @param {Event} e The event
20689      * @param {Object} data An object containing arbitrary data supplied by the drag source
20690      */
20691     onNodeEnter : function(n, dd, e, data){
20692         
20693     },
20694
20695     /**
20696      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20697      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20698      * overridden to provide the proper feedback.
20699      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20700      * {@link #getTargetFromEvent} for this node)
20701      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20702      * @param {Event} e The event
20703      * @param {Object} data An object containing arbitrary data supplied by the drag source
20704      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20705      * underlying {@link Roo.dd.StatusProxy} can be updated
20706      */
20707     onNodeOver : function(n, dd, e, data){
20708         return this.dropAllowed;
20709     },
20710
20711     /**
20712      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20713      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20714      * node-specific processing if necessary.
20715      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20716      * {@link #getTargetFromEvent} for this node)
20717      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20718      * @param {Event} e The event
20719      * @param {Object} data An object containing arbitrary data supplied by the drag source
20720      */
20721     onNodeOut : function(n, dd, e, data){
20722         
20723     },
20724
20725     /**
20726      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20727      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20728      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20729      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20730      * {@link #getTargetFromEvent} for this node)
20731      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20732      * @param {Event} e The event
20733      * @param {Object} data An object containing arbitrary data supplied by the drag source
20734      * @return {Boolean} True if the drop was valid, else false
20735      */
20736     onNodeDrop : function(n, dd, e, data){
20737         return false;
20738     },
20739
20740     /**
20741      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20742      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20743      * it should be overridden to provide the proper feedback if necessary.
20744      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20745      * @param {Event} e The event
20746      * @param {Object} data An object containing arbitrary data supplied by the drag source
20747      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20748      * underlying {@link Roo.dd.StatusProxy} can be updated
20749      */
20750     onContainerOver : function(dd, e, data){
20751         return this.dropNotAllowed;
20752     },
20753
20754     /**
20755      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20756      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20757      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20758      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20759      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20760      * @param {Event} e The event
20761      * @param {Object} data An object containing arbitrary data supplied by the drag source
20762      * @return {Boolean} True if the drop was valid, else false
20763      */
20764     onContainerDrop : function(dd, e, data){
20765         return false;
20766     },
20767
20768     /**
20769      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20770      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20771      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20772      * you should override this method and provide a custom implementation.
20773      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20774      * @param {Event} e The event
20775      * @param {Object} data An object containing arbitrary data supplied by the drag source
20776      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20777      * underlying {@link Roo.dd.StatusProxy} can be updated
20778      */
20779     notifyEnter : function(dd, e, data){
20780         return this.dropNotAllowed;
20781     },
20782
20783     /**
20784      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20785      * This method will be called on every mouse movement while the drag source is over the drop zone.
20786      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20787      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20788      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20789      * registered node, it will call {@link #onContainerOver}.
20790      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20791      * @param {Event} e The event
20792      * @param {Object} data An object containing arbitrary data supplied by the drag source
20793      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20794      * underlying {@link Roo.dd.StatusProxy} can be updated
20795      */
20796     notifyOver : function(dd, e, data){
20797         var n = this.getTargetFromEvent(e);
20798         if(!n){ // not over valid drop target
20799             if(this.lastOverNode){
20800                 this.onNodeOut(this.lastOverNode, dd, e, data);
20801                 this.lastOverNode = null;
20802             }
20803             return this.onContainerOver(dd, e, data);
20804         }
20805         if(this.lastOverNode != n){
20806             if(this.lastOverNode){
20807                 this.onNodeOut(this.lastOverNode, dd, e, data);
20808             }
20809             this.onNodeEnter(n, dd, e, data);
20810             this.lastOverNode = n;
20811         }
20812         return this.onNodeOver(n, dd, e, data);
20813     },
20814
20815     /**
20816      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20817      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20818      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20819      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20820      * @param {Event} e The event
20821      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20822      */
20823     notifyOut : function(dd, e, data){
20824         if(this.lastOverNode){
20825             this.onNodeOut(this.lastOverNode, dd, e, data);
20826             this.lastOverNode = null;
20827         }
20828     },
20829
20830     /**
20831      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20832      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20833      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20834      * otherwise it will call {@link #onContainerDrop}.
20835      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20836      * @param {Event} e The event
20837      * @param {Object} data An object containing arbitrary data supplied by the drag source
20838      * @return {Boolean} True if the drop was valid, else false
20839      */
20840     notifyDrop : function(dd, e, data){
20841         if(this.lastOverNode){
20842             this.onNodeOut(this.lastOverNode, dd, e, data);
20843             this.lastOverNode = null;
20844         }
20845         var n = this.getTargetFromEvent(e);
20846         return n ?
20847             this.onNodeDrop(n, dd, e, data) :
20848             this.onContainerDrop(dd, e, data);
20849     },
20850
20851     // private
20852     triggerCacheRefresh : function(){
20853         Roo.dd.DDM.refreshCache(this.groups);
20854     }  
20855 });/*
20856  * Based on:
20857  * Ext JS Library 1.1.1
20858  * Copyright(c) 2006-2007, Ext JS, LLC.
20859  *
20860  * Originally Released Under LGPL - original licence link has changed is not relivant.
20861  *
20862  * Fork - LGPL
20863  * <script type="text/javascript">
20864  */
20865
20866
20867 /**
20868  * @class Roo.data.SortTypes
20869  * @singleton
20870  * Defines the default sorting (casting?) comparison functions used when sorting data.
20871  */
20872 Roo.data.SortTypes = {
20873     /**
20874      * Default sort that does nothing
20875      * @param {Mixed} s The value being converted
20876      * @return {Mixed} The comparison value
20877      */
20878     none : function(s){
20879         return s;
20880     },
20881     
20882     /**
20883      * The regular expression used to strip tags
20884      * @type {RegExp}
20885      * @property
20886      */
20887     stripTagsRE : /<\/?[^>]+>/gi,
20888     
20889     /**
20890      * Strips all HTML tags to sort on text only
20891      * @param {Mixed} s The value being converted
20892      * @return {String} The comparison value
20893      */
20894     asText : function(s){
20895         return String(s).replace(this.stripTagsRE, "");
20896     },
20897     
20898     /**
20899      * Strips all HTML tags to sort on text only - Case insensitive
20900      * @param {Mixed} s The value being converted
20901      * @return {String} The comparison value
20902      */
20903     asUCText : function(s){
20904         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20905     },
20906     
20907     /**
20908      * Case insensitive string
20909      * @param {Mixed} s The value being converted
20910      * @return {String} The comparison value
20911      */
20912     asUCString : function(s) {
20913         return String(s).toUpperCase();
20914     },
20915     
20916     /**
20917      * Date sorting
20918      * @param {Mixed} s The value being converted
20919      * @return {Number} The comparison value
20920      */
20921     asDate : function(s) {
20922         if(!s){
20923             return 0;
20924         }
20925         if(s instanceof Date){
20926             return s.getTime();
20927         }
20928         return Date.parse(String(s));
20929     },
20930     
20931     /**
20932      * Float sorting
20933      * @param {Mixed} s The value being converted
20934      * @return {Float} The comparison value
20935      */
20936     asFloat : function(s) {
20937         var val = parseFloat(String(s).replace(/,/g, ""));
20938         if(isNaN(val)) val = 0;
20939         return val;
20940     },
20941     
20942     /**
20943      * Integer sorting
20944      * @param {Mixed} s The value being converted
20945      * @return {Number} The comparison value
20946      */
20947     asInt : function(s) {
20948         var val = parseInt(String(s).replace(/,/g, ""));
20949         if(isNaN(val)) val = 0;
20950         return val;
20951     }
20952 };/*
20953  * Based on:
20954  * Ext JS Library 1.1.1
20955  * Copyright(c) 2006-2007, Ext JS, LLC.
20956  *
20957  * Originally Released Under LGPL - original licence link has changed is not relivant.
20958  *
20959  * Fork - LGPL
20960  * <script type="text/javascript">
20961  */
20962
20963 /**
20964 * @class Roo.data.Record
20965  * Instances of this class encapsulate both record <em>definition</em> information, and record
20966  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20967  * to access Records cached in an {@link Roo.data.Store} object.<br>
20968  * <p>
20969  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20970  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20971  * objects.<br>
20972  * <p>
20973  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20974  * @constructor
20975  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20976  * {@link #create}. The parameters are the same.
20977  * @param {Array} data An associative Array of data values keyed by the field name.
20978  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20979  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20980  * not specified an integer id is generated.
20981  */
20982 Roo.data.Record = function(data, id){
20983     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20984     this.data = data;
20985 };
20986
20987 /**
20988  * Generate a constructor for a specific record layout.
20989  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20990  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20991  * Each field definition object may contain the following properties: <ul>
20992  * <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,
20993  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20994  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20995  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20996  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20997  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20998  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20999  * this may be omitted.</p></li>
21000  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
21001  * <ul><li>auto (Default, implies no conversion)</li>
21002  * <li>string</li>
21003  * <li>int</li>
21004  * <li>float</li>
21005  * <li>boolean</li>
21006  * <li>date</li></ul></p></li>
21007  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
21008  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
21009  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
21010  * by the Reader into an object that will be stored in the Record. It is passed the
21011  * following parameters:<ul>
21012  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
21013  * </ul></p></li>
21014  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
21015  * </ul>
21016  * <br>usage:<br><pre><code>
21017 var TopicRecord = Roo.data.Record.create(
21018     {name: 'title', mapping: 'topic_title'},
21019     {name: 'author', mapping: 'username'},
21020     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
21021     {name: 'lastPost', mapping: 'post_time', type: 'date'},
21022     {name: 'lastPoster', mapping: 'user2'},
21023     {name: 'excerpt', mapping: 'post_text'}
21024 );
21025
21026 var myNewRecord = new TopicRecord({
21027     title: 'Do my job please',
21028     author: 'noobie',
21029     totalPosts: 1,
21030     lastPost: new Date(),
21031     lastPoster: 'Animal',
21032     excerpt: 'No way dude!'
21033 });
21034 myStore.add(myNewRecord);
21035 </code></pre>
21036  * @method create
21037  * @static
21038  */
21039 Roo.data.Record.create = function(o){
21040     var f = function(){
21041         f.superclass.constructor.apply(this, arguments);
21042     };
21043     Roo.extend(f, Roo.data.Record);
21044     var p = f.prototype;
21045     p.fields = new Roo.util.MixedCollection(false, function(field){
21046         return field.name;
21047     });
21048     for(var i = 0, len = o.length; i < len; i++){
21049         p.fields.add(new Roo.data.Field(o[i]));
21050     }
21051     f.getField = function(name){
21052         return p.fields.get(name);  
21053     };
21054     return f;
21055 };
21056
21057 Roo.data.Record.AUTO_ID = 1000;
21058 Roo.data.Record.EDIT = 'edit';
21059 Roo.data.Record.REJECT = 'reject';
21060 Roo.data.Record.COMMIT = 'commit';
21061
21062 Roo.data.Record.prototype = {
21063     /**
21064      * Readonly flag - true if this record has been modified.
21065      * @type Boolean
21066      */
21067     dirty : false,
21068     editing : false,
21069     error: null,
21070     modified: null,
21071
21072     // private
21073     join : function(store){
21074         this.store = store;
21075     },
21076
21077     /**
21078      * Set the named field to the specified value.
21079      * @param {String} name The name of the field to set.
21080      * @param {Object} value The value to set the field to.
21081      */
21082     set : function(name, value){
21083         if(this.data[name] == value){
21084             return;
21085         }
21086         this.dirty = true;
21087         if(!this.modified){
21088             this.modified = {};
21089         }
21090         if(typeof this.modified[name] == 'undefined'){
21091             this.modified[name] = this.data[name];
21092         }
21093         this.data[name] = value;
21094         if(!this.editing && this.store){
21095             this.store.afterEdit(this);
21096         }       
21097     },
21098
21099     /**
21100      * Get the value of the named field.
21101      * @param {String} name The name of the field to get the value of.
21102      * @return {Object} The value of the field.
21103      */
21104     get : function(name){
21105         return this.data[name]; 
21106     },
21107
21108     // private
21109     beginEdit : function(){
21110         this.editing = true;
21111         this.modified = {}; 
21112     },
21113
21114     // private
21115     cancelEdit : function(){
21116         this.editing = false;
21117         delete this.modified;
21118     },
21119
21120     // private
21121     endEdit : function(){
21122         this.editing = false;
21123         if(this.dirty && this.store){
21124             this.store.afterEdit(this);
21125         }
21126     },
21127
21128     /**
21129      * Usually called by the {@link Roo.data.Store} which owns the Record.
21130      * Rejects all changes made to the Record since either creation, or the last commit operation.
21131      * Modified fields are reverted to their original values.
21132      * <p>
21133      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21134      * of reject operations.
21135      */
21136     reject : function(){
21137         var m = this.modified;
21138         for(var n in m){
21139             if(typeof m[n] != "function"){
21140                 this.data[n] = m[n];
21141             }
21142         }
21143         this.dirty = false;
21144         delete this.modified;
21145         this.editing = false;
21146         if(this.store){
21147             this.store.afterReject(this);
21148         }
21149     },
21150
21151     /**
21152      * Usually called by the {@link Roo.data.Store} which owns the Record.
21153      * Commits all changes made to the Record since either creation, or the last commit operation.
21154      * <p>
21155      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21156      * of commit operations.
21157      */
21158     commit : function(){
21159         this.dirty = false;
21160         delete this.modified;
21161         this.editing = false;
21162         if(this.store){
21163             this.store.afterCommit(this);
21164         }
21165     },
21166
21167     // private
21168     hasError : function(){
21169         return this.error != null;
21170     },
21171
21172     // private
21173     clearError : function(){
21174         this.error = null;
21175     },
21176
21177     /**
21178      * Creates a copy of this record.
21179      * @param {String} id (optional) A new record id if you don't want to use this record's id
21180      * @return {Record}
21181      */
21182     copy : function(newId) {
21183         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21184     }
21185 };/*
21186  * Based on:
21187  * Ext JS Library 1.1.1
21188  * Copyright(c) 2006-2007, Ext JS, LLC.
21189  *
21190  * Originally Released Under LGPL - original licence link has changed is not relivant.
21191  *
21192  * Fork - LGPL
21193  * <script type="text/javascript">
21194  */
21195
21196
21197
21198 /**
21199  * @class Roo.data.Store
21200  * @extends Roo.util.Observable
21201  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21202  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21203  * <p>
21204  * 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
21205  * has no knowledge of the format of the data returned by the Proxy.<br>
21206  * <p>
21207  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21208  * instances from the data object. These records are cached and made available through accessor functions.
21209  * @constructor
21210  * Creates a new Store.
21211  * @param {Object} config A config object containing the objects needed for the Store to access data,
21212  * and read the data into Records.
21213  */
21214 Roo.data.Store = function(config){
21215     this.data = new Roo.util.MixedCollection(false);
21216     this.data.getKey = function(o){
21217         return o.id;
21218     };
21219     this.baseParams = {};
21220     // private
21221     this.paramNames = {
21222         "start" : "start",
21223         "limit" : "limit",
21224         "sort" : "sort",
21225         "dir" : "dir",
21226         "multisort" : "_multisort"
21227     };
21228
21229     if(config && config.data){
21230         this.inlineData = config.data;
21231         delete config.data;
21232     }
21233
21234     Roo.apply(this, config);
21235     
21236     if(this.reader){ // reader passed
21237         this.reader = Roo.factory(this.reader, Roo.data);
21238         this.reader.xmodule = this.xmodule || false;
21239         if(!this.recordType){
21240             this.recordType = this.reader.recordType;
21241         }
21242         if(this.reader.onMetaChange){
21243             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21244         }
21245     }
21246
21247     if(this.recordType){
21248         this.fields = this.recordType.prototype.fields;
21249     }
21250     this.modified = [];
21251
21252     this.addEvents({
21253         /**
21254          * @event datachanged
21255          * Fires when the data cache has changed, and a widget which is using this Store
21256          * as a Record cache should refresh its view.
21257          * @param {Store} this
21258          */
21259         datachanged : true,
21260         /**
21261          * @event metachange
21262          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21263          * @param {Store} this
21264          * @param {Object} meta The JSON metadata
21265          */
21266         metachange : true,
21267         /**
21268          * @event add
21269          * Fires when Records have been added to the Store
21270          * @param {Store} this
21271          * @param {Roo.data.Record[]} records The array of Records added
21272          * @param {Number} index The index at which the record(s) were added
21273          */
21274         add : true,
21275         /**
21276          * @event remove
21277          * Fires when a Record has been removed from the Store
21278          * @param {Store} this
21279          * @param {Roo.data.Record} record The Record that was removed
21280          * @param {Number} index The index at which the record was removed
21281          */
21282         remove : true,
21283         /**
21284          * @event update
21285          * Fires when a Record has been updated
21286          * @param {Store} this
21287          * @param {Roo.data.Record} record The Record that was updated
21288          * @param {String} operation The update operation being performed.  Value may be one of:
21289          * <pre><code>
21290  Roo.data.Record.EDIT
21291  Roo.data.Record.REJECT
21292  Roo.data.Record.COMMIT
21293          * </code></pre>
21294          */
21295         update : true,
21296         /**
21297          * @event clear
21298          * Fires when the data cache has been cleared.
21299          * @param {Store} this
21300          */
21301         clear : true,
21302         /**
21303          * @event beforeload
21304          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21305          * the load action will be canceled.
21306          * @param {Store} this
21307          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21308          */
21309         beforeload : true,
21310         /**
21311          * @event beforeloadadd
21312          * Fires after a new set of Records has been loaded.
21313          * @param {Store} this
21314          * @param {Roo.data.Record[]} records The Records that were loaded
21315          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21316          */
21317         beforeloadadd : true,
21318         /**
21319          * @event load
21320          * Fires after a new set of Records has been loaded, before they are added to the store.
21321          * @param {Store} this
21322          * @param {Roo.data.Record[]} records The Records that were loaded
21323          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21324          * @params {Object} return from reader
21325          */
21326         load : true,
21327         /**
21328          * @event loadexception
21329          * Fires if an exception occurs in the Proxy during loading.
21330          * Called with the signature of the Proxy's "loadexception" event.
21331          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21332          * 
21333          * @param {Proxy} 
21334          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21335          * @param {Object} load options 
21336          * @param {Object} jsonData from your request (normally this contains the Exception)
21337          */
21338         loadexception : true
21339     });
21340     
21341     if(this.proxy){
21342         this.proxy = Roo.factory(this.proxy, Roo.data);
21343         this.proxy.xmodule = this.xmodule || false;
21344         this.relayEvents(this.proxy,  ["loadexception"]);
21345     }
21346     this.sortToggle = {};
21347     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21348
21349     Roo.data.Store.superclass.constructor.call(this);
21350
21351     if(this.inlineData){
21352         this.loadData(this.inlineData);
21353         delete this.inlineData;
21354     }
21355 };
21356
21357 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21358      /**
21359     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21360     * without a remote query - used by combo/forms at present.
21361     */
21362     
21363     /**
21364     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21365     */
21366     /**
21367     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21368     */
21369     /**
21370     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21371     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21372     */
21373     /**
21374     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21375     * on any HTTP request
21376     */
21377     /**
21378     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21379     */
21380     /**
21381     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21382     */
21383     multiSort: false,
21384     /**
21385     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21386     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21387     */
21388     remoteSort : false,
21389
21390     /**
21391     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21392      * loaded or when a record is removed. (defaults to false).
21393     */
21394     pruneModifiedRecords : false,
21395
21396     // private
21397     lastOptions : null,
21398
21399     /**
21400      * Add Records to the Store and fires the add event.
21401      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21402      */
21403     add : function(records){
21404         records = [].concat(records);
21405         for(var i = 0, len = records.length; i < len; i++){
21406             records[i].join(this);
21407         }
21408         var index = this.data.length;
21409         this.data.addAll(records);
21410         this.fireEvent("add", this, records, index);
21411     },
21412
21413     /**
21414      * Remove a Record from the Store and fires the remove event.
21415      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21416      */
21417     remove : function(record){
21418         var index = this.data.indexOf(record);
21419         this.data.removeAt(index);
21420         if(this.pruneModifiedRecords){
21421             this.modified.remove(record);
21422         }
21423         this.fireEvent("remove", this, record, index);
21424     },
21425
21426     /**
21427      * Remove all Records from the Store and fires the clear event.
21428      */
21429     removeAll : function(){
21430         this.data.clear();
21431         if(this.pruneModifiedRecords){
21432             this.modified = [];
21433         }
21434         this.fireEvent("clear", this);
21435     },
21436
21437     /**
21438      * Inserts Records to the Store at the given index and fires the add event.
21439      * @param {Number} index The start index at which to insert the passed Records.
21440      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21441      */
21442     insert : function(index, records){
21443         records = [].concat(records);
21444         for(var i = 0, len = records.length; i < len; i++){
21445             this.data.insert(index, records[i]);
21446             records[i].join(this);
21447         }
21448         this.fireEvent("add", this, records, index);
21449     },
21450
21451     /**
21452      * Get the index within the cache of the passed Record.
21453      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21454      * @return {Number} The index of the passed Record. Returns -1 if not found.
21455      */
21456     indexOf : function(record){
21457         return this.data.indexOf(record);
21458     },
21459
21460     /**
21461      * Get the index within the cache of the Record with the passed id.
21462      * @param {String} id The id of the Record to find.
21463      * @return {Number} The index of the Record. Returns -1 if not found.
21464      */
21465     indexOfId : function(id){
21466         return this.data.indexOfKey(id);
21467     },
21468
21469     /**
21470      * Get the Record with the specified id.
21471      * @param {String} id The id of the Record to find.
21472      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21473      */
21474     getById : function(id){
21475         return this.data.key(id);
21476     },
21477
21478     /**
21479      * Get the Record at the specified index.
21480      * @param {Number} index The index of the Record to find.
21481      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21482      */
21483     getAt : function(index){
21484         return this.data.itemAt(index);
21485     },
21486
21487     /**
21488      * Returns a range of Records between specified indices.
21489      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21490      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21491      * @return {Roo.data.Record[]} An array of Records
21492      */
21493     getRange : function(start, end){
21494         return this.data.getRange(start, end);
21495     },
21496
21497     // private
21498     storeOptions : function(o){
21499         o = Roo.apply({}, o);
21500         delete o.callback;
21501         delete o.scope;
21502         this.lastOptions = o;
21503     },
21504
21505     /**
21506      * Loads the Record cache from the configured Proxy using the configured Reader.
21507      * <p>
21508      * If using remote paging, then the first load call must specify the <em>start</em>
21509      * and <em>limit</em> properties in the options.params property to establish the initial
21510      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21511      * <p>
21512      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21513      * and this call will return before the new data has been loaded. Perform any post-processing
21514      * in a callback function, or in a "load" event handler.</strong>
21515      * <p>
21516      * @param {Object} options An object containing properties which control loading options:<ul>
21517      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21518      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21519      * passed the following arguments:<ul>
21520      * <li>r : Roo.data.Record[]</li>
21521      * <li>options: Options object from the load call</li>
21522      * <li>success: Boolean success indicator</li></ul></li>
21523      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21524      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21525      * </ul>
21526      */
21527     load : function(options){
21528         options = options || {};
21529         if(this.fireEvent("beforeload", this, options) !== false){
21530             this.storeOptions(options);
21531             var p = Roo.apply(options.params || {}, this.baseParams);
21532             // if meta was not loaded from remote source.. try requesting it.
21533             if (!this.reader.metaFromRemote) {
21534                 p._requestMeta = 1;
21535             }
21536             if(this.sortInfo && this.remoteSort){
21537                 var pn = this.paramNames;
21538                 p[pn["sort"]] = this.sortInfo.field;
21539                 p[pn["dir"]] = this.sortInfo.direction;
21540             }
21541             if (this.multiSort) {
21542                 var pn = this.paramNames;
21543                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21544             }
21545             
21546             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21547         }
21548     },
21549
21550     /**
21551      * Reloads the Record cache from the configured Proxy using the configured Reader and
21552      * the options from the last load operation performed.
21553      * @param {Object} options (optional) An object containing properties which may override the options
21554      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21555      * the most recently used options are reused).
21556      */
21557     reload : function(options){
21558         this.load(Roo.applyIf(options||{}, this.lastOptions));
21559     },
21560
21561     // private
21562     // Called as a callback by the Reader during a load operation.
21563     loadRecords : function(o, options, success){
21564         if(!o || success === false){
21565             if(success !== false){
21566                 this.fireEvent("load", this, [], options, o);
21567             }
21568             if(options.callback){
21569                 options.callback.call(options.scope || this, [], options, false);
21570             }
21571             return;
21572         }
21573         // if data returned failure - throw an exception.
21574         if (o.success === false) {
21575             // show a message if no listener is registered.
21576             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21577                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21578             }
21579             // loadmask wil be hooked into this..
21580             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21581             return;
21582         }
21583         var r = o.records, t = o.totalRecords || r.length;
21584         
21585         this.fireEvent("beforeloadadd", this, r, options, o);
21586         
21587         if(!options || options.add !== true){
21588             if(this.pruneModifiedRecords){
21589                 this.modified = [];
21590             }
21591             for(var i = 0, len = r.length; i < len; i++){
21592                 r[i].join(this);
21593             }
21594             if(this.snapshot){
21595                 this.data = this.snapshot;
21596                 delete this.snapshot;
21597             }
21598             this.data.clear();
21599             this.data.addAll(r);
21600             this.totalLength = t;
21601             this.applySort();
21602             this.fireEvent("datachanged", this);
21603         }else{
21604             this.totalLength = Math.max(t, this.data.length+r.length);
21605             this.add(r);
21606         }
21607         this.fireEvent("load", this, r, options, o);
21608         if(options.callback){
21609             options.callback.call(options.scope || this, r, options, true);
21610         }
21611     },
21612
21613
21614     /**
21615      * Loads data from a passed data block. A Reader which understands the format of the data
21616      * must have been configured in the constructor.
21617      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21618      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21619      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21620      */
21621     loadData : function(o, append){
21622         var r = this.reader.readRecords(o);
21623         this.loadRecords(r, {add: append}, true);
21624     },
21625
21626     /**
21627      * Gets the number of cached records.
21628      * <p>
21629      * <em>If using paging, this may not be the total size of the dataset. If the data object
21630      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21631      * the data set size</em>
21632      */
21633     getCount : function(){
21634         return this.data.length || 0;
21635     },
21636
21637     /**
21638      * Gets the total number of records in the dataset as returned by the server.
21639      * <p>
21640      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21641      * the dataset size</em>
21642      */
21643     getTotalCount : function(){
21644         return this.totalLength || 0;
21645     },
21646
21647     /**
21648      * Returns the sort state of the Store as an object with two properties:
21649      * <pre><code>
21650  field {String} The name of the field by which the Records are sorted
21651  direction {String} The sort order, "ASC" or "DESC"
21652      * </code></pre>
21653      */
21654     getSortState : function(){
21655         return this.sortInfo;
21656     },
21657
21658     // private
21659     applySort : function(){
21660         if(this.sortInfo && !this.remoteSort){
21661             var s = this.sortInfo, f = s.field;
21662             var st = this.fields.get(f).sortType;
21663             var fn = function(r1, r2){
21664                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21665                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21666             };
21667             this.data.sort(s.direction, fn);
21668             if(this.snapshot && this.snapshot != this.data){
21669                 this.snapshot.sort(s.direction, fn);
21670             }
21671         }
21672     },
21673
21674     /**
21675      * Sets the default sort column and order to be used by the next load operation.
21676      * @param {String} fieldName The name of the field to sort by.
21677      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21678      */
21679     setDefaultSort : function(field, dir){
21680         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21681     },
21682
21683     /**
21684      * Sort the Records.
21685      * If remote sorting is used, the sort is performed on the server, and the cache is
21686      * reloaded. If local sorting is used, the cache is sorted internally.
21687      * @param {String} fieldName The name of the field to sort by.
21688      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21689      */
21690     sort : function(fieldName, dir){
21691         var f = this.fields.get(fieldName);
21692         if(!dir){
21693             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21694             
21695             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21696                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21697             }else{
21698                 dir = f.sortDir;
21699             }
21700         }
21701         this.sortToggle[f.name] = dir;
21702         this.sortInfo = {field: f.name, direction: dir};
21703         if(!this.remoteSort){
21704             this.applySort();
21705             this.fireEvent("datachanged", this);
21706         }else{
21707             this.load(this.lastOptions);
21708         }
21709     },
21710
21711     /**
21712      * Calls the specified function for each of the Records in the cache.
21713      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21714      * Returning <em>false</em> aborts and exits the iteration.
21715      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21716      */
21717     each : function(fn, scope){
21718         this.data.each(fn, scope);
21719     },
21720
21721     /**
21722      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21723      * (e.g., during paging).
21724      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21725      */
21726     getModifiedRecords : function(){
21727         return this.modified;
21728     },
21729
21730     // private
21731     createFilterFn : function(property, value, anyMatch){
21732         if(!value.exec){ // not a regex
21733             value = String(value);
21734             if(value.length == 0){
21735                 return false;
21736             }
21737             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21738         }
21739         return function(r){
21740             return value.test(r.data[property]);
21741         };
21742     },
21743
21744     /**
21745      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21746      * @param {String} property A field on your records
21747      * @param {Number} start The record index to start at (defaults to 0)
21748      * @param {Number} end The last record index to include (defaults to length - 1)
21749      * @return {Number} The sum
21750      */
21751     sum : function(property, start, end){
21752         var rs = this.data.items, v = 0;
21753         start = start || 0;
21754         end = (end || end === 0) ? end : rs.length-1;
21755
21756         for(var i = start; i <= end; i++){
21757             v += (rs[i].data[property] || 0);
21758         }
21759         return v;
21760     },
21761
21762     /**
21763      * Filter the records by a specified property.
21764      * @param {String} field A field on your records
21765      * @param {String/RegExp} value Either a string that the field
21766      * should start with or a RegExp to test against the field
21767      * @param {Boolean} anyMatch True to match any part not just the beginning
21768      */
21769     filter : function(property, value, anyMatch){
21770         var fn = this.createFilterFn(property, value, anyMatch);
21771         return fn ? this.filterBy(fn) : this.clearFilter();
21772     },
21773
21774     /**
21775      * Filter by a function. The specified function will be called with each
21776      * record in this data source. If the function returns true the record is included,
21777      * otherwise it is filtered.
21778      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21779      * @param {Object} scope (optional) The scope of the function (defaults to this)
21780      */
21781     filterBy : function(fn, scope){
21782         this.snapshot = this.snapshot || this.data;
21783         this.data = this.queryBy(fn, scope||this);
21784         this.fireEvent("datachanged", this);
21785     },
21786
21787     /**
21788      * Query the records by a specified property.
21789      * @param {String} field A field on your records
21790      * @param {String/RegExp} value Either a string that the field
21791      * should start with or a RegExp to test against the field
21792      * @param {Boolean} anyMatch True to match any part not just the beginning
21793      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21794      */
21795     query : function(property, value, anyMatch){
21796         var fn = this.createFilterFn(property, value, anyMatch);
21797         return fn ? this.queryBy(fn) : this.data.clone();
21798     },
21799
21800     /**
21801      * Query by a function. The specified function will be called with each
21802      * record in this data source. If the function returns true the record is included
21803      * in the results.
21804      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21805      * @param {Object} scope (optional) The scope of the function (defaults to this)
21806       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21807      **/
21808     queryBy : function(fn, scope){
21809         var data = this.snapshot || this.data;
21810         return data.filterBy(fn, scope||this);
21811     },
21812
21813     /**
21814      * Collects unique values for a particular dataIndex from this store.
21815      * @param {String} dataIndex The property to collect
21816      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21817      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21818      * @return {Array} An array of the unique values
21819      **/
21820     collect : function(dataIndex, allowNull, bypassFilter){
21821         var d = (bypassFilter === true && this.snapshot) ?
21822                 this.snapshot.items : this.data.items;
21823         var v, sv, r = [], l = {};
21824         for(var i = 0, len = d.length; i < len; i++){
21825             v = d[i].data[dataIndex];
21826             sv = String(v);
21827             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21828                 l[sv] = true;
21829                 r[r.length] = v;
21830             }
21831         }
21832         return r;
21833     },
21834
21835     /**
21836      * Revert to a view of the Record cache with no filtering applied.
21837      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21838      */
21839     clearFilter : function(suppressEvent){
21840         if(this.snapshot && this.snapshot != this.data){
21841             this.data = this.snapshot;
21842             delete this.snapshot;
21843             if(suppressEvent !== true){
21844                 this.fireEvent("datachanged", this);
21845             }
21846         }
21847     },
21848
21849     // private
21850     afterEdit : function(record){
21851         if(this.modified.indexOf(record) == -1){
21852             this.modified.push(record);
21853         }
21854         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21855     },
21856     
21857     // private
21858     afterReject : function(record){
21859         this.modified.remove(record);
21860         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21861     },
21862
21863     // private
21864     afterCommit : function(record){
21865         this.modified.remove(record);
21866         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21867     },
21868
21869     /**
21870      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21871      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21872      */
21873     commitChanges : function(){
21874         var m = this.modified.slice(0);
21875         this.modified = [];
21876         for(var i = 0, len = m.length; i < len; i++){
21877             m[i].commit();
21878         }
21879     },
21880
21881     /**
21882      * Cancel outstanding changes on all changed records.
21883      */
21884     rejectChanges : function(){
21885         var m = this.modified.slice(0);
21886         this.modified = [];
21887         for(var i = 0, len = m.length; i < len; i++){
21888             m[i].reject();
21889         }
21890     },
21891
21892     onMetaChange : function(meta, rtype, o){
21893         this.recordType = rtype;
21894         this.fields = rtype.prototype.fields;
21895         delete this.snapshot;
21896         this.sortInfo = meta.sortInfo || this.sortInfo;
21897         this.modified = [];
21898         this.fireEvent('metachange', this, this.reader.meta);
21899     },
21900     
21901     moveIndex : function(data, type)
21902     {
21903         var index = this.indexOf(data);
21904         
21905         var newIndex = index + type;
21906         
21907         this.remove(data);
21908         
21909         this.insert(newIndex, data);
21910         
21911     }
21912 });/*
21913  * Based on:
21914  * Ext JS Library 1.1.1
21915  * Copyright(c) 2006-2007, Ext JS, LLC.
21916  *
21917  * Originally Released Under LGPL - original licence link has changed is not relivant.
21918  *
21919  * Fork - LGPL
21920  * <script type="text/javascript">
21921  */
21922
21923 /**
21924  * @class Roo.data.SimpleStore
21925  * @extends Roo.data.Store
21926  * Small helper class to make creating Stores from Array data easier.
21927  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21928  * @cfg {Array} fields An array of field definition objects, or field name strings.
21929  * @cfg {Array} data The multi-dimensional array of data
21930  * @constructor
21931  * @param {Object} config
21932  */
21933 Roo.data.SimpleStore = function(config){
21934     Roo.data.SimpleStore.superclass.constructor.call(this, {
21935         isLocal : true,
21936         reader: new Roo.data.ArrayReader({
21937                 id: config.id
21938             },
21939             Roo.data.Record.create(config.fields)
21940         ),
21941         proxy : new Roo.data.MemoryProxy(config.data)
21942     });
21943     this.load();
21944 };
21945 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21946  * Based on:
21947  * Ext JS Library 1.1.1
21948  * Copyright(c) 2006-2007, Ext JS, LLC.
21949  *
21950  * Originally Released Under LGPL - original licence link has changed is not relivant.
21951  *
21952  * Fork - LGPL
21953  * <script type="text/javascript">
21954  */
21955
21956 /**
21957 /**
21958  * @extends Roo.data.Store
21959  * @class Roo.data.JsonStore
21960  * Small helper class to make creating Stores for JSON data easier. <br/>
21961 <pre><code>
21962 var store = new Roo.data.JsonStore({
21963     url: 'get-images.php',
21964     root: 'images',
21965     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21966 });
21967 </code></pre>
21968  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21969  * JsonReader and HttpProxy (unless inline data is provided).</b>
21970  * @cfg {Array} fields An array of field definition objects, or field name strings.
21971  * @constructor
21972  * @param {Object} config
21973  */
21974 Roo.data.JsonStore = function(c){
21975     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21976         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21977         reader: new Roo.data.JsonReader(c, c.fields)
21978     }));
21979 };
21980 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21981  * Based on:
21982  * Ext JS Library 1.1.1
21983  * Copyright(c) 2006-2007, Ext JS, LLC.
21984  *
21985  * Originally Released Under LGPL - original licence link has changed is not relivant.
21986  *
21987  * Fork - LGPL
21988  * <script type="text/javascript">
21989  */
21990
21991  
21992 Roo.data.Field = function(config){
21993     if(typeof config == "string"){
21994         config = {name: config};
21995     }
21996     Roo.apply(this, config);
21997     
21998     if(!this.type){
21999         this.type = "auto";
22000     }
22001     
22002     var st = Roo.data.SortTypes;
22003     // named sortTypes are supported, here we look them up
22004     if(typeof this.sortType == "string"){
22005         this.sortType = st[this.sortType];
22006     }
22007     
22008     // set default sortType for strings and dates
22009     if(!this.sortType){
22010         switch(this.type){
22011             case "string":
22012                 this.sortType = st.asUCString;
22013                 break;
22014             case "date":
22015                 this.sortType = st.asDate;
22016                 break;
22017             default:
22018                 this.sortType = st.none;
22019         }
22020     }
22021
22022     // define once
22023     var stripRe = /[\$,%]/g;
22024
22025     // prebuilt conversion function for this field, instead of
22026     // switching every time we're reading a value
22027     if(!this.convert){
22028         var cv, dateFormat = this.dateFormat;
22029         switch(this.type){
22030             case "":
22031             case "auto":
22032             case undefined:
22033                 cv = function(v){ return v; };
22034                 break;
22035             case "string":
22036                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
22037                 break;
22038             case "int":
22039                 cv = function(v){
22040                     return v !== undefined && v !== null && v !== '' ?
22041                            parseInt(String(v).replace(stripRe, ""), 10) : '';
22042                     };
22043                 break;
22044             case "float":
22045                 cv = function(v){
22046                     return v !== undefined && v !== null && v !== '' ?
22047                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
22048                     };
22049                 break;
22050             case "bool":
22051             case "boolean":
22052                 cv = function(v){ return v === true || v === "true" || v == 1; };
22053                 break;
22054             case "date":
22055                 cv = function(v){
22056                     if(!v){
22057                         return '';
22058                     }
22059                     if(v instanceof Date){
22060                         return v;
22061                     }
22062                     if(dateFormat){
22063                         if(dateFormat == "timestamp"){
22064                             return new Date(v*1000);
22065                         }
22066                         return Date.parseDate(v, dateFormat);
22067                     }
22068                     var parsed = Date.parse(v);
22069                     return parsed ? new Date(parsed) : null;
22070                 };
22071              break;
22072             
22073         }
22074         this.convert = cv;
22075     }
22076 };
22077
22078 Roo.data.Field.prototype = {
22079     dateFormat: null,
22080     defaultValue: "",
22081     mapping: null,
22082     sortType : null,
22083     sortDir : "ASC"
22084 };/*
22085  * Based on:
22086  * Ext JS Library 1.1.1
22087  * Copyright(c) 2006-2007, Ext JS, LLC.
22088  *
22089  * Originally Released Under LGPL - original licence link has changed is not relivant.
22090  *
22091  * Fork - LGPL
22092  * <script type="text/javascript">
22093  */
22094  
22095 // Base class for reading structured data from a data source.  This class is intended to be
22096 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22097
22098 /**
22099  * @class Roo.data.DataReader
22100  * Base class for reading structured data from a data source.  This class is intended to be
22101  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22102  */
22103
22104 Roo.data.DataReader = function(meta, recordType){
22105     
22106     this.meta = meta;
22107     
22108     this.recordType = recordType instanceof Array ? 
22109         Roo.data.Record.create(recordType) : recordType;
22110 };
22111
22112 Roo.data.DataReader.prototype = {
22113      /**
22114      * Create an empty record
22115      * @param {Object} data (optional) - overlay some values
22116      * @return {Roo.data.Record} record created.
22117      */
22118     newRow :  function(d) {
22119         var da =  {};
22120         this.recordType.prototype.fields.each(function(c) {
22121             switch( c.type) {
22122                 case 'int' : da[c.name] = 0; break;
22123                 case 'date' : da[c.name] = new Date(); break;
22124                 case 'float' : da[c.name] = 0.0; break;
22125                 case 'boolean' : da[c.name] = false; break;
22126                 default : da[c.name] = ""; break;
22127             }
22128             
22129         });
22130         return new this.recordType(Roo.apply(da, d));
22131     }
22132     
22133 };/*
22134  * Based on:
22135  * Ext JS Library 1.1.1
22136  * Copyright(c) 2006-2007, Ext JS, LLC.
22137  *
22138  * Originally Released Under LGPL - original licence link has changed is not relivant.
22139  *
22140  * Fork - LGPL
22141  * <script type="text/javascript">
22142  */
22143
22144 /**
22145  * @class Roo.data.DataProxy
22146  * @extends Roo.data.Observable
22147  * This class is an abstract base class for implementations which provide retrieval of
22148  * unformatted data objects.<br>
22149  * <p>
22150  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22151  * (of the appropriate type which knows how to parse the data object) to provide a block of
22152  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22153  * <p>
22154  * Custom implementations must implement the load method as described in
22155  * {@link Roo.data.HttpProxy#load}.
22156  */
22157 Roo.data.DataProxy = function(){
22158     this.addEvents({
22159         /**
22160          * @event beforeload
22161          * Fires before a network request is made to retrieve a data object.
22162          * @param {Object} This DataProxy object.
22163          * @param {Object} params The params parameter to the load function.
22164          */
22165         beforeload : true,
22166         /**
22167          * @event load
22168          * Fires before the load method's callback is called.
22169          * @param {Object} This DataProxy object.
22170          * @param {Object} o The data object.
22171          * @param {Object} arg The callback argument object passed to the load function.
22172          */
22173         load : true,
22174         /**
22175          * @event loadexception
22176          * Fires if an Exception occurs during data retrieval.
22177          * @param {Object} This DataProxy object.
22178          * @param {Object} o The data object.
22179          * @param {Object} arg The callback argument object passed to the load function.
22180          * @param {Object} e The Exception.
22181          */
22182         loadexception : true
22183     });
22184     Roo.data.DataProxy.superclass.constructor.call(this);
22185 };
22186
22187 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22188
22189     /**
22190      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22191      */
22192 /*
22193  * Based on:
22194  * Ext JS Library 1.1.1
22195  * Copyright(c) 2006-2007, Ext JS, LLC.
22196  *
22197  * Originally Released Under LGPL - original licence link has changed is not relivant.
22198  *
22199  * Fork - LGPL
22200  * <script type="text/javascript">
22201  */
22202 /**
22203  * @class Roo.data.MemoryProxy
22204  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22205  * to the Reader when its load method is called.
22206  * @constructor
22207  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22208  */
22209 Roo.data.MemoryProxy = function(data){
22210     if (data.data) {
22211         data = data.data;
22212     }
22213     Roo.data.MemoryProxy.superclass.constructor.call(this);
22214     this.data = data;
22215 };
22216
22217 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22218     /**
22219      * Load data from the requested source (in this case an in-memory
22220      * data object passed to the constructor), read the data object into
22221      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22222      * process that block using the passed callback.
22223      * @param {Object} params This parameter is not used by the MemoryProxy class.
22224      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22225      * object into a block of Roo.data.Records.
22226      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22227      * The function must be passed <ul>
22228      * <li>The Record block object</li>
22229      * <li>The "arg" argument from the load function</li>
22230      * <li>A boolean success indicator</li>
22231      * </ul>
22232      * @param {Object} scope The scope in which to call the callback
22233      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22234      */
22235     load : function(params, reader, callback, scope, arg){
22236         params = params || {};
22237         var result;
22238         try {
22239             result = reader.readRecords(this.data);
22240         }catch(e){
22241             this.fireEvent("loadexception", this, arg, null, e);
22242             callback.call(scope, null, arg, false);
22243             return;
22244         }
22245         callback.call(scope, result, arg, true);
22246     },
22247     
22248     // private
22249     update : function(params, records){
22250         
22251     }
22252 });/*
22253  * Based on:
22254  * Ext JS Library 1.1.1
22255  * Copyright(c) 2006-2007, Ext JS, LLC.
22256  *
22257  * Originally Released Under LGPL - original licence link has changed is not relivant.
22258  *
22259  * Fork - LGPL
22260  * <script type="text/javascript">
22261  */
22262 /**
22263  * @class Roo.data.HttpProxy
22264  * @extends Roo.data.DataProxy
22265  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22266  * configured to reference a certain URL.<br><br>
22267  * <p>
22268  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22269  * from which the running page was served.<br><br>
22270  * <p>
22271  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22272  * <p>
22273  * Be aware that to enable the browser to parse an XML document, the server must set
22274  * the Content-Type header in the HTTP response to "text/xml".
22275  * @constructor
22276  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22277  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22278  * will be used to make the request.
22279  */
22280 Roo.data.HttpProxy = function(conn){
22281     Roo.data.HttpProxy.superclass.constructor.call(this);
22282     // is conn a conn config or a real conn?
22283     this.conn = conn;
22284     this.useAjax = !conn || !conn.events;
22285   
22286 };
22287
22288 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22289     // thse are take from connection...
22290     
22291     /**
22292      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22293      */
22294     /**
22295      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22296      * extra parameters to each request made by this object. (defaults to undefined)
22297      */
22298     /**
22299      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22300      *  to each request made by this object. (defaults to undefined)
22301      */
22302     /**
22303      * @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)
22304      */
22305     /**
22306      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22307      */
22308      /**
22309      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22310      * @type Boolean
22311      */
22312   
22313
22314     /**
22315      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22316      * @type Boolean
22317      */
22318     /**
22319      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22320      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22321      * a finer-grained basis than the DataProxy events.
22322      */
22323     getConnection : function(){
22324         return this.useAjax ? Roo.Ajax : this.conn;
22325     },
22326
22327     /**
22328      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22329      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22330      * process that block using the passed callback.
22331      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22332      * for the request to the remote server.
22333      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22334      * object into a block of Roo.data.Records.
22335      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22336      * The function must be passed <ul>
22337      * <li>The Record block object</li>
22338      * <li>The "arg" argument from the load function</li>
22339      * <li>A boolean success indicator</li>
22340      * </ul>
22341      * @param {Object} scope The scope in which to call the callback
22342      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22343      */
22344     load : function(params, reader, callback, scope, arg){
22345         if(this.fireEvent("beforeload", this, params) !== false){
22346             var  o = {
22347                 params : params || {},
22348                 request: {
22349                     callback : callback,
22350                     scope : scope,
22351                     arg : arg
22352                 },
22353                 reader: reader,
22354                 callback : this.loadResponse,
22355                 scope: this
22356             };
22357             if(this.useAjax){
22358                 Roo.applyIf(o, this.conn);
22359                 if(this.activeRequest){
22360                     Roo.Ajax.abort(this.activeRequest);
22361                 }
22362                 this.activeRequest = Roo.Ajax.request(o);
22363             }else{
22364                 this.conn.request(o);
22365             }
22366         }else{
22367             callback.call(scope||this, null, arg, false);
22368         }
22369     },
22370
22371     // private
22372     loadResponse : function(o, success, response){
22373         delete this.activeRequest;
22374         if(!success){
22375             this.fireEvent("loadexception", this, o, response);
22376             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22377             return;
22378         }
22379         var result;
22380         try {
22381             result = o.reader.read(response);
22382         }catch(e){
22383             this.fireEvent("loadexception", this, o, response, e);
22384             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22385             return;
22386         }
22387         
22388         this.fireEvent("load", this, o, o.request.arg);
22389         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22390     },
22391
22392     // private
22393     update : function(dataSet){
22394
22395     },
22396
22397     // private
22398     updateResponse : function(dataSet){
22399
22400     }
22401 });/*
22402  * Based on:
22403  * Ext JS Library 1.1.1
22404  * Copyright(c) 2006-2007, Ext JS, LLC.
22405  *
22406  * Originally Released Under LGPL - original licence link has changed is not relivant.
22407  *
22408  * Fork - LGPL
22409  * <script type="text/javascript">
22410  */
22411
22412 /**
22413  * @class Roo.data.ScriptTagProxy
22414  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22415  * other than the originating domain of the running page.<br><br>
22416  * <p>
22417  * <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
22418  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22419  * <p>
22420  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22421  * source code that is used as the source inside a &lt;script> tag.<br><br>
22422  * <p>
22423  * In order for the browser to process the returned data, the server must wrap the data object
22424  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22425  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22426  * depending on whether the callback name was passed:
22427  * <p>
22428  * <pre><code>
22429 boolean scriptTag = false;
22430 String cb = request.getParameter("callback");
22431 if (cb != null) {
22432     scriptTag = true;
22433     response.setContentType("text/javascript");
22434 } else {
22435     response.setContentType("application/x-json");
22436 }
22437 Writer out = response.getWriter();
22438 if (scriptTag) {
22439     out.write(cb + "(");
22440 }
22441 out.print(dataBlock.toJsonString());
22442 if (scriptTag) {
22443     out.write(");");
22444 }
22445 </pre></code>
22446  *
22447  * @constructor
22448  * @param {Object} config A configuration object.
22449  */
22450 Roo.data.ScriptTagProxy = function(config){
22451     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22452     Roo.apply(this, config);
22453     this.head = document.getElementsByTagName("head")[0];
22454 };
22455
22456 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22457
22458 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22459     /**
22460      * @cfg {String} url The URL from which to request the data object.
22461      */
22462     /**
22463      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22464      */
22465     timeout : 30000,
22466     /**
22467      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22468      * the server the name of the callback function set up by the load call to process the returned data object.
22469      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22470      * javascript output which calls this named function passing the data object as its only parameter.
22471      */
22472     callbackParam : "callback",
22473     /**
22474      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22475      * name to the request.
22476      */
22477     nocache : true,
22478
22479     /**
22480      * Load data from the configured URL, read the data object into
22481      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22482      * process that block using the passed callback.
22483      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22484      * for the request to the remote server.
22485      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22486      * object into a block of Roo.data.Records.
22487      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22488      * The function must be passed <ul>
22489      * <li>The Record block object</li>
22490      * <li>The "arg" argument from the load function</li>
22491      * <li>A boolean success indicator</li>
22492      * </ul>
22493      * @param {Object} scope The scope in which to call the callback
22494      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22495      */
22496     load : function(params, reader, callback, scope, arg){
22497         if(this.fireEvent("beforeload", this, params) !== false){
22498
22499             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22500
22501             var url = this.url;
22502             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22503             if(this.nocache){
22504                 url += "&_dc=" + (new Date().getTime());
22505             }
22506             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22507             var trans = {
22508                 id : transId,
22509                 cb : "stcCallback"+transId,
22510                 scriptId : "stcScript"+transId,
22511                 params : params,
22512                 arg : arg,
22513                 url : url,
22514                 callback : callback,
22515                 scope : scope,
22516                 reader : reader
22517             };
22518             var conn = this;
22519
22520             window[trans.cb] = function(o){
22521                 conn.handleResponse(o, trans);
22522             };
22523
22524             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22525
22526             if(this.autoAbort !== false){
22527                 this.abort();
22528             }
22529
22530             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22531
22532             var script = document.createElement("script");
22533             script.setAttribute("src", url);
22534             script.setAttribute("type", "text/javascript");
22535             script.setAttribute("id", trans.scriptId);
22536             this.head.appendChild(script);
22537
22538             this.trans = trans;
22539         }else{
22540             callback.call(scope||this, null, arg, false);
22541         }
22542     },
22543
22544     // private
22545     isLoading : function(){
22546         return this.trans ? true : false;
22547     },
22548
22549     /**
22550      * Abort the current server request.
22551      */
22552     abort : function(){
22553         if(this.isLoading()){
22554             this.destroyTrans(this.trans);
22555         }
22556     },
22557
22558     // private
22559     destroyTrans : function(trans, isLoaded){
22560         this.head.removeChild(document.getElementById(trans.scriptId));
22561         clearTimeout(trans.timeoutId);
22562         if(isLoaded){
22563             window[trans.cb] = undefined;
22564             try{
22565                 delete window[trans.cb];
22566             }catch(e){}
22567         }else{
22568             // if hasn't been loaded, wait for load to remove it to prevent script error
22569             window[trans.cb] = function(){
22570                 window[trans.cb] = undefined;
22571                 try{
22572                     delete window[trans.cb];
22573                 }catch(e){}
22574             };
22575         }
22576     },
22577
22578     // private
22579     handleResponse : function(o, trans){
22580         this.trans = false;
22581         this.destroyTrans(trans, true);
22582         var result;
22583         try {
22584             result = trans.reader.readRecords(o);
22585         }catch(e){
22586             this.fireEvent("loadexception", this, o, trans.arg, e);
22587             trans.callback.call(trans.scope||window, null, trans.arg, false);
22588             return;
22589         }
22590         this.fireEvent("load", this, o, trans.arg);
22591         trans.callback.call(trans.scope||window, result, trans.arg, true);
22592     },
22593
22594     // private
22595     handleFailure : function(trans){
22596         this.trans = false;
22597         this.destroyTrans(trans, false);
22598         this.fireEvent("loadexception", this, null, trans.arg);
22599         trans.callback.call(trans.scope||window, null, trans.arg, false);
22600     }
22601 });/*
22602  * Based on:
22603  * Ext JS Library 1.1.1
22604  * Copyright(c) 2006-2007, Ext JS, LLC.
22605  *
22606  * Originally Released Under LGPL - original licence link has changed is not relivant.
22607  *
22608  * Fork - LGPL
22609  * <script type="text/javascript">
22610  */
22611
22612 /**
22613  * @class Roo.data.JsonReader
22614  * @extends Roo.data.DataReader
22615  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22616  * based on mappings in a provided Roo.data.Record constructor.
22617  * 
22618  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22619  * in the reply previously. 
22620  * 
22621  * <p>
22622  * Example code:
22623  * <pre><code>
22624 var RecordDef = Roo.data.Record.create([
22625     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22626     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22627 ]);
22628 var myReader = new Roo.data.JsonReader({
22629     totalProperty: "results",    // The property which contains the total dataset size (optional)
22630     root: "rows",                // The property which contains an Array of row objects
22631     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22632 }, RecordDef);
22633 </code></pre>
22634  * <p>
22635  * This would consume a JSON file like this:
22636  * <pre><code>
22637 { 'results': 2, 'rows': [
22638     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22639     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22640 }
22641 </code></pre>
22642  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22643  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22644  * paged from the remote server.
22645  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22646  * @cfg {String} root name of the property which contains the Array of row objects.
22647  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22648  * @cfg {Array} fields Array of field definition objects
22649  * @constructor
22650  * Create a new JsonReader
22651  * @param {Object} meta Metadata configuration options
22652  * @param {Object} recordType Either an Array of field definition objects,
22653  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22654  */
22655 Roo.data.JsonReader = function(meta, recordType){
22656     
22657     meta = meta || {};
22658     // set some defaults:
22659     Roo.applyIf(meta, {
22660         totalProperty: 'total',
22661         successProperty : 'success',
22662         root : 'data',
22663         id : 'id'
22664     });
22665     
22666     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22667 };
22668 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22669     
22670     /**
22671      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22672      * Used by Store query builder to append _requestMeta to params.
22673      * 
22674      */
22675     metaFromRemote : false,
22676     /**
22677      * This method is only used by a DataProxy which has retrieved data from a remote server.
22678      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22679      * @return {Object} data A data block which is used by an Roo.data.Store object as
22680      * a cache of Roo.data.Records.
22681      */
22682     read : function(response){
22683         var json = response.responseText;
22684        
22685         var o = /* eval:var:o */ eval("("+json+")");
22686         if(!o) {
22687             throw {message: "JsonReader.read: Json object not found"};
22688         }
22689         
22690         if(o.metaData){
22691             
22692             delete this.ef;
22693             this.metaFromRemote = true;
22694             this.meta = o.metaData;
22695             this.recordType = Roo.data.Record.create(o.metaData.fields);
22696             this.onMetaChange(this.meta, this.recordType, o);
22697         }
22698         return this.readRecords(o);
22699     },
22700
22701     // private function a store will implement
22702     onMetaChange : function(meta, recordType, o){
22703
22704     },
22705
22706     /**
22707          * @ignore
22708          */
22709     simpleAccess: function(obj, subsc) {
22710         return obj[subsc];
22711     },
22712
22713         /**
22714          * @ignore
22715          */
22716     getJsonAccessor: function(){
22717         var re = /[\[\.]/;
22718         return function(expr) {
22719             try {
22720                 return(re.test(expr))
22721                     ? new Function("obj", "return obj." + expr)
22722                     : function(obj){
22723                         return obj[expr];
22724                     };
22725             } catch(e){}
22726             return Roo.emptyFn;
22727         };
22728     }(),
22729
22730     /**
22731      * Create a data block containing Roo.data.Records from an XML document.
22732      * @param {Object} o An object which contains an Array of row objects in the property specified
22733      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22734      * which contains the total size of the dataset.
22735      * @return {Object} data A data block which is used by an Roo.data.Store object as
22736      * a cache of Roo.data.Records.
22737      */
22738     readRecords : function(o){
22739         /**
22740          * After any data loads, the raw JSON data is available for further custom processing.
22741          * @type Object
22742          */
22743         this.o = o;
22744         var s = this.meta, Record = this.recordType,
22745             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
22746
22747 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22748         if (!this.ef) {
22749             if(s.totalProperty) {
22750                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22751                 }
22752                 if(s.successProperty) {
22753                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22754                 }
22755                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22756                 if (s.id) {
22757                         var g = this.getJsonAccessor(s.id);
22758                         this.getId = function(rec) {
22759                                 var r = g(rec);  
22760                                 return (r === undefined || r === "") ? null : r;
22761                         };
22762                 } else {
22763                         this.getId = function(){return null;};
22764                 }
22765             this.ef = [];
22766             for(var jj = 0; jj < fl; jj++){
22767                 f = fi[jj];
22768                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22769                 this.ef[jj] = this.getJsonAccessor(map);
22770             }
22771         }
22772
22773         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22774         if(s.totalProperty){
22775             var vt = parseInt(this.getTotal(o), 10);
22776             if(!isNaN(vt)){
22777                 totalRecords = vt;
22778             }
22779         }
22780         if(s.successProperty){
22781             var vs = this.getSuccess(o);
22782             if(vs === false || vs === 'false'){
22783                 success = false;
22784             }
22785         }
22786         var records = [];
22787         for(var i = 0; i < c; i++){
22788                 var n = root[i];
22789             var values = {};
22790             var id = this.getId(n);
22791             for(var j = 0; j < fl; j++){
22792                 f = fi[j];
22793             var v = this.ef[j](n);
22794             if (!f.convert) {
22795                 Roo.log('missing convert for ' + f.name);
22796                 Roo.log(f);
22797                 continue;
22798             }
22799             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22800             }
22801             var record = new Record(values, id);
22802             record.json = n;
22803             records[i] = record;
22804         }
22805         return {
22806             raw : o,
22807             success : success,
22808             records : records,
22809             totalRecords : totalRecords
22810         };
22811     }
22812 });/*
22813  * Based on:
22814  * Ext JS Library 1.1.1
22815  * Copyright(c) 2006-2007, Ext JS, LLC.
22816  *
22817  * Originally Released Under LGPL - original licence link has changed is not relivant.
22818  *
22819  * Fork - LGPL
22820  * <script type="text/javascript">
22821  */
22822
22823 /**
22824  * @class Roo.data.XmlReader
22825  * @extends Roo.data.DataReader
22826  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22827  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22828  * <p>
22829  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22830  * header in the HTTP response must be set to "text/xml".</em>
22831  * <p>
22832  * Example code:
22833  * <pre><code>
22834 var RecordDef = Roo.data.Record.create([
22835    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22836    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22837 ]);
22838 var myReader = new Roo.data.XmlReader({
22839    totalRecords: "results", // The element which contains the total dataset size (optional)
22840    record: "row",           // The repeated element which contains row information
22841    id: "id"                 // The element within the row that provides an ID for the record (optional)
22842 }, RecordDef);
22843 </code></pre>
22844  * <p>
22845  * This would consume an XML file like this:
22846  * <pre><code>
22847 &lt;?xml?>
22848 &lt;dataset>
22849  &lt;results>2&lt;/results>
22850  &lt;row>
22851    &lt;id>1&lt;/id>
22852    &lt;name>Bill&lt;/name>
22853    &lt;occupation>Gardener&lt;/occupation>
22854  &lt;/row>
22855  &lt;row>
22856    &lt;id>2&lt;/id>
22857    &lt;name>Ben&lt;/name>
22858    &lt;occupation>Horticulturalist&lt;/occupation>
22859  &lt;/row>
22860 &lt;/dataset>
22861 </code></pre>
22862  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22863  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22864  * paged from the remote server.
22865  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22866  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22867  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22868  * a record identifier value.
22869  * @constructor
22870  * Create a new XmlReader
22871  * @param {Object} meta Metadata configuration options
22872  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22873  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22874  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22875  */
22876 Roo.data.XmlReader = function(meta, recordType){
22877     meta = meta || {};
22878     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22879 };
22880 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22881     /**
22882      * This method is only used by a DataProxy which has retrieved data from a remote server.
22883          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22884          * to contain a method called 'responseXML' that returns an XML document object.
22885      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22886      * a cache of Roo.data.Records.
22887      */
22888     read : function(response){
22889         var doc = response.responseXML;
22890         if(!doc) {
22891             throw {message: "XmlReader.read: XML Document not available"};
22892         }
22893         return this.readRecords(doc);
22894     },
22895
22896     /**
22897      * Create a data block containing Roo.data.Records from an XML document.
22898          * @param {Object} doc A parsed XML document.
22899      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22900      * a cache of Roo.data.Records.
22901      */
22902     readRecords : function(doc){
22903         /**
22904          * After any data loads/reads, the raw XML Document is available for further custom processing.
22905          * @type XMLDocument
22906          */
22907         this.xmlData = doc;
22908         var root = doc.documentElement || doc;
22909         var q = Roo.DomQuery;
22910         var recordType = this.recordType, fields = recordType.prototype.fields;
22911         var sid = this.meta.id;
22912         var totalRecords = 0, success = true;
22913         if(this.meta.totalRecords){
22914             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22915         }
22916         
22917         if(this.meta.success){
22918             var sv = q.selectValue(this.meta.success, root, true);
22919             success = sv !== false && sv !== 'false';
22920         }
22921         var records = [];
22922         var ns = q.select(this.meta.record, root);
22923         for(var i = 0, len = ns.length; i < len; i++) {
22924                 var n = ns[i];
22925                 var values = {};
22926                 var id = sid ? q.selectValue(sid, n) : undefined;
22927                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22928                     var f = fields.items[j];
22929                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22930                     v = f.convert(v);
22931                     values[f.name] = v;
22932                 }
22933                 var record = new recordType(values, id);
22934                 record.node = n;
22935                 records[records.length] = record;
22936             }
22937
22938             return {
22939                 success : success,
22940                 records : records,
22941                 totalRecords : totalRecords || records.length
22942             };
22943     }
22944 });/*
22945  * Based on:
22946  * Ext JS Library 1.1.1
22947  * Copyright(c) 2006-2007, Ext JS, LLC.
22948  *
22949  * Originally Released Under LGPL - original licence link has changed is not relivant.
22950  *
22951  * Fork - LGPL
22952  * <script type="text/javascript">
22953  */
22954
22955 /**
22956  * @class Roo.data.ArrayReader
22957  * @extends Roo.data.DataReader
22958  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22959  * Each element of that Array represents a row of data fields. The
22960  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22961  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22962  * <p>
22963  * Example code:.
22964  * <pre><code>
22965 var RecordDef = Roo.data.Record.create([
22966     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22967     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22968 ]);
22969 var myReader = new Roo.data.ArrayReader({
22970     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22971 }, RecordDef);
22972 </code></pre>
22973  * <p>
22974  * This would consume an Array like this:
22975  * <pre><code>
22976 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22977   </code></pre>
22978  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22979  * @constructor
22980  * Create a new JsonReader
22981  * @param {Object} meta Metadata configuration options.
22982  * @param {Object} recordType Either an Array of field definition objects
22983  * as specified to {@link Roo.data.Record#create},
22984  * or an {@link Roo.data.Record} object
22985  * created using {@link Roo.data.Record#create}.
22986  */
22987 Roo.data.ArrayReader = function(meta, recordType){
22988     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22989 };
22990
22991 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22992     /**
22993      * Create a data block containing Roo.data.Records from an XML document.
22994      * @param {Object} o An Array of row objects which represents the dataset.
22995      * @return {Object} data A data block which is used by an Roo.data.Store object as
22996      * a cache of Roo.data.Records.
22997      */
22998     readRecords : function(o){
22999         var sid = this.meta ? this.meta.id : null;
23000         var recordType = this.recordType, fields = recordType.prototype.fields;
23001         var records = [];
23002         var root = o;
23003             for(var i = 0; i < root.length; i++){
23004                     var n = root[i];
23005                 var values = {};
23006                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
23007                 for(var j = 0, jlen = fields.length; j < jlen; j++){
23008                 var f = fields.items[j];
23009                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
23010                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
23011                 v = f.convert(v);
23012                 values[f.name] = v;
23013             }
23014                 var record = new recordType(values, id);
23015                 record.json = n;
23016                 records[records.length] = record;
23017             }
23018             return {
23019                 records : records,
23020                 totalRecords : records.length
23021             };
23022     }
23023 });/*
23024  * Based on:
23025  * Ext JS Library 1.1.1
23026  * Copyright(c) 2006-2007, Ext JS, LLC.
23027  *
23028  * Originally Released Under LGPL - original licence link has changed is not relivant.
23029  *
23030  * Fork - LGPL
23031  * <script type="text/javascript">
23032  */
23033
23034
23035 /**
23036  * @class Roo.data.Tree
23037  * @extends Roo.util.Observable
23038  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
23039  * in the tree have most standard DOM functionality.
23040  * @constructor
23041  * @param {Node} root (optional) The root node
23042  */
23043 Roo.data.Tree = function(root){
23044    this.nodeHash = {};
23045    /**
23046     * The root node for this tree
23047     * @type Node
23048     */
23049    this.root = null;
23050    if(root){
23051        this.setRootNode(root);
23052    }
23053    this.addEvents({
23054        /**
23055         * @event append
23056         * Fires when a new child node is appended to a node in this tree.
23057         * @param {Tree} tree The owner tree
23058         * @param {Node} parent The parent node
23059         * @param {Node} node The newly appended node
23060         * @param {Number} index The index of the newly appended node
23061         */
23062        "append" : true,
23063        /**
23064         * @event remove
23065         * Fires when a child node is removed from a node in this tree.
23066         * @param {Tree} tree The owner tree
23067         * @param {Node} parent The parent node
23068         * @param {Node} node The child node removed
23069         */
23070        "remove" : true,
23071        /**
23072         * @event move
23073         * Fires when a node is moved to a new location in the tree
23074         * @param {Tree} tree The owner tree
23075         * @param {Node} node The node moved
23076         * @param {Node} oldParent The old parent of this node
23077         * @param {Node} newParent The new parent of this node
23078         * @param {Number} index The index it was moved to
23079         */
23080        "move" : true,
23081        /**
23082         * @event insert
23083         * Fires when a new child node is inserted in a node in this tree.
23084         * @param {Tree} tree The owner tree
23085         * @param {Node} parent The parent node
23086         * @param {Node} node The child node inserted
23087         * @param {Node} refNode The child node the node was inserted before
23088         */
23089        "insert" : true,
23090        /**
23091         * @event beforeappend
23092         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23093         * @param {Tree} tree The owner tree
23094         * @param {Node} parent The parent node
23095         * @param {Node} node The child node to be appended
23096         */
23097        "beforeappend" : true,
23098        /**
23099         * @event beforeremove
23100         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23101         * @param {Tree} tree The owner tree
23102         * @param {Node} parent The parent node
23103         * @param {Node} node The child node to be removed
23104         */
23105        "beforeremove" : true,
23106        /**
23107         * @event beforemove
23108         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23109         * @param {Tree} tree The owner tree
23110         * @param {Node} node The node being moved
23111         * @param {Node} oldParent The parent of the node
23112         * @param {Node} newParent The new parent the node is moving to
23113         * @param {Number} index The index it is being moved to
23114         */
23115        "beforemove" : true,
23116        /**
23117         * @event beforeinsert
23118         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23119         * @param {Tree} tree The owner tree
23120         * @param {Node} parent The parent node
23121         * @param {Node} node The child node to be inserted
23122         * @param {Node} refNode The child node the node is being inserted before
23123         */
23124        "beforeinsert" : true
23125    });
23126
23127     Roo.data.Tree.superclass.constructor.call(this);
23128 };
23129
23130 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23131     pathSeparator: "/",
23132
23133     proxyNodeEvent : function(){
23134         return this.fireEvent.apply(this, arguments);
23135     },
23136
23137     /**
23138      * Returns the root node for this tree.
23139      * @return {Node}
23140      */
23141     getRootNode : function(){
23142         return this.root;
23143     },
23144
23145     /**
23146      * Sets the root node for this tree.
23147      * @param {Node} node
23148      * @return {Node}
23149      */
23150     setRootNode : function(node){
23151         this.root = node;
23152         node.ownerTree = this;
23153         node.isRoot = true;
23154         this.registerNode(node);
23155         return node;
23156     },
23157
23158     /**
23159      * Gets a node in this tree by its id.
23160      * @param {String} id
23161      * @return {Node}
23162      */
23163     getNodeById : function(id){
23164         return this.nodeHash[id];
23165     },
23166
23167     registerNode : function(node){
23168         this.nodeHash[node.id] = node;
23169     },
23170
23171     unregisterNode : function(node){
23172         delete this.nodeHash[node.id];
23173     },
23174
23175     toString : function(){
23176         return "[Tree"+(this.id?" "+this.id:"")+"]";
23177     }
23178 });
23179
23180 /**
23181  * @class Roo.data.Node
23182  * @extends Roo.util.Observable
23183  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23184  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23185  * @constructor
23186  * @param {Object} attributes The attributes/config for the node
23187  */
23188 Roo.data.Node = function(attributes){
23189     /**
23190      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23191      * @type {Object}
23192      */
23193     this.attributes = attributes || {};
23194     this.leaf = this.attributes.leaf;
23195     /**
23196      * The node id. @type String
23197      */
23198     this.id = this.attributes.id;
23199     if(!this.id){
23200         this.id = Roo.id(null, "ynode-");
23201         this.attributes.id = this.id;
23202     }
23203      
23204     
23205     /**
23206      * All child nodes of this node. @type Array
23207      */
23208     this.childNodes = [];
23209     if(!this.childNodes.indexOf){ // indexOf is a must
23210         this.childNodes.indexOf = function(o){
23211             for(var i = 0, len = this.length; i < len; i++){
23212                 if(this[i] == o) {
23213                     return i;
23214                 }
23215             }
23216             return -1;
23217         };
23218     }
23219     /**
23220      * The parent node for this node. @type Node
23221      */
23222     this.parentNode = null;
23223     /**
23224      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23225      */
23226     this.firstChild = null;
23227     /**
23228      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23229      */
23230     this.lastChild = null;
23231     /**
23232      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23233      */
23234     this.previousSibling = null;
23235     /**
23236      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23237      */
23238     this.nextSibling = null;
23239
23240     this.addEvents({
23241        /**
23242         * @event append
23243         * Fires when a new child node is appended
23244         * @param {Tree} tree The owner tree
23245         * @param {Node} this This node
23246         * @param {Node} node The newly appended node
23247         * @param {Number} index The index of the newly appended node
23248         */
23249        "append" : true,
23250        /**
23251         * @event remove
23252         * Fires when a child node is removed
23253         * @param {Tree} tree The owner tree
23254         * @param {Node} this This node
23255         * @param {Node} node The removed node
23256         */
23257        "remove" : true,
23258        /**
23259         * @event move
23260         * Fires when this node is moved to a new location in the tree
23261         * @param {Tree} tree The owner tree
23262         * @param {Node} this This node
23263         * @param {Node} oldParent The old parent of this node
23264         * @param {Node} newParent The new parent of this node
23265         * @param {Number} index The index it was moved to
23266         */
23267        "move" : true,
23268        /**
23269         * @event insert
23270         * Fires when a new child node is inserted.
23271         * @param {Tree} tree The owner tree
23272         * @param {Node} this This node
23273         * @param {Node} node The child node inserted
23274         * @param {Node} refNode The child node the node was inserted before
23275         */
23276        "insert" : true,
23277        /**
23278         * @event beforeappend
23279         * Fires before a new child is appended, return false to cancel the append.
23280         * @param {Tree} tree The owner tree
23281         * @param {Node} this This node
23282         * @param {Node} node The child node to be appended
23283         */
23284        "beforeappend" : true,
23285        /**
23286         * @event beforeremove
23287         * Fires before a child is removed, return false to cancel the remove.
23288         * @param {Tree} tree The owner tree
23289         * @param {Node} this This node
23290         * @param {Node} node The child node to be removed
23291         */
23292        "beforeremove" : true,
23293        /**
23294         * @event beforemove
23295         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23296         * @param {Tree} tree The owner tree
23297         * @param {Node} this This node
23298         * @param {Node} oldParent The parent of this node
23299         * @param {Node} newParent The new parent this node is moving to
23300         * @param {Number} index The index it is being moved to
23301         */
23302        "beforemove" : true,
23303        /**
23304         * @event beforeinsert
23305         * Fires before a new child is inserted, return false to cancel the insert.
23306         * @param {Tree} tree The owner tree
23307         * @param {Node} this This node
23308         * @param {Node} node The child node to be inserted
23309         * @param {Node} refNode The child node the node is being inserted before
23310         */
23311        "beforeinsert" : true
23312    });
23313     this.listeners = this.attributes.listeners;
23314     Roo.data.Node.superclass.constructor.call(this);
23315 };
23316
23317 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23318     fireEvent : function(evtName){
23319         // first do standard event for this node
23320         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23321             return false;
23322         }
23323         // then bubble it up to the tree if the event wasn't cancelled
23324         var ot = this.getOwnerTree();
23325         if(ot){
23326             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23327                 return false;
23328             }
23329         }
23330         return true;
23331     },
23332
23333     /**
23334      * Returns true if this node is a leaf
23335      * @return {Boolean}
23336      */
23337     isLeaf : function(){
23338         return this.leaf === true;
23339     },
23340
23341     // private
23342     setFirstChild : function(node){
23343         this.firstChild = node;
23344     },
23345
23346     //private
23347     setLastChild : function(node){
23348         this.lastChild = node;
23349     },
23350
23351
23352     /**
23353      * Returns true if this node is the last child of its parent
23354      * @return {Boolean}
23355      */
23356     isLast : function(){
23357        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23358     },
23359
23360     /**
23361      * Returns true if this node is the first child of its parent
23362      * @return {Boolean}
23363      */
23364     isFirst : function(){
23365        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23366     },
23367
23368     hasChildNodes : function(){
23369         return !this.isLeaf() && this.childNodes.length > 0;
23370     },
23371
23372     /**
23373      * Insert node(s) as the last child node of this node.
23374      * @param {Node/Array} node The node or Array of nodes to append
23375      * @return {Node} The appended node if single append, or null if an array was passed
23376      */
23377     appendChild : function(node){
23378         var multi = false;
23379         if(node instanceof Array){
23380             multi = node;
23381         }else if(arguments.length > 1){
23382             multi = arguments;
23383         }
23384         // if passed an array or multiple args do them one by one
23385         if(multi){
23386             for(var i = 0, len = multi.length; i < len; i++) {
23387                 this.appendChild(multi[i]);
23388             }
23389         }else{
23390             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23391                 return false;
23392             }
23393             var index = this.childNodes.length;
23394             var oldParent = node.parentNode;
23395             // it's a move, make sure we move it cleanly
23396             if(oldParent){
23397                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23398                     return false;
23399                 }
23400                 oldParent.removeChild(node);
23401             }
23402             index = this.childNodes.length;
23403             if(index == 0){
23404                 this.setFirstChild(node);
23405             }
23406             this.childNodes.push(node);
23407             node.parentNode = this;
23408             var ps = this.childNodes[index-1];
23409             if(ps){
23410                 node.previousSibling = ps;
23411                 ps.nextSibling = node;
23412             }else{
23413                 node.previousSibling = null;
23414             }
23415             node.nextSibling = null;
23416             this.setLastChild(node);
23417             node.setOwnerTree(this.getOwnerTree());
23418             this.fireEvent("append", this.ownerTree, this, node, index);
23419             if(oldParent){
23420                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23421             }
23422             return node;
23423         }
23424     },
23425
23426     /**
23427      * Removes a child node from this node.
23428      * @param {Node} node The node to remove
23429      * @return {Node} The removed node
23430      */
23431     removeChild : function(node){
23432         var index = this.childNodes.indexOf(node);
23433         if(index == -1){
23434             return false;
23435         }
23436         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23437             return false;
23438         }
23439
23440         // remove it from childNodes collection
23441         this.childNodes.splice(index, 1);
23442
23443         // update siblings
23444         if(node.previousSibling){
23445             node.previousSibling.nextSibling = node.nextSibling;
23446         }
23447         if(node.nextSibling){
23448             node.nextSibling.previousSibling = node.previousSibling;
23449         }
23450
23451         // update child refs
23452         if(this.firstChild == node){
23453             this.setFirstChild(node.nextSibling);
23454         }
23455         if(this.lastChild == node){
23456             this.setLastChild(node.previousSibling);
23457         }
23458
23459         node.setOwnerTree(null);
23460         // clear any references from the node
23461         node.parentNode = null;
23462         node.previousSibling = null;
23463         node.nextSibling = null;
23464         this.fireEvent("remove", this.ownerTree, this, node);
23465         return node;
23466     },
23467
23468     /**
23469      * Inserts the first node before the second node in this nodes childNodes collection.
23470      * @param {Node} node The node to insert
23471      * @param {Node} refNode The node to insert before (if null the node is appended)
23472      * @return {Node} The inserted node
23473      */
23474     insertBefore : function(node, refNode){
23475         if(!refNode){ // like standard Dom, refNode can be null for append
23476             return this.appendChild(node);
23477         }
23478         // nothing to do
23479         if(node == refNode){
23480             return false;
23481         }
23482
23483         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23484             return false;
23485         }
23486         var index = this.childNodes.indexOf(refNode);
23487         var oldParent = node.parentNode;
23488         var refIndex = index;
23489
23490         // when moving internally, indexes will change after remove
23491         if(oldParent == this && this.childNodes.indexOf(node) < index){
23492             refIndex--;
23493         }
23494
23495         // it's a move, make sure we move it cleanly
23496         if(oldParent){
23497             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23498                 return false;
23499             }
23500             oldParent.removeChild(node);
23501         }
23502         if(refIndex == 0){
23503             this.setFirstChild(node);
23504         }
23505         this.childNodes.splice(refIndex, 0, node);
23506         node.parentNode = this;
23507         var ps = this.childNodes[refIndex-1];
23508         if(ps){
23509             node.previousSibling = ps;
23510             ps.nextSibling = node;
23511         }else{
23512             node.previousSibling = null;
23513         }
23514         node.nextSibling = refNode;
23515         refNode.previousSibling = node;
23516         node.setOwnerTree(this.getOwnerTree());
23517         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23518         if(oldParent){
23519             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23520         }
23521         return node;
23522     },
23523
23524     /**
23525      * Returns the child node at the specified index.
23526      * @param {Number} index
23527      * @return {Node}
23528      */
23529     item : function(index){
23530         return this.childNodes[index];
23531     },
23532
23533     /**
23534      * Replaces one child node in this node with another.
23535      * @param {Node} newChild The replacement node
23536      * @param {Node} oldChild The node to replace
23537      * @return {Node} The replaced node
23538      */
23539     replaceChild : function(newChild, oldChild){
23540         this.insertBefore(newChild, oldChild);
23541         this.removeChild(oldChild);
23542         return oldChild;
23543     },
23544
23545     /**
23546      * Returns the index of a child node
23547      * @param {Node} node
23548      * @return {Number} The index of the node or -1 if it was not found
23549      */
23550     indexOf : function(child){
23551         return this.childNodes.indexOf(child);
23552     },
23553
23554     /**
23555      * Returns the tree this node is in.
23556      * @return {Tree}
23557      */
23558     getOwnerTree : function(){
23559         // if it doesn't have one, look for one
23560         if(!this.ownerTree){
23561             var p = this;
23562             while(p){
23563                 if(p.ownerTree){
23564                     this.ownerTree = p.ownerTree;
23565                     break;
23566                 }
23567                 p = p.parentNode;
23568             }
23569         }
23570         return this.ownerTree;
23571     },
23572
23573     /**
23574      * Returns depth of this node (the root node has a depth of 0)
23575      * @return {Number}
23576      */
23577     getDepth : function(){
23578         var depth = 0;
23579         var p = this;
23580         while(p.parentNode){
23581             ++depth;
23582             p = p.parentNode;
23583         }
23584         return depth;
23585     },
23586
23587     // private
23588     setOwnerTree : function(tree){
23589         // if it's move, we need to update everyone
23590         if(tree != this.ownerTree){
23591             if(this.ownerTree){
23592                 this.ownerTree.unregisterNode(this);
23593             }
23594             this.ownerTree = tree;
23595             var cs = this.childNodes;
23596             for(var i = 0, len = cs.length; i < len; i++) {
23597                 cs[i].setOwnerTree(tree);
23598             }
23599             if(tree){
23600                 tree.registerNode(this);
23601             }
23602         }
23603     },
23604
23605     /**
23606      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23607      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23608      * @return {String} The path
23609      */
23610     getPath : function(attr){
23611         attr = attr || "id";
23612         var p = this.parentNode;
23613         var b = [this.attributes[attr]];
23614         while(p){
23615             b.unshift(p.attributes[attr]);
23616             p = p.parentNode;
23617         }
23618         var sep = this.getOwnerTree().pathSeparator;
23619         return sep + b.join(sep);
23620     },
23621
23622     /**
23623      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23624      * function call will be the scope provided or the current node. The arguments to the function
23625      * will be the args provided or the current node. If the function returns false at any point,
23626      * the bubble is stopped.
23627      * @param {Function} fn The function to call
23628      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23629      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23630      */
23631     bubble : function(fn, scope, args){
23632         var p = this;
23633         while(p){
23634             if(fn.call(scope || p, args || p) === false){
23635                 break;
23636             }
23637             p = p.parentNode;
23638         }
23639     },
23640
23641     /**
23642      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23643      * function call will be the scope provided or the current node. The arguments to the function
23644      * will be the args provided or the current node. If the function returns false at any point,
23645      * the cascade is stopped on that branch.
23646      * @param {Function} fn The function to call
23647      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23648      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23649      */
23650     cascade : function(fn, scope, args){
23651         if(fn.call(scope || this, args || this) !== false){
23652             var cs = this.childNodes;
23653             for(var i = 0, len = cs.length; i < len; i++) {
23654                 cs[i].cascade(fn, scope, args);
23655             }
23656         }
23657     },
23658
23659     /**
23660      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23661      * function call will be the scope provided or the current node. The arguments to the function
23662      * will be the args provided or the current node. If the function returns false at any point,
23663      * the iteration stops.
23664      * @param {Function} fn The function to call
23665      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23666      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23667      */
23668     eachChild : function(fn, scope, args){
23669         var cs = this.childNodes;
23670         for(var i = 0, len = cs.length; i < len; i++) {
23671                 if(fn.call(scope || this, args || cs[i]) === false){
23672                     break;
23673                 }
23674         }
23675     },
23676
23677     /**
23678      * Finds the first child that has the attribute with the specified value.
23679      * @param {String} attribute The attribute name
23680      * @param {Mixed} value The value to search for
23681      * @return {Node} The found child or null if none was found
23682      */
23683     findChild : function(attribute, value){
23684         var cs = this.childNodes;
23685         for(var i = 0, len = cs.length; i < len; i++) {
23686                 if(cs[i].attributes[attribute] == value){
23687                     return cs[i];
23688                 }
23689         }
23690         return null;
23691     },
23692
23693     /**
23694      * Finds the first child by a custom function. The child matches if the function passed
23695      * returns true.
23696      * @param {Function} fn
23697      * @param {Object} scope (optional)
23698      * @return {Node} The found child or null if none was found
23699      */
23700     findChildBy : function(fn, scope){
23701         var cs = this.childNodes;
23702         for(var i = 0, len = cs.length; i < len; i++) {
23703                 if(fn.call(scope||cs[i], cs[i]) === true){
23704                     return cs[i];
23705                 }
23706         }
23707         return null;
23708     },
23709
23710     /**
23711      * Sorts this nodes children using the supplied sort function
23712      * @param {Function} fn
23713      * @param {Object} scope (optional)
23714      */
23715     sort : function(fn, scope){
23716         var cs = this.childNodes;
23717         var len = cs.length;
23718         if(len > 0){
23719             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23720             cs.sort(sortFn);
23721             for(var i = 0; i < len; i++){
23722                 var n = cs[i];
23723                 n.previousSibling = cs[i-1];
23724                 n.nextSibling = cs[i+1];
23725                 if(i == 0){
23726                     this.setFirstChild(n);
23727                 }
23728                 if(i == len-1){
23729                     this.setLastChild(n);
23730                 }
23731             }
23732         }
23733     },
23734
23735     /**
23736      * Returns true if this node is an ancestor (at any point) of the passed node.
23737      * @param {Node} node
23738      * @return {Boolean}
23739      */
23740     contains : function(node){
23741         return node.isAncestor(this);
23742     },
23743
23744     /**
23745      * Returns true if the passed node is an ancestor (at any point) of this node.
23746      * @param {Node} node
23747      * @return {Boolean}
23748      */
23749     isAncestor : function(node){
23750         var p = this.parentNode;
23751         while(p){
23752             if(p == node){
23753                 return true;
23754             }
23755             p = p.parentNode;
23756         }
23757         return false;
23758     },
23759
23760     toString : function(){
23761         return "[Node"+(this.id?" "+this.id:"")+"]";
23762     }
23763 });/*
23764  * Based on:
23765  * Ext JS Library 1.1.1
23766  * Copyright(c) 2006-2007, Ext JS, LLC.
23767  *
23768  * Originally Released Under LGPL - original licence link has changed is not relivant.
23769  *
23770  * Fork - LGPL
23771  * <script type="text/javascript">
23772  */
23773  (function(){ 
23774 /**
23775  * @class Roo.Layer
23776  * @extends Roo.Element
23777  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23778  * automatic maintaining of shadow/shim positions.
23779  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23780  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23781  * you can pass a string with a CSS class name. False turns off the shadow.
23782  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23783  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23784  * @cfg {String} cls CSS class to add to the element
23785  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23786  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23787  * @constructor
23788  * @param {Object} config An object with config options.
23789  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23790  */
23791
23792 Roo.Layer = function(config, existingEl){
23793     config = config || {};
23794     var dh = Roo.DomHelper;
23795     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23796     if(existingEl){
23797         this.dom = Roo.getDom(existingEl);
23798     }
23799     if(!this.dom){
23800         var o = config.dh || {tag: "div", cls: "x-layer"};
23801         this.dom = dh.append(pel, o);
23802     }
23803     if(config.cls){
23804         this.addClass(config.cls);
23805     }
23806     this.constrain = config.constrain !== false;
23807     this.visibilityMode = Roo.Element.VISIBILITY;
23808     if(config.id){
23809         this.id = this.dom.id = config.id;
23810     }else{
23811         this.id = Roo.id(this.dom);
23812     }
23813     this.zindex = config.zindex || this.getZIndex();
23814     this.position("absolute", this.zindex);
23815     if(config.shadow){
23816         this.shadowOffset = config.shadowOffset || 4;
23817         this.shadow = new Roo.Shadow({
23818             offset : this.shadowOffset,
23819             mode : config.shadow
23820         });
23821     }else{
23822         this.shadowOffset = 0;
23823     }
23824     this.useShim = config.shim !== false && Roo.useShims;
23825     this.useDisplay = config.useDisplay;
23826     this.hide();
23827 };
23828
23829 var supr = Roo.Element.prototype;
23830
23831 // shims are shared among layer to keep from having 100 iframes
23832 var shims = [];
23833
23834 Roo.extend(Roo.Layer, Roo.Element, {
23835
23836     getZIndex : function(){
23837         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23838     },
23839
23840     getShim : function(){
23841         if(!this.useShim){
23842             return null;
23843         }
23844         if(this.shim){
23845             return this.shim;
23846         }
23847         var shim = shims.shift();
23848         if(!shim){
23849             shim = this.createShim();
23850             shim.enableDisplayMode('block');
23851             shim.dom.style.display = 'none';
23852             shim.dom.style.visibility = 'visible';
23853         }
23854         var pn = this.dom.parentNode;
23855         if(shim.dom.parentNode != pn){
23856             pn.insertBefore(shim.dom, this.dom);
23857         }
23858         shim.setStyle('z-index', this.getZIndex()-2);
23859         this.shim = shim;
23860         return shim;
23861     },
23862
23863     hideShim : function(){
23864         if(this.shim){
23865             this.shim.setDisplayed(false);
23866             shims.push(this.shim);
23867             delete this.shim;
23868         }
23869     },
23870
23871     disableShadow : function(){
23872         if(this.shadow){
23873             this.shadowDisabled = true;
23874             this.shadow.hide();
23875             this.lastShadowOffset = this.shadowOffset;
23876             this.shadowOffset = 0;
23877         }
23878     },
23879
23880     enableShadow : function(show){
23881         if(this.shadow){
23882             this.shadowDisabled = false;
23883             this.shadowOffset = this.lastShadowOffset;
23884             delete this.lastShadowOffset;
23885             if(show){
23886                 this.sync(true);
23887             }
23888         }
23889     },
23890
23891     // private
23892     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23893     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23894     sync : function(doShow){
23895         var sw = this.shadow;
23896         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23897             var sh = this.getShim();
23898
23899             var w = this.getWidth(),
23900                 h = this.getHeight();
23901
23902             var l = this.getLeft(true),
23903                 t = this.getTop(true);
23904
23905             if(sw && !this.shadowDisabled){
23906                 if(doShow && !sw.isVisible()){
23907                     sw.show(this);
23908                 }else{
23909                     sw.realign(l, t, w, h);
23910                 }
23911                 if(sh){
23912                     if(doShow){
23913                        sh.show();
23914                     }
23915                     // fit the shim behind the shadow, so it is shimmed too
23916                     var a = sw.adjusts, s = sh.dom.style;
23917                     s.left = (Math.min(l, l+a.l))+"px";
23918                     s.top = (Math.min(t, t+a.t))+"px";
23919                     s.width = (w+a.w)+"px";
23920                     s.height = (h+a.h)+"px";
23921                 }
23922             }else if(sh){
23923                 if(doShow){
23924                    sh.show();
23925                 }
23926                 sh.setSize(w, h);
23927                 sh.setLeftTop(l, t);
23928             }
23929             
23930         }
23931     },
23932
23933     // private
23934     destroy : function(){
23935         this.hideShim();
23936         if(this.shadow){
23937             this.shadow.hide();
23938         }
23939         this.removeAllListeners();
23940         var pn = this.dom.parentNode;
23941         if(pn){
23942             pn.removeChild(this.dom);
23943         }
23944         Roo.Element.uncache(this.id);
23945     },
23946
23947     remove : function(){
23948         this.destroy();
23949     },
23950
23951     // private
23952     beginUpdate : function(){
23953         this.updating = true;
23954     },
23955
23956     // private
23957     endUpdate : function(){
23958         this.updating = false;
23959         this.sync(true);
23960     },
23961
23962     // private
23963     hideUnders : function(negOffset){
23964         if(this.shadow){
23965             this.shadow.hide();
23966         }
23967         this.hideShim();
23968     },
23969
23970     // private
23971     constrainXY : function(){
23972         if(this.constrain){
23973             var vw = Roo.lib.Dom.getViewWidth(),
23974                 vh = Roo.lib.Dom.getViewHeight();
23975             var s = Roo.get(document).getScroll();
23976
23977             var xy = this.getXY();
23978             var x = xy[0], y = xy[1];   
23979             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23980             // only move it if it needs it
23981             var moved = false;
23982             // first validate right/bottom
23983             if((x + w) > vw+s.left){
23984                 x = vw - w - this.shadowOffset;
23985                 moved = true;
23986             }
23987             if((y + h) > vh+s.top){
23988                 y = vh - h - this.shadowOffset;
23989                 moved = true;
23990             }
23991             // then make sure top/left isn't negative
23992             if(x < s.left){
23993                 x = s.left;
23994                 moved = true;
23995             }
23996             if(y < s.top){
23997                 y = s.top;
23998                 moved = true;
23999             }
24000             if(moved){
24001                 if(this.avoidY){
24002                     var ay = this.avoidY;
24003                     if(y <= ay && (y+h) >= ay){
24004                         y = ay-h-5;   
24005                     }
24006                 }
24007                 xy = [x, y];
24008                 this.storeXY(xy);
24009                 supr.setXY.call(this, xy);
24010                 this.sync();
24011             }
24012         }
24013     },
24014
24015     isVisible : function(){
24016         return this.visible;    
24017     },
24018
24019     // private
24020     showAction : function(){
24021         this.visible = true; // track visibility to prevent getStyle calls
24022         if(this.useDisplay === true){
24023             this.setDisplayed("");
24024         }else if(this.lastXY){
24025             supr.setXY.call(this, this.lastXY);
24026         }else if(this.lastLT){
24027             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
24028         }
24029     },
24030
24031     // private
24032     hideAction : function(){
24033         this.visible = false;
24034         if(this.useDisplay === true){
24035             this.setDisplayed(false);
24036         }else{
24037             this.setLeftTop(-10000,-10000);
24038         }
24039     },
24040
24041     // overridden Element method
24042     setVisible : function(v, a, d, c, e){
24043         if(v){
24044             this.showAction();
24045         }
24046         if(a && v){
24047             var cb = function(){
24048                 this.sync(true);
24049                 if(c){
24050                     c();
24051                 }
24052             }.createDelegate(this);
24053             supr.setVisible.call(this, true, true, d, cb, e);
24054         }else{
24055             if(!v){
24056                 this.hideUnders(true);
24057             }
24058             var cb = c;
24059             if(a){
24060                 cb = function(){
24061                     this.hideAction();
24062                     if(c){
24063                         c();
24064                     }
24065                 }.createDelegate(this);
24066             }
24067             supr.setVisible.call(this, v, a, d, cb, e);
24068             if(v){
24069                 this.sync(true);
24070             }else if(!a){
24071                 this.hideAction();
24072             }
24073         }
24074     },
24075
24076     storeXY : function(xy){
24077         delete this.lastLT;
24078         this.lastXY = xy;
24079     },
24080
24081     storeLeftTop : function(left, top){
24082         delete this.lastXY;
24083         this.lastLT = [left, top];
24084     },
24085
24086     // private
24087     beforeFx : function(){
24088         this.beforeAction();
24089         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24090     },
24091
24092     // private
24093     afterFx : function(){
24094         Roo.Layer.superclass.afterFx.apply(this, arguments);
24095         this.sync(this.isVisible());
24096     },
24097
24098     // private
24099     beforeAction : function(){
24100         if(!this.updating && this.shadow){
24101             this.shadow.hide();
24102         }
24103     },
24104
24105     // overridden Element method
24106     setLeft : function(left){
24107         this.storeLeftTop(left, this.getTop(true));
24108         supr.setLeft.apply(this, arguments);
24109         this.sync();
24110     },
24111
24112     setTop : function(top){
24113         this.storeLeftTop(this.getLeft(true), top);
24114         supr.setTop.apply(this, arguments);
24115         this.sync();
24116     },
24117
24118     setLeftTop : function(left, top){
24119         this.storeLeftTop(left, top);
24120         supr.setLeftTop.apply(this, arguments);
24121         this.sync();
24122     },
24123
24124     setXY : function(xy, a, d, c, e){
24125         this.fixDisplay();
24126         this.beforeAction();
24127         this.storeXY(xy);
24128         var cb = this.createCB(c);
24129         supr.setXY.call(this, xy, a, d, cb, e);
24130         if(!a){
24131             cb();
24132         }
24133     },
24134
24135     // private
24136     createCB : function(c){
24137         var el = this;
24138         return function(){
24139             el.constrainXY();
24140             el.sync(true);
24141             if(c){
24142                 c();
24143             }
24144         };
24145     },
24146
24147     // overridden Element method
24148     setX : function(x, a, d, c, e){
24149         this.setXY([x, this.getY()], a, d, c, e);
24150     },
24151
24152     // overridden Element method
24153     setY : function(y, a, d, c, e){
24154         this.setXY([this.getX(), y], a, d, c, e);
24155     },
24156
24157     // overridden Element method
24158     setSize : function(w, h, a, d, c, e){
24159         this.beforeAction();
24160         var cb = this.createCB(c);
24161         supr.setSize.call(this, w, h, a, d, cb, e);
24162         if(!a){
24163             cb();
24164         }
24165     },
24166
24167     // overridden Element method
24168     setWidth : function(w, a, d, c, e){
24169         this.beforeAction();
24170         var cb = this.createCB(c);
24171         supr.setWidth.call(this, w, a, d, cb, e);
24172         if(!a){
24173             cb();
24174         }
24175     },
24176
24177     // overridden Element method
24178     setHeight : function(h, a, d, c, e){
24179         this.beforeAction();
24180         var cb = this.createCB(c);
24181         supr.setHeight.call(this, h, a, d, cb, e);
24182         if(!a){
24183             cb();
24184         }
24185     },
24186
24187     // overridden Element method
24188     setBounds : function(x, y, w, h, a, d, c, e){
24189         this.beforeAction();
24190         var cb = this.createCB(c);
24191         if(!a){
24192             this.storeXY([x, y]);
24193             supr.setXY.call(this, [x, y]);
24194             supr.setSize.call(this, w, h, a, d, cb, e);
24195             cb();
24196         }else{
24197             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24198         }
24199         return this;
24200     },
24201     
24202     /**
24203      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24204      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24205      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24206      * @param {Number} zindex The new z-index to set
24207      * @return {this} The Layer
24208      */
24209     setZIndex : function(zindex){
24210         this.zindex = zindex;
24211         this.setStyle("z-index", zindex + 2);
24212         if(this.shadow){
24213             this.shadow.setZIndex(zindex + 1);
24214         }
24215         if(this.shim){
24216             this.shim.setStyle("z-index", zindex);
24217         }
24218     }
24219 });
24220 })();/*
24221  * Based on:
24222  * Ext JS Library 1.1.1
24223  * Copyright(c) 2006-2007, Ext JS, LLC.
24224  *
24225  * Originally Released Under LGPL - original licence link has changed is not relivant.
24226  *
24227  * Fork - LGPL
24228  * <script type="text/javascript">
24229  */
24230
24231
24232 /**
24233  * @class Roo.Shadow
24234  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24235  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24236  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24237  * @constructor
24238  * Create a new Shadow
24239  * @param {Object} config The config object
24240  */
24241 Roo.Shadow = function(config){
24242     Roo.apply(this, config);
24243     if(typeof this.mode != "string"){
24244         this.mode = this.defaultMode;
24245     }
24246     var o = this.offset, a = {h: 0};
24247     var rad = Math.floor(this.offset/2);
24248     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24249         case "drop":
24250             a.w = 0;
24251             a.l = a.t = o;
24252             a.t -= 1;
24253             if(Roo.isIE){
24254                 a.l -= this.offset + rad;
24255                 a.t -= this.offset + rad;
24256                 a.w -= rad;
24257                 a.h -= rad;
24258                 a.t += 1;
24259             }
24260         break;
24261         case "sides":
24262             a.w = (o*2);
24263             a.l = -o;
24264             a.t = o-1;
24265             if(Roo.isIE){
24266                 a.l -= (this.offset - rad);
24267                 a.t -= this.offset + rad;
24268                 a.l += 1;
24269                 a.w -= (this.offset - rad)*2;
24270                 a.w -= rad + 1;
24271                 a.h -= 1;
24272             }
24273         break;
24274         case "frame":
24275             a.w = a.h = (o*2);
24276             a.l = a.t = -o;
24277             a.t += 1;
24278             a.h -= 2;
24279             if(Roo.isIE){
24280                 a.l -= (this.offset - rad);
24281                 a.t -= (this.offset - rad);
24282                 a.l += 1;
24283                 a.w -= (this.offset + rad + 1);
24284                 a.h -= (this.offset + rad);
24285                 a.h += 1;
24286             }
24287         break;
24288     };
24289
24290     this.adjusts = a;
24291 };
24292
24293 Roo.Shadow.prototype = {
24294     /**
24295      * @cfg {String} mode
24296      * The shadow display mode.  Supports the following options:<br />
24297      * sides: Shadow displays on both sides and bottom only<br />
24298      * frame: Shadow displays equally on all four sides<br />
24299      * drop: Traditional bottom-right drop shadow (default)
24300      */
24301     /**
24302      * @cfg {String} offset
24303      * The number of pixels to offset the shadow from the element (defaults to 4)
24304      */
24305     offset: 4,
24306
24307     // private
24308     defaultMode: "drop",
24309
24310     /**
24311      * Displays the shadow under the target element
24312      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24313      */
24314     show : function(target){
24315         target = Roo.get(target);
24316         if(!this.el){
24317             this.el = Roo.Shadow.Pool.pull();
24318             if(this.el.dom.nextSibling != target.dom){
24319                 this.el.insertBefore(target);
24320             }
24321         }
24322         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24323         if(Roo.isIE){
24324             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24325         }
24326         this.realign(
24327             target.getLeft(true),
24328             target.getTop(true),
24329             target.getWidth(),
24330             target.getHeight()
24331         );
24332         this.el.dom.style.display = "block";
24333     },
24334
24335     /**
24336      * Returns true if the shadow is visible, else false
24337      */
24338     isVisible : function(){
24339         return this.el ? true : false;  
24340     },
24341
24342     /**
24343      * Direct alignment when values are already available. Show must be called at least once before
24344      * calling this method to ensure it is initialized.
24345      * @param {Number} left The target element left position
24346      * @param {Number} top The target element top position
24347      * @param {Number} width The target element width
24348      * @param {Number} height The target element height
24349      */
24350     realign : function(l, t, w, h){
24351         if(!this.el){
24352             return;
24353         }
24354         var a = this.adjusts, d = this.el.dom, s = d.style;
24355         var iea = 0;
24356         s.left = (l+a.l)+"px";
24357         s.top = (t+a.t)+"px";
24358         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24359  
24360         if(s.width != sws || s.height != shs){
24361             s.width = sws;
24362             s.height = shs;
24363             if(!Roo.isIE){
24364                 var cn = d.childNodes;
24365                 var sww = Math.max(0, (sw-12))+"px";
24366                 cn[0].childNodes[1].style.width = sww;
24367                 cn[1].childNodes[1].style.width = sww;
24368                 cn[2].childNodes[1].style.width = sww;
24369                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24370             }
24371         }
24372     },
24373
24374     /**
24375      * Hides this shadow
24376      */
24377     hide : function(){
24378         if(this.el){
24379             this.el.dom.style.display = "none";
24380             Roo.Shadow.Pool.push(this.el);
24381             delete this.el;
24382         }
24383     },
24384
24385     /**
24386      * Adjust the z-index of this shadow
24387      * @param {Number} zindex The new z-index
24388      */
24389     setZIndex : function(z){
24390         this.zIndex = z;
24391         if(this.el){
24392             this.el.setStyle("z-index", z);
24393         }
24394     }
24395 };
24396
24397 // Private utility class that manages the internal Shadow cache
24398 Roo.Shadow.Pool = function(){
24399     var p = [];
24400     var markup = Roo.isIE ?
24401                  '<div class="x-ie-shadow"></div>' :
24402                  '<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>';
24403     return {
24404         pull : function(){
24405             var sh = p.shift();
24406             if(!sh){
24407                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24408                 sh.autoBoxAdjust = false;
24409             }
24410             return sh;
24411         },
24412
24413         push : function(sh){
24414             p.push(sh);
24415         }
24416     };
24417 }();/*
24418  * Based on:
24419  * Ext JS Library 1.1.1
24420  * Copyright(c) 2006-2007, Ext JS, LLC.
24421  *
24422  * Originally Released Under LGPL - original licence link has changed is not relivant.
24423  *
24424  * Fork - LGPL
24425  * <script type="text/javascript">
24426  */
24427
24428
24429 /**
24430  * @class Roo.SplitBar
24431  * @extends Roo.util.Observable
24432  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24433  * <br><br>
24434  * Usage:
24435  * <pre><code>
24436 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24437                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24438 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24439 split.minSize = 100;
24440 split.maxSize = 600;
24441 split.animate = true;
24442 split.on('moved', splitterMoved);
24443 </code></pre>
24444  * @constructor
24445  * Create a new SplitBar
24446  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24447  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24448  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24449  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24450                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24451                         position of the SplitBar).
24452  */
24453 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24454     
24455     /** @private */
24456     this.el = Roo.get(dragElement, true);
24457     this.el.dom.unselectable = "on";
24458     /** @private */
24459     this.resizingEl = Roo.get(resizingElement, true);
24460
24461     /**
24462      * @private
24463      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24464      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24465      * @type Number
24466      */
24467     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24468     
24469     /**
24470      * The minimum size of the resizing element. (Defaults to 0)
24471      * @type Number
24472      */
24473     this.minSize = 0;
24474     
24475     /**
24476      * The maximum size of the resizing element. (Defaults to 2000)
24477      * @type Number
24478      */
24479     this.maxSize = 2000;
24480     
24481     /**
24482      * Whether to animate the transition to the new size
24483      * @type Boolean
24484      */
24485     this.animate = false;
24486     
24487     /**
24488      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24489      * @type Boolean
24490      */
24491     this.useShim = false;
24492     
24493     /** @private */
24494     this.shim = null;
24495     
24496     if(!existingProxy){
24497         /** @private */
24498         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24499     }else{
24500         this.proxy = Roo.get(existingProxy).dom;
24501     }
24502     /** @private */
24503     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24504     
24505     /** @private */
24506     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24507     
24508     /** @private */
24509     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24510     
24511     /** @private */
24512     this.dragSpecs = {};
24513     
24514     /**
24515      * @private The adapter to use to positon and resize elements
24516      */
24517     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24518     this.adapter.init(this);
24519     
24520     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24521         /** @private */
24522         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24523         this.el.addClass("x-splitbar-h");
24524     }else{
24525         /** @private */
24526         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24527         this.el.addClass("x-splitbar-v");
24528     }
24529     
24530     this.addEvents({
24531         /**
24532          * @event resize
24533          * Fires when the splitter is moved (alias for {@link #event-moved})
24534          * @param {Roo.SplitBar} this
24535          * @param {Number} newSize the new width or height
24536          */
24537         "resize" : true,
24538         /**
24539          * @event moved
24540          * Fires when the splitter is moved
24541          * @param {Roo.SplitBar} this
24542          * @param {Number} newSize the new width or height
24543          */
24544         "moved" : true,
24545         /**
24546          * @event beforeresize
24547          * Fires before the splitter is dragged
24548          * @param {Roo.SplitBar} this
24549          */
24550         "beforeresize" : true,
24551
24552         "beforeapply" : true
24553     });
24554
24555     Roo.util.Observable.call(this);
24556 };
24557
24558 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24559     onStartProxyDrag : function(x, y){
24560         this.fireEvent("beforeresize", this);
24561         if(!this.overlay){
24562             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24563             o.unselectable();
24564             o.enableDisplayMode("block");
24565             // all splitbars share the same overlay
24566             Roo.SplitBar.prototype.overlay = o;
24567         }
24568         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24569         this.overlay.show();
24570         Roo.get(this.proxy).setDisplayed("block");
24571         var size = this.adapter.getElementSize(this);
24572         this.activeMinSize = this.getMinimumSize();;
24573         this.activeMaxSize = this.getMaximumSize();;
24574         var c1 = size - this.activeMinSize;
24575         var c2 = Math.max(this.activeMaxSize - size, 0);
24576         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24577             this.dd.resetConstraints();
24578             this.dd.setXConstraint(
24579                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24580                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24581             );
24582             this.dd.setYConstraint(0, 0);
24583         }else{
24584             this.dd.resetConstraints();
24585             this.dd.setXConstraint(0, 0);
24586             this.dd.setYConstraint(
24587                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24588                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24589             );
24590          }
24591         this.dragSpecs.startSize = size;
24592         this.dragSpecs.startPoint = [x, y];
24593         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24594     },
24595     
24596     /** 
24597      * @private Called after the drag operation by the DDProxy
24598      */
24599     onEndProxyDrag : function(e){
24600         Roo.get(this.proxy).setDisplayed(false);
24601         var endPoint = Roo.lib.Event.getXY(e);
24602         if(this.overlay){
24603             this.overlay.hide();
24604         }
24605         var newSize;
24606         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24607             newSize = this.dragSpecs.startSize + 
24608                 (this.placement == Roo.SplitBar.LEFT ?
24609                     endPoint[0] - this.dragSpecs.startPoint[0] :
24610                     this.dragSpecs.startPoint[0] - endPoint[0]
24611                 );
24612         }else{
24613             newSize = this.dragSpecs.startSize + 
24614                 (this.placement == Roo.SplitBar.TOP ?
24615                     endPoint[1] - this.dragSpecs.startPoint[1] :
24616                     this.dragSpecs.startPoint[1] - endPoint[1]
24617                 );
24618         }
24619         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24620         if(newSize != this.dragSpecs.startSize){
24621             if(this.fireEvent('beforeapply', this, newSize) !== false){
24622                 this.adapter.setElementSize(this, newSize);
24623                 this.fireEvent("moved", this, newSize);
24624                 this.fireEvent("resize", this, newSize);
24625             }
24626         }
24627     },
24628     
24629     /**
24630      * Get the adapter this SplitBar uses
24631      * @return The adapter object
24632      */
24633     getAdapter : function(){
24634         return this.adapter;
24635     },
24636     
24637     /**
24638      * Set the adapter this SplitBar uses
24639      * @param {Object} adapter A SplitBar adapter object
24640      */
24641     setAdapter : function(adapter){
24642         this.adapter = adapter;
24643         this.adapter.init(this);
24644     },
24645     
24646     /**
24647      * Gets the minimum size for the resizing element
24648      * @return {Number} The minimum size
24649      */
24650     getMinimumSize : function(){
24651         return this.minSize;
24652     },
24653     
24654     /**
24655      * Sets the minimum size for the resizing element
24656      * @param {Number} minSize The minimum size
24657      */
24658     setMinimumSize : function(minSize){
24659         this.minSize = minSize;
24660     },
24661     
24662     /**
24663      * Gets the maximum size for the resizing element
24664      * @return {Number} The maximum size
24665      */
24666     getMaximumSize : function(){
24667         return this.maxSize;
24668     },
24669     
24670     /**
24671      * Sets the maximum size for the resizing element
24672      * @param {Number} maxSize The maximum size
24673      */
24674     setMaximumSize : function(maxSize){
24675         this.maxSize = maxSize;
24676     },
24677     
24678     /**
24679      * Sets the initialize size for the resizing element
24680      * @param {Number} size The initial size
24681      */
24682     setCurrentSize : function(size){
24683         var oldAnimate = this.animate;
24684         this.animate = false;
24685         this.adapter.setElementSize(this, size);
24686         this.animate = oldAnimate;
24687     },
24688     
24689     /**
24690      * Destroy this splitbar. 
24691      * @param {Boolean} removeEl True to remove the element
24692      */
24693     destroy : function(removeEl){
24694         if(this.shim){
24695             this.shim.remove();
24696         }
24697         this.dd.unreg();
24698         this.proxy.parentNode.removeChild(this.proxy);
24699         if(removeEl){
24700             this.el.remove();
24701         }
24702     }
24703 });
24704
24705 /**
24706  * @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.
24707  */
24708 Roo.SplitBar.createProxy = function(dir){
24709     var proxy = new Roo.Element(document.createElement("div"));
24710     proxy.unselectable();
24711     var cls = 'x-splitbar-proxy';
24712     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24713     document.body.appendChild(proxy.dom);
24714     return proxy.dom;
24715 };
24716
24717 /** 
24718  * @class Roo.SplitBar.BasicLayoutAdapter
24719  * Default Adapter. It assumes the splitter and resizing element are not positioned
24720  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24721  */
24722 Roo.SplitBar.BasicLayoutAdapter = function(){
24723 };
24724
24725 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24726     // do nothing for now
24727     init : function(s){
24728     
24729     },
24730     /**
24731      * Called before drag operations to get the current size of the resizing element. 
24732      * @param {Roo.SplitBar} s The SplitBar using this adapter
24733      */
24734      getElementSize : function(s){
24735         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24736             return s.resizingEl.getWidth();
24737         }else{
24738             return s.resizingEl.getHeight();
24739         }
24740     },
24741     
24742     /**
24743      * Called after drag operations to set the size of the resizing element.
24744      * @param {Roo.SplitBar} s The SplitBar using this adapter
24745      * @param {Number} newSize The new size to set
24746      * @param {Function} onComplete A function to be invoked when resizing is complete
24747      */
24748     setElementSize : function(s, newSize, onComplete){
24749         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24750             if(!s.animate){
24751                 s.resizingEl.setWidth(newSize);
24752                 if(onComplete){
24753                     onComplete(s, newSize);
24754                 }
24755             }else{
24756                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24757             }
24758         }else{
24759             
24760             if(!s.animate){
24761                 s.resizingEl.setHeight(newSize);
24762                 if(onComplete){
24763                     onComplete(s, newSize);
24764                 }
24765             }else{
24766                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24767             }
24768         }
24769     }
24770 };
24771
24772 /** 
24773  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24774  * @extends Roo.SplitBar.BasicLayoutAdapter
24775  * Adapter that  moves the splitter element to align with the resized sizing element. 
24776  * Used with an absolute positioned SplitBar.
24777  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24778  * document.body, make sure you assign an id to the body element.
24779  */
24780 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24781     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24782     this.container = Roo.get(container);
24783 };
24784
24785 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24786     init : function(s){
24787         this.basic.init(s);
24788     },
24789     
24790     getElementSize : function(s){
24791         return this.basic.getElementSize(s);
24792     },
24793     
24794     setElementSize : function(s, newSize, onComplete){
24795         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24796     },
24797     
24798     moveSplitter : function(s){
24799         var yes = Roo.SplitBar;
24800         switch(s.placement){
24801             case yes.LEFT:
24802                 s.el.setX(s.resizingEl.getRight());
24803                 break;
24804             case yes.RIGHT:
24805                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24806                 break;
24807             case yes.TOP:
24808                 s.el.setY(s.resizingEl.getBottom());
24809                 break;
24810             case yes.BOTTOM:
24811                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24812                 break;
24813         }
24814     }
24815 };
24816
24817 /**
24818  * Orientation constant - Create a vertical SplitBar
24819  * @static
24820  * @type Number
24821  */
24822 Roo.SplitBar.VERTICAL = 1;
24823
24824 /**
24825  * Orientation constant - Create a horizontal SplitBar
24826  * @static
24827  * @type Number
24828  */
24829 Roo.SplitBar.HORIZONTAL = 2;
24830
24831 /**
24832  * Placement constant - The resizing element is to the left of the splitter element
24833  * @static
24834  * @type Number
24835  */
24836 Roo.SplitBar.LEFT = 1;
24837
24838 /**
24839  * Placement constant - The resizing element is to the right of the splitter element
24840  * @static
24841  * @type Number
24842  */
24843 Roo.SplitBar.RIGHT = 2;
24844
24845 /**
24846  * Placement constant - The resizing element is positioned above the splitter element
24847  * @static
24848  * @type Number
24849  */
24850 Roo.SplitBar.TOP = 3;
24851
24852 /**
24853  * Placement constant - The resizing element is positioned under splitter element
24854  * @static
24855  * @type Number
24856  */
24857 Roo.SplitBar.BOTTOM = 4;
24858 /*
24859  * Based on:
24860  * Ext JS Library 1.1.1
24861  * Copyright(c) 2006-2007, Ext JS, LLC.
24862  *
24863  * Originally Released Under LGPL - original licence link has changed is not relivant.
24864  *
24865  * Fork - LGPL
24866  * <script type="text/javascript">
24867  */
24868
24869 /**
24870  * @class Roo.View
24871  * @extends Roo.util.Observable
24872  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24873  * This class also supports single and multi selection modes. <br>
24874  * Create a data model bound view:
24875  <pre><code>
24876  var store = new Roo.data.Store(...);
24877
24878  var view = new Roo.View({
24879     el : "my-element",
24880     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24881  
24882     singleSelect: true,
24883     selectedClass: "ydataview-selected",
24884     store: store
24885  });
24886
24887  // listen for node click?
24888  view.on("click", function(vw, index, node, e){
24889  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24890  });
24891
24892  // load XML data
24893  dataModel.load("foobar.xml");
24894  </code></pre>
24895  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24896  * <br><br>
24897  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24898  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24899  * 
24900  * Note: old style constructor is still suported (container, template, config)
24901  * 
24902  * @constructor
24903  * Create a new View
24904  * @param {Object} config The config object
24905  * 
24906  */
24907 Roo.View = function(config, depreciated_tpl, depreciated_config){
24908     
24909     this.parent = false;
24910     
24911     if (typeof(depreciated_tpl) == 'undefined') {
24912         // new way.. - universal constructor.
24913         Roo.apply(this, config);
24914         this.el  = Roo.get(this.el);
24915     } else {
24916         // old format..
24917         this.el  = Roo.get(config);
24918         this.tpl = depreciated_tpl;
24919         Roo.apply(this, depreciated_config);
24920     }
24921     this.wrapEl  = this.el.wrap().wrap();
24922     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24923     
24924     
24925     if(typeof(this.tpl) == "string"){
24926         this.tpl = new Roo.Template(this.tpl);
24927     } else {
24928         // support xtype ctors..
24929         this.tpl = new Roo.factory(this.tpl, Roo);
24930     }
24931     
24932     
24933     this.tpl.compile();
24934     
24935     /** @private */
24936     this.addEvents({
24937         /**
24938          * @event beforeclick
24939          * Fires before a click is processed. Returns false to cancel the default action.
24940          * @param {Roo.View} this
24941          * @param {Number} index The index of the target node
24942          * @param {HTMLElement} node The target node
24943          * @param {Roo.EventObject} e The raw event object
24944          */
24945             "beforeclick" : true,
24946         /**
24947          * @event click
24948          * Fires when a template node is clicked.
24949          * @param {Roo.View} this
24950          * @param {Number} index The index of the target node
24951          * @param {HTMLElement} node The target node
24952          * @param {Roo.EventObject} e The raw event object
24953          */
24954             "click" : true,
24955         /**
24956          * @event dblclick
24957          * Fires when a template node is double clicked.
24958          * @param {Roo.View} this
24959          * @param {Number} index The index of the target node
24960          * @param {HTMLElement} node The target node
24961          * @param {Roo.EventObject} e The raw event object
24962          */
24963             "dblclick" : true,
24964         /**
24965          * @event contextmenu
24966          * Fires when a template node is right clicked.
24967          * @param {Roo.View} this
24968          * @param {Number} index The index of the target node
24969          * @param {HTMLElement} node The target node
24970          * @param {Roo.EventObject} e The raw event object
24971          */
24972             "contextmenu" : true,
24973         /**
24974          * @event selectionchange
24975          * Fires when the selected nodes change.
24976          * @param {Roo.View} this
24977          * @param {Array} selections Array of the selected nodes
24978          */
24979             "selectionchange" : true,
24980     
24981         /**
24982          * @event beforeselect
24983          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24984          * @param {Roo.View} this
24985          * @param {HTMLElement} node The node to be selected
24986          * @param {Array} selections Array of currently selected nodes
24987          */
24988             "beforeselect" : true,
24989         /**
24990          * @event preparedata
24991          * Fires on every row to render, to allow you to change the data.
24992          * @param {Roo.View} this
24993          * @param {Object} data to be rendered (change this)
24994          */
24995           "preparedata" : true
24996           
24997           
24998         });
24999
25000
25001
25002     this.el.on({
25003         "click": this.onClick,
25004         "dblclick": this.onDblClick,
25005         "contextmenu": this.onContextMenu,
25006         scope:this
25007     });
25008
25009     this.selections = [];
25010     this.nodes = [];
25011     this.cmp = new Roo.CompositeElementLite([]);
25012     if(this.store){
25013         this.store = Roo.factory(this.store, Roo.data);
25014         this.setStore(this.store, true);
25015     }
25016     
25017     if ( this.footer && this.footer.xtype) {
25018            
25019          var fctr = this.wrapEl.appendChild(document.createElement("div"));
25020         
25021         this.footer.dataSource = this.store
25022         this.footer.container = fctr;
25023         this.footer = Roo.factory(this.footer, Roo);
25024         fctr.insertFirst(this.el);
25025         
25026         // this is a bit insane - as the paging toolbar seems to detach the el..
25027 //        dom.parentNode.parentNode.parentNode
25028          // they get detached?
25029     }
25030     
25031     
25032     Roo.View.superclass.constructor.call(this);
25033     
25034     
25035 };
25036
25037 Roo.extend(Roo.View, Roo.util.Observable, {
25038     
25039      /**
25040      * @cfg {Roo.data.Store} store Data store to load data from.
25041      */
25042     store : false,
25043     
25044     /**
25045      * @cfg {String|Roo.Element} el The container element.
25046      */
25047     el : '',
25048     
25049     /**
25050      * @cfg {String|Roo.Template} tpl The template used by this View 
25051      */
25052     tpl : false,
25053     /**
25054      * @cfg {String} dataName the named area of the template to use as the data area
25055      *                          Works with domtemplates roo-name="name"
25056      */
25057     dataName: false,
25058     /**
25059      * @cfg {String} selectedClass The css class to add to selected nodes
25060      */
25061     selectedClass : "x-view-selected",
25062      /**
25063      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25064      */
25065     emptyText : "",
25066     
25067     /**
25068      * @cfg {String} text to display on mask (default Loading)
25069      */
25070     mask : false,
25071     /**
25072      * @cfg {Boolean} multiSelect Allow multiple selection
25073      */
25074     multiSelect : false,
25075     /**
25076      * @cfg {Boolean} singleSelect Allow single selection
25077      */
25078     singleSelect:  false,
25079     
25080     /**
25081      * @cfg {Boolean} toggleSelect - selecting 
25082      */
25083     toggleSelect : false,
25084     
25085     /**
25086      * @cfg {Boolean} tickable - selecting 
25087      */
25088     tickable : false,
25089     
25090     /**
25091      * Returns the element this view is bound to.
25092      * @return {Roo.Element}
25093      */
25094     getEl : function(){
25095         return this.wrapEl;
25096     },
25097     
25098     
25099
25100     /**
25101      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25102      */
25103     refresh : function(){
25104         //Roo.log('refresh');
25105         var t = this.tpl;
25106         
25107         // if we are using something like 'domtemplate', then
25108         // the what gets used is:
25109         // t.applySubtemplate(NAME, data, wrapping data..)
25110         // the outer template then get' applied with
25111         //     the store 'extra data'
25112         // and the body get's added to the
25113         //      roo-name="data" node?
25114         //      <span class='roo-tpl-{name}'></span> ?????
25115         
25116         
25117         
25118         this.clearSelections();
25119         this.el.update("");
25120         var html = [];
25121         var records = this.store.getRange();
25122         if(records.length < 1) {
25123             
25124             // is this valid??  = should it render a template??
25125             
25126             this.el.update(this.emptyText);
25127             return;
25128         }
25129         var el = this.el;
25130         if (this.dataName) {
25131             this.el.update(t.apply(this.store.meta)); //????
25132             el = this.el.child('.roo-tpl-' + this.dataName);
25133         }
25134         
25135         for(var i = 0, len = records.length; i < len; i++){
25136             var data = this.prepareData(records[i].data, i, records[i]);
25137             this.fireEvent("preparedata", this, data, i, records[i]);
25138             
25139             var d = Roo.apply({}, data);
25140             
25141             if(this.tickable){
25142                 Roo.apply(d, {'roo-id' : Roo.id()});
25143                 
25144                 var _this = this;
25145             
25146                 Roo.each(this.parent.item, function(item){
25147                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25148                         return;
25149                     }
25150                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25151                 });
25152             }
25153             
25154             html[html.length] = Roo.util.Format.trim(
25155                 this.dataName ?
25156                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25157                     t.apply(d)
25158             );
25159         }
25160         
25161         
25162         
25163         el.update(html.join(""));
25164         this.nodes = el.dom.childNodes;
25165         this.updateIndexes(0);
25166     },
25167     
25168
25169     /**
25170      * Function to override to reformat the data that is sent to
25171      * the template for each node.
25172      * DEPRICATED - use the preparedata event handler.
25173      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25174      * a JSON object for an UpdateManager bound view).
25175      */
25176     prepareData : function(data, index, record)
25177     {
25178         this.fireEvent("preparedata", this, data, index, record);
25179         return data;
25180     },
25181
25182     onUpdate : function(ds, record){
25183         // Roo.log('on update');   
25184         this.clearSelections();
25185         var index = this.store.indexOf(record);
25186         var n = this.nodes[index];
25187         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25188         n.parentNode.removeChild(n);
25189         this.updateIndexes(index, index);
25190     },
25191
25192     
25193     
25194 // --------- FIXME     
25195     onAdd : function(ds, records, index)
25196     {
25197         //Roo.log(['on Add', ds, records, index] );        
25198         this.clearSelections();
25199         if(this.nodes.length == 0){
25200             this.refresh();
25201             return;
25202         }
25203         var n = this.nodes[index];
25204         for(var i = 0, len = records.length; i < len; i++){
25205             var d = this.prepareData(records[i].data, i, records[i]);
25206             if(n){
25207                 this.tpl.insertBefore(n, d);
25208             }else{
25209                 
25210                 this.tpl.append(this.el, d);
25211             }
25212         }
25213         this.updateIndexes(index);
25214     },
25215
25216     onRemove : function(ds, record, index){
25217        // Roo.log('onRemove');
25218         this.clearSelections();
25219         var el = this.dataName  ?
25220             this.el.child('.roo-tpl-' + this.dataName) :
25221             this.el; 
25222         
25223         el.dom.removeChild(this.nodes[index]);
25224         this.updateIndexes(index);
25225     },
25226
25227     /**
25228      * Refresh an individual node.
25229      * @param {Number} index
25230      */
25231     refreshNode : function(index){
25232         this.onUpdate(this.store, this.store.getAt(index));
25233     },
25234
25235     updateIndexes : function(startIndex, endIndex){
25236         var ns = this.nodes;
25237         startIndex = startIndex || 0;
25238         endIndex = endIndex || ns.length - 1;
25239         for(var i = startIndex; i <= endIndex; i++){
25240             ns[i].nodeIndex = i;
25241         }
25242     },
25243
25244     /**
25245      * Changes the data store this view uses and refresh the view.
25246      * @param {Store} store
25247      */
25248     setStore : function(store, initial){
25249         if(!initial && this.store){
25250             this.store.un("datachanged", this.refresh);
25251             this.store.un("add", this.onAdd);
25252             this.store.un("remove", this.onRemove);
25253             this.store.un("update", this.onUpdate);
25254             this.store.un("clear", this.refresh);
25255             this.store.un("beforeload", this.onBeforeLoad);
25256             this.store.un("load", this.onLoad);
25257             this.store.un("loadexception", this.onLoad);
25258         }
25259         if(store){
25260           
25261             store.on("datachanged", this.refresh, this);
25262             store.on("add", this.onAdd, this);
25263             store.on("remove", this.onRemove, this);
25264             store.on("update", this.onUpdate, this);
25265             store.on("clear", this.refresh, this);
25266             store.on("beforeload", this.onBeforeLoad, this);
25267             store.on("load", this.onLoad, this);
25268             store.on("loadexception", this.onLoad, this);
25269         }
25270         
25271         if(store){
25272             this.refresh();
25273         }
25274     },
25275     /**
25276      * onbeforeLoad - masks the loading area.
25277      *
25278      */
25279     onBeforeLoad : function(store,opts)
25280     {
25281          //Roo.log('onBeforeLoad');   
25282         if (!opts.add) {
25283             this.el.update("");
25284         }
25285         this.el.mask(this.mask ? this.mask : "Loading" ); 
25286     },
25287     onLoad : function ()
25288     {
25289         this.el.unmask();
25290     },
25291     
25292
25293     /**
25294      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25295      * @param {HTMLElement} node
25296      * @return {HTMLElement} The template node
25297      */
25298     findItemFromChild : function(node){
25299         var el = this.dataName  ?
25300             this.el.child('.roo-tpl-' + this.dataName,true) :
25301             this.el.dom; 
25302         
25303         if(!node || node.parentNode == el){
25304                     return node;
25305             }
25306             var p = node.parentNode;
25307             while(p && p != el){
25308             if(p.parentNode == el){
25309                 return p;
25310             }
25311             p = p.parentNode;
25312         }
25313             return null;
25314     },
25315
25316     /** @ignore */
25317     onClick : function(e){
25318         var item = this.findItemFromChild(e.getTarget());
25319         if(item){
25320             var index = this.indexOf(item);
25321             if(this.onItemClick(item, index, e) !== false){
25322                 this.fireEvent("click", this, index, item, e);
25323             }
25324         }else{
25325             this.clearSelections();
25326         }
25327     },
25328
25329     /** @ignore */
25330     onContextMenu : function(e){
25331         var item = this.findItemFromChild(e.getTarget());
25332         if(item){
25333             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25334         }
25335     },
25336
25337     /** @ignore */
25338     onDblClick : function(e){
25339         var item = this.findItemFromChild(e.getTarget());
25340         if(item){
25341             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25342         }
25343     },
25344
25345     onItemClick : function(item, index, e)
25346     {
25347         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25348             return false;
25349         }
25350         if (this.toggleSelect) {
25351             var m = this.isSelected(item) ? 'unselect' : 'select';
25352             //Roo.log(m);
25353             var _t = this;
25354             _t[m](item, true, false);
25355             return true;
25356         }
25357         if(this.multiSelect || this.singleSelect){
25358             if(this.multiSelect && e.shiftKey && this.lastSelection){
25359                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25360             }else{
25361                 this.select(item, this.multiSelect && e.ctrlKey);
25362                 this.lastSelection = item;
25363             }
25364             
25365             if(!this.tickable){
25366                 e.preventDefault();
25367             }
25368             
25369         }
25370         return true;
25371     },
25372
25373     /**
25374      * Get the number of selected nodes.
25375      * @return {Number}
25376      */
25377     getSelectionCount : function(){
25378         return this.selections.length;
25379     },
25380
25381     /**
25382      * Get the currently selected nodes.
25383      * @return {Array} An array of HTMLElements
25384      */
25385     getSelectedNodes : function(){
25386         return this.selections;
25387     },
25388
25389     /**
25390      * Get the indexes of the selected nodes.
25391      * @return {Array}
25392      */
25393     getSelectedIndexes : function(){
25394         var indexes = [], s = this.selections;
25395         for(var i = 0, len = s.length; i < len; i++){
25396             indexes.push(s[i].nodeIndex);
25397         }
25398         return indexes;
25399     },
25400
25401     /**
25402      * Clear all selections
25403      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25404      */
25405     clearSelections : function(suppressEvent){
25406         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25407             this.cmp.elements = this.selections;
25408             this.cmp.removeClass(this.selectedClass);
25409             this.selections = [];
25410             if(!suppressEvent){
25411                 this.fireEvent("selectionchange", this, this.selections);
25412             }
25413         }
25414     },
25415
25416     /**
25417      * Returns true if the passed node is selected
25418      * @param {HTMLElement/Number} node The node or node index
25419      * @return {Boolean}
25420      */
25421     isSelected : function(node){
25422         var s = this.selections;
25423         if(s.length < 1){
25424             return false;
25425         }
25426         node = this.getNode(node);
25427         return s.indexOf(node) !== -1;
25428     },
25429
25430     /**
25431      * Selects nodes.
25432      * @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
25433      * @param {Boolean} keepExisting (optional) true to keep existing selections
25434      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25435      */
25436     select : function(nodeInfo, keepExisting, suppressEvent){
25437         if(nodeInfo instanceof Array){
25438             if(!keepExisting){
25439                 this.clearSelections(true);
25440             }
25441             for(var i = 0, len = nodeInfo.length; i < len; i++){
25442                 this.select(nodeInfo[i], true, true);
25443             }
25444             return;
25445         } 
25446         var node = this.getNode(nodeInfo);
25447         if(!node || this.isSelected(node)){
25448             return; // already selected.
25449         }
25450         if(!keepExisting){
25451             this.clearSelections(true);
25452         }
25453         
25454         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25455             Roo.fly(node).addClass(this.selectedClass);
25456             this.selections.push(node);
25457             if(!suppressEvent){
25458                 this.fireEvent("selectionchange", this, this.selections);
25459             }
25460         }
25461         
25462         
25463     },
25464       /**
25465      * Unselects nodes.
25466      * @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
25467      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25468      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25469      */
25470     unselect : function(nodeInfo, keepExisting, suppressEvent)
25471     {
25472         if(nodeInfo instanceof Array){
25473             Roo.each(this.selections, function(s) {
25474                 this.unselect(s, nodeInfo);
25475             }, this);
25476             return;
25477         }
25478         var node = this.getNode(nodeInfo);
25479         if(!node || !this.isSelected(node)){
25480             //Roo.log("not selected");
25481             return; // not selected.
25482         }
25483         // fireevent???
25484         var ns = [];
25485         Roo.each(this.selections, function(s) {
25486             if (s == node ) {
25487                 Roo.fly(node).removeClass(this.selectedClass);
25488
25489                 return;
25490             }
25491             ns.push(s);
25492         },this);
25493         
25494         this.selections= ns;
25495         this.fireEvent("selectionchange", this, this.selections);
25496     },
25497
25498     /**
25499      * Gets a template node.
25500      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25501      * @return {HTMLElement} The node or null if it wasn't found
25502      */
25503     getNode : function(nodeInfo){
25504         if(typeof nodeInfo == "string"){
25505             return document.getElementById(nodeInfo);
25506         }else if(typeof nodeInfo == "number"){
25507             return this.nodes[nodeInfo];
25508         }
25509         return nodeInfo;
25510     },
25511
25512     /**
25513      * Gets a range template nodes.
25514      * @param {Number} startIndex
25515      * @param {Number} endIndex
25516      * @return {Array} An array of nodes
25517      */
25518     getNodes : function(start, end){
25519         var ns = this.nodes;
25520         start = start || 0;
25521         end = typeof end == "undefined" ? ns.length - 1 : end;
25522         var nodes = [];
25523         if(start <= end){
25524             for(var i = start; i <= end; i++){
25525                 nodes.push(ns[i]);
25526             }
25527         } else{
25528             for(var i = start; i >= end; i--){
25529                 nodes.push(ns[i]);
25530             }
25531         }
25532         return nodes;
25533     },
25534
25535     /**
25536      * Finds the index of the passed node
25537      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25538      * @return {Number} The index of the node or -1
25539      */
25540     indexOf : function(node){
25541         node = this.getNode(node);
25542         if(typeof node.nodeIndex == "number"){
25543             return node.nodeIndex;
25544         }
25545         var ns = this.nodes;
25546         for(var i = 0, len = ns.length; i < len; i++){
25547             if(ns[i] == node){
25548                 return i;
25549             }
25550         }
25551         return -1;
25552     }
25553 });
25554 /*
25555  * Based on:
25556  * Ext JS Library 1.1.1
25557  * Copyright(c) 2006-2007, Ext JS, LLC.
25558  *
25559  * Originally Released Under LGPL - original licence link has changed is not relivant.
25560  *
25561  * Fork - LGPL
25562  * <script type="text/javascript">
25563  */
25564
25565 /**
25566  * @class Roo.JsonView
25567  * @extends Roo.View
25568  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25569 <pre><code>
25570 var view = new Roo.JsonView({
25571     container: "my-element",
25572     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25573     multiSelect: true, 
25574     jsonRoot: "data" 
25575 });
25576
25577 // listen for node click?
25578 view.on("click", function(vw, index, node, e){
25579     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25580 });
25581
25582 // direct load of JSON data
25583 view.load("foobar.php");
25584
25585 // Example from my blog list
25586 var tpl = new Roo.Template(
25587     '&lt;div class="entry"&gt;' +
25588     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25589     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25590     "&lt;/div&gt;&lt;hr /&gt;"
25591 );
25592
25593 var moreView = new Roo.JsonView({
25594     container :  "entry-list", 
25595     template : tpl,
25596     jsonRoot: "posts"
25597 });
25598 moreView.on("beforerender", this.sortEntries, this);
25599 moreView.load({
25600     url: "/blog/get-posts.php",
25601     params: "allposts=true",
25602     text: "Loading Blog Entries..."
25603 });
25604 </code></pre>
25605
25606 * Note: old code is supported with arguments : (container, template, config)
25607
25608
25609  * @constructor
25610  * Create a new JsonView
25611  * 
25612  * @param {Object} config The config object
25613  * 
25614  */
25615 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25616     
25617     
25618     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25619
25620     var um = this.el.getUpdateManager();
25621     um.setRenderer(this);
25622     um.on("update", this.onLoad, this);
25623     um.on("failure", this.onLoadException, this);
25624
25625     /**
25626      * @event beforerender
25627      * Fires before rendering of the downloaded JSON data.
25628      * @param {Roo.JsonView} this
25629      * @param {Object} data The JSON data loaded
25630      */
25631     /**
25632      * @event load
25633      * Fires when data is loaded.
25634      * @param {Roo.JsonView} this
25635      * @param {Object} data The JSON data loaded
25636      * @param {Object} response The raw Connect response object
25637      */
25638     /**
25639      * @event loadexception
25640      * Fires when loading fails.
25641      * @param {Roo.JsonView} this
25642      * @param {Object} response The raw Connect response object
25643      */
25644     this.addEvents({
25645         'beforerender' : true,
25646         'load' : true,
25647         'loadexception' : true
25648     });
25649 };
25650 Roo.extend(Roo.JsonView, Roo.View, {
25651     /**
25652      * @type {String} The root property in the loaded JSON object that contains the data
25653      */
25654     jsonRoot : "",
25655
25656     /**
25657      * Refreshes the view.
25658      */
25659     refresh : function(){
25660         this.clearSelections();
25661         this.el.update("");
25662         var html = [];
25663         var o = this.jsonData;
25664         if(o && o.length > 0){
25665             for(var i = 0, len = o.length; i < len; i++){
25666                 var data = this.prepareData(o[i], i, o);
25667                 html[html.length] = this.tpl.apply(data);
25668             }
25669         }else{
25670             html.push(this.emptyText);
25671         }
25672         this.el.update(html.join(""));
25673         this.nodes = this.el.dom.childNodes;
25674         this.updateIndexes(0);
25675     },
25676
25677     /**
25678      * 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.
25679      * @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:
25680      <pre><code>
25681      view.load({
25682          url: "your-url.php",
25683          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25684          callback: yourFunction,
25685          scope: yourObject, //(optional scope)
25686          discardUrl: false,
25687          nocache: false,
25688          text: "Loading...",
25689          timeout: 30,
25690          scripts: false
25691      });
25692      </code></pre>
25693      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25694      * 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.
25695      * @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}
25696      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25697      * @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.
25698      */
25699     load : function(){
25700         var um = this.el.getUpdateManager();
25701         um.update.apply(um, arguments);
25702     },
25703
25704     render : function(el, response){
25705         this.clearSelections();
25706         this.el.update("");
25707         var o;
25708         try{
25709             o = Roo.util.JSON.decode(response.responseText);
25710             if(this.jsonRoot){
25711                 
25712                 o = o[this.jsonRoot];
25713             }
25714         } catch(e){
25715         }
25716         /**
25717          * The current JSON data or null
25718          */
25719         this.jsonData = o;
25720         this.beforeRender();
25721         this.refresh();
25722     },
25723
25724 /**
25725  * Get the number of records in the current JSON dataset
25726  * @return {Number}
25727  */
25728     getCount : function(){
25729         return this.jsonData ? this.jsonData.length : 0;
25730     },
25731
25732 /**
25733  * Returns the JSON object for the specified node(s)
25734  * @param {HTMLElement/Array} node The node or an array of nodes
25735  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25736  * you get the JSON object for the node
25737  */
25738     getNodeData : function(node){
25739         if(node instanceof Array){
25740             var data = [];
25741             for(var i = 0, len = node.length; i < len; i++){
25742                 data.push(this.getNodeData(node[i]));
25743             }
25744             return data;
25745         }
25746         return this.jsonData[this.indexOf(node)] || null;
25747     },
25748
25749     beforeRender : function(){
25750         this.snapshot = this.jsonData;
25751         if(this.sortInfo){
25752             this.sort.apply(this, this.sortInfo);
25753         }
25754         this.fireEvent("beforerender", this, this.jsonData);
25755     },
25756
25757     onLoad : function(el, o){
25758         this.fireEvent("load", this, this.jsonData, o);
25759     },
25760
25761     onLoadException : function(el, o){
25762         this.fireEvent("loadexception", this, o);
25763     },
25764
25765 /**
25766  * Filter the data by a specific property.
25767  * @param {String} property A property on your JSON objects
25768  * @param {String/RegExp} value Either string that the property values
25769  * should start with, or a RegExp to test against the property
25770  */
25771     filter : function(property, value){
25772         if(this.jsonData){
25773             var data = [];
25774             var ss = this.snapshot;
25775             if(typeof value == "string"){
25776                 var vlen = value.length;
25777                 if(vlen == 0){
25778                     this.clearFilter();
25779                     return;
25780                 }
25781                 value = value.toLowerCase();
25782                 for(var i = 0, len = ss.length; i < len; i++){
25783                     var o = ss[i];
25784                     if(o[property].substr(0, vlen).toLowerCase() == value){
25785                         data.push(o);
25786                     }
25787                 }
25788             } else if(value.exec){ // regex?
25789                 for(var i = 0, len = ss.length; i < len; i++){
25790                     var o = ss[i];
25791                     if(value.test(o[property])){
25792                         data.push(o);
25793                     }
25794                 }
25795             } else{
25796                 return;
25797             }
25798             this.jsonData = data;
25799             this.refresh();
25800         }
25801     },
25802
25803 /**
25804  * Filter by a function. The passed function will be called with each
25805  * object in the current dataset. If the function returns true the value is kept,
25806  * otherwise it is filtered.
25807  * @param {Function} fn
25808  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25809  */
25810     filterBy : function(fn, scope){
25811         if(this.jsonData){
25812             var data = [];
25813             var ss = this.snapshot;
25814             for(var i = 0, len = ss.length; i < len; i++){
25815                 var o = ss[i];
25816                 if(fn.call(scope || this, o)){
25817                     data.push(o);
25818                 }
25819             }
25820             this.jsonData = data;
25821             this.refresh();
25822         }
25823     },
25824
25825 /**
25826  * Clears the current filter.
25827  */
25828     clearFilter : function(){
25829         if(this.snapshot && this.jsonData != this.snapshot){
25830             this.jsonData = this.snapshot;
25831             this.refresh();
25832         }
25833     },
25834
25835
25836 /**
25837  * Sorts the data for this view and refreshes it.
25838  * @param {String} property A property on your JSON objects to sort on
25839  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25840  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25841  */
25842     sort : function(property, dir, sortType){
25843         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25844         if(this.jsonData){
25845             var p = property;
25846             var dsc = dir && dir.toLowerCase() == "desc";
25847             var f = function(o1, o2){
25848                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25849                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25850                 ;
25851                 if(v1 < v2){
25852                     return dsc ? +1 : -1;
25853                 } else if(v1 > v2){
25854                     return dsc ? -1 : +1;
25855                 } else{
25856                     return 0;
25857                 }
25858             };
25859             this.jsonData.sort(f);
25860             this.refresh();
25861             if(this.jsonData != this.snapshot){
25862                 this.snapshot.sort(f);
25863             }
25864         }
25865     }
25866 });/*
25867  * Based on:
25868  * Ext JS Library 1.1.1
25869  * Copyright(c) 2006-2007, Ext JS, LLC.
25870  *
25871  * Originally Released Under LGPL - original licence link has changed is not relivant.
25872  *
25873  * Fork - LGPL
25874  * <script type="text/javascript">
25875  */
25876  
25877
25878 /**
25879  * @class Roo.ColorPalette
25880  * @extends Roo.Component
25881  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25882  * Here's an example of typical usage:
25883  * <pre><code>
25884 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25885 cp.render('my-div');
25886
25887 cp.on('select', function(palette, selColor){
25888     // do something with selColor
25889 });
25890 </code></pre>
25891  * @constructor
25892  * Create a new ColorPalette
25893  * @param {Object} config The config object
25894  */
25895 Roo.ColorPalette = function(config){
25896     Roo.ColorPalette.superclass.constructor.call(this, config);
25897     this.addEvents({
25898         /**
25899              * @event select
25900              * Fires when a color is selected
25901              * @param {ColorPalette} this
25902              * @param {String} color The 6-digit color hex code (without the # symbol)
25903              */
25904         select: true
25905     });
25906
25907     if(this.handler){
25908         this.on("select", this.handler, this.scope, true);
25909     }
25910 };
25911 Roo.extend(Roo.ColorPalette, Roo.Component, {
25912     /**
25913      * @cfg {String} itemCls
25914      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25915      */
25916     itemCls : "x-color-palette",
25917     /**
25918      * @cfg {String} value
25919      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25920      * the hex codes are case-sensitive.
25921      */
25922     value : null,
25923     clickEvent:'click',
25924     // private
25925     ctype: "Roo.ColorPalette",
25926
25927     /**
25928      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25929      */
25930     allowReselect : false,
25931
25932     /**
25933      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25934      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25935      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25936      * of colors with the width setting until the box is symmetrical.</p>
25937      * <p>You can override individual colors if needed:</p>
25938      * <pre><code>
25939 var cp = new Roo.ColorPalette();
25940 cp.colors[0] = "FF0000";  // change the first box to red
25941 </code></pre>
25942
25943 Or you can provide a custom array of your own for complete control:
25944 <pre><code>
25945 var cp = new Roo.ColorPalette();
25946 cp.colors = ["000000", "993300", "333300"];
25947 </code></pre>
25948      * @type Array
25949      */
25950     colors : [
25951         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25952         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25953         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25954         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25955         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25956     ],
25957
25958     // private
25959     onRender : function(container, position){
25960         var t = new Roo.MasterTemplate(
25961             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25962         );
25963         var c = this.colors;
25964         for(var i = 0, len = c.length; i < len; i++){
25965             t.add([c[i]]);
25966         }
25967         var el = document.createElement("div");
25968         el.className = this.itemCls;
25969         t.overwrite(el);
25970         container.dom.insertBefore(el, position);
25971         this.el = Roo.get(el);
25972         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25973         if(this.clickEvent != 'click'){
25974             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25975         }
25976     },
25977
25978     // private
25979     afterRender : function(){
25980         Roo.ColorPalette.superclass.afterRender.call(this);
25981         if(this.value){
25982             var s = this.value;
25983             this.value = null;
25984             this.select(s);
25985         }
25986     },
25987
25988     // private
25989     handleClick : function(e, t){
25990         e.preventDefault();
25991         if(!this.disabled){
25992             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25993             this.select(c.toUpperCase());
25994         }
25995     },
25996
25997     /**
25998      * Selects the specified color in the palette (fires the select event)
25999      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
26000      */
26001     select : function(color){
26002         color = color.replace("#", "");
26003         if(color != this.value || this.allowReselect){
26004             var el = this.el;
26005             if(this.value){
26006                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
26007             }
26008             el.child("a.color-"+color).addClass("x-color-palette-sel");
26009             this.value = color;
26010             this.fireEvent("select", this, color);
26011         }
26012     }
26013 });/*
26014  * Based on:
26015  * Ext JS Library 1.1.1
26016  * Copyright(c) 2006-2007, Ext JS, LLC.
26017  *
26018  * Originally Released Under LGPL - original licence link has changed is not relivant.
26019  *
26020  * Fork - LGPL
26021  * <script type="text/javascript">
26022  */
26023  
26024 /**
26025  * @class Roo.DatePicker
26026  * @extends Roo.Component
26027  * Simple date picker class.
26028  * @constructor
26029  * Create a new DatePicker
26030  * @param {Object} config The config object
26031  */
26032 Roo.DatePicker = function(config){
26033     Roo.DatePicker.superclass.constructor.call(this, config);
26034
26035     this.value = config && config.value ?
26036                  config.value.clearTime() : new Date().clearTime();
26037
26038     this.addEvents({
26039         /**
26040              * @event select
26041              * Fires when a date is selected
26042              * @param {DatePicker} this
26043              * @param {Date} date The selected date
26044              */
26045         'select': true,
26046         /**
26047              * @event monthchange
26048              * Fires when the displayed month changes 
26049              * @param {DatePicker} this
26050              * @param {Date} date The selected month
26051              */
26052         'monthchange': true
26053     });
26054
26055     if(this.handler){
26056         this.on("select", this.handler,  this.scope || this);
26057     }
26058     // build the disabledDatesRE
26059     if(!this.disabledDatesRE && this.disabledDates){
26060         var dd = this.disabledDates;
26061         var re = "(?:";
26062         for(var i = 0; i < dd.length; i++){
26063             re += dd[i];
26064             if(i != dd.length-1) re += "|";
26065         }
26066         this.disabledDatesRE = new RegExp(re + ")");
26067     }
26068 };
26069
26070 Roo.extend(Roo.DatePicker, Roo.Component, {
26071     /**
26072      * @cfg {String} todayText
26073      * The text to display on the button that selects the current date (defaults to "Today")
26074      */
26075     todayText : "Today",
26076     /**
26077      * @cfg {String} okText
26078      * The text to display on the ok button
26079      */
26080     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26081     /**
26082      * @cfg {String} cancelText
26083      * The text to display on the cancel button
26084      */
26085     cancelText : "Cancel",
26086     /**
26087      * @cfg {String} todayTip
26088      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26089      */
26090     todayTip : "{0} (Spacebar)",
26091     /**
26092      * @cfg {Date} minDate
26093      * Minimum allowable date (JavaScript date object, defaults to null)
26094      */
26095     minDate : null,
26096     /**
26097      * @cfg {Date} maxDate
26098      * Maximum allowable date (JavaScript date object, defaults to null)
26099      */
26100     maxDate : null,
26101     /**
26102      * @cfg {String} minText
26103      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26104      */
26105     minText : "This date is before the minimum date",
26106     /**
26107      * @cfg {String} maxText
26108      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26109      */
26110     maxText : "This date is after the maximum date",
26111     /**
26112      * @cfg {String} format
26113      * The default date format string which can be overriden for localization support.  The format must be
26114      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26115      */
26116     format : "m/d/y",
26117     /**
26118      * @cfg {Array} disabledDays
26119      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26120      */
26121     disabledDays : null,
26122     /**
26123      * @cfg {String} disabledDaysText
26124      * The tooltip to display when the date falls on a disabled day (defaults to "")
26125      */
26126     disabledDaysText : "",
26127     /**
26128      * @cfg {RegExp} disabledDatesRE
26129      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26130      */
26131     disabledDatesRE : null,
26132     /**
26133      * @cfg {String} disabledDatesText
26134      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26135      */
26136     disabledDatesText : "",
26137     /**
26138      * @cfg {Boolean} constrainToViewport
26139      * True to constrain the date picker to the viewport (defaults to true)
26140      */
26141     constrainToViewport : true,
26142     /**
26143      * @cfg {Array} monthNames
26144      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26145      */
26146     monthNames : Date.monthNames,
26147     /**
26148      * @cfg {Array} dayNames
26149      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26150      */
26151     dayNames : Date.dayNames,
26152     /**
26153      * @cfg {String} nextText
26154      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26155      */
26156     nextText: 'Next Month (Control+Right)',
26157     /**
26158      * @cfg {String} prevText
26159      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26160      */
26161     prevText: 'Previous Month (Control+Left)',
26162     /**
26163      * @cfg {String} monthYearText
26164      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26165      */
26166     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26167     /**
26168      * @cfg {Number} startDay
26169      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26170      */
26171     startDay : 0,
26172     /**
26173      * @cfg {Bool} showClear
26174      * Show a clear button (usefull for date form elements that can be blank.)
26175      */
26176     
26177     showClear: false,
26178     
26179     /**
26180      * Sets the value of the date field
26181      * @param {Date} value The date to set
26182      */
26183     setValue : function(value){
26184         var old = this.value;
26185         
26186         if (typeof(value) == 'string') {
26187          
26188             value = Date.parseDate(value, this.format);
26189         }
26190         if (!value) {
26191             value = new Date();
26192         }
26193         
26194         this.value = value.clearTime(true);
26195         if(this.el){
26196             this.update(this.value);
26197         }
26198     },
26199
26200     /**
26201      * Gets the current selected value of the date field
26202      * @return {Date} The selected date
26203      */
26204     getValue : function(){
26205         return this.value;
26206     },
26207
26208     // private
26209     focus : function(){
26210         if(this.el){
26211             this.update(this.activeDate);
26212         }
26213     },
26214
26215     // privateval
26216     onRender : function(container, position){
26217         
26218         var m = [
26219              '<table cellspacing="0">',
26220                 '<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>',
26221                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26222         var dn = this.dayNames;
26223         for(var i = 0; i < 7; i++){
26224             var d = this.startDay+i;
26225             if(d > 6){
26226                 d = d-7;
26227             }
26228             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26229         }
26230         m[m.length] = "</tr></thead><tbody><tr>";
26231         for(var i = 0; i < 42; i++) {
26232             if(i % 7 == 0 && i != 0){
26233                 m[m.length] = "</tr><tr>";
26234             }
26235             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26236         }
26237         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26238             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26239
26240         var el = document.createElement("div");
26241         el.className = "x-date-picker";
26242         el.innerHTML = m.join("");
26243
26244         container.dom.insertBefore(el, position);
26245
26246         this.el = Roo.get(el);
26247         this.eventEl = Roo.get(el.firstChild);
26248
26249         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26250             handler: this.showPrevMonth,
26251             scope: this,
26252             preventDefault:true,
26253             stopDefault:true
26254         });
26255
26256         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26257             handler: this.showNextMonth,
26258             scope: this,
26259             preventDefault:true,
26260             stopDefault:true
26261         });
26262
26263         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26264
26265         this.monthPicker = this.el.down('div.x-date-mp');
26266         this.monthPicker.enableDisplayMode('block');
26267         
26268         var kn = new Roo.KeyNav(this.eventEl, {
26269             "left" : function(e){
26270                 e.ctrlKey ?
26271                     this.showPrevMonth() :
26272                     this.update(this.activeDate.add("d", -1));
26273             },
26274
26275             "right" : function(e){
26276                 e.ctrlKey ?
26277                     this.showNextMonth() :
26278                     this.update(this.activeDate.add("d", 1));
26279             },
26280
26281             "up" : function(e){
26282                 e.ctrlKey ?
26283                     this.showNextYear() :
26284                     this.update(this.activeDate.add("d", -7));
26285             },
26286
26287             "down" : function(e){
26288                 e.ctrlKey ?
26289                     this.showPrevYear() :
26290                     this.update(this.activeDate.add("d", 7));
26291             },
26292
26293             "pageUp" : function(e){
26294                 this.showNextMonth();
26295             },
26296
26297             "pageDown" : function(e){
26298                 this.showPrevMonth();
26299             },
26300
26301             "enter" : function(e){
26302                 e.stopPropagation();
26303                 return true;
26304             },
26305
26306             scope : this
26307         });
26308
26309         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26310
26311         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26312
26313         this.el.unselectable();
26314         
26315         this.cells = this.el.select("table.x-date-inner tbody td");
26316         this.textNodes = this.el.query("table.x-date-inner tbody span");
26317
26318         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26319             text: "&#160;",
26320             tooltip: this.monthYearText
26321         });
26322
26323         this.mbtn.on('click', this.showMonthPicker, this);
26324         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26325
26326
26327         var today = (new Date()).dateFormat(this.format);
26328         
26329         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26330         if (this.showClear) {
26331             baseTb.add( new Roo.Toolbar.Fill());
26332         }
26333         baseTb.add({
26334             text: String.format(this.todayText, today),
26335             tooltip: String.format(this.todayTip, today),
26336             handler: this.selectToday,
26337             scope: this
26338         });
26339         
26340         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26341             
26342         //});
26343         if (this.showClear) {
26344             
26345             baseTb.add( new Roo.Toolbar.Fill());
26346             baseTb.add({
26347                 text: '&#160;',
26348                 cls: 'x-btn-icon x-btn-clear',
26349                 handler: function() {
26350                     //this.value = '';
26351                     this.fireEvent("select", this, '');
26352                 },
26353                 scope: this
26354             });
26355         }
26356         
26357         
26358         if(Roo.isIE){
26359             this.el.repaint();
26360         }
26361         this.update(this.value);
26362     },
26363
26364     createMonthPicker : function(){
26365         if(!this.monthPicker.dom.firstChild){
26366             var buf = ['<table border="0" cellspacing="0">'];
26367             for(var i = 0; i < 6; i++){
26368                 buf.push(
26369                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26370                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26371                     i == 0 ?
26372                     '<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>' :
26373                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26374                 );
26375             }
26376             buf.push(
26377                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26378                     this.okText,
26379                     '</button><button type="button" class="x-date-mp-cancel">',
26380                     this.cancelText,
26381                     '</button></td></tr>',
26382                 '</table>'
26383             );
26384             this.monthPicker.update(buf.join(''));
26385             this.monthPicker.on('click', this.onMonthClick, this);
26386             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26387
26388             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26389             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26390
26391             this.mpMonths.each(function(m, a, i){
26392                 i += 1;
26393                 if((i%2) == 0){
26394                     m.dom.xmonth = 5 + Math.round(i * .5);
26395                 }else{
26396                     m.dom.xmonth = Math.round((i-1) * .5);
26397                 }
26398             });
26399         }
26400     },
26401
26402     showMonthPicker : function(){
26403         this.createMonthPicker();
26404         var size = this.el.getSize();
26405         this.monthPicker.setSize(size);
26406         this.monthPicker.child('table').setSize(size);
26407
26408         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26409         this.updateMPMonth(this.mpSelMonth);
26410         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26411         this.updateMPYear(this.mpSelYear);
26412
26413         this.monthPicker.slideIn('t', {duration:.2});
26414     },
26415
26416     updateMPYear : function(y){
26417         this.mpyear = y;
26418         var ys = this.mpYears.elements;
26419         for(var i = 1; i <= 10; i++){
26420             var td = ys[i-1], y2;
26421             if((i%2) == 0){
26422                 y2 = y + Math.round(i * .5);
26423                 td.firstChild.innerHTML = y2;
26424                 td.xyear = y2;
26425             }else{
26426                 y2 = y - (5-Math.round(i * .5));
26427                 td.firstChild.innerHTML = y2;
26428                 td.xyear = y2;
26429             }
26430             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26431         }
26432     },
26433
26434     updateMPMonth : function(sm){
26435         this.mpMonths.each(function(m, a, i){
26436             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26437         });
26438     },
26439
26440     selectMPMonth: function(m){
26441         
26442     },
26443
26444     onMonthClick : function(e, t){
26445         e.stopEvent();
26446         var el = new Roo.Element(t), pn;
26447         if(el.is('button.x-date-mp-cancel')){
26448             this.hideMonthPicker();
26449         }
26450         else if(el.is('button.x-date-mp-ok')){
26451             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26452             this.hideMonthPicker();
26453         }
26454         else if(pn = el.up('td.x-date-mp-month', 2)){
26455             this.mpMonths.removeClass('x-date-mp-sel');
26456             pn.addClass('x-date-mp-sel');
26457             this.mpSelMonth = pn.dom.xmonth;
26458         }
26459         else if(pn = el.up('td.x-date-mp-year', 2)){
26460             this.mpYears.removeClass('x-date-mp-sel');
26461             pn.addClass('x-date-mp-sel');
26462             this.mpSelYear = pn.dom.xyear;
26463         }
26464         else if(el.is('a.x-date-mp-prev')){
26465             this.updateMPYear(this.mpyear-10);
26466         }
26467         else if(el.is('a.x-date-mp-next')){
26468             this.updateMPYear(this.mpyear+10);
26469         }
26470     },
26471
26472     onMonthDblClick : function(e, t){
26473         e.stopEvent();
26474         var el = new Roo.Element(t), pn;
26475         if(pn = el.up('td.x-date-mp-month', 2)){
26476             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26477             this.hideMonthPicker();
26478         }
26479         else if(pn = el.up('td.x-date-mp-year', 2)){
26480             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26481             this.hideMonthPicker();
26482         }
26483     },
26484
26485     hideMonthPicker : function(disableAnim){
26486         if(this.monthPicker){
26487             if(disableAnim === true){
26488                 this.monthPicker.hide();
26489             }else{
26490                 this.monthPicker.slideOut('t', {duration:.2});
26491             }
26492         }
26493     },
26494
26495     // private
26496     showPrevMonth : function(e){
26497         this.update(this.activeDate.add("mo", -1));
26498     },
26499
26500     // private
26501     showNextMonth : function(e){
26502         this.update(this.activeDate.add("mo", 1));
26503     },
26504
26505     // private
26506     showPrevYear : function(){
26507         this.update(this.activeDate.add("y", -1));
26508     },
26509
26510     // private
26511     showNextYear : function(){
26512         this.update(this.activeDate.add("y", 1));
26513     },
26514
26515     // private
26516     handleMouseWheel : function(e){
26517         var delta = e.getWheelDelta();
26518         if(delta > 0){
26519             this.showPrevMonth();
26520             e.stopEvent();
26521         } else if(delta < 0){
26522             this.showNextMonth();
26523             e.stopEvent();
26524         }
26525     },
26526
26527     // private
26528     handleDateClick : function(e, t){
26529         e.stopEvent();
26530         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26531             this.setValue(new Date(t.dateValue));
26532             this.fireEvent("select", this, this.value);
26533         }
26534     },
26535
26536     // private
26537     selectToday : function(){
26538         this.setValue(new Date().clearTime());
26539         this.fireEvent("select", this, this.value);
26540     },
26541
26542     // private
26543     update : function(date)
26544     {
26545         var vd = this.activeDate;
26546         this.activeDate = date;
26547         if(vd && this.el){
26548             var t = date.getTime();
26549             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26550                 this.cells.removeClass("x-date-selected");
26551                 this.cells.each(function(c){
26552                    if(c.dom.firstChild.dateValue == t){
26553                        c.addClass("x-date-selected");
26554                        setTimeout(function(){
26555                             try{c.dom.firstChild.focus();}catch(e){}
26556                        }, 50);
26557                        return false;
26558                    }
26559                 });
26560                 return;
26561             }
26562         }
26563         
26564         var days = date.getDaysInMonth();
26565         var firstOfMonth = date.getFirstDateOfMonth();
26566         var startingPos = firstOfMonth.getDay()-this.startDay;
26567
26568         if(startingPos <= this.startDay){
26569             startingPos += 7;
26570         }
26571
26572         var pm = date.add("mo", -1);
26573         var prevStart = pm.getDaysInMonth()-startingPos;
26574
26575         var cells = this.cells.elements;
26576         var textEls = this.textNodes;
26577         days += startingPos;
26578
26579         // convert everything to numbers so it's fast
26580         var day = 86400000;
26581         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26582         var today = new Date().clearTime().getTime();
26583         var sel = date.clearTime().getTime();
26584         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26585         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26586         var ddMatch = this.disabledDatesRE;
26587         var ddText = this.disabledDatesText;
26588         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26589         var ddaysText = this.disabledDaysText;
26590         var format = this.format;
26591
26592         var setCellClass = function(cal, cell){
26593             cell.title = "";
26594             var t = d.getTime();
26595             cell.firstChild.dateValue = t;
26596             if(t == today){
26597                 cell.className += " x-date-today";
26598                 cell.title = cal.todayText;
26599             }
26600             if(t == sel){
26601                 cell.className += " x-date-selected";
26602                 setTimeout(function(){
26603                     try{cell.firstChild.focus();}catch(e){}
26604                 }, 50);
26605             }
26606             // disabling
26607             if(t < min) {
26608                 cell.className = " x-date-disabled";
26609                 cell.title = cal.minText;
26610                 return;
26611             }
26612             if(t > max) {
26613                 cell.className = " x-date-disabled";
26614                 cell.title = cal.maxText;
26615                 return;
26616             }
26617             if(ddays){
26618                 if(ddays.indexOf(d.getDay()) != -1){
26619                     cell.title = ddaysText;
26620                     cell.className = " x-date-disabled";
26621                 }
26622             }
26623             if(ddMatch && format){
26624                 var fvalue = d.dateFormat(format);
26625                 if(ddMatch.test(fvalue)){
26626                     cell.title = ddText.replace("%0", fvalue);
26627                     cell.className = " x-date-disabled";
26628                 }
26629             }
26630         };
26631
26632         var i = 0;
26633         for(; i < startingPos; i++) {
26634             textEls[i].innerHTML = (++prevStart);
26635             d.setDate(d.getDate()+1);
26636             cells[i].className = "x-date-prevday";
26637             setCellClass(this, cells[i]);
26638         }
26639         for(; i < days; i++){
26640             intDay = i - startingPos + 1;
26641             textEls[i].innerHTML = (intDay);
26642             d.setDate(d.getDate()+1);
26643             cells[i].className = "x-date-active";
26644             setCellClass(this, cells[i]);
26645         }
26646         var extraDays = 0;
26647         for(; i < 42; i++) {
26648              textEls[i].innerHTML = (++extraDays);
26649              d.setDate(d.getDate()+1);
26650              cells[i].className = "x-date-nextday";
26651              setCellClass(this, cells[i]);
26652         }
26653
26654         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26655         this.fireEvent('monthchange', this, date);
26656         
26657         if(!this.internalRender){
26658             var main = this.el.dom.firstChild;
26659             var w = main.offsetWidth;
26660             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26661             Roo.fly(main).setWidth(w);
26662             this.internalRender = true;
26663             // opera does not respect the auto grow header center column
26664             // then, after it gets a width opera refuses to recalculate
26665             // without a second pass
26666             if(Roo.isOpera && !this.secondPass){
26667                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26668                 this.secondPass = true;
26669                 this.update.defer(10, this, [date]);
26670             }
26671         }
26672         
26673         
26674     }
26675 });        /*
26676  * Based on:
26677  * Ext JS Library 1.1.1
26678  * Copyright(c) 2006-2007, Ext JS, LLC.
26679  *
26680  * Originally Released Under LGPL - original licence link has changed is not relivant.
26681  *
26682  * Fork - LGPL
26683  * <script type="text/javascript">
26684  */
26685 /**
26686  * @class Roo.TabPanel
26687  * @extends Roo.util.Observable
26688  * A lightweight tab container.
26689  * <br><br>
26690  * Usage:
26691  * <pre><code>
26692 // basic tabs 1, built from existing content
26693 var tabs = new Roo.TabPanel("tabs1");
26694 tabs.addTab("script", "View Script");
26695 tabs.addTab("markup", "View Markup");
26696 tabs.activate("script");
26697
26698 // more advanced tabs, built from javascript
26699 var jtabs = new Roo.TabPanel("jtabs");
26700 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26701
26702 // set up the UpdateManager
26703 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26704 var updater = tab2.getUpdateManager();
26705 updater.setDefaultUrl("ajax1.htm");
26706 tab2.on('activate', updater.refresh, updater, true);
26707
26708 // Use setUrl for Ajax loading
26709 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26710 tab3.setUrl("ajax2.htm", null, true);
26711
26712 // Disabled tab
26713 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26714 tab4.disable();
26715
26716 jtabs.activate("jtabs-1");
26717  * </code></pre>
26718  * @constructor
26719  * Create a new TabPanel.
26720  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26721  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26722  */
26723 Roo.TabPanel = function(container, config){
26724     /**
26725     * The container element for this TabPanel.
26726     * @type Roo.Element
26727     */
26728     this.el = Roo.get(container, true);
26729     if(config){
26730         if(typeof config == "boolean"){
26731             this.tabPosition = config ? "bottom" : "top";
26732         }else{
26733             Roo.apply(this, config);
26734         }
26735     }
26736     if(this.tabPosition == "bottom"){
26737         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26738         this.el.addClass("x-tabs-bottom");
26739     }
26740     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26741     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26742     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26743     if(Roo.isIE){
26744         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26745     }
26746     if(this.tabPosition != "bottom"){
26747         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26748          * @type Roo.Element
26749          */
26750         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26751         this.el.addClass("x-tabs-top");
26752     }
26753     this.items = [];
26754
26755     this.bodyEl.setStyle("position", "relative");
26756
26757     this.active = null;
26758     this.activateDelegate = this.activate.createDelegate(this);
26759
26760     this.addEvents({
26761         /**
26762          * @event tabchange
26763          * Fires when the active tab changes
26764          * @param {Roo.TabPanel} this
26765          * @param {Roo.TabPanelItem} activePanel The new active tab
26766          */
26767         "tabchange": true,
26768         /**
26769          * @event beforetabchange
26770          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26771          * @param {Roo.TabPanel} this
26772          * @param {Object} e Set cancel to true on this object to cancel the tab change
26773          * @param {Roo.TabPanelItem} tab The tab being changed to
26774          */
26775         "beforetabchange" : true
26776     });
26777
26778     Roo.EventManager.onWindowResize(this.onResize, this);
26779     this.cpad = this.el.getPadding("lr");
26780     this.hiddenCount = 0;
26781
26782
26783     // toolbar on the tabbar support...
26784     if (this.toolbar) {
26785         var tcfg = this.toolbar;
26786         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26787         this.toolbar = new Roo.Toolbar(tcfg);
26788         if (Roo.isSafari) {
26789             var tbl = tcfg.container.child('table', true);
26790             tbl.setAttribute('width', '100%');
26791         }
26792         
26793     }
26794    
26795
26796
26797     Roo.TabPanel.superclass.constructor.call(this);
26798 };
26799
26800 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26801     /*
26802      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26803      */
26804     tabPosition : "top",
26805     /*
26806      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26807      */
26808     currentTabWidth : 0,
26809     /*
26810      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26811      */
26812     minTabWidth : 40,
26813     /*
26814      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26815      */
26816     maxTabWidth : 250,
26817     /*
26818      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26819      */
26820     preferredTabWidth : 175,
26821     /*
26822      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26823      */
26824     resizeTabs : false,
26825     /*
26826      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26827      */
26828     monitorResize : true,
26829     /*
26830      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26831      */
26832     toolbar : false,
26833
26834     /**
26835      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26836      * @param {String} id The id of the div to use <b>or create</b>
26837      * @param {String} text The text for the tab
26838      * @param {String} content (optional) Content to put in the TabPanelItem body
26839      * @param {Boolean} closable (optional) True to create a close icon on the tab
26840      * @return {Roo.TabPanelItem} The created TabPanelItem
26841      */
26842     addTab : function(id, text, content, closable){
26843         var item = new Roo.TabPanelItem(this, id, text, closable);
26844         this.addTabItem(item);
26845         if(content){
26846             item.setContent(content);
26847         }
26848         return item;
26849     },
26850
26851     /**
26852      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26853      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26854      * @return {Roo.TabPanelItem}
26855      */
26856     getTab : function(id){
26857         return this.items[id];
26858     },
26859
26860     /**
26861      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26862      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26863      */
26864     hideTab : function(id){
26865         var t = this.items[id];
26866         if(!t.isHidden()){
26867            t.setHidden(true);
26868            this.hiddenCount++;
26869            this.autoSizeTabs();
26870         }
26871     },
26872
26873     /**
26874      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26875      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26876      */
26877     unhideTab : function(id){
26878         var t = this.items[id];
26879         if(t.isHidden()){
26880            t.setHidden(false);
26881            this.hiddenCount--;
26882            this.autoSizeTabs();
26883         }
26884     },
26885
26886     /**
26887      * Adds an existing {@link Roo.TabPanelItem}.
26888      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26889      */
26890     addTabItem : function(item){
26891         this.items[item.id] = item;
26892         this.items.push(item);
26893         if(this.resizeTabs){
26894            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26895            this.autoSizeTabs();
26896         }else{
26897             item.autoSize();
26898         }
26899     },
26900
26901     /**
26902      * Removes a {@link Roo.TabPanelItem}.
26903      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26904      */
26905     removeTab : function(id){
26906         var items = this.items;
26907         var tab = items[id];
26908         if(!tab) { return; }
26909         var index = items.indexOf(tab);
26910         if(this.active == tab && items.length > 1){
26911             var newTab = this.getNextAvailable(index);
26912             if(newTab) {
26913                 newTab.activate();
26914             }
26915         }
26916         this.stripEl.dom.removeChild(tab.pnode.dom);
26917         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26918             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26919         }
26920         items.splice(index, 1);
26921         delete this.items[tab.id];
26922         tab.fireEvent("close", tab);
26923         tab.purgeListeners();
26924         this.autoSizeTabs();
26925     },
26926
26927     getNextAvailable : function(start){
26928         var items = this.items;
26929         var index = start;
26930         // look for a next tab that will slide over to
26931         // replace the one being removed
26932         while(index < items.length){
26933             var item = items[++index];
26934             if(item && !item.isHidden()){
26935                 return item;
26936             }
26937         }
26938         // if one isn't found select the previous tab (on the left)
26939         index = start;
26940         while(index >= 0){
26941             var item = items[--index];
26942             if(item && !item.isHidden()){
26943                 return item;
26944             }
26945         }
26946         return null;
26947     },
26948
26949     /**
26950      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26951      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26952      */
26953     disableTab : function(id){
26954         var tab = this.items[id];
26955         if(tab && this.active != tab){
26956             tab.disable();
26957         }
26958     },
26959
26960     /**
26961      * Enables a {@link Roo.TabPanelItem} that is disabled.
26962      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26963      */
26964     enableTab : function(id){
26965         var tab = this.items[id];
26966         tab.enable();
26967     },
26968
26969     /**
26970      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26971      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26972      * @return {Roo.TabPanelItem} The TabPanelItem.
26973      */
26974     activate : function(id){
26975         var tab = this.items[id];
26976         if(!tab){
26977             return null;
26978         }
26979         if(tab == this.active || tab.disabled){
26980             return tab;
26981         }
26982         var e = {};
26983         this.fireEvent("beforetabchange", this, e, tab);
26984         if(e.cancel !== true && !tab.disabled){
26985             if(this.active){
26986                 this.active.hide();
26987             }
26988             this.active = this.items[id];
26989             this.active.show();
26990             this.fireEvent("tabchange", this, this.active);
26991         }
26992         return tab;
26993     },
26994
26995     /**
26996      * Gets the active {@link Roo.TabPanelItem}.
26997      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26998      */
26999     getActiveTab : function(){
27000         return this.active;
27001     },
27002
27003     /**
27004      * Updates the tab body element to fit the height of the container element
27005      * for overflow scrolling
27006      * @param {Number} targetHeight (optional) Override the starting height from the elements height
27007      */
27008     syncHeight : function(targetHeight){
27009         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
27010         var bm = this.bodyEl.getMargins();
27011         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
27012         this.bodyEl.setHeight(newHeight);
27013         return newHeight;
27014     },
27015
27016     onResize : function(){
27017         if(this.monitorResize){
27018             this.autoSizeTabs();
27019         }
27020     },
27021
27022     /**
27023      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
27024      */
27025     beginUpdate : function(){
27026         this.updating = true;
27027     },
27028
27029     /**
27030      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
27031      */
27032     endUpdate : function(){
27033         this.updating = false;
27034         this.autoSizeTabs();
27035     },
27036
27037     /**
27038      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
27039      */
27040     autoSizeTabs : function(){
27041         var count = this.items.length;
27042         var vcount = count - this.hiddenCount;
27043         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
27044         var w = Math.max(this.el.getWidth() - this.cpad, 10);
27045         var availWidth = Math.floor(w / vcount);
27046         var b = this.stripBody;
27047         if(b.getWidth() > w){
27048             var tabs = this.items;
27049             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
27050             if(availWidth < this.minTabWidth){
27051                 /*if(!this.sleft){    // incomplete scrolling code
27052                     this.createScrollButtons();
27053                 }
27054                 this.showScroll();
27055                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27056             }
27057         }else{
27058             if(this.currentTabWidth < this.preferredTabWidth){
27059                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27060             }
27061         }
27062     },
27063
27064     /**
27065      * Returns the number of tabs in this TabPanel.
27066      * @return {Number}
27067      */
27068      getCount : function(){
27069          return this.items.length;
27070      },
27071
27072     /**
27073      * Resizes all the tabs to the passed width
27074      * @param {Number} The new width
27075      */
27076     setTabWidth : function(width){
27077         this.currentTabWidth = width;
27078         for(var i = 0, len = this.items.length; i < len; i++) {
27079                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
27080         }
27081     },
27082
27083     /**
27084      * Destroys this TabPanel
27085      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27086      */
27087     destroy : function(removeEl){
27088         Roo.EventManager.removeResizeListener(this.onResize, this);
27089         for(var i = 0, len = this.items.length; i < len; i++){
27090             this.items[i].purgeListeners();
27091         }
27092         if(removeEl === true){
27093             this.el.update("");
27094             this.el.remove();
27095         }
27096     }
27097 });
27098
27099 /**
27100  * @class Roo.TabPanelItem
27101  * @extends Roo.util.Observable
27102  * Represents an individual item (tab plus body) in a TabPanel.
27103  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27104  * @param {String} id The id of this TabPanelItem
27105  * @param {String} text The text for the tab of this TabPanelItem
27106  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27107  */
27108 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27109     /**
27110      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27111      * @type Roo.TabPanel
27112      */
27113     this.tabPanel = tabPanel;
27114     /**
27115      * The id for this TabPanelItem
27116      * @type String
27117      */
27118     this.id = id;
27119     /** @private */
27120     this.disabled = false;
27121     /** @private */
27122     this.text = text;
27123     /** @private */
27124     this.loaded = false;
27125     this.closable = closable;
27126
27127     /**
27128      * The body element for this TabPanelItem.
27129      * @type Roo.Element
27130      */
27131     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27132     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27133     this.bodyEl.setStyle("display", "block");
27134     this.bodyEl.setStyle("zoom", "1");
27135     this.hideAction();
27136
27137     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27138     /** @private */
27139     this.el = Roo.get(els.el, true);
27140     this.inner = Roo.get(els.inner, true);
27141     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27142     this.pnode = Roo.get(els.el.parentNode, true);
27143     this.el.on("mousedown", this.onTabMouseDown, this);
27144     this.el.on("click", this.onTabClick, this);
27145     /** @private */
27146     if(closable){
27147         var c = Roo.get(els.close, true);
27148         c.dom.title = this.closeText;
27149         c.addClassOnOver("close-over");
27150         c.on("click", this.closeClick, this);
27151      }
27152
27153     this.addEvents({
27154          /**
27155          * @event activate
27156          * Fires when this tab becomes the active tab.
27157          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27158          * @param {Roo.TabPanelItem} this
27159          */
27160         "activate": true,
27161         /**
27162          * @event beforeclose
27163          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27164          * @param {Roo.TabPanelItem} this
27165          * @param {Object} e Set cancel to true on this object to cancel the close.
27166          */
27167         "beforeclose": true,
27168         /**
27169          * @event close
27170          * Fires when this tab is closed.
27171          * @param {Roo.TabPanelItem} this
27172          */
27173          "close": true,
27174         /**
27175          * @event deactivate
27176          * Fires when this tab is no longer the active tab.
27177          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27178          * @param {Roo.TabPanelItem} this
27179          */
27180          "deactivate" : true
27181     });
27182     this.hidden = false;
27183
27184     Roo.TabPanelItem.superclass.constructor.call(this);
27185 };
27186
27187 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27188     purgeListeners : function(){
27189        Roo.util.Observable.prototype.purgeListeners.call(this);
27190        this.el.removeAllListeners();
27191     },
27192     /**
27193      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27194      */
27195     show : function(){
27196         this.pnode.addClass("on");
27197         this.showAction();
27198         if(Roo.isOpera){
27199             this.tabPanel.stripWrap.repaint();
27200         }
27201         this.fireEvent("activate", this.tabPanel, this);
27202     },
27203
27204     /**
27205      * Returns true if this tab is the active tab.
27206      * @return {Boolean}
27207      */
27208     isActive : function(){
27209         return this.tabPanel.getActiveTab() == this;
27210     },
27211
27212     /**
27213      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27214      */
27215     hide : function(){
27216         this.pnode.removeClass("on");
27217         this.hideAction();
27218         this.fireEvent("deactivate", this.tabPanel, this);
27219     },
27220
27221     hideAction : function(){
27222         this.bodyEl.hide();
27223         this.bodyEl.setStyle("position", "absolute");
27224         this.bodyEl.setLeft("-20000px");
27225         this.bodyEl.setTop("-20000px");
27226     },
27227
27228     showAction : function(){
27229         this.bodyEl.setStyle("position", "relative");
27230         this.bodyEl.setTop("");
27231         this.bodyEl.setLeft("");
27232         this.bodyEl.show();
27233     },
27234
27235     /**
27236      * Set the tooltip for the tab.
27237      * @param {String} tooltip The tab's tooltip
27238      */
27239     setTooltip : function(text){
27240         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27241             this.textEl.dom.qtip = text;
27242             this.textEl.dom.removeAttribute('title');
27243         }else{
27244             this.textEl.dom.title = text;
27245         }
27246     },
27247
27248     onTabClick : function(e){
27249         e.preventDefault();
27250         this.tabPanel.activate(this.id);
27251     },
27252
27253     onTabMouseDown : function(e){
27254         e.preventDefault();
27255         this.tabPanel.activate(this.id);
27256     },
27257
27258     getWidth : function(){
27259         return this.inner.getWidth();
27260     },
27261
27262     setWidth : function(width){
27263         var iwidth = width - this.pnode.getPadding("lr");
27264         this.inner.setWidth(iwidth);
27265         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27266         this.pnode.setWidth(width);
27267     },
27268
27269     /**
27270      * Show or hide the tab
27271      * @param {Boolean} hidden True to hide or false to show.
27272      */
27273     setHidden : function(hidden){
27274         this.hidden = hidden;
27275         this.pnode.setStyle("display", hidden ? "none" : "");
27276     },
27277
27278     /**
27279      * Returns true if this tab is "hidden"
27280      * @return {Boolean}
27281      */
27282     isHidden : function(){
27283         return this.hidden;
27284     },
27285
27286     /**
27287      * Returns the text for this tab
27288      * @return {String}
27289      */
27290     getText : function(){
27291         return this.text;
27292     },
27293
27294     autoSize : function(){
27295         //this.el.beginMeasure();
27296         this.textEl.setWidth(1);
27297         /*
27298          *  #2804 [new] Tabs in Roojs
27299          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27300          */
27301         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27302         //this.el.endMeasure();
27303     },
27304
27305     /**
27306      * Sets the text for the tab (Note: this also sets the tooltip text)
27307      * @param {String} text The tab's text and tooltip
27308      */
27309     setText : function(text){
27310         this.text = text;
27311         this.textEl.update(text);
27312         this.setTooltip(text);
27313         if(!this.tabPanel.resizeTabs){
27314             this.autoSize();
27315         }
27316     },
27317     /**
27318      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27319      */
27320     activate : function(){
27321         this.tabPanel.activate(this.id);
27322     },
27323
27324     /**
27325      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27326      */
27327     disable : function(){
27328         if(this.tabPanel.active != this){
27329             this.disabled = true;
27330             this.pnode.addClass("disabled");
27331         }
27332     },
27333
27334     /**
27335      * Enables this TabPanelItem if it was previously disabled.
27336      */
27337     enable : function(){
27338         this.disabled = false;
27339         this.pnode.removeClass("disabled");
27340     },
27341
27342     /**
27343      * Sets the content for this TabPanelItem.
27344      * @param {String} content The content
27345      * @param {Boolean} loadScripts true to look for and load scripts
27346      */
27347     setContent : function(content, loadScripts){
27348         this.bodyEl.update(content, loadScripts);
27349     },
27350
27351     /**
27352      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27353      * @return {Roo.UpdateManager} The UpdateManager
27354      */
27355     getUpdateManager : function(){
27356         return this.bodyEl.getUpdateManager();
27357     },
27358
27359     /**
27360      * Set a URL to be used to load the content for this TabPanelItem.
27361      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27362      * @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)
27363      * @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)
27364      * @return {Roo.UpdateManager} The UpdateManager
27365      */
27366     setUrl : function(url, params, loadOnce){
27367         if(this.refreshDelegate){
27368             this.un('activate', this.refreshDelegate);
27369         }
27370         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27371         this.on("activate", this.refreshDelegate);
27372         return this.bodyEl.getUpdateManager();
27373     },
27374
27375     /** @private */
27376     _handleRefresh : function(url, params, loadOnce){
27377         if(!loadOnce || !this.loaded){
27378             var updater = this.bodyEl.getUpdateManager();
27379             updater.update(url, params, this._setLoaded.createDelegate(this));
27380         }
27381     },
27382
27383     /**
27384      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27385      *   Will fail silently if the setUrl method has not been called.
27386      *   This does not activate the panel, just updates its content.
27387      */
27388     refresh : function(){
27389         if(this.refreshDelegate){
27390            this.loaded = false;
27391            this.refreshDelegate();
27392         }
27393     },
27394
27395     /** @private */
27396     _setLoaded : function(){
27397         this.loaded = true;
27398     },
27399
27400     /** @private */
27401     closeClick : function(e){
27402         var o = {};
27403         e.stopEvent();
27404         this.fireEvent("beforeclose", this, o);
27405         if(o.cancel !== true){
27406             this.tabPanel.removeTab(this.id);
27407         }
27408     },
27409     /**
27410      * The text displayed in the tooltip for the close icon.
27411      * @type String
27412      */
27413     closeText : "Close this tab"
27414 });
27415
27416 /** @private */
27417 Roo.TabPanel.prototype.createStrip = function(container){
27418     var strip = document.createElement("div");
27419     strip.className = "x-tabs-wrap";
27420     container.appendChild(strip);
27421     return strip;
27422 };
27423 /** @private */
27424 Roo.TabPanel.prototype.createStripList = function(strip){
27425     // div wrapper for retard IE
27426     // returns the "tr" element.
27427     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27428         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27429         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27430     return strip.firstChild.firstChild.firstChild.firstChild;
27431 };
27432 /** @private */
27433 Roo.TabPanel.prototype.createBody = function(container){
27434     var body = document.createElement("div");
27435     Roo.id(body, "tab-body");
27436     Roo.fly(body).addClass("x-tabs-body");
27437     container.appendChild(body);
27438     return body;
27439 };
27440 /** @private */
27441 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27442     var body = Roo.getDom(id);
27443     if(!body){
27444         body = document.createElement("div");
27445         body.id = id;
27446     }
27447     Roo.fly(body).addClass("x-tabs-item-body");
27448     bodyEl.insertBefore(body, bodyEl.firstChild);
27449     return body;
27450 };
27451 /** @private */
27452 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27453     var td = document.createElement("td");
27454     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27455     //stripEl.appendChild(td);
27456     if(closable){
27457         td.className = "x-tabs-closable";
27458         if(!this.closeTpl){
27459             this.closeTpl = new Roo.Template(
27460                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27461                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27462                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27463             );
27464         }
27465         var el = this.closeTpl.overwrite(td, {"text": text});
27466         var close = el.getElementsByTagName("div")[0];
27467         var inner = el.getElementsByTagName("em")[0];
27468         return {"el": el, "close": close, "inner": inner};
27469     } else {
27470         if(!this.tabTpl){
27471             this.tabTpl = new Roo.Template(
27472                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27473                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27474             );
27475         }
27476         var el = this.tabTpl.overwrite(td, {"text": text});
27477         var inner = el.getElementsByTagName("em")[0];
27478         return {"el": el, "inner": inner};
27479     }
27480 };/*
27481  * Based on:
27482  * Ext JS Library 1.1.1
27483  * Copyright(c) 2006-2007, Ext JS, LLC.
27484  *
27485  * Originally Released Under LGPL - original licence link has changed is not relivant.
27486  *
27487  * Fork - LGPL
27488  * <script type="text/javascript">
27489  */
27490
27491 /**
27492  * @class Roo.Button
27493  * @extends Roo.util.Observable
27494  * Simple Button class
27495  * @cfg {String} text The button text
27496  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27497  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27498  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27499  * @cfg {Object} scope The scope of the handler
27500  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27501  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27502  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27503  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27504  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27505  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27506    applies if enableToggle = true)
27507  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27508  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27509   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27510  * @constructor
27511  * Create a new button
27512  * @param {Object} config The config object
27513  */
27514 Roo.Button = function(renderTo, config)
27515 {
27516     if (!config) {
27517         config = renderTo;
27518         renderTo = config.renderTo || false;
27519     }
27520     
27521     Roo.apply(this, config);
27522     this.addEvents({
27523         /**
27524              * @event click
27525              * Fires when this button is clicked
27526              * @param {Button} this
27527              * @param {EventObject} e The click event
27528              */
27529             "click" : true,
27530         /**
27531              * @event toggle
27532              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27533              * @param {Button} this
27534              * @param {Boolean} pressed
27535              */
27536             "toggle" : true,
27537         /**
27538              * @event mouseover
27539              * Fires when the mouse hovers over the button
27540              * @param {Button} this
27541              * @param {Event} e The event object
27542              */
27543         'mouseover' : true,
27544         /**
27545              * @event mouseout
27546              * Fires when the mouse exits the button
27547              * @param {Button} this
27548              * @param {Event} e The event object
27549              */
27550         'mouseout': true,
27551          /**
27552              * @event render
27553              * Fires when the button is rendered
27554              * @param {Button} this
27555              */
27556         'render': true
27557     });
27558     if(this.menu){
27559         this.menu = Roo.menu.MenuMgr.get(this.menu);
27560     }
27561     // register listeners first!!  - so render can be captured..
27562     Roo.util.Observable.call(this);
27563     if(renderTo){
27564         this.render(renderTo);
27565     }
27566     
27567   
27568 };
27569
27570 Roo.extend(Roo.Button, Roo.util.Observable, {
27571     /**
27572      * 
27573      */
27574     
27575     /**
27576      * Read-only. True if this button is hidden
27577      * @type Boolean
27578      */
27579     hidden : false,
27580     /**
27581      * Read-only. True if this button is disabled
27582      * @type Boolean
27583      */
27584     disabled : false,
27585     /**
27586      * Read-only. True if this button is pressed (only if enableToggle = true)
27587      * @type Boolean
27588      */
27589     pressed : false,
27590
27591     /**
27592      * @cfg {Number} tabIndex 
27593      * The DOM tabIndex for this button (defaults to undefined)
27594      */
27595     tabIndex : undefined,
27596
27597     /**
27598      * @cfg {Boolean} enableToggle
27599      * True to enable pressed/not pressed toggling (defaults to false)
27600      */
27601     enableToggle: false,
27602     /**
27603      * @cfg {Mixed} menu
27604      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27605      */
27606     menu : undefined,
27607     /**
27608      * @cfg {String} menuAlign
27609      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27610      */
27611     menuAlign : "tl-bl?",
27612
27613     /**
27614      * @cfg {String} iconCls
27615      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27616      */
27617     iconCls : undefined,
27618     /**
27619      * @cfg {String} type
27620      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27621      */
27622     type : 'button',
27623
27624     // private
27625     menuClassTarget: 'tr',
27626
27627     /**
27628      * @cfg {String} clickEvent
27629      * The type of event to map to the button's event handler (defaults to 'click')
27630      */
27631     clickEvent : 'click',
27632
27633     /**
27634      * @cfg {Boolean} handleMouseEvents
27635      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27636      */
27637     handleMouseEvents : true,
27638
27639     /**
27640      * @cfg {String} tooltipType
27641      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27642      */
27643     tooltipType : 'qtip',
27644
27645     /**
27646      * @cfg {String} cls
27647      * A CSS class to apply to the button's main element.
27648      */
27649     
27650     /**
27651      * @cfg {Roo.Template} template (Optional)
27652      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27653      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27654      * require code modifications if required elements (e.g. a button) aren't present.
27655      */
27656
27657     // private
27658     render : function(renderTo){
27659         var btn;
27660         if(this.hideParent){
27661             this.parentEl = Roo.get(renderTo);
27662         }
27663         if(!this.dhconfig){
27664             if(!this.template){
27665                 if(!Roo.Button.buttonTemplate){
27666                     // hideous table template
27667                     Roo.Button.buttonTemplate = new Roo.Template(
27668                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27669                         '<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>',
27670                         "</tr></tbody></table>");
27671                 }
27672                 this.template = Roo.Button.buttonTemplate;
27673             }
27674             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27675             var btnEl = btn.child("button:first");
27676             btnEl.on('focus', this.onFocus, this);
27677             btnEl.on('blur', this.onBlur, this);
27678             if(this.cls){
27679                 btn.addClass(this.cls);
27680             }
27681             if(this.icon){
27682                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27683             }
27684             if(this.iconCls){
27685                 btnEl.addClass(this.iconCls);
27686                 if(!this.cls){
27687                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27688                 }
27689             }
27690             if(this.tabIndex !== undefined){
27691                 btnEl.dom.tabIndex = this.tabIndex;
27692             }
27693             if(this.tooltip){
27694                 if(typeof this.tooltip == 'object'){
27695                     Roo.QuickTips.tips(Roo.apply({
27696                           target: btnEl.id
27697                     }, this.tooltip));
27698                 } else {
27699                     btnEl.dom[this.tooltipType] = this.tooltip;
27700                 }
27701             }
27702         }else{
27703             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27704         }
27705         this.el = btn;
27706         if(this.id){
27707             this.el.dom.id = this.el.id = this.id;
27708         }
27709         if(this.menu){
27710             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27711             this.menu.on("show", this.onMenuShow, this);
27712             this.menu.on("hide", this.onMenuHide, this);
27713         }
27714         btn.addClass("x-btn");
27715         if(Roo.isIE && !Roo.isIE7){
27716             this.autoWidth.defer(1, this);
27717         }else{
27718             this.autoWidth();
27719         }
27720         if(this.handleMouseEvents){
27721             btn.on("mouseover", this.onMouseOver, this);
27722             btn.on("mouseout", this.onMouseOut, this);
27723             btn.on("mousedown", this.onMouseDown, this);
27724         }
27725         btn.on(this.clickEvent, this.onClick, this);
27726         //btn.on("mouseup", this.onMouseUp, this);
27727         if(this.hidden){
27728             this.hide();
27729         }
27730         if(this.disabled){
27731             this.disable();
27732         }
27733         Roo.ButtonToggleMgr.register(this);
27734         if(this.pressed){
27735             this.el.addClass("x-btn-pressed");
27736         }
27737         if(this.repeat){
27738             var repeater = new Roo.util.ClickRepeater(btn,
27739                 typeof this.repeat == "object" ? this.repeat : {}
27740             );
27741             repeater.on("click", this.onClick,  this);
27742         }
27743         
27744         this.fireEvent('render', this);
27745         
27746     },
27747     /**
27748      * Returns the button's underlying element
27749      * @return {Roo.Element} The element
27750      */
27751     getEl : function(){
27752         return this.el;  
27753     },
27754     
27755     /**
27756      * Destroys this Button and removes any listeners.
27757      */
27758     destroy : function(){
27759         Roo.ButtonToggleMgr.unregister(this);
27760         this.el.removeAllListeners();
27761         this.purgeListeners();
27762         this.el.remove();
27763     },
27764
27765     // private
27766     autoWidth : function(){
27767         if(this.el){
27768             this.el.setWidth("auto");
27769             if(Roo.isIE7 && Roo.isStrict){
27770                 var ib = this.el.child('button');
27771                 if(ib && ib.getWidth() > 20){
27772                     ib.clip();
27773                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27774                 }
27775             }
27776             if(this.minWidth){
27777                 if(this.hidden){
27778                     this.el.beginMeasure();
27779                 }
27780                 if(this.el.getWidth() < this.minWidth){
27781                     this.el.setWidth(this.minWidth);
27782                 }
27783                 if(this.hidden){
27784                     this.el.endMeasure();
27785                 }
27786             }
27787         }
27788     },
27789
27790     /**
27791      * Assigns this button's click handler
27792      * @param {Function} handler The function to call when the button is clicked
27793      * @param {Object} scope (optional) Scope for the function passed in
27794      */
27795     setHandler : function(handler, scope){
27796         this.handler = handler;
27797         this.scope = scope;  
27798     },
27799     
27800     /**
27801      * Sets this button's text
27802      * @param {String} text The button text
27803      */
27804     setText : function(text){
27805         this.text = text;
27806         if(this.el){
27807             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27808         }
27809         this.autoWidth();
27810     },
27811     
27812     /**
27813      * Gets the text for this button
27814      * @return {String} The button text
27815      */
27816     getText : function(){
27817         return this.text;  
27818     },
27819     
27820     /**
27821      * Show this button
27822      */
27823     show: function(){
27824         this.hidden = false;
27825         if(this.el){
27826             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27827         }
27828     },
27829     
27830     /**
27831      * Hide this button
27832      */
27833     hide: function(){
27834         this.hidden = true;
27835         if(this.el){
27836             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27837         }
27838     },
27839     
27840     /**
27841      * Convenience function for boolean show/hide
27842      * @param {Boolean} visible True to show, false to hide
27843      */
27844     setVisible: function(visible){
27845         if(visible) {
27846             this.show();
27847         }else{
27848             this.hide();
27849         }
27850     },
27851     
27852     /**
27853      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27854      * @param {Boolean} state (optional) Force a particular state
27855      */
27856     toggle : function(state){
27857         state = state === undefined ? !this.pressed : state;
27858         if(state != this.pressed){
27859             if(state){
27860                 this.el.addClass("x-btn-pressed");
27861                 this.pressed = true;
27862                 this.fireEvent("toggle", this, true);
27863             }else{
27864                 this.el.removeClass("x-btn-pressed");
27865                 this.pressed = false;
27866                 this.fireEvent("toggle", this, false);
27867             }
27868             if(this.toggleHandler){
27869                 this.toggleHandler.call(this.scope || this, this, state);
27870             }
27871         }
27872     },
27873     
27874     /**
27875      * Focus the button
27876      */
27877     focus : function(){
27878         this.el.child('button:first').focus();
27879     },
27880     
27881     /**
27882      * Disable this button
27883      */
27884     disable : function(){
27885         if(this.el){
27886             this.el.addClass("x-btn-disabled");
27887         }
27888         this.disabled = true;
27889     },
27890     
27891     /**
27892      * Enable this button
27893      */
27894     enable : function(){
27895         if(this.el){
27896             this.el.removeClass("x-btn-disabled");
27897         }
27898         this.disabled = false;
27899     },
27900
27901     /**
27902      * Convenience function for boolean enable/disable
27903      * @param {Boolean} enabled True to enable, false to disable
27904      */
27905     setDisabled : function(v){
27906         this[v !== true ? "enable" : "disable"]();
27907     },
27908
27909     // private
27910     onClick : function(e)
27911     {
27912         if(e){
27913             e.preventDefault();
27914         }
27915         if(e.button != 0){
27916             return;
27917         }
27918         if(!this.disabled){
27919             if(this.enableToggle){
27920                 this.toggle();
27921             }
27922             if(this.menu && !this.menu.isVisible()){
27923                 this.menu.show(this.el, this.menuAlign);
27924             }
27925             this.fireEvent("click", this, e);
27926             if(this.handler){
27927                 this.el.removeClass("x-btn-over");
27928                 this.handler.call(this.scope || this, this, e);
27929             }
27930         }
27931     },
27932     // private
27933     onMouseOver : function(e){
27934         if(!this.disabled){
27935             this.el.addClass("x-btn-over");
27936             this.fireEvent('mouseover', this, e);
27937         }
27938     },
27939     // private
27940     onMouseOut : function(e){
27941         if(!e.within(this.el,  true)){
27942             this.el.removeClass("x-btn-over");
27943             this.fireEvent('mouseout', this, e);
27944         }
27945     },
27946     // private
27947     onFocus : function(e){
27948         if(!this.disabled){
27949             this.el.addClass("x-btn-focus");
27950         }
27951     },
27952     // private
27953     onBlur : function(e){
27954         this.el.removeClass("x-btn-focus");
27955     },
27956     // private
27957     onMouseDown : function(e){
27958         if(!this.disabled && e.button == 0){
27959             this.el.addClass("x-btn-click");
27960             Roo.get(document).on('mouseup', this.onMouseUp, this);
27961         }
27962     },
27963     // private
27964     onMouseUp : function(e){
27965         if(e.button == 0){
27966             this.el.removeClass("x-btn-click");
27967             Roo.get(document).un('mouseup', this.onMouseUp, this);
27968         }
27969     },
27970     // private
27971     onMenuShow : function(e){
27972         this.el.addClass("x-btn-menu-active");
27973     },
27974     // private
27975     onMenuHide : function(e){
27976         this.el.removeClass("x-btn-menu-active");
27977     }   
27978 });
27979
27980 // Private utility class used by Button
27981 Roo.ButtonToggleMgr = function(){
27982    var groups = {};
27983    
27984    function toggleGroup(btn, state){
27985        if(state){
27986            var g = groups[btn.toggleGroup];
27987            for(var i = 0, l = g.length; i < l; i++){
27988                if(g[i] != btn){
27989                    g[i].toggle(false);
27990                }
27991            }
27992        }
27993    }
27994    
27995    return {
27996        register : function(btn){
27997            if(!btn.toggleGroup){
27998                return;
27999            }
28000            var g = groups[btn.toggleGroup];
28001            if(!g){
28002                g = groups[btn.toggleGroup] = [];
28003            }
28004            g.push(btn);
28005            btn.on("toggle", toggleGroup);
28006        },
28007        
28008        unregister : function(btn){
28009            if(!btn.toggleGroup){
28010                return;
28011            }
28012            var g = groups[btn.toggleGroup];
28013            if(g){
28014                g.remove(btn);
28015                btn.un("toggle", toggleGroup);
28016            }
28017        }
28018    };
28019 }();/*
28020  * Based on:
28021  * Ext JS Library 1.1.1
28022  * Copyright(c) 2006-2007, Ext JS, LLC.
28023  *
28024  * Originally Released Under LGPL - original licence link has changed is not relivant.
28025  *
28026  * Fork - LGPL
28027  * <script type="text/javascript">
28028  */
28029  
28030 /**
28031  * @class Roo.SplitButton
28032  * @extends Roo.Button
28033  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
28034  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
28035  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
28036  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
28037  * @cfg {String} arrowTooltip The title attribute of the arrow
28038  * @constructor
28039  * Create a new menu button
28040  * @param {String/HTMLElement/Element} renderTo The element to append the button to
28041  * @param {Object} config The config object
28042  */
28043 Roo.SplitButton = function(renderTo, config){
28044     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
28045     /**
28046      * @event arrowclick
28047      * Fires when this button's arrow is clicked
28048      * @param {SplitButton} this
28049      * @param {EventObject} e The click event
28050      */
28051     this.addEvents({"arrowclick":true});
28052 };
28053
28054 Roo.extend(Roo.SplitButton, Roo.Button, {
28055     render : function(renderTo){
28056         // this is one sweet looking template!
28057         var tpl = new Roo.Template(
28058             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28059             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28060             '<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>',
28061             "</tbody></table></td><td>",
28062             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28063             '<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>',
28064             "</tbody></table></td></tr></table>"
28065         );
28066         var btn = tpl.append(renderTo, [this.text, this.type], true);
28067         var btnEl = btn.child("button");
28068         if(this.cls){
28069             btn.addClass(this.cls);
28070         }
28071         if(this.icon){
28072             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28073         }
28074         if(this.iconCls){
28075             btnEl.addClass(this.iconCls);
28076             if(!this.cls){
28077                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28078             }
28079         }
28080         this.el = btn;
28081         if(this.handleMouseEvents){
28082             btn.on("mouseover", this.onMouseOver, this);
28083             btn.on("mouseout", this.onMouseOut, this);
28084             btn.on("mousedown", this.onMouseDown, this);
28085             btn.on("mouseup", this.onMouseUp, this);
28086         }
28087         btn.on(this.clickEvent, this.onClick, this);
28088         if(this.tooltip){
28089             if(typeof this.tooltip == 'object'){
28090                 Roo.QuickTips.tips(Roo.apply({
28091                       target: btnEl.id
28092                 }, this.tooltip));
28093             } else {
28094                 btnEl.dom[this.tooltipType] = this.tooltip;
28095             }
28096         }
28097         if(this.arrowTooltip){
28098             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28099         }
28100         if(this.hidden){
28101             this.hide();
28102         }
28103         if(this.disabled){
28104             this.disable();
28105         }
28106         if(this.pressed){
28107             this.el.addClass("x-btn-pressed");
28108         }
28109         if(Roo.isIE && !Roo.isIE7){
28110             this.autoWidth.defer(1, this);
28111         }else{
28112             this.autoWidth();
28113         }
28114         if(this.menu){
28115             this.menu.on("show", this.onMenuShow, this);
28116             this.menu.on("hide", this.onMenuHide, this);
28117         }
28118         this.fireEvent('render', this);
28119     },
28120
28121     // private
28122     autoWidth : function(){
28123         if(this.el){
28124             var tbl = this.el.child("table:first");
28125             var tbl2 = this.el.child("table:last");
28126             this.el.setWidth("auto");
28127             tbl.setWidth("auto");
28128             if(Roo.isIE7 && Roo.isStrict){
28129                 var ib = this.el.child('button:first');
28130                 if(ib && ib.getWidth() > 20){
28131                     ib.clip();
28132                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28133                 }
28134             }
28135             if(this.minWidth){
28136                 if(this.hidden){
28137                     this.el.beginMeasure();
28138                 }
28139                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28140                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28141                 }
28142                 if(this.hidden){
28143                     this.el.endMeasure();
28144                 }
28145             }
28146             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28147         } 
28148     },
28149     /**
28150      * Sets this button's click handler
28151      * @param {Function} handler The function to call when the button is clicked
28152      * @param {Object} scope (optional) Scope for the function passed above
28153      */
28154     setHandler : function(handler, scope){
28155         this.handler = handler;
28156         this.scope = scope;  
28157     },
28158     
28159     /**
28160      * Sets this button's arrow click handler
28161      * @param {Function} handler The function to call when the arrow is clicked
28162      * @param {Object} scope (optional) Scope for the function passed above
28163      */
28164     setArrowHandler : function(handler, scope){
28165         this.arrowHandler = handler;
28166         this.scope = scope;  
28167     },
28168     
28169     /**
28170      * Focus the button
28171      */
28172     focus : function(){
28173         if(this.el){
28174             this.el.child("button:first").focus();
28175         }
28176     },
28177
28178     // private
28179     onClick : function(e){
28180         e.preventDefault();
28181         if(!this.disabled){
28182             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28183                 if(this.menu && !this.menu.isVisible()){
28184                     this.menu.show(this.el, this.menuAlign);
28185                 }
28186                 this.fireEvent("arrowclick", this, e);
28187                 if(this.arrowHandler){
28188                     this.arrowHandler.call(this.scope || this, this, e);
28189                 }
28190             }else{
28191                 this.fireEvent("click", this, e);
28192                 if(this.handler){
28193                     this.handler.call(this.scope || this, this, e);
28194                 }
28195             }
28196         }
28197     },
28198     // private
28199     onMouseDown : function(e){
28200         if(!this.disabled){
28201             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28202         }
28203     },
28204     // private
28205     onMouseUp : function(e){
28206         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28207     }   
28208 });
28209
28210
28211 // backwards compat
28212 Roo.MenuButton = Roo.SplitButton;/*
28213  * Based on:
28214  * Ext JS Library 1.1.1
28215  * Copyright(c) 2006-2007, Ext JS, LLC.
28216  *
28217  * Originally Released Under LGPL - original licence link has changed is not relivant.
28218  *
28219  * Fork - LGPL
28220  * <script type="text/javascript">
28221  */
28222
28223 /**
28224  * @class Roo.Toolbar
28225  * Basic Toolbar class.
28226  * @constructor
28227  * Creates a new Toolbar
28228  * @param {Object} container The config object
28229  */ 
28230 Roo.Toolbar = function(container, buttons, config)
28231 {
28232     /// old consturctor format still supported..
28233     if(container instanceof Array){ // omit the container for later rendering
28234         buttons = container;
28235         config = buttons;
28236         container = null;
28237     }
28238     if (typeof(container) == 'object' && container.xtype) {
28239         config = container;
28240         container = config.container;
28241         buttons = config.buttons || []; // not really - use items!!
28242     }
28243     var xitems = [];
28244     if (config && config.items) {
28245         xitems = config.items;
28246         delete config.items;
28247     }
28248     Roo.apply(this, config);
28249     this.buttons = buttons;
28250     
28251     if(container){
28252         this.render(container);
28253     }
28254     this.xitems = xitems;
28255     Roo.each(xitems, function(b) {
28256         this.add(b);
28257     }, this);
28258     
28259 };
28260
28261 Roo.Toolbar.prototype = {
28262     /**
28263      * @cfg {Array} items
28264      * array of button configs or elements to add (will be converted to a MixedCollection)
28265      */
28266     
28267     /**
28268      * @cfg {String/HTMLElement/Element} container
28269      * The id or element that will contain the toolbar
28270      */
28271     // private
28272     render : function(ct){
28273         this.el = Roo.get(ct);
28274         if(this.cls){
28275             this.el.addClass(this.cls);
28276         }
28277         // using a table allows for vertical alignment
28278         // 100% width is needed by Safari...
28279         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28280         this.tr = this.el.child("tr", true);
28281         var autoId = 0;
28282         this.items = new Roo.util.MixedCollection(false, function(o){
28283             return o.id || ("item" + (++autoId));
28284         });
28285         if(this.buttons){
28286             this.add.apply(this, this.buttons);
28287             delete this.buttons;
28288         }
28289     },
28290
28291     /**
28292      * Adds element(s) to the toolbar -- this function takes a variable number of 
28293      * arguments of mixed type and adds them to the toolbar.
28294      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28295      * <ul>
28296      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28297      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28298      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28299      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28300      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28301      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28302      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28303      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28304      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28305      * </ul>
28306      * @param {Mixed} arg2
28307      * @param {Mixed} etc.
28308      */
28309     add : function(){
28310         var a = arguments, l = a.length;
28311         for(var i = 0; i < l; i++){
28312             this._add(a[i]);
28313         }
28314     },
28315     // private..
28316     _add : function(el) {
28317         
28318         if (el.xtype) {
28319             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28320         }
28321         
28322         if (el.applyTo){ // some kind of form field
28323             return this.addField(el);
28324         } 
28325         if (el.render){ // some kind of Toolbar.Item
28326             return this.addItem(el);
28327         }
28328         if (typeof el == "string"){ // string
28329             if(el == "separator" || el == "-"){
28330                 return this.addSeparator();
28331             }
28332             if (el == " "){
28333                 return this.addSpacer();
28334             }
28335             if(el == "->"){
28336                 return this.addFill();
28337             }
28338             return this.addText(el);
28339             
28340         }
28341         if(el.tagName){ // element
28342             return this.addElement(el);
28343         }
28344         if(typeof el == "object"){ // must be button config?
28345             return this.addButton(el);
28346         }
28347         // and now what?!?!
28348         return false;
28349         
28350     },
28351     
28352     /**
28353      * Add an Xtype element
28354      * @param {Object} xtype Xtype Object
28355      * @return {Object} created Object
28356      */
28357     addxtype : function(e){
28358         return this.add(e);  
28359     },
28360     
28361     /**
28362      * Returns the Element for this toolbar.
28363      * @return {Roo.Element}
28364      */
28365     getEl : function(){
28366         return this.el;  
28367     },
28368     
28369     /**
28370      * Adds a separator
28371      * @return {Roo.Toolbar.Item} The separator item
28372      */
28373     addSeparator : function(){
28374         return this.addItem(new Roo.Toolbar.Separator());
28375     },
28376
28377     /**
28378      * Adds a spacer element
28379      * @return {Roo.Toolbar.Spacer} The spacer item
28380      */
28381     addSpacer : function(){
28382         return this.addItem(new Roo.Toolbar.Spacer());
28383     },
28384
28385     /**
28386      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28387      * @return {Roo.Toolbar.Fill} The fill item
28388      */
28389     addFill : function(){
28390         return this.addItem(new Roo.Toolbar.Fill());
28391     },
28392
28393     /**
28394      * Adds any standard HTML element to the toolbar
28395      * @param {String/HTMLElement/Element} el The element or id of the element to add
28396      * @return {Roo.Toolbar.Item} The element's item
28397      */
28398     addElement : function(el){
28399         return this.addItem(new Roo.Toolbar.Item(el));
28400     },
28401     /**
28402      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28403      * @type Roo.util.MixedCollection  
28404      */
28405     items : false,
28406      
28407     /**
28408      * Adds any Toolbar.Item or subclass
28409      * @param {Roo.Toolbar.Item} item
28410      * @return {Roo.Toolbar.Item} The item
28411      */
28412     addItem : function(item){
28413         var td = this.nextBlock();
28414         item.render(td);
28415         this.items.add(item);
28416         return item;
28417     },
28418     
28419     /**
28420      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28421      * @param {Object/Array} config A button config or array of configs
28422      * @return {Roo.Toolbar.Button/Array}
28423      */
28424     addButton : function(config){
28425         if(config instanceof Array){
28426             var buttons = [];
28427             for(var i = 0, len = config.length; i < len; i++) {
28428                 buttons.push(this.addButton(config[i]));
28429             }
28430             return buttons;
28431         }
28432         var b = config;
28433         if(!(config instanceof Roo.Toolbar.Button)){
28434             b = config.split ?
28435                 new Roo.Toolbar.SplitButton(config) :
28436                 new Roo.Toolbar.Button(config);
28437         }
28438         var td = this.nextBlock();
28439         b.render(td);
28440         this.items.add(b);
28441         return b;
28442     },
28443     
28444     /**
28445      * Adds text to the toolbar
28446      * @param {String} text The text to add
28447      * @return {Roo.Toolbar.Item} The element's item
28448      */
28449     addText : function(text){
28450         return this.addItem(new Roo.Toolbar.TextItem(text));
28451     },
28452     
28453     /**
28454      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28455      * @param {Number} index The index where the item is to be inserted
28456      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28457      * @return {Roo.Toolbar.Button/Item}
28458      */
28459     insertButton : function(index, item){
28460         if(item instanceof Array){
28461             var buttons = [];
28462             for(var i = 0, len = item.length; i < len; i++) {
28463                buttons.push(this.insertButton(index + i, item[i]));
28464             }
28465             return buttons;
28466         }
28467         if (!(item instanceof Roo.Toolbar.Button)){
28468            item = new Roo.Toolbar.Button(item);
28469         }
28470         var td = document.createElement("td");
28471         this.tr.insertBefore(td, this.tr.childNodes[index]);
28472         item.render(td);
28473         this.items.insert(index, item);
28474         return item;
28475     },
28476     
28477     /**
28478      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28479      * @param {Object} config
28480      * @return {Roo.Toolbar.Item} The element's item
28481      */
28482     addDom : function(config, returnEl){
28483         var td = this.nextBlock();
28484         Roo.DomHelper.overwrite(td, config);
28485         var ti = new Roo.Toolbar.Item(td.firstChild);
28486         ti.render(td);
28487         this.items.add(ti);
28488         return ti;
28489     },
28490
28491     /**
28492      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28493      * @type Roo.util.MixedCollection  
28494      */
28495     fields : false,
28496     
28497     /**
28498      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28499      * Note: the field should not have been rendered yet. For a field that has already been
28500      * rendered, use {@link #addElement}.
28501      * @param {Roo.form.Field} field
28502      * @return {Roo.ToolbarItem}
28503      */
28504      
28505       
28506     addField : function(field) {
28507         if (!this.fields) {
28508             var autoId = 0;
28509             this.fields = new Roo.util.MixedCollection(false, function(o){
28510                 return o.id || ("item" + (++autoId));
28511             });
28512
28513         }
28514         
28515         var td = this.nextBlock();
28516         field.render(td);
28517         var ti = new Roo.Toolbar.Item(td.firstChild);
28518         ti.render(td);
28519         this.items.add(ti);
28520         this.fields.add(field);
28521         return ti;
28522     },
28523     /**
28524      * Hide the toolbar
28525      * @method hide
28526      */
28527      
28528       
28529     hide : function()
28530     {
28531         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28532         this.el.child('div').hide();
28533     },
28534     /**
28535      * Show the toolbar
28536      * @method show
28537      */
28538     show : function()
28539     {
28540         this.el.child('div').show();
28541     },
28542       
28543     // private
28544     nextBlock : function(){
28545         var td = document.createElement("td");
28546         this.tr.appendChild(td);
28547         return td;
28548     },
28549
28550     // private
28551     destroy : function(){
28552         if(this.items){ // rendered?
28553             Roo.destroy.apply(Roo, this.items.items);
28554         }
28555         if(this.fields){ // rendered?
28556             Roo.destroy.apply(Roo, this.fields.items);
28557         }
28558         Roo.Element.uncache(this.el, this.tr);
28559     }
28560 };
28561
28562 /**
28563  * @class Roo.Toolbar.Item
28564  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28565  * @constructor
28566  * Creates a new Item
28567  * @param {HTMLElement} el 
28568  */
28569 Roo.Toolbar.Item = function(el){
28570     var cfg = {};
28571     if (typeof (el.xtype) != 'undefined') {
28572         cfg = el;
28573         el = cfg.el;
28574     }
28575     
28576     this.el = Roo.getDom(el);
28577     this.id = Roo.id(this.el);
28578     this.hidden = false;
28579     
28580     this.addEvents({
28581          /**
28582              * @event render
28583              * Fires when the button is rendered
28584              * @param {Button} this
28585              */
28586         'render': true
28587     });
28588     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
28589 };
28590 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
28591 //Roo.Toolbar.Item.prototype = {
28592     
28593     /**
28594      * Get this item's HTML Element
28595      * @return {HTMLElement}
28596      */
28597     getEl : function(){
28598        return this.el;  
28599     },
28600
28601     // private
28602     render : function(td){
28603         
28604          this.td = td;
28605         td.appendChild(this.el);
28606         
28607         this.fireEvent('render', this);
28608     },
28609     
28610     /**
28611      * Removes and destroys this item.
28612      */
28613     destroy : function(){
28614         this.td.parentNode.removeChild(this.td);
28615     },
28616     
28617     /**
28618      * Shows this item.
28619      */
28620     show: function(){
28621         this.hidden = false;
28622         this.td.style.display = "";
28623     },
28624     
28625     /**
28626      * Hides this item.
28627      */
28628     hide: function(){
28629         this.hidden = true;
28630         this.td.style.display = "none";
28631     },
28632     
28633     /**
28634      * Convenience function for boolean show/hide.
28635      * @param {Boolean} visible true to show/false to hide
28636      */
28637     setVisible: function(visible){
28638         if(visible) {
28639             this.show();
28640         }else{
28641             this.hide();
28642         }
28643     },
28644     
28645     /**
28646      * Try to focus this item.
28647      */
28648     focus : function(){
28649         Roo.fly(this.el).focus();
28650     },
28651     
28652     /**
28653      * Disables this item.
28654      */
28655     disable : function(){
28656         Roo.fly(this.td).addClass("x-item-disabled");
28657         this.disabled = true;
28658         this.el.disabled = true;
28659     },
28660     
28661     /**
28662      * Enables this item.
28663      */
28664     enable : function(){
28665         Roo.fly(this.td).removeClass("x-item-disabled");
28666         this.disabled = false;
28667         this.el.disabled = false;
28668     }
28669 });
28670
28671
28672 /**
28673  * @class Roo.Toolbar.Separator
28674  * @extends Roo.Toolbar.Item
28675  * A simple toolbar separator class
28676  * @constructor
28677  * Creates a new Separator
28678  */
28679 Roo.Toolbar.Separator = function(cfg){
28680     
28681     var s = document.createElement("span");
28682     s.className = "ytb-sep";
28683     if (cfg) {
28684         cfg.el = s;
28685     }
28686     
28687     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
28688 };
28689 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28690     enable:Roo.emptyFn,
28691     disable:Roo.emptyFn,
28692     focus:Roo.emptyFn
28693 });
28694
28695 /**
28696  * @class Roo.Toolbar.Spacer
28697  * @extends Roo.Toolbar.Item
28698  * A simple element that adds extra horizontal space to a toolbar.
28699  * @constructor
28700  * Creates a new Spacer
28701  */
28702 Roo.Toolbar.Spacer = function(cfg){
28703     var s = document.createElement("div");
28704     s.className = "ytb-spacer";
28705     if (cfg) {
28706         cfg.el = s;
28707     }
28708     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
28709 };
28710 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28711     enable:Roo.emptyFn,
28712     disable:Roo.emptyFn,
28713     focus:Roo.emptyFn
28714 });
28715
28716 /**
28717  * @class Roo.Toolbar.Fill
28718  * @extends Roo.Toolbar.Spacer
28719  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28720  * @constructor
28721  * Creates a new Spacer
28722  */
28723 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28724     // private
28725     render : function(td){
28726         td.style.width = '100%';
28727         Roo.Toolbar.Fill.superclass.render.call(this, td);
28728     }
28729 });
28730
28731 /**
28732  * @class Roo.Toolbar.TextItem
28733  * @extends Roo.Toolbar.Item
28734  * A simple class that renders text directly into a toolbar.
28735  * @constructor
28736  * Creates a new TextItem
28737  * @param {String} text
28738  */
28739 Roo.Toolbar.TextItem = function(cfg){
28740     var  text = cfg || "";
28741     if (typeof(cfg) == 'object') {
28742         text = cfg.text || "";
28743     }  else {
28744         cfg = null;
28745     }
28746     var s = document.createElement("span");
28747     s.className = "ytb-text";
28748     s.innerHTML = text;
28749     if (cfg) {
28750         cfg.el  = s;
28751     }
28752     
28753     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
28754 };
28755 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28756     
28757      
28758     enable:Roo.emptyFn,
28759     disable:Roo.emptyFn,
28760     focus:Roo.emptyFn
28761 });
28762
28763 /**
28764  * @class Roo.Toolbar.Button
28765  * @extends Roo.Button
28766  * A button that renders into a toolbar.
28767  * @constructor
28768  * Creates a new Button
28769  * @param {Object} config A standard {@link Roo.Button} config object
28770  */
28771 Roo.Toolbar.Button = function(config){
28772     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28773 };
28774 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28775     render : function(td){
28776         this.td = td;
28777         Roo.Toolbar.Button.superclass.render.call(this, td);
28778     },
28779     
28780     /**
28781      * Removes and destroys this button
28782      */
28783     destroy : function(){
28784         Roo.Toolbar.Button.superclass.destroy.call(this);
28785         this.td.parentNode.removeChild(this.td);
28786     },
28787     
28788     /**
28789      * Shows this button
28790      */
28791     show: function(){
28792         this.hidden = false;
28793         this.td.style.display = "";
28794     },
28795     
28796     /**
28797      * Hides this button
28798      */
28799     hide: function(){
28800         this.hidden = true;
28801         this.td.style.display = "none";
28802     },
28803
28804     /**
28805      * Disables this item
28806      */
28807     disable : function(){
28808         Roo.fly(this.td).addClass("x-item-disabled");
28809         this.disabled = true;
28810     },
28811
28812     /**
28813      * Enables this item
28814      */
28815     enable : function(){
28816         Roo.fly(this.td).removeClass("x-item-disabled");
28817         this.disabled = false;
28818     }
28819 });
28820 // backwards compat
28821 Roo.ToolbarButton = Roo.Toolbar.Button;
28822
28823 /**
28824  * @class Roo.Toolbar.SplitButton
28825  * @extends Roo.SplitButton
28826  * A menu button that renders into a toolbar.
28827  * @constructor
28828  * Creates a new SplitButton
28829  * @param {Object} config A standard {@link Roo.SplitButton} config object
28830  */
28831 Roo.Toolbar.SplitButton = function(config){
28832     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28833 };
28834 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28835     render : function(td){
28836         this.td = td;
28837         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28838     },
28839     
28840     /**
28841      * Removes and destroys this button
28842      */
28843     destroy : function(){
28844         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28845         this.td.parentNode.removeChild(this.td);
28846     },
28847     
28848     /**
28849      * Shows this button
28850      */
28851     show: function(){
28852         this.hidden = false;
28853         this.td.style.display = "";
28854     },
28855     
28856     /**
28857      * Hides this button
28858      */
28859     hide: function(){
28860         this.hidden = true;
28861         this.td.style.display = "none";
28862     }
28863 });
28864
28865 // backwards compat
28866 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28867  * Based on:
28868  * Ext JS Library 1.1.1
28869  * Copyright(c) 2006-2007, Ext JS, LLC.
28870  *
28871  * Originally Released Under LGPL - original licence link has changed is not relivant.
28872  *
28873  * Fork - LGPL
28874  * <script type="text/javascript">
28875  */
28876  
28877 /**
28878  * @class Roo.PagingToolbar
28879  * @extends Roo.Toolbar
28880  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28881  * @constructor
28882  * Create a new PagingToolbar
28883  * @param {Object} config The config object
28884  */
28885 Roo.PagingToolbar = function(el, ds, config)
28886 {
28887     // old args format still supported... - xtype is prefered..
28888     if (typeof(el) == 'object' && el.xtype) {
28889         // created from xtype...
28890         config = el;
28891         ds = el.dataSource;
28892         el = config.container;
28893     }
28894     var items = [];
28895     if (config.items) {
28896         items = config.items;
28897         config.items = [];
28898     }
28899     
28900     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28901     this.ds = ds;
28902     this.cursor = 0;
28903     this.renderButtons(this.el);
28904     this.bind(ds);
28905     
28906     // supprot items array.
28907    
28908     Roo.each(items, function(e) {
28909         this.add(Roo.factory(e));
28910     },this);
28911     
28912 };
28913
28914 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28915     /**
28916      * @cfg {Roo.data.Store} dataSource
28917      * The underlying data store providing the paged data
28918      */
28919     /**
28920      * @cfg {String/HTMLElement/Element} container
28921      * container The id or element that will contain the toolbar
28922      */
28923     /**
28924      * @cfg {Boolean} displayInfo
28925      * True to display the displayMsg (defaults to false)
28926      */
28927     /**
28928      * @cfg {Number} pageSize
28929      * The number of records to display per page (defaults to 20)
28930      */
28931     pageSize: 20,
28932     /**
28933      * @cfg {String} displayMsg
28934      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28935      */
28936     displayMsg : 'Displaying {0} - {1} of {2}',
28937     /**
28938      * @cfg {String} emptyMsg
28939      * The message to display when no records are found (defaults to "No data to display")
28940      */
28941     emptyMsg : 'No data to display',
28942     /**
28943      * Customizable piece of the default paging text (defaults to "Page")
28944      * @type String
28945      */
28946     beforePageText : "Page",
28947     /**
28948      * Customizable piece of the default paging text (defaults to "of %0")
28949      * @type String
28950      */
28951     afterPageText : "of {0}",
28952     /**
28953      * Customizable piece of the default paging text (defaults to "First Page")
28954      * @type String
28955      */
28956     firstText : "First Page",
28957     /**
28958      * Customizable piece of the default paging text (defaults to "Previous Page")
28959      * @type String
28960      */
28961     prevText : "Previous Page",
28962     /**
28963      * Customizable piece of the default paging text (defaults to "Next Page")
28964      * @type String
28965      */
28966     nextText : "Next Page",
28967     /**
28968      * Customizable piece of the default paging text (defaults to "Last Page")
28969      * @type String
28970      */
28971     lastText : "Last Page",
28972     /**
28973      * Customizable piece of the default paging text (defaults to "Refresh")
28974      * @type String
28975      */
28976     refreshText : "Refresh",
28977
28978     // private
28979     renderButtons : function(el){
28980         Roo.PagingToolbar.superclass.render.call(this, el);
28981         this.first = this.addButton({
28982             tooltip: this.firstText,
28983             cls: "x-btn-icon x-grid-page-first",
28984             disabled: true,
28985             handler: this.onClick.createDelegate(this, ["first"])
28986         });
28987         this.prev = this.addButton({
28988             tooltip: this.prevText,
28989             cls: "x-btn-icon x-grid-page-prev",
28990             disabled: true,
28991             handler: this.onClick.createDelegate(this, ["prev"])
28992         });
28993         //this.addSeparator();
28994         this.add(this.beforePageText);
28995         this.field = Roo.get(this.addDom({
28996            tag: "input",
28997            type: "text",
28998            size: "3",
28999            value: "1",
29000            cls: "x-grid-page-number"
29001         }).el);
29002         this.field.on("keydown", this.onPagingKeydown, this);
29003         this.field.on("focus", function(){this.dom.select();});
29004         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
29005         this.field.setHeight(18);
29006         //this.addSeparator();
29007         this.next = this.addButton({
29008             tooltip: this.nextText,
29009             cls: "x-btn-icon x-grid-page-next",
29010             disabled: true,
29011             handler: this.onClick.createDelegate(this, ["next"])
29012         });
29013         this.last = this.addButton({
29014             tooltip: this.lastText,
29015             cls: "x-btn-icon x-grid-page-last",
29016             disabled: true,
29017             handler: this.onClick.createDelegate(this, ["last"])
29018         });
29019         //this.addSeparator();
29020         this.loading = this.addButton({
29021             tooltip: this.refreshText,
29022             cls: "x-btn-icon x-grid-loading",
29023             handler: this.onClick.createDelegate(this, ["refresh"])
29024         });
29025
29026         if(this.displayInfo){
29027             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
29028         }
29029     },
29030
29031     // private
29032     updateInfo : function(){
29033         if(this.displayEl){
29034             var count = this.ds.getCount();
29035             var msg = count == 0 ?
29036                 this.emptyMsg :
29037                 String.format(
29038                     this.displayMsg,
29039                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
29040                 );
29041             this.displayEl.update(msg);
29042         }
29043     },
29044
29045     // private
29046     onLoad : function(ds, r, o){
29047        this.cursor = o.params ? o.params.start : 0;
29048        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
29049
29050        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
29051        this.field.dom.value = ap;
29052        this.first.setDisabled(ap == 1);
29053        this.prev.setDisabled(ap == 1);
29054        this.next.setDisabled(ap == ps);
29055        this.last.setDisabled(ap == ps);
29056        this.loading.enable();
29057        this.updateInfo();
29058     },
29059
29060     // private
29061     getPageData : function(){
29062         var total = this.ds.getTotalCount();
29063         return {
29064             total : total,
29065             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
29066             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
29067         };
29068     },
29069
29070     // private
29071     onLoadError : function(){
29072         this.loading.enable();
29073     },
29074
29075     // private
29076     onPagingKeydown : function(e){
29077         var k = e.getKey();
29078         var d = this.getPageData();
29079         if(k == e.RETURN){
29080             var v = this.field.dom.value, pageNum;
29081             if(!v || isNaN(pageNum = parseInt(v, 10))){
29082                 this.field.dom.value = d.activePage;
29083                 return;
29084             }
29085             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
29086             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29087             e.stopEvent();
29088         }
29089         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))
29090         {
29091           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29092           this.field.dom.value = pageNum;
29093           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29094           e.stopEvent();
29095         }
29096         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29097         {
29098           var v = this.field.dom.value, pageNum; 
29099           var increment = (e.shiftKey) ? 10 : 1;
29100           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29101             increment *= -1;
29102           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29103             this.field.dom.value = d.activePage;
29104             return;
29105           }
29106           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29107           {
29108             this.field.dom.value = parseInt(v, 10) + increment;
29109             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29110             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29111           }
29112           e.stopEvent();
29113         }
29114     },
29115
29116     // private
29117     beforeLoad : function(){
29118         if(this.loading){
29119             this.loading.disable();
29120         }
29121     },
29122
29123     // private
29124     onClick : function(which){
29125         var ds = this.ds;
29126         switch(which){
29127             case "first":
29128                 ds.load({params:{start: 0, limit: this.pageSize}});
29129             break;
29130             case "prev":
29131                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29132             break;
29133             case "next":
29134                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29135             break;
29136             case "last":
29137                 var total = ds.getTotalCount();
29138                 var extra = total % this.pageSize;
29139                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29140                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29141             break;
29142             case "refresh":
29143                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29144             break;
29145         }
29146     },
29147
29148     /**
29149      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29150      * @param {Roo.data.Store} store The data store to unbind
29151      */
29152     unbind : function(ds){
29153         ds.un("beforeload", this.beforeLoad, this);
29154         ds.un("load", this.onLoad, this);
29155         ds.un("loadexception", this.onLoadError, this);
29156         ds.un("remove", this.updateInfo, this);
29157         ds.un("add", this.updateInfo, this);
29158         this.ds = undefined;
29159     },
29160
29161     /**
29162      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29163      * @param {Roo.data.Store} store The data store to bind
29164      */
29165     bind : function(ds){
29166         ds.on("beforeload", this.beforeLoad, this);
29167         ds.on("load", this.onLoad, this);
29168         ds.on("loadexception", this.onLoadError, this);
29169         ds.on("remove", this.updateInfo, this);
29170         ds.on("add", this.updateInfo, this);
29171         this.ds = ds;
29172     }
29173 });/*
29174  * Based on:
29175  * Ext JS Library 1.1.1
29176  * Copyright(c) 2006-2007, Ext JS, LLC.
29177  *
29178  * Originally Released Under LGPL - original licence link has changed is not relivant.
29179  *
29180  * Fork - LGPL
29181  * <script type="text/javascript">
29182  */
29183
29184 /**
29185  * @class Roo.Resizable
29186  * @extends Roo.util.Observable
29187  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29188  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29189  * 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
29190  * the element will be wrapped for you automatically.</p>
29191  * <p>Here is the list of valid resize handles:</p>
29192  * <pre>
29193 Value   Description
29194 ------  -------------------
29195  'n'     north
29196  's'     south
29197  'e'     east
29198  'w'     west
29199  'nw'    northwest
29200  'sw'    southwest
29201  'se'    southeast
29202  'ne'    northeast
29203  'hd'    horizontal drag
29204  'all'   all
29205 </pre>
29206  * <p>Here's an example showing the creation of a typical Resizable:</p>
29207  * <pre><code>
29208 var resizer = new Roo.Resizable("element-id", {
29209     handles: 'all',
29210     minWidth: 200,
29211     minHeight: 100,
29212     maxWidth: 500,
29213     maxHeight: 400,
29214     pinned: true
29215 });
29216 resizer.on("resize", myHandler);
29217 </code></pre>
29218  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29219  * resizer.east.setDisplayed(false);</p>
29220  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29221  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29222  * resize operation's new size (defaults to [0, 0])
29223  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29224  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29225  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29226  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29227  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29228  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29229  * @cfg {Number} width The width of the element in pixels (defaults to null)
29230  * @cfg {Number} height The height of the element in pixels (defaults to null)
29231  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29232  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29233  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29234  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29235  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29236  * in favor of the handles config option (defaults to false)
29237  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29238  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29239  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29240  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29241  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29242  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29243  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29244  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29245  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29246  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29247  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29248  * @constructor
29249  * Create a new resizable component
29250  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29251  * @param {Object} config configuration options
29252   */
29253 Roo.Resizable = function(el, config)
29254 {
29255     this.el = Roo.get(el);
29256
29257     if(config && config.wrap){
29258         config.resizeChild = this.el;
29259         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29260         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29261         this.el.setStyle("overflow", "hidden");
29262         this.el.setPositioning(config.resizeChild.getPositioning());
29263         config.resizeChild.clearPositioning();
29264         if(!config.width || !config.height){
29265             var csize = config.resizeChild.getSize();
29266             this.el.setSize(csize.width, csize.height);
29267         }
29268         if(config.pinned && !config.adjustments){
29269             config.adjustments = "auto";
29270         }
29271     }
29272
29273     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29274     this.proxy.unselectable();
29275     this.proxy.enableDisplayMode('block');
29276
29277     Roo.apply(this, config);
29278
29279     if(this.pinned){
29280         this.disableTrackOver = true;
29281         this.el.addClass("x-resizable-pinned");
29282     }
29283     // if the element isn't positioned, make it relative
29284     var position = this.el.getStyle("position");
29285     if(position != "absolute" && position != "fixed"){
29286         this.el.setStyle("position", "relative");
29287     }
29288     if(!this.handles){ // no handles passed, must be legacy style
29289         this.handles = 's,e,se';
29290         if(this.multiDirectional){
29291             this.handles += ',n,w';
29292         }
29293     }
29294     if(this.handles == "all"){
29295         this.handles = "n s e w ne nw se sw";
29296     }
29297     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29298     var ps = Roo.Resizable.positions;
29299     for(var i = 0, len = hs.length; i < len; i++){
29300         if(hs[i] && ps[hs[i]]){
29301             var pos = ps[hs[i]];
29302             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29303         }
29304     }
29305     // legacy
29306     this.corner = this.southeast;
29307     
29308     // updateBox = the box can move..
29309     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29310         this.updateBox = true;
29311     }
29312
29313     this.activeHandle = null;
29314
29315     if(this.resizeChild){
29316         if(typeof this.resizeChild == "boolean"){
29317             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29318         }else{
29319             this.resizeChild = Roo.get(this.resizeChild, true);
29320         }
29321     }
29322     
29323     if(this.adjustments == "auto"){
29324         var rc = this.resizeChild;
29325         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29326         if(rc && (hw || hn)){
29327             rc.position("relative");
29328             rc.setLeft(hw ? hw.el.getWidth() : 0);
29329             rc.setTop(hn ? hn.el.getHeight() : 0);
29330         }
29331         this.adjustments = [
29332             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29333             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29334         ];
29335     }
29336
29337     if(this.draggable){
29338         this.dd = this.dynamic ?
29339             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29340         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29341     }
29342
29343     // public events
29344     this.addEvents({
29345         /**
29346          * @event beforeresize
29347          * Fired before resize is allowed. Set enabled to false to cancel resize.
29348          * @param {Roo.Resizable} this
29349          * @param {Roo.EventObject} e The mousedown event
29350          */
29351         "beforeresize" : true,
29352         /**
29353          * @event resizing
29354          * Fired a resizing.
29355          * @param {Roo.Resizable} this
29356          * @param {Number} x The new x position
29357          * @param {Number} y The new y position
29358          * @param {Number} w The new w width
29359          * @param {Number} h The new h hight
29360          * @param {Roo.EventObject} e The mouseup event
29361          */
29362         "resizing" : true,
29363         /**
29364          * @event resize
29365          * Fired after a resize.
29366          * @param {Roo.Resizable} this
29367          * @param {Number} width The new width
29368          * @param {Number} height The new height
29369          * @param {Roo.EventObject} e The mouseup event
29370          */
29371         "resize" : true
29372     });
29373
29374     if(this.width !== null && this.height !== null){
29375         this.resizeTo(this.width, this.height);
29376     }else{
29377         this.updateChildSize();
29378     }
29379     if(Roo.isIE){
29380         this.el.dom.style.zoom = 1;
29381     }
29382     Roo.Resizable.superclass.constructor.call(this);
29383 };
29384
29385 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29386         resizeChild : false,
29387         adjustments : [0, 0],
29388         minWidth : 5,
29389         minHeight : 5,
29390         maxWidth : 10000,
29391         maxHeight : 10000,
29392         enabled : true,
29393         animate : false,
29394         duration : .35,
29395         dynamic : false,
29396         handles : false,
29397         multiDirectional : false,
29398         disableTrackOver : false,
29399         easing : 'easeOutStrong',
29400         widthIncrement : 0,
29401         heightIncrement : 0,
29402         pinned : false,
29403         width : null,
29404         height : null,
29405         preserveRatio : false,
29406         transparent: false,
29407         minX: 0,
29408         minY: 0,
29409         draggable: false,
29410
29411         /**
29412          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29413          */
29414         constrainTo: undefined,
29415         /**
29416          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29417          */
29418         resizeRegion: undefined,
29419
29420
29421     /**
29422      * Perform a manual resize
29423      * @param {Number} width
29424      * @param {Number} height
29425      */
29426     resizeTo : function(width, height){
29427         this.el.setSize(width, height);
29428         this.updateChildSize();
29429         this.fireEvent("resize", this, width, height, null);
29430     },
29431
29432     // private
29433     startSizing : function(e, handle){
29434         this.fireEvent("beforeresize", this, e);
29435         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29436
29437             if(!this.overlay){
29438                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29439                 this.overlay.unselectable();
29440                 this.overlay.enableDisplayMode("block");
29441                 this.overlay.on("mousemove", this.onMouseMove, this);
29442                 this.overlay.on("mouseup", this.onMouseUp, this);
29443             }
29444             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29445
29446             this.resizing = true;
29447             this.startBox = this.el.getBox();
29448             this.startPoint = e.getXY();
29449             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29450                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29451
29452             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29453             this.overlay.show();
29454
29455             if(this.constrainTo) {
29456                 var ct = Roo.get(this.constrainTo);
29457                 this.resizeRegion = ct.getRegion().adjust(
29458                     ct.getFrameWidth('t'),
29459                     ct.getFrameWidth('l'),
29460                     -ct.getFrameWidth('b'),
29461                     -ct.getFrameWidth('r')
29462                 );
29463             }
29464
29465             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29466             this.proxy.show();
29467             this.proxy.setBox(this.startBox);
29468             if(!this.dynamic){
29469                 this.proxy.setStyle('visibility', 'visible');
29470             }
29471         }
29472     },
29473
29474     // private
29475     onMouseDown : function(handle, e){
29476         if(this.enabled){
29477             e.stopEvent();
29478             this.activeHandle = handle;
29479             this.startSizing(e, handle);
29480         }
29481     },
29482
29483     // private
29484     onMouseUp : function(e){
29485         var size = this.resizeElement();
29486         this.resizing = false;
29487         this.handleOut();
29488         this.overlay.hide();
29489         this.proxy.hide();
29490         this.fireEvent("resize", this, size.width, size.height, e);
29491     },
29492
29493     // private
29494     updateChildSize : function(){
29495         
29496         if(this.resizeChild){
29497             var el = this.el;
29498             var child = this.resizeChild;
29499             var adj = this.adjustments;
29500             if(el.dom.offsetWidth){
29501                 var b = el.getSize(true);
29502                 child.setSize(b.width+adj[0], b.height+adj[1]);
29503             }
29504             // Second call here for IE
29505             // The first call enables instant resizing and
29506             // the second call corrects scroll bars if they
29507             // exist
29508             if(Roo.isIE){
29509                 setTimeout(function(){
29510                     if(el.dom.offsetWidth){
29511                         var b = el.getSize(true);
29512                         child.setSize(b.width+adj[0], b.height+adj[1]);
29513                     }
29514                 }, 10);
29515             }
29516         }
29517     },
29518
29519     // private
29520     snap : function(value, inc, min){
29521         if(!inc || !value) return value;
29522         var newValue = value;
29523         var m = value % inc;
29524         if(m > 0){
29525             if(m > (inc/2)){
29526                 newValue = value + (inc-m);
29527             }else{
29528                 newValue = value - m;
29529             }
29530         }
29531         return Math.max(min, newValue);
29532     },
29533
29534     // private
29535     resizeElement : function(){
29536         var box = this.proxy.getBox();
29537         if(this.updateBox){
29538             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29539         }else{
29540             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29541         }
29542         this.updateChildSize();
29543         if(!this.dynamic){
29544             this.proxy.hide();
29545         }
29546         return box;
29547     },
29548
29549     // private
29550     constrain : function(v, diff, m, mx){
29551         if(v - diff < m){
29552             diff = v - m;
29553         }else if(v - diff > mx){
29554             diff = mx - v;
29555         }
29556         return diff;
29557     },
29558
29559     // private
29560     onMouseMove : function(e){
29561         
29562         if(this.enabled){
29563             try{// try catch so if something goes wrong the user doesn't get hung
29564
29565             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29566                 return;
29567             }
29568
29569             //var curXY = this.startPoint;
29570             var curSize = this.curSize || this.startBox;
29571             var x = this.startBox.x, y = this.startBox.y;
29572             var ox = x, oy = y;
29573             var w = curSize.width, h = curSize.height;
29574             var ow = w, oh = h;
29575             var mw = this.minWidth, mh = this.minHeight;
29576             var mxw = this.maxWidth, mxh = this.maxHeight;
29577             var wi = this.widthIncrement;
29578             var hi = this.heightIncrement;
29579
29580             var eventXY = e.getXY();
29581             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29582             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29583
29584             var pos = this.activeHandle.position;
29585
29586             switch(pos){
29587                 case "east":
29588                     w += diffX;
29589                     w = Math.min(Math.max(mw, w), mxw);
29590                     break;
29591              
29592                 case "south":
29593                     h += diffY;
29594                     h = Math.min(Math.max(mh, h), mxh);
29595                     break;
29596                 case "southeast":
29597                     w += diffX;
29598                     h += diffY;
29599                     w = Math.min(Math.max(mw, w), mxw);
29600                     h = Math.min(Math.max(mh, h), mxh);
29601                     break;
29602                 case "north":
29603                     diffY = this.constrain(h, diffY, mh, mxh);
29604                     y += diffY;
29605                     h -= diffY;
29606                     break;
29607                 case "hdrag":
29608                     
29609                     if (wi) {
29610                         var adiffX = Math.abs(diffX);
29611                         var sub = (adiffX % wi); // how much 
29612                         if (sub > (wi/2)) { // far enough to snap
29613                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29614                         } else {
29615                             // remove difference.. 
29616                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29617                         }
29618                     }
29619                     x += diffX;
29620                     x = Math.max(this.minX, x);
29621                     break;
29622                 case "west":
29623                     diffX = this.constrain(w, diffX, mw, mxw);
29624                     x += diffX;
29625                     w -= diffX;
29626                     break;
29627                 case "northeast":
29628                     w += diffX;
29629                     w = Math.min(Math.max(mw, w), mxw);
29630                     diffY = this.constrain(h, diffY, mh, mxh);
29631                     y += diffY;
29632                     h -= diffY;
29633                     break;
29634                 case "northwest":
29635                     diffX = this.constrain(w, diffX, mw, mxw);
29636                     diffY = this.constrain(h, diffY, mh, mxh);
29637                     y += diffY;
29638                     h -= diffY;
29639                     x += diffX;
29640                     w -= diffX;
29641                     break;
29642                case "southwest":
29643                     diffX = this.constrain(w, diffX, mw, mxw);
29644                     h += diffY;
29645                     h = Math.min(Math.max(mh, h), mxh);
29646                     x += diffX;
29647                     w -= diffX;
29648                     break;
29649             }
29650
29651             var sw = this.snap(w, wi, mw);
29652             var sh = this.snap(h, hi, mh);
29653             if(sw != w || sh != h){
29654                 switch(pos){
29655                     case "northeast":
29656                         y -= sh - h;
29657                     break;
29658                     case "north":
29659                         y -= sh - h;
29660                         break;
29661                     case "southwest":
29662                         x -= sw - w;
29663                     break;
29664                     case "west":
29665                         x -= sw - w;
29666                         break;
29667                     case "northwest":
29668                         x -= sw - w;
29669                         y -= sh - h;
29670                     break;
29671                 }
29672                 w = sw;
29673                 h = sh;
29674             }
29675
29676             if(this.preserveRatio){
29677                 switch(pos){
29678                     case "southeast":
29679                     case "east":
29680                         h = oh * (w/ow);
29681                         h = Math.min(Math.max(mh, h), mxh);
29682                         w = ow * (h/oh);
29683                        break;
29684                     case "south":
29685                         w = ow * (h/oh);
29686                         w = Math.min(Math.max(mw, w), mxw);
29687                         h = oh * (w/ow);
29688                         break;
29689                     case "northeast":
29690                         w = ow * (h/oh);
29691                         w = Math.min(Math.max(mw, w), mxw);
29692                         h = oh * (w/ow);
29693                     break;
29694                     case "north":
29695                         var tw = w;
29696                         w = ow * (h/oh);
29697                         w = Math.min(Math.max(mw, w), mxw);
29698                         h = oh * (w/ow);
29699                         x += (tw - w) / 2;
29700                         break;
29701                     case "southwest":
29702                         h = oh * (w/ow);
29703                         h = Math.min(Math.max(mh, h), mxh);
29704                         var tw = w;
29705                         w = ow * (h/oh);
29706                         x += tw - w;
29707                         break;
29708                     case "west":
29709                         var th = h;
29710                         h = oh * (w/ow);
29711                         h = Math.min(Math.max(mh, h), mxh);
29712                         y += (th - h) / 2;
29713                         var tw = w;
29714                         w = ow * (h/oh);
29715                         x += tw - w;
29716                        break;
29717                     case "northwest":
29718                         var tw = w;
29719                         var th = h;
29720                         h = oh * (w/ow);
29721                         h = Math.min(Math.max(mh, h), mxh);
29722                         w = ow * (h/oh);
29723                         y += th - h;
29724                         x += tw - w;
29725                        break;
29726
29727                 }
29728             }
29729             if (pos == 'hdrag') {
29730                 w = ow;
29731             }
29732             this.proxy.setBounds(x, y, w, h);
29733             if(this.dynamic){
29734                 this.resizeElement();
29735             }
29736             }catch(e){}
29737         }
29738         this.fireEvent("resizing", this, x, y, w, h, e);
29739     },
29740
29741     // private
29742     handleOver : function(){
29743         if(this.enabled){
29744             this.el.addClass("x-resizable-over");
29745         }
29746     },
29747
29748     // private
29749     handleOut : function(){
29750         if(!this.resizing){
29751             this.el.removeClass("x-resizable-over");
29752         }
29753     },
29754
29755     /**
29756      * Returns the element this component is bound to.
29757      * @return {Roo.Element}
29758      */
29759     getEl : function(){
29760         return this.el;
29761     },
29762
29763     /**
29764      * Returns the resizeChild element (or null).
29765      * @return {Roo.Element}
29766      */
29767     getResizeChild : function(){
29768         return this.resizeChild;
29769     },
29770     groupHandler : function()
29771     {
29772         
29773     },
29774     /**
29775      * Destroys this resizable. If the element was wrapped and
29776      * removeEl is not true then the element remains.
29777      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29778      */
29779     destroy : function(removeEl){
29780         this.proxy.remove();
29781         if(this.overlay){
29782             this.overlay.removeAllListeners();
29783             this.overlay.remove();
29784         }
29785         var ps = Roo.Resizable.positions;
29786         for(var k in ps){
29787             if(typeof ps[k] != "function" && this[ps[k]]){
29788                 var h = this[ps[k]];
29789                 h.el.removeAllListeners();
29790                 h.el.remove();
29791             }
29792         }
29793         if(removeEl){
29794             this.el.update("");
29795             this.el.remove();
29796         }
29797     }
29798 });
29799
29800 // private
29801 // hash to map config positions to true positions
29802 Roo.Resizable.positions = {
29803     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29804     hd: "hdrag"
29805 };
29806
29807 // private
29808 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29809     if(!this.tpl){
29810         // only initialize the template if resizable is used
29811         var tpl = Roo.DomHelper.createTemplate(
29812             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29813         );
29814         tpl.compile();
29815         Roo.Resizable.Handle.prototype.tpl = tpl;
29816     }
29817     this.position = pos;
29818     this.rz = rz;
29819     // show north drag fro topdra
29820     var handlepos = pos == 'hdrag' ? 'north' : pos;
29821     
29822     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29823     if (pos == 'hdrag') {
29824         this.el.setStyle('cursor', 'pointer');
29825     }
29826     this.el.unselectable();
29827     if(transparent){
29828         this.el.setOpacity(0);
29829     }
29830     this.el.on("mousedown", this.onMouseDown, this);
29831     if(!disableTrackOver){
29832         this.el.on("mouseover", this.onMouseOver, this);
29833         this.el.on("mouseout", this.onMouseOut, this);
29834     }
29835 };
29836
29837 // private
29838 Roo.Resizable.Handle.prototype = {
29839     afterResize : function(rz){
29840         Roo.log('after?');
29841         // do nothing
29842     },
29843     // private
29844     onMouseDown : function(e){
29845         this.rz.onMouseDown(this, e);
29846     },
29847     // private
29848     onMouseOver : function(e){
29849         this.rz.handleOver(this, e);
29850     },
29851     // private
29852     onMouseOut : function(e){
29853         this.rz.handleOut(this, e);
29854     }
29855 };/*
29856  * Based on:
29857  * Ext JS Library 1.1.1
29858  * Copyright(c) 2006-2007, Ext JS, LLC.
29859  *
29860  * Originally Released Under LGPL - original licence link has changed is not relivant.
29861  *
29862  * Fork - LGPL
29863  * <script type="text/javascript">
29864  */
29865
29866 /**
29867  * @class Roo.Editor
29868  * @extends Roo.Component
29869  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29870  * @constructor
29871  * Create a new Editor
29872  * @param {Roo.form.Field} field The Field object (or descendant)
29873  * @param {Object} config The config object
29874  */
29875 Roo.Editor = function(field, config){
29876     Roo.Editor.superclass.constructor.call(this, config);
29877     this.field = field;
29878     this.addEvents({
29879         /**
29880              * @event beforestartedit
29881              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29882              * false from the handler of this event.
29883              * @param {Editor} this
29884              * @param {Roo.Element} boundEl The underlying element bound to this editor
29885              * @param {Mixed} value The field value being set
29886              */
29887         "beforestartedit" : true,
29888         /**
29889              * @event startedit
29890              * Fires when this editor is displayed
29891              * @param {Roo.Element} boundEl The underlying element bound to this editor
29892              * @param {Mixed} value The starting field value
29893              */
29894         "startedit" : true,
29895         /**
29896              * @event beforecomplete
29897              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29898              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29899              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29900              * event will not fire since no edit actually occurred.
29901              * @param {Editor} this
29902              * @param {Mixed} value The current field value
29903              * @param {Mixed} startValue The original field value
29904              */
29905         "beforecomplete" : true,
29906         /**
29907              * @event complete
29908              * Fires after editing is complete and any changed value has been written to the underlying field.
29909              * @param {Editor} this
29910              * @param {Mixed} value The current field value
29911              * @param {Mixed} startValue The original field value
29912              */
29913         "complete" : true,
29914         /**
29915          * @event specialkey
29916          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29917          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29918          * @param {Roo.form.Field} this
29919          * @param {Roo.EventObject} e The event object
29920          */
29921         "specialkey" : true
29922     });
29923 };
29924
29925 Roo.extend(Roo.Editor, Roo.Component, {
29926     /**
29927      * @cfg {Boolean/String} autosize
29928      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29929      * or "height" to adopt the height only (defaults to false)
29930      */
29931     /**
29932      * @cfg {Boolean} revertInvalid
29933      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29934      * validation fails (defaults to true)
29935      */
29936     /**
29937      * @cfg {Boolean} ignoreNoChange
29938      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29939      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29940      * will never be ignored.
29941      */
29942     /**
29943      * @cfg {Boolean} hideEl
29944      * False to keep the bound element visible while the editor is displayed (defaults to true)
29945      */
29946     /**
29947      * @cfg {Mixed} value
29948      * The data value of the underlying field (defaults to "")
29949      */
29950     value : "",
29951     /**
29952      * @cfg {String} alignment
29953      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29954      */
29955     alignment: "c-c?",
29956     /**
29957      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29958      * for bottom-right shadow (defaults to "frame")
29959      */
29960     shadow : "frame",
29961     /**
29962      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29963      */
29964     constrain : false,
29965     /**
29966      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29967      */
29968     completeOnEnter : false,
29969     /**
29970      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29971      */
29972     cancelOnEsc : false,
29973     /**
29974      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29975      */
29976     updateEl : false,
29977
29978     // private
29979     onRender : function(ct, position){
29980         this.el = new Roo.Layer({
29981             shadow: this.shadow,
29982             cls: "x-editor",
29983             parentEl : ct,
29984             shim : this.shim,
29985             shadowOffset:4,
29986             id: this.id,
29987             constrain: this.constrain
29988         });
29989         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29990         if(this.field.msgTarget != 'title'){
29991             this.field.msgTarget = 'qtip';
29992         }
29993         this.field.render(this.el);
29994         if(Roo.isGecko){
29995             this.field.el.dom.setAttribute('autocomplete', 'off');
29996         }
29997         this.field.on("specialkey", this.onSpecialKey, this);
29998         if(this.swallowKeys){
29999             this.field.el.swallowEvent(['keydown','keypress']);
30000         }
30001         this.field.show();
30002         this.field.on("blur", this.onBlur, this);
30003         if(this.field.grow){
30004             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
30005         }
30006     },
30007
30008     onSpecialKey : function(field, e)
30009     {
30010         //Roo.log('editor onSpecialKey');
30011         if(this.completeOnEnter && e.getKey() == e.ENTER){
30012             e.stopEvent();
30013             this.completeEdit();
30014             return;
30015         }
30016         // do not fire special key otherwise it might hide close the editor...
30017         if(e.getKey() == e.ENTER){    
30018             return;
30019         }
30020         if(this.cancelOnEsc && e.getKey() == e.ESC){
30021             this.cancelEdit();
30022             return;
30023         } 
30024         this.fireEvent('specialkey', field, e);
30025     
30026     },
30027
30028     /**
30029      * Starts the editing process and shows the editor.
30030      * @param {String/HTMLElement/Element} el The element to edit
30031      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
30032       * to the innerHTML of el.
30033      */
30034     startEdit : function(el, value){
30035         if(this.editing){
30036             this.completeEdit();
30037         }
30038         this.boundEl = Roo.get(el);
30039         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
30040         if(!this.rendered){
30041             this.render(this.parentEl || document.body);
30042         }
30043         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
30044             return;
30045         }
30046         this.startValue = v;
30047         this.field.setValue(v);
30048         if(this.autoSize){
30049             var sz = this.boundEl.getSize();
30050             switch(this.autoSize){
30051                 case "width":
30052                 this.setSize(sz.width,  "");
30053                 break;
30054                 case "height":
30055                 this.setSize("",  sz.height);
30056                 break;
30057                 default:
30058                 this.setSize(sz.width,  sz.height);
30059             }
30060         }
30061         this.el.alignTo(this.boundEl, this.alignment);
30062         this.editing = true;
30063         if(Roo.QuickTips){
30064             Roo.QuickTips.disable();
30065         }
30066         this.show();
30067     },
30068
30069     /**
30070      * Sets the height and width of this editor.
30071      * @param {Number} width The new width
30072      * @param {Number} height The new height
30073      */
30074     setSize : function(w, h){
30075         this.field.setSize(w, h);
30076         if(this.el){
30077             this.el.sync();
30078         }
30079     },
30080
30081     /**
30082      * Realigns the editor to the bound field based on the current alignment config value.
30083      */
30084     realign : function(){
30085         this.el.alignTo(this.boundEl, this.alignment);
30086     },
30087
30088     /**
30089      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
30090      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30091      */
30092     completeEdit : function(remainVisible){
30093         if(!this.editing){
30094             return;
30095         }
30096         var v = this.getValue();
30097         if(this.revertInvalid !== false && !this.field.isValid()){
30098             v = this.startValue;
30099             this.cancelEdit(true);
30100         }
30101         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30102             this.editing = false;
30103             this.hide();
30104             return;
30105         }
30106         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30107             this.editing = false;
30108             if(this.updateEl && this.boundEl){
30109                 this.boundEl.update(v);
30110             }
30111             if(remainVisible !== true){
30112                 this.hide();
30113             }
30114             this.fireEvent("complete", this, v, this.startValue);
30115         }
30116     },
30117
30118     // private
30119     onShow : function(){
30120         this.el.show();
30121         if(this.hideEl !== false){
30122             this.boundEl.hide();
30123         }
30124         this.field.show();
30125         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30126             this.fixIEFocus = true;
30127             this.deferredFocus.defer(50, this);
30128         }else{
30129             this.field.focus();
30130         }
30131         this.fireEvent("startedit", this.boundEl, this.startValue);
30132     },
30133
30134     deferredFocus : function(){
30135         if(this.editing){
30136             this.field.focus();
30137         }
30138     },
30139
30140     /**
30141      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30142      * reverted to the original starting value.
30143      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30144      * cancel (defaults to false)
30145      */
30146     cancelEdit : function(remainVisible){
30147         if(this.editing){
30148             this.setValue(this.startValue);
30149             if(remainVisible !== true){
30150                 this.hide();
30151             }
30152         }
30153     },
30154
30155     // private
30156     onBlur : function(){
30157         if(this.allowBlur !== true && this.editing){
30158             this.completeEdit();
30159         }
30160     },
30161
30162     // private
30163     onHide : function(){
30164         if(this.editing){
30165             this.completeEdit();
30166             return;
30167         }
30168         this.field.blur();
30169         if(this.field.collapse){
30170             this.field.collapse();
30171         }
30172         this.el.hide();
30173         if(this.hideEl !== false){
30174             this.boundEl.show();
30175         }
30176         if(Roo.QuickTips){
30177             Roo.QuickTips.enable();
30178         }
30179     },
30180
30181     /**
30182      * Sets the data value of the editor
30183      * @param {Mixed} value Any valid value supported by the underlying field
30184      */
30185     setValue : function(v){
30186         this.field.setValue(v);
30187     },
30188
30189     /**
30190      * Gets the data value of the editor
30191      * @return {Mixed} The data value
30192      */
30193     getValue : function(){
30194         return this.field.getValue();
30195     }
30196 });/*
30197  * Based on:
30198  * Ext JS Library 1.1.1
30199  * Copyright(c) 2006-2007, Ext JS, LLC.
30200  *
30201  * Originally Released Under LGPL - original licence link has changed is not relivant.
30202  *
30203  * Fork - LGPL
30204  * <script type="text/javascript">
30205  */
30206  
30207 /**
30208  * @class Roo.BasicDialog
30209  * @extends Roo.util.Observable
30210  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30211  * <pre><code>
30212 var dlg = new Roo.BasicDialog("my-dlg", {
30213     height: 200,
30214     width: 300,
30215     minHeight: 100,
30216     minWidth: 150,
30217     modal: true,
30218     proxyDrag: true,
30219     shadow: true
30220 });
30221 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30222 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30223 dlg.addButton('Cancel', dlg.hide, dlg);
30224 dlg.show();
30225 </code></pre>
30226   <b>A Dialog should always be a direct child of the body element.</b>
30227  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30228  * @cfg {String} title Default text to display in the title bar (defaults to null)
30229  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30230  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30231  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30232  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30233  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30234  * (defaults to null with no animation)
30235  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30236  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30237  * property for valid values (defaults to 'all')
30238  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30239  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30240  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30241  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30242  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30243  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30244  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30245  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30246  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30247  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30248  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30249  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30250  * draggable = true (defaults to false)
30251  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30252  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30253  * shadow (defaults to false)
30254  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30255  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30256  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30257  * @cfg {Array} buttons Array of buttons
30258  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30259  * @constructor
30260  * Create a new BasicDialog.
30261  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30262  * @param {Object} config Configuration options
30263  */
30264 Roo.BasicDialog = function(el, config){
30265     this.el = Roo.get(el);
30266     var dh = Roo.DomHelper;
30267     if(!this.el && config && config.autoCreate){
30268         if(typeof config.autoCreate == "object"){
30269             if(!config.autoCreate.id){
30270                 config.autoCreate.id = el;
30271             }
30272             this.el = dh.append(document.body,
30273                         config.autoCreate, true);
30274         }else{
30275             this.el = dh.append(document.body,
30276                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30277         }
30278     }
30279     el = this.el;
30280     el.setDisplayed(true);
30281     el.hide = this.hideAction;
30282     this.id = el.id;
30283     el.addClass("x-dlg");
30284
30285     Roo.apply(this, config);
30286
30287     this.proxy = el.createProxy("x-dlg-proxy");
30288     this.proxy.hide = this.hideAction;
30289     this.proxy.setOpacity(.5);
30290     this.proxy.hide();
30291
30292     if(config.width){
30293         el.setWidth(config.width);
30294     }
30295     if(config.height){
30296         el.setHeight(config.height);
30297     }
30298     this.size = el.getSize();
30299     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30300         this.xy = [config.x,config.y];
30301     }else{
30302         this.xy = el.getCenterXY(true);
30303     }
30304     /** The header element @type Roo.Element */
30305     this.header = el.child("> .x-dlg-hd");
30306     /** The body element @type Roo.Element */
30307     this.body = el.child("> .x-dlg-bd");
30308     /** The footer element @type Roo.Element */
30309     this.footer = el.child("> .x-dlg-ft");
30310
30311     if(!this.header){
30312         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30313     }
30314     if(!this.body){
30315         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30316     }
30317
30318     this.header.unselectable();
30319     if(this.title){
30320         this.header.update(this.title);
30321     }
30322     // this element allows the dialog to be focused for keyboard event
30323     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30324     this.focusEl.swallowEvent("click", true);
30325
30326     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30327
30328     // wrap the body and footer for special rendering
30329     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30330     if(this.footer){
30331         this.bwrap.dom.appendChild(this.footer.dom);
30332     }
30333
30334     this.bg = this.el.createChild({
30335         tag: "div", cls:"x-dlg-bg",
30336         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30337     });
30338     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30339
30340
30341     if(this.autoScroll !== false && !this.autoTabs){
30342         this.body.setStyle("overflow", "auto");
30343     }
30344
30345     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30346
30347     if(this.closable !== false){
30348         this.el.addClass("x-dlg-closable");
30349         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30350         this.close.on("click", this.closeClick, this);
30351         this.close.addClassOnOver("x-dlg-close-over");
30352     }
30353     if(this.collapsible !== false){
30354         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30355         this.collapseBtn.on("click", this.collapseClick, this);
30356         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30357         this.header.on("dblclick", this.collapseClick, this);
30358     }
30359     if(this.resizable !== false){
30360         this.el.addClass("x-dlg-resizable");
30361         this.resizer = new Roo.Resizable(el, {
30362             minWidth: this.minWidth || 80,
30363             minHeight:this.minHeight || 80,
30364             handles: this.resizeHandles || "all",
30365             pinned: true
30366         });
30367         this.resizer.on("beforeresize", this.beforeResize, this);
30368         this.resizer.on("resize", this.onResize, this);
30369     }
30370     if(this.draggable !== false){
30371         el.addClass("x-dlg-draggable");
30372         if (!this.proxyDrag) {
30373             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30374         }
30375         else {
30376             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30377         }
30378         dd.setHandleElId(this.header.id);
30379         dd.endDrag = this.endMove.createDelegate(this);
30380         dd.startDrag = this.startMove.createDelegate(this);
30381         dd.onDrag = this.onDrag.createDelegate(this);
30382         dd.scroll = false;
30383         this.dd = dd;
30384     }
30385     if(this.modal){
30386         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30387         this.mask.enableDisplayMode("block");
30388         this.mask.hide();
30389         this.el.addClass("x-dlg-modal");
30390     }
30391     if(this.shadow){
30392         this.shadow = new Roo.Shadow({
30393             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30394             offset : this.shadowOffset
30395         });
30396     }else{
30397         this.shadowOffset = 0;
30398     }
30399     if(Roo.useShims && this.shim !== false){
30400         this.shim = this.el.createShim();
30401         this.shim.hide = this.hideAction;
30402         this.shim.hide();
30403     }else{
30404         this.shim = false;
30405     }
30406     if(this.autoTabs){
30407         this.initTabs();
30408     }
30409     if (this.buttons) { 
30410         var bts= this.buttons;
30411         this.buttons = [];
30412         Roo.each(bts, function(b) {
30413             this.addButton(b);
30414         }, this);
30415     }
30416     
30417     
30418     this.addEvents({
30419         /**
30420          * @event keydown
30421          * Fires when a key is pressed
30422          * @param {Roo.BasicDialog} this
30423          * @param {Roo.EventObject} e
30424          */
30425         "keydown" : true,
30426         /**
30427          * @event move
30428          * Fires when this dialog is moved by the user.
30429          * @param {Roo.BasicDialog} this
30430          * @param {Number} x The new page X
30431          * @param {Number} y The new page Y
30432          */
30433         "move" : true,
30434         /**
30435          * @event resize
30436          * Fires when this dialog is resized by the user.
30437          * @param {Roo.BasicDialog} this
30438          * @param {Number} width The new width
30439          * @param {Number} height The new height
30440          */
30441         "resize" : true,
30442         /**
30443          * @event beforehide
30444          * Fires before this dialog is hidden.
30445          * @param {Roo.BasicDialog} this
30446          */
30447         "beforehide" : true,
30448         /**
30449          * @event hide
30450          * Fires when this dialog is hidden.
30451          * @param {Roo.BasicDialog} this
30452          */
30453         "hide" : true,
30454         /**
30455          * @event beforeshow
30456          * Fires before this dialog is shown.
30457          * @param {Roo.BasicDialog} this
30458          */
30459         "beforeshow" : true,
30460         /**
30461          * @event show
30462          * Fires when this dialog is shown.
30463          * @param {Roo.BasicDialog} this
30464          */
30465         "show" : true
30466     });
30467     el.on("keydown", this.onKeyDown, this);
30468     el.on("mousedown", this.toFront, this);
30469     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30470     this.el.hide();
30471     Roo.DialogManager.register(this);
30472     Roo.BasicDialog.superclass.constructor.call(this);
30473 };
30474
30475 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30476     shadowOffset: Roo.isIE ? 6 : 5,
30477     minHeight: 80,
30478     minWidth: 200,
30479     minButtonWidth: 75,
30480     defaultButton: null,
30481     buttonAlign: "right",
30482     tabTag: 'div',
30483     firstShow: true,
30484
30485     /**
30486      * Sets the dialog title text
30487      * @param {String} text The title text to display
30488      * @return {Roo.BasicDialog} this
30489      */
30490     setTitle : function(text){
30491         this.header.update(text);
30492         return this;
30493     },
30494
30495     // private
30496     closeClick : function(){
30497         this.hide();
30498     },
30499
30500     // private
30501     collapseClick : function(){
30502         this[this.collapsed ? "expand" : "collapse"]();
30503     },
30504
30505     /**
30506      * Collapses the dialog to its minimized state (only the title bar is visible).
30507      * Equivalent to the user clicking the collapse dialog button.
30508      */
30509     collapse : function(){
30510         if(!this.collapsed){
30511             this.collapsed = true;
30512             this.el.addClass("x-dlg-collapsed");
30513             this.restoreHeight = this.el.getHeight();
30514             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30515         }
30516     },
30517
30518     /**
30519      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30520      * clicking the expand dialog button.
30521      */
30522     expand : function(){
30523         if(this.collapsed){
30524             this.collapsed = false;
30525             this.el.removeClass("x-dlg-collapsed");
30526             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30527         }
30528     },
30529
30530     /**
30531      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30532      * @return {Roo.TabPanel} The tabs component
30533      */
30534     initTabs : function(){
30535         var tabs = this.getTabs();
30536         while(tabs.getTab(0)){
30537             tabs.removeTab(0);
30538         }
30539         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30540             var dom = el.dom;
30541             tabs.addTab(Roo.id(dom), dom.title);
30542             dom.title = "";
30543         });
30544         tabs.activate(0);
30545         return tabs;
30546     },
30547
30548     // private
30549     beforeResize : function(){
30550         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30551     },
30552
30553     // private
30554     onResize : function(){
30555         this.refreshSize();
30556         this.syncBodyHeight();
30557         this.adjustAssets();
30558         this.focus();
30559         this.fireEvent("resize", this, this.size.width, this.size.height);
30560     },
30561
30562     // private
30563     onKeyDown : function(e){
30564         if(this.isVisible()){
30565             this.fireEvent("keydown", this, e);
30566         }
30567     },
30568
30569     /**
30570      * Resizes the dialog.
30571      * @param {Number} width
30572      * @param {Number} height
30573      * @return {Roo.BasicDialog} this
30574      */
30575     resizeTo : function(width, height){
30576         this.el.setSize(width, height);
30577         this.size = {width: width, height: height};
30578         this.syncBodyHeight();
30579         if(this.fixedcenter){
30580             this.center();
30581         }
30582         if(this.isVisible()){
30583             this.constrainXY();
30584             this.adjustAssets();
30585         }
30586         this.fireEvent("resize", this, width, height);
30587         return this;
30588     },
30589
30590
30591     /**
30592      * Resizes the dialog to fit the specified content size.
30593      * @param {Number} width
30594      * @param {Number} height
30595      * @return {Roo.BasicDialog} this
30596      */
30597     setContentSize : function(w, h){
30598         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30599         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30600         //if(!this.el.isBorderBox()){
30601             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30602             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30603         //}
30604         if(this.tabs){
30605             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30606             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30607         }
30608         this.resizeTo(w, h);
30609         return this;
30610     },
30611
30612     /**
30613      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30614      * executed in response to a particular key being pressed while the dialog is active.
30615      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30616      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30617      * @param {Function} fn The function to call
30618      * @param {Object} scope (optional) The scope of the function
30619      * @return {Roo.BasicDialog} this
30620      */
30621     addKeyListener : function(key, fn, scope){
30622         var keyCode, shift, ctrl, alt;
30623         if(typeof key == "object" && !(key instanceof Array)){
30624             keyCode = key["key"];
30625             shift = key["shift"];
30626             ctrl = key["ctrl"];
30627             alt = key["alt"];
30628         }else{
30629             keyCode = key;
30630         }
30631         var handler = function(dlg, e){
30632             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30633                 var k = e.getKey();
30634                 if(keyCode instanceof Array){
30635                     for(var i = 0, len = keyCode.length; i < len; i++){
30636                         if(keyCode[i] == k){
30637                           fn.call(scope || window, dlg, k, e);
30638                           return;
30639                         }
30640                     }
30641                 }else{
30642                     if(k == keyCode){
30643                         fn.call(scope || window, dlg, k, e);
30644                     }
30645                 }
30646             }
30647         };
30648         this.on("keydown", handler);
30649         return this;
30650     },
30651
30652     /**
30653      * Returns the TabPanel component (creates it if it doesn't exist).
30654      * Note: If you wish to simply check for the existence of tabs without creating them,
30655      * check for a null 'tabs' property.
30656      * @return {Roo.TabPanel} The tabs component
30657      */
30658     getTabs : function(){
30659         if(!this.tabs){
30660             this.el.addClass("x-dlg-auto-tabs");
30661             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30662             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30663         }
30664         return this.tabs;
30665     },
30666
30667     /**
30668      * Adds a button to the footer section of the dialog.
30669      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30670      * object or a valid Roo.DomHelper element config
30671      * @param {Function} handler The function called when the button is clicked
30672      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30673      * @return {Roo.Button} The new button
30674      */
30675     addButton : function(config, handler, scope){
30676         var dh = Roo.DomHelper;
30677         if(!this.footer){
30678             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30679         }
30680         if(!this.btnContainer){
30681             var tb = this.footer.createChild({
30682
30683                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30684                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30685             }, null, true);
30686             this.btnContainer = tb.firstChild.firstChild.firstChild;
30687         }
30688         var bconfig = {
30689             handler: handler,
30690             scope: scope,
30691             minWidth: this.minButtonWidth,
30692             hideParent:true
30693         };
30694         if(typeof config == "string"){
30695             bconfig.text = config;
30696         }else{
30697             if(config.tag){
30698                 bconfig.dhconfig = config;
30699             }else{
30700                 Roo.apply(bconfig, config);
30701             }
30702         }
30703         var fc = false;
30704         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30705             bconfig.position = Math.max(0, bconfig.position);
30706             fc = this.btnContainer.childNodes[bconfig.position];
30707         }
30708          
30709         var btn = new Roo.Button(
30710             fc ? 
30711                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30712                 : this.btnContainer.appendChild(document.createElement("td")),
30713             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30714             bconfig
30715         );
30716         this.syncBodyHeight();
30717         if(!this.buttons){
30718             /**
30719              * Array of all the buttons that have been added to this dialog via addButton
30720              * @type Array
30721              */
30722             this.buttons = [];
30723         }
30724         this.buttons.push(btn);
30725         return btn;
30726     },
30727
30728     /**
30729      * Sets the default button to be focused when the dialog is displayed.
30730      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30731      * @return {Roo.BasicDialog} this
30732      */
30733     setDefaultButton : function(btn){
30734         this.defaultButton = btn;
30735         return this;
30736     },
30737
30738     // private
30739     getHeaderFooterHeight : function(safe){
30740         var height = 0;
30741         if(this.header){
30742            height += this.header.getHeight();
30743         }
30744         if(this.footer){
30745            var fm = this.footer.getMargins();
30746             height += (this.footer.getHeight()+fm.top+fm.bottom);
30747         }
30748         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30749         height += this.centerBg.getPadding("tb");
30750         return height;
30751     },
30752
30753     // private
30754     syncBodyHeight : function()
30755     {
30756         var bd = this.body, // the text
30757             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30758             bw = this.bwrap;
30759         var height = this.size.height - this.getHeaderFooterHeight(false);
30760         bd.setHeight(height-bd.getMargins("tb"));
30761         var hh = this.header.getHeight();
30762         var h = this.size.height-hh;
30763         cb.setHeight(h);
30764         
30765         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30766         bw.setHeight(h-cb.getPadding("tb"));
30767         
30768         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30769         bd.setWidth(bw.getWidth(true));
30770         if(this.tabs){
30771             this.tabs.syncHeight();
30772             if(Roo.isIE){
30773                 this.tabs.el.repaint();
30774             }
30775         }
30776     },
30777
30778     /**
30779      * Restores the previous state of the dialog if Roo.state is configured.
30780      * @return {Roo.BasicDialog} this
30781      */
30782     restoreState : function(){
30783         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30784         if(box && box.width){
30785             this.xy = [box.x, box.y];
30786             this.resizeTo(box.width, box.height);
30787         }
30788         return this;
30789     },
30790
30791     // private
30792     beforeShow : function(){
30793         this.expand();
30794         if(this.fixedcenter){
30795             this.xy = this.el.getCenterXY(true);
30796         }
30797         if(this.modal){
30798             Roo.get(document.body).addClass("x-body-masked");
30799             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30800             this.mask.show();
30801         }
30802         this.constrainXY();
30803     },
30804
30805     // private
30806     animShow : function(){
30807         var b = Roo.get(this.animateTarget).getBox();
30808         this.proxy.setSize(b.width, b.height);
30809         this.proxy.setLocation(b.x, b.y);
30810         this.proxy.show();
30811         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30812                     true, .35, this.showEl.createDelegate(this));
30813     },
30814
30815     /**
30816      * Shows the dialog.
30817      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30818      * @return {Roo.BasicDialog} this
30819      */
30820     show : function(animateTarget){
30821         if (this.fireEvent("beforeshow", this) === false){
30822             return;
30823         }
30824         if(this.syncHeightBeforeShow){
30825             this.syncBodyHeight();
30826         }else if(this.firstShow){
30827             this.firstShow = false;
30828             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30829         }
30830         this.animateTarget = animateTarget || this.animateTarget;
30831         if(!this.el.isVisible()){
30832             this.beforeShow();
30833             if(this.animateTarget && Roo.get(this.animateTarget)){
30834                 this.animShow();
30835             }else{
30836                 this.showEl();
30837             }
30838         }
30839         return this;
30840     },
30841
30842     // private
30843     showEl : function(){
30844         this.proxy.hide();
30845         this.el.setXY(this.xy);
30846         this.el.show();
30847         this.adjustAssets(true);
30848         this.toFront();
30849         this.focus();
30850         // IE peekaboo bug - fix found by Dave Fenwick
30851         if(Roo.isIE){
30852             this.el.repaint();
30853         }
30854         this.fireEvent("show", this);
30855     },
30856
30857     /**
30858      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30859      * dialog itself will receive focus.
30860      */
30861     focus : function(){
30862         if(this.defaultButton){
30863             this.defaultButton.focus();
30864         }else{
30865             this.focusEl.focus();
30866         }
30867     },
30868
30869     // private
30870     constrainXY : function(){
30871         if(this.constraintoviewport !== false){
30872             if(!this.viewSize){
30873                 if(this.container){
30874                     var s = this.container.getSize();
30875                     this.viewSize = [s.width, s.height];
30876                 }else{
30877                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30878                 }
30879             }
30880             var s = Roo.get(this.container||document).getScroll();
30881
30882             var x = this.xy[0], y = this.xy[1];
30883             var w = this.size.width, h = this.size.height;
30884             var vw = this.viewSize[0], vh = this.viewSize[1];
30885             // only move it if it needs it
30886             var moved = false;
30887             // first validate right/bottom
30888             if(x + w > vw+s.left){
30889                 x = vw - w;
30890                 moved = true;
30891             }
30892             if(y + h > vh+s.top){
30893                 y = vh - h;
30894                 moved = true;
30895             }
30896             // then make sure top/left isn't negative
30897             if(x < s.left){
30898                 x = s.left;
30899                 moved = true;
30900             }
30901             if(y < s.top){
30902                 y = s.top;
30903                 moved = true;
30904             }
30905             if(moved){
30906                 // cache xy
30907                 this.xy = [x, y];
30908                 if(this.isVisible()){
30909                     this.el.setLocation(x, y);
30910                     this.adjustAssets();
30911                 }
30912             }
30913         }
30914     },
30915
30916     // private
30917     onDrag : function(){
30918         if(!this.proxyDrag){
30919             this.xy = this.el.getXY();
30920             this.adjustAssets();
30921         }
30922     },
30923
30924     // private
30925     adjustAssets : function(doShow){
30926         var x = this.xy[0], y = this.xy[1];
30927         var w = this.size.width, h = this.size.height;
30928         if(doShow === true){
30929             if(this.shadow){
30930                 this.shadow.show(this.el);
30931             }
30932             if(this.shim){
30933                 this.shim.show();
30934             }
30935         }
30936         if(this.shadow && this.shadow.isVisible()){
30937             this.shadow.show(this.el);
30938         }
30939         if(this.shim && this.shim.isVisible()){
30940             this.shim.setBounds(x, y, w, h);
30941         }
30942     },
30943
30944     // private
30945     adjustViewport : function(w, h){
30946         if(!w || !h){
30947             w = Roo.lib.Dom.getViewWidth();
30948             h = Roo.lib.Dom.getViewHeight();
30949         }
30950         // cache the size
30951         this.viewSize = [w, h];
30952         if(this.modal && this.mask.isVisible()){
30953             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30954             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30955         }
30956         if(this.isVisible()){
30957             this.constrainXY();
30958         }
30959     },
30960
30961     /**
30962      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30963      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30964      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30965      */
30966     destroy : function(removeEl){
30967         if(this.isVisible()){
30968             this.animateTarget = null;
30969             this.hide();
30970         }
30971         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30972         if(this.tabs){
30973             this.tabs.destroy(removeEl);
30974         }
30975         Roo.destroy(
30976              this.shim,
30977              this.proxy,
30978              this.resizer,
30979              this.close,
30980              this.mask
30981         );
30982         if(this.dd){
30983             this.dd.unreg();
30984         }
30985         if(this.buttons){
30986            for(var i = 0, len = this.buttons.length; i < len; i++){
30987                this.buttons[i].destroy();
30988            }
30989         }
30990         this.el.removeAllListeners();
30991         if(removeEl === true){
30992             this.el.update("");
30993             this.el.remove();
30994         }
30995         Roo.DialogManager.unregister(this);
30996     },
30997
30998     // private
30999     startMove : function(){
31000         if(this.proxyDrag){
31001             this.proxy.show();
31002         }
31003         if(this.constraintoviewport !== false){
31004             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
31005         }
31006     },
31007
31008     // private
31009     endMove : function(){
31010         if(!this.proxyDrag){
31011             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
31012         }else{
31013             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
31014             this.proxy.hide();
31015         }
31016         this.refreshSize();
31017         this.adjustAssets();
31018         this.focus();
31019         this.fireEvent("move", this, this.xy[0], this.xy[1]);
31020     },
31021
31022     /**
31023      * Brings this dialog to the front of any other visible dialogs
31024      * @return {Roo.BasicDialog} this
31025      */
31026     toFront : function(){
31027         Roo.DialogManager.bringToFront(this);
31028         return this;
31029     },
31030
31031     /**
31032      * Sends this dialog to the back (under) of any other visible dialogs
31033      * @return {Roo.BasicDialog} this
31034      */
31035     toBack : function(){
31036         Roo.DialogManager.sendToBack(this);
31037         return this;
31038     },
31039
31040     /**
31041      * Centers this dialog in the viewport
31042      * @return {Roo.BasicDialog} this
31043      */
31044     center : function(){
31045         var xy = this.el.getCenterXY(true);
31046         this.moveTo(xy[0], xy[1]);
31047         return this;
31048     },
31049
31050     /**
31051      * Moves the dialog's top-left corner to the specified point
31052      * @param {Number} x
31053      * @param {Number} y
31054      * @return {Roo.BasicDialog} this
31055      */
31056     moveTo : function(x, y){
31057         this.xy = [x,y];
31058         if(this.isVisible()){
31059             this.el.setXY(this.xy);
31060             this.adjustAssets();
31061         }
31062         return this;
31063     },
31064
31065     /**
31066      * Aligns the dialog to the specified element
31067      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31068      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
31069      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31070      * @return {Roo.BasicDialog} this
31071      */
31072     alignTo : function(element, position, offsets){
31073         this.xy = this.el.getAlignToXY(element, position, offsets);
31074         if(this.isVisible()){
31075             this.el.setXY(this.xy);
31076             this.adjustAssets();
31077         }
31078         return this;
31079     },
31080
31081     /**
31082      * Anchors an element to another element and realigns it when the window is resized.
31083      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31084      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
31085      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31086      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
31087      * is a number, it is used as the buffer delay (defaults to 50ms).
31088      * @return {Roo.BasicDialog} this
31089      */
31090     anchorTo : function(el, alignment, offsets, monitorScroll){
31091         var action = function(){
31092             this.alignTo(el, alignment, offsets);
31093         };
31094         Roo.EventManager.onWindowResize(action, this);
31095         var tm = typeof monitorScroll;
31096         if(tm != 'undefined'){
31097             Roo.EventManager.on(window, 'scroll', action, this,
31098                 {buffer: tm == 'number' ? monitorScroll : 50});
31099         }
31100         action.call(this);
31101         return this;
31102     },
31103
31104     /**
31105      * Returns true if the dialog is visible
31106      * @return {Boolean}
31107      */
31108     isVisible : function(){
31109         return this.el.isVisible();
31110     },
31111
31112     // private
31113     animHide : function(callback){
31114         var b = Roo.get(this.animateTarget).getBox();
31115         this.proxy.show();
31116         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31117         this.el.hide();
31118         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31119                     this.hideEl.createDelegate(this, [callback]));
31120     },
31121
31122     /**
31123      * Hides the dialog.
31124      * @param {Function} callback (optional) Function to call when the dialog is hidden
31125      * @return {Roo.BasicDialog} this
31126      */
31127     hide : function(callback){
31128         if (this.fireEvent("beforehide", this) === false){
31129             return;
31130         }
31131         if(this.shadow){
31132             this.shadow.hide();
31133         }
31134         if(this.shim) {
31135           this.shim.hide();
31136         }
31137         // sometimes animateTarget seems to get set.. causing problems...
31138         // this just double checks..
31139         if(this.animateTarget && Roo.get(this.animateTarget)) {
31140            this.animHide(callback);
31141         }else{
31142             this.el.hide();
31143             this.hideEl(callback);
31144         }
31145         return this;
31146     },
31147
31148     // private
31149     hideEl : function(callback){
31150         this.proxy.hide();
31151         if(this.modal){
31152             this.mask.hide();
31153             Roo.get(document.body).removeClass("x-body-masked");
31154         }
31155         this.fireEvent("hide", this);
31156         if(typeof callback == "function"){
31157             callback();
31158         }
31159     },
31160
31161     // private
31162     hideAction : function(){
31163         this.setLeft("-10000px");
31164         this.setTop("-10000px");
31165         this.setStyle("visibility", "hidden");
31166     },
31167
31168     // private
31169     refreshSize : function(){
31170         this.size = this.el.getSize();
31171         this.xy = this.el.getXY();
31172         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31173     },
31174
31175     // private
31176     // z-index is managed by the DialogManager and may be overwritten at any time
31177     setZIndex : function(index){
31178         if(this.modal){
31179             this.mask.setStyle("z-index", index);
31180         }
31181         if(this.shim){
31182             this.shim.setStyle("z-index", ++index);
31183         }
31184         if(this.shadow){
31185             this.shadow.setZIndex(++index);
31186         }
31187         this.el.setStyle("z-index", ++index);
31188         if(this.proxy){
31189             this.proxy.setStyle("z-index", ++index);
31190         }
31191         if(this.resizer){
31192             this.resizer.proxy.setStyle("z-index", ++index);
31193         }
31194
31195         this.lastZIndex = index;
31196     },
31197
31198     /**
31199      * Returns the element for this dialog
31200      * @return {Roo.Element} The underlying dialog Element
31201      */
31202     getEl : function(){
31203         return this.el;
31204     }
31205 });
31206
31207 /**
31208  * @class Roo.DialogManager
31209  * Provides global access to BasicDialogs that have been created and
31210  * support for z-indexing (layering) multiple open dialogs.
31211  */
31212 Roo.DialogManager = function(){
31213     var list = {};
31214     var accessList = [];
31215     var front = null;
31216
31217     // private
31218     var sortDialogs = function(d1, d2){
31219         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31220     };
31221
31222     // private
31223     var orderDialogs = function(){
31224         accessList.sort(sortDialogs);
31225         var seed = Roo.DialogManager.zseed;
31226         for(var i = 0, len = accessList.length; i < len; i++){
31227             var dlg = accessList[i];
31228             if(dlg){
31229                 dlg.setZIndex(seed + (i*10));
31230             }
31231         }
31232     };
31233
31234     return {
31235         /**
31236          * The starting z-index for BasicDialogs (defaults to 9000)
31237          * @type Number The z-index value
31238          */
31239         zseed : 9000,
31240
31241         // private
31242         register : function(dlg){
31243             list[dlg.id] = dlg;
31244             accessList.push(dlg);
31245         },
31246
31247         // private
31248         unregister : function(dlg){
31249             delete list[dlg.id];
31250             var i=0;
31251             var len=0;
31252             if(!accessList.indexOf){
31253                 for(  i = 0, len = accessList.length; i < len; i++){
31254                     if(accessList[i] == dlg){
31255                         accessList.splice(i, 1);
31256                         return;
31257                     }
31258                 }
31259             }else{
31260                  i = accessList.indexOf(dlg);
31261                 if(i != -1){
31262                     accessList.splice(i, 1);
31263                 }
31264             }
31265         },
31266
31267         /**
31268          * Gets a registered dialog by id
31269          * @param {String/Object} id The id of the dialog or a dialog
31270          * @return {Roo.BasicDialog} this
31271          */
31272         get : function(id){
31273             return typeof id == "object" ? id : list[id];
31274         },
31275
31276         /**
31277          * Brings the specified dialog to the front
31278          * @param {String/Object} dlg The id of the dialog or a dialog
31279          * @return {Roo.BasicDialog} this
31280          */
31281         bringToFront : function(dlg){
31282             dlg = this.get(dlg);
31283             if(dlg != front){
31284                 front = dlg;
31285                 dlg._lastAccess = new Date().getTime();
31286                 orderDialogs();
31287             }
31288             return dlg;
31289         },
31290
31291         /**
31292          * Sends the specified dialog to the back
31293          * @param {String/Object} dlg The id of the dialog or a dialog
31294          * @return {Roo.BasicDialog} this
31295          */
31296         sendToBack : function(dlg){
31297             dlg = this.get(dlg);
31298             dlg._lastAccess = -(new Date().getTime());
31299             orderDialogs();
31300             return dlg;
31301         },
31302
31303         /**
31304          * Hides all dialogs
31305          */
31306         hideAll : function(){
31307             for(var id in list){
31308                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31309                     list[id].hide();
31310                 }
31311             }
31312         }
31313     };
31314 }();
31315
31316 /**
31317  * @class Roo.LayoutDialog
31318  * @extends Roo.BasicDialog
31319  * Dialog which provides adjustments for working with a layout in a Dialog.
31320  * Add your necessary layout config options to the dialog's config.<br>
31321  * Example usage (including a nested layout):
31322  * <pre><code>
31323 if(!dialog){
31324     dialog = new Roo.LayoutDialog("download-dlg", {
31325         modal: true,
31326         width:600,
31327         height:450,
31328         shadow:true,
31329         minWidth:500,
31330         minHeight:350,
31331         autoTabs:true,
31332         proxyDrag:true,
31333         // layout config merges with the dialog config
31334         center:{
31335             tabPosition: "top",
31336             alwaysShowTabs: true
31337         }
31338     });
31339     dialog.addKeyListener(27, dialog.hide, dialog);
31340     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31341     dialog.addButton("Build It!", this.getDownload, this);
31342
31343     // we can even add nested layouts
31344     var innerLayout = new Roo.BorderLayout("dl-inner", {
31345         east: {
31346             initialSize: 200,
31347             autoScroll:true,
31348             split:true
31349         },
31350         center: {
31351             autoScroll:true
31352         }
31353     });
31354     innerLayout.beginUpdate();
31355     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31356     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31357     innerLayout.endUpdate(true);
31358
31359     var layout = dialog.getLayout();
31360     layout.beginUpdate();
31361     layout.add("center", new Roo.ContentPanel("standard-panel",
31362                         {title: "Download the Source", fitToFrame:true}));
31363     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31364                {title: "Build your own roo.js"}));
31365     layout.getRegion("center").showPanel(sp);
31366     layout.endUpdate();
31367 }
31368 </code></pre>
31369     * @constructor
31370     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31371     * @param {Object} config configuration options
31372   */
31373 Roo.LayoutDialog = function(el, cfg){
31374     
31375     var config=  cfg;
31376     if (typeof(cfg) == 'undefined') {
31377         config = Roo.apply({}, el);
31378         // not sure why we use documentElement here.. - it should always be body.
31379         // IE7 borks horribly if we use documentElement.
31380         // webkit also does not like documentElement - it creates a body element...
31381         el = Roo.get( document.body || document.documentElement ).createChild();
31382         //config.autoCreate = true;
31383     }
31384     
31385     
31386     config.autoTabs = false;
31387     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31388     this.body.setStyle({overflow:"hidden", position:"relative"});
31389     this.layout = new Roo.BorderLayout(this.body.dom, config);
31390     this.layout.monitorWindowResize = false;
31391     this.el.addClass("x-dlg-auto-layout");
31392     // fix case when center region overwrites center function
31393     this.center = Roo.BasicDialog.prototype.center;
31394     this.on("show", this.layout.layout, this.layout, true);
31395     if (config.items) {
31396         var xitems = config.items;
31397         delete config.items;
31398         Roo.each(xitems, this.addxtype, this);
31399     }
31400     
31401     
31402 };
31403 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31404     /**
31405      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31406      * @deprecated
31407      */
31408     endUpdate : function(){
31409         this.layout.endUpdate();
31410     },
31411
31412     /**
31413      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31414      *  @deprecated
31415      */
31416     beginUpdate : function(){
31417         this.layout.beginUpdate();
31418     },
31419
31420     /**
31421      * Get the BorderLayout for this dialog
31422      * @return {Roo.BorderLayout}
31423      */
31424     getLayout : function(){
31425         return this.layout;
31426     },
31427
31428     showEl : function(){
31429         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31430         if(Roo.isIE7){
31431             this.layout.layout();
31432         }
31433     },
31434
31435     // private
31436     // Use the syncHeightBeforeShow config option to control this automatically
31437     syncBodyHeight : function(){
31438         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31439         if(this.layout){this.layout.layout();}
31440     },
31441     
31442       /**
31443      * Add an xtype element (actually adds to the layout.)
31444      * @return {Object} xdata xtype object data.
31445      */
31446     
31447     addxtype : function(c) {
31448         return this.layout.addxtype(c);
31449     }
31450 });/*
31451  * Based on:
31452  * Ext JS Library 1.1.1
31453  * Copyright(c) 2006-2007, Ext JS, LLC.
31454  *
31455  * Originally Released Under LGPL - original licence link has changed is not relivant.
31456  *
31457  * Fork - LGPL
31458  * <script type="text/javascript">
31459  */
31460  
31461 /**
31462  * @class Roo.MessageBox
31463  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31464  * Example usage:
31465  *<pre><code>
31466 // Basic alert:
31467 Roo.Msg.alert('Status', 'Changes saved successfully.');
31468
31469 // Prompt for user data:
31470 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31471     if (btn == 'ok'){
31472         // process text value...
31473     }
31474 });
31475
31476 // Show a dialog using config options:
31477 Roo.Msg.show({
31478    title:'Save Changes?',
31479    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31480    buttons: Roo.Msg.YESNOCANCEL,
31481    fn: processResult,
31482    animEl: 'elId'
31483 });
31484 </code></pre>
31485  * @singleton
31486  */
31487 Roo.MessageBox = function(){
31488     var dlg, opt, mask, waitTimer;
31489     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31490     var buttons, activeTextEl, bwidth;
31491
31492     // private
31493     var handleButton = function(button){
31494         dlg.hide();
31495         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31496     };
31497
31498     // private
31499     var handleHide = function(){
31500         if(opt && opt.cls){
31501             dlg.el.removeClass(opt.cls);
31502         }
31503         if(waitTimer){
31504             Roo.TaskMgr.stop(waitTimer);
31505             waitTimer = null;
31506         }
31507     };
31508
31509     // private
31510     var updateButtons = function(b){
31511         var width = 0;
31512         if(!b){
31513             buttons["ok"].hide();
31514             buttons["cancel"].hide();
31515             buttons["yes"].hide();
31516             buttons["no"].hide();
31517             dlg.footer.dom.style.display = 'none';
31518             return width;
31519         }
31520         dlg.footer.dom.style.display = '';
31521         for(var k in buttons){
31522             if(typeof buttons[k] != "function"){
31523                 if(b[k]){
31524                     buttons[k].show();
31525                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31526                     width += buttons[k].el.getWidth()+15;
31527                 }else{
31528                     buttons[k].hide();
31529                 }
31530             }
31531         }
31532         return width;
31533     };
31534
31535     // private
31536     var handleEsc = function(d, k, e){
31537         if(opt && opt.closable !== false){
31538             dlg.hide();
31539         }
31540         if(e){
31541             e.stopEvent();
31542         }
31543     };
31544
31545     return {
31546         /**
31547          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31548          * @return {Roo.BasicDialog} The BasicDialog element
31549          */
31550         getDialog : function(){
31551            if(!dlg){
31552                 dlg = new Roo.BasicDialog("x-msg-box", {
31553                     autoCreate : true,
31554                     shadow: true,
31555                     draggable: true,
31556                     resizable:false,
31557                     constraintoviewport:false,
31558                     fixedcenter:true,
31559                     collapsible : false,
31560                     shim:true,
31561                     modal: true,
31562                     width:400, height:100,
31563                     buttonAlign:"center",
31564                     closeClick : function(){
31565                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31566                             handleButton("no");
31567                         }else{
31568                             handleButton("cancel");
31569                         }
31570                     }
31571                 });
31572                 dlg.on("hide", handleHide);
31573                 mask = dlg.mask;
31574                 dlg.addKeyListener(27, handleEsc);
31575                 buttons = {};
31576                 var bt = this.buttonText;
31577                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31578                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31579                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31580                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31581                 bodyEl = dlg.body.createChild({
31582
31583                     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>'
31584                 });
31585                 msgEl = bodyEl.dom.firstChild;
31586                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31587                 textboxEl.enableDisplayMode();
31588                 textboxEl.addKeyListener([10,13], function(){
31589                     if(dlg.isVisible() && opt && opt.buttons){
31590                         if(opt.buttons.ok){
31591                             handleButton("ok");
31592                         }else if(opt.buttons.yes){
31593                             handleButton("yes");
31594                         }
31595                     }
31596                 });
31597                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31598                 textareaEl.enableDisplayMode();
31599                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31600                 progressEl.enableDisplayMode();
31601                 var pf = progressEl.dom.firstChild;
31602                 if (pf) {
31603                     pp = Roo.get(pf.firstChild);
31604                     pp.setHeight(pf.offsetHeight);
31605                 }
31606                 
31607             }
31608             return dlg;
31609         },
31610
31611         /**
31612          * Updates the message box body text
31613          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31614          * the XHTML-compliant non-breaking space character '&amp;#160;')
31615          * @return {Roo.MessageBox} This message box
31616          */
31617         updateText : function(text){
31618             if(!dlg.isVisible() && !opt.width){
31619                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31620             }
31621             msgEl.innerHTML = text || '&#160;';
31622       
31623             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31624             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31625             var w = Math.max(
31626                     Math.min(opt.width || cw , this.maxWidth), 
31627                     Math.max(opt.minWidth || this.minWidth, bwidth)
31628             );
31629             if(opt.prompt){
31630                 activeTextEl.setWidth(w);
31631             }
31632             if(dlg.isVisible()){
31633                 dlg.fixedcenter = false;
31634             }
31635             // to big, make it scroll. = But as usual stupid IE does not support
31636             // !important..
31637             
31638             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31639                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31640                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31641             } else {
31642                 bodyEl.dom.style.height = '';
31643                 bodyEl.dom.style.overflowY = '';
31644             }
31645             if (cw > w) {
31646                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31647             } else {
31648                 bodyEl.dom.style.overflowX = '';
31649             }
31650             
31651             dlg.setContentSize(w, bodyEl.getHeight());
31652             if(dlg.isVisible()){
31653                 dlg.fixedcenter = true;
31654             }
31655             return this;
31656         },
31657
31658         /**
31659          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31660          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31661          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31662          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31663          * @return {Roo.MessageBox} This message box
31664          */
31665         updateProgress : function(value, text){
31666             if(text){
31667                 this.updateText(text);
31668             }
31669             if (pp) { // weird bug on my firefox - for some reason this is not defined
31670                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31671             }
31672             return this;
31673         },        
31674
31675         /**
31676          * Returns true if the message box is currently displayed
31677          * @return {Boolean} True if the message box is visible, else false
31678          */
31679         isVisible : function(){
31680             return dlg && dlg.isVisible();  
31681         },
31682
31683         /**
31684          * Hides the message box if it is displayed
31685          */
31686         hide : function(){
31687             if(this.isVisible()){
31688                 dlg.hide();
31689             }  
31690         },
31691
31692         /**
31693          * Displays a new message box, or reinitializes an existing message box, based on the config options
31694          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31695          * The following config object properties are supported:
31696          * <pre>
31697 Property    Type             Description
31698 ----------  ---------------  ------------------------------------------------------------------------------------
31699 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31700                                    closes (defaults to undefined)
31701 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31702                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31703 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31704                                    progress and wait dialogs will ignore this property and always hide the
31705                                    close button as they can only be closed programmatically.
31706 cls               String           A custom CSS class to apply to the message box element
31707 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31708                                    displayed (defaults to 75)
31709 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31710                                    function will be btn (the name of the button that was clicked, if applicable,
31711                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31712                                    Progress and wait dialogs will ignore this option since they do not respond to
31713                                    user actions and can only be closed programmatically, so any required function
31714                                    should be called by the same code after it closes the dialog.
31715 icon              String           A CSS class that provides a background image to be used as an icon for
31716                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31717 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31718 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31719 modal             Boolean          False to allow user interaction with the page while the message box is
31720                                    displayed (defaults to true)
31721 msg               String           A string that will replace the existing message box body text (defaults
31722                                    to the XHTML-compliant non-breaking space character '&#160;')
31723 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31724 progress          Boolean          True to display a progress bar (defaults to false)
31725 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31726 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31727 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31728 title             String           The title text
31729 value             String           The string value to set into the active textbox element if displayed
31730 wait              Boolean          True to display a progress bar (defaults to false)
31731 width             Number           The width of the dialog in pixels
31732 </pre>
31733          *
31734          * Example usage:
31735          * <pre><code>
31736 Roo.Msg.show({
31737    title: 'Address',
31738    msg: 'Please enter your address:',
31739    width: 300,
31740    buttons: Roo.MessageBox.OKCANCEL,
31741    multiline: true,
31742    fn: saveAddress,
31743    animEl: 'addAddressBtn'
31744 });
31745 </code></pre>
31746          * @param {Object} config Configuration options
31747          * @return {Roo.MessageBox} This message box
31748          */
31749         show : function(options)
31750         {
31751             
31752             // this causes nightmares if you show one dialog after another
31753             // especially on callbacks..
31754              
31755             if(this.isVisible()){
31756                 
31757                 this.hide();
31758                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31759                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31760                 Roo.log("New Dialog Message:" +  options.msg )
31761                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31762                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31763                 
31764             }
31765             var d = this.getDialog();
31766             opt = options;
31767             d.setTitle(opt.title || "&#160;");
31768             d.close.setDisplayed(opt.closable !== false);
31769             activeTextEl = textboxEl;
31770             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31771             if(opt.prompt){
31772                 if(opt.multiline){
31773                     textboxEl.hide();
31774                     textareaEl.show();
31775                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31776                         opt.multiline : this.defaultTextHeight);
31777                     activeTextEl = textareaEl;
31778                 }else{
31779                     textboxEl.show();
31780                     textareaEl.hide();
31781                 }
31782             }else{
31783                 textboxEl.hide();
31784                 textareaEl.hide();
31785             }
31786             progressEl.setDisplayed(opt.progress === true);
31787             this.updateProgress(0);
31788             activeTextEl.dom.value = opt.value || "";
31789             if(opt.prompt){
31790                 dlg.setDefaultButton(activeTextEl);
31791             }else{
31792                 var bs = opt.buttons;
31793                 var db = null;
31794                 if(bs && bs.ok){
31795                     db = buttons["ok"];
31796                 }else if(bs && bs.yes){
31797                     db = buttons["yes"];
31798                 }
31799                 dlg.setDefaultButton(db);
31800             }
31801             bwidth = updateButtons(opt.buttons);
31802             this.updateText(opt.msg);
31803             if(opt.cls){
31804                 d.el.addClass(opt.cls);
31805             }
31806             d.proxyDrag = opt.proxyDrag === true;
31807             d.modal = opt.modal !== false;
31808             d.mask = opt.modal !== false ? mask : false;
31809             if(!d.isVisible()){
31810                 // force it to the end of the z-index stack so it gets a cursor in FF
31811                 document.body.appendChild(dlg.el.dom);
31812                 d.animateTarget = null;
31813                 d.show(options.animEl);
31814             }
31815             return this;
31816         },
31817
31818         /**
31819          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31820          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31821          * and closing the message box when the process is complete.
31822          * @param {String} title The title bar text
31823          * @param {String} msg The message box body text
31824          * @return {Roo.MessageBox} This message box
31825          */
31826         progress : function(title, msg){
31827             this.show({
31828                 title : title,
31829                 msg : msg,
31830                 buttons: false,
31831                 progress:true,
31832                 closable:false,
31833                 minWidth: this.minProgressWidth,
31834                 modal : true
31835             });
31836             return this;
31837         },
31838
31839         /**
31840          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31841          * If a callback function is passed it will be called after the user clicks the button, and the
31842          * id of the button that was clicked will be passed as the only parameter to the callback
31843          * (could also be the top-right close button).
31844          * @param {String} title The title bar text
31845          * @param {String} msg The message box body text
31846          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31847          * @param {Object} scope (optional) The scope of the callback function
31848          * @return {Roo.MessageBox} This message box
31849          */
31850         alert : function(title, msg, fn, scope){
31851             this.show({
31852                 title : title,
31853                 msg : msg,
31854                 buttons: this.OK,
31855                 fn: fn,
31856                 scope : scope,
31857                 modal : true
31858             });
31859             return this;
31860         },
31861
31862         /**
31863          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31864          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31865          * You are responsible for closing the message box when the process is complete.
31866          * @param {String} msg The message box body text
31867          * @param {String} title (optional) The title bar text
31868          * @return {Roo.MessageBox} This message box
31869          */
31870         wait : function(msg, title){
31871             this.show({
31872                 title : title,
31873                 msg : msg,
31874                 buttons: false,
31875                 closable:false,
31876                 progress:true,
31877                 modal:true,
31878                 width:300,
31879                 wait:true
31880             });
31881             waitTimer = Roo.TaskMgr.start({
31882                 run: function(i){
31883                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31884                 },
31885                 interval: 1000
31886             });
31887             return this;
31888         },
31889
31890         /**
31891          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31892          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31893          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31894          * @param {String} title The title bar text
31895          * @param {String} msg The message box body text
31896          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31897          * @param {Object} scope (optional) The scope of the callback function
31898          * @return {Roo.MessageBox} This message box
31899          */
31900         confirm : function(title, msg, fn, scope){
31901             this.show({
31902                 title : title,
31903                 msg : msg,
31904                 buttons: this.YESNO,
31905                 fn: fn,
31906                 scope : scope,
31907                 modal : true
31908             });
31909             return this;
31910         },
31911
31912         /**
31913          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31914          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31915          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31916          * (could also be the top-right close button) and the text that was entered will be passed as the two
31917          * parameters to the callback.
31918          * @param {String} title The title bar text
31919          * @param {String} msg The message box body text
31920          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31921          * @param {Object} scope (optional) The scope of the callback function
31922          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31923          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31924          * @return {Roo.MessageBox} This message box
31925          */
31926         prompt : function(title, msg, fn, scope, multiline){
31927             this.show({
31928                 title : title,
31929                 msg : msg,
31930                 buttons: this.OKCANCEL,
31931                 fn: fn,
31932                 minWidth:250,
31933                 scope : scope,
31934                 prompt:true,
31935                 multiline: multiline,
31936                 modal : true
31937             });
31938             return this;
31939         },
31940
31941         /**
31942          * Button config that displays a single OK button
31943          * @type Object
31944          */
31945         OK : {ok:true},
31946         /**
31947          * Button config that displays Yes and No buttons
31948          * @type Object
31949          */
31950         YESNO : {yes:true, no:true},
31951         /**
31952          * Button config that displays OK and Cancel buttons
31953          * @type Object
31954          */
31955         OKCANCEL : {ok:true, cancel:true},
31956         /**
31957          * Button config that displays Yes, No and Cancel buttons
31958          * @type Object
31959          */
31960         YESNOCANCEL : {yes:true, no:true, cancel:true},
31961
31962         /**
31963          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31964          * @type Number
31965          */
31966         defaultTextHeight : 75,
31967         /**
31968          * The maximum width in pixels of the message box (defaults to 600)
31969          * @type Number
31970          */
31971         maxWidth : 600,
31972         /**
31973          * The minimum width in pixels of the message box (defaults to 100)
31974          * @type Number
31975          */
31976         minWidth : 100,
31977         /**
31978          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31979          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31980          * @type Number
31981          */
31982         minProgressWidth : 250,
31983         /**
31984          * An object containing the default button text strings that can be overriden for localized language support.
31985          * Supported properties are: ok, cancel, yes and no.
31986          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31987          * @type Object
31988          */
31989         buttonText : {
31990             ok : "OK",
31991             cancel : "Cancel",
31992             yes : "Yes",
31993             no : "No"
31994         }
31995     };
31996 }();
31997
31998 /**
31999  * Shorthand for {@link Roo.MessageBox}
32000  */
32001 Roo.Msg = Roo.MessageBox;/*
32002  * Based on:
32003  * Ext JS Library 1.1.1
32004  * Copyright(c) 2006-2007, Ext JS, LLC.
32005  *
32006  * Originally Released Under LGPL - original licence link has changed is not relivant.
32007  *
32008  * Fork - LGPL
32009  * <script type="text/javascript">
32010  */
32011 /**
32012  * @class Roo.QuickTips
32013  * Provides attractive and customizable tooltips for any element.
32014  * @singleton
32015  */
32016 Roo.QuickTips = function(){
32017     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
32018     var ce, bd, xy, dd;
32019     var visible = false, disabled = true, inited = false;
32020     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
32021     
32022     var onOver = function(e){
32023         if(disabled){
32024             return;
32025         }
32026         var t = e.getTarget();
32027         if(!t || t.nodeType !== 1 || t == document || t == document.body){
32028             return;
32029         }
32030         if(ce && t == ce.el){
32031             clearTimeout(hideProc);
32032             return;
32033         }
32034         if(t && tagEls[t.id]){
32035             tagEls[t.id].el = t;
32036             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
32037             return;
32038         }
32039         var ttp, et = Roo.fly(t);
32040         var ns = cfg.namespace;
32041         if(tm.interceptTitles && t.title){
32042             ttp = t.title;
32043             t.qtip = ttp;
32044             t.removeAttribute("title");
32045             e.preventDefault();
32046         }else{
32047             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
32048         }
32049         if(ttp){
32050             showProc = show.defer(tm.showDelay, tm, [{
32051                 el: t, 
32052                 text: ttp, 
32053                 width: et.getAttributeNS(ns, cfg.width),
32054                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
32055                 title: et.getAttributeNS(ns, cfg.title),
32056                     cls: et.getAttributeNS(ns, cfg.cls)
32057             }]);
32058         }
32059     };
32060     
32061     var onOut = function(e){
32062         clearTimeout(showProc);
32063         var t = e.getTarget();
32064         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
32065             hideProc = setTimeout(hide, tm.hideDelay);
32066         }
32067     };
32068     
32069     var onMove = function(e){
32070         if(disabled){
32071             return;
32072         }
32073         xy = e.getXY();
32074         xy[1] += 18;
32075         if(tm.trackMouse && ce){
32076             el.setXY(xy);
32077         }
32078     };
32079     
32080     var onDown = function(e){
32081         clearTimeout(showProc);
32082         clearTimeout(hideProc);
32083         if(!e.within(el)){
32084             if(tm.hideOnClick){
32085                 hide();
32086                 tm.disable();
32087                 tm.enable.defer(100, tm);
32088             }
32089         }
32090     };
32091     
32092     var getPad = function(){
32093         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32094     };
32095
32096     var show = function(o){
32097         if(disabled){
32098             return;
32099         }
32100         clearTimeout(dismissProc);
32101         ce = o;
32102         if(removeCls){ // in case manually hidden
32103             el.removeClass(removeCls);
32104             removeCls = null;
32105         }
32106         if(ce.cls){
32107             el.addClass(ce.cls);
32108             removeCls = ce.cls;
32109         }
32110         if(ce.title){
32111             tipTitle.update(ce.title);
32112             tipTitle.show();
32113         }else{
32114             tipTitle.update('');
32115             tipTitle.hide();
32116         }
32117         el.dom.style.width  = tm.maxWidth+'px';
32118         //tipBody.dom.style.width = '';
32119         tipBodyText.update(o.text);
32120         var p = getPad(), w = ce.width;
32121         if(!w){
32122             var td = tipBodyText.dom;
32123             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32124             if(aw > tm.maxWidth){
32125                 w = tm.maxWidth;
32126             }else if(aw < tm.minWidth){
32127                 w = tm.minWidth;
32128             }else{
32129                 w = aw;
32130             }
32131         }
32132         //tipBody.setWidth(w);
32133         el.setWidth(parseInt(w, 10) + p);
32134         if(ce.autoHide === false){
32135             close.setDisplayed(true);
32136             if(dd){
32137                 dd.unlock();
32138             }
32139         }else{
32140             close.setDisplayed(false);
32141             if(dd){
32142                 dd.lock();
32143             }
32144         }
32145         if(xy){
32146             el.avoidY = xy[1]-18;
32147             el.setXY(xy);
32148         }
32149         if(tm.animate){
32150             el.setOpacity(.1);
32151             el.setStyle("visibility", "visible");
32152             el.fadeIn({callback: afterShow});
32153         }else{
32154             afterShow();
32155         }
32156     };
32157     
32158     var afterShow = function(){
32159         if(ce){
32160             el.show();
32161             esc.enable();
32162             if(tm.autoDismiss && ce.autoHide !== false){
32163                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32164             }
32165         }
32166     };
32167     
32168     var hide = function(noanim){
32169         clearTimeout(dismissProc);
32170         clearTimeout(hideProc);
32171         ce = null;
32172         if(el.isVisible()){
32173             esc.disable();
32174             if(noanim !== true && tm.animate){
32175                 el.fadeOut({callback: afterHide});
32176             }else{
32177                 afterHide();
32178             } 
32179         }
32180     };
32181     
32182     var afterHide = function(){
32183         el.hide();
32184         if(removeCls){
32185             el.removeClass(removeCls);
32186             removeCls = null;
32187         }
32188     };
32189     
32190     return {
32191         /**
32192         * @cfg {Number} minWidth
32193         * The minimum width of the quick tip (defaults to 40)
32194         */
32195        minWidth : 40,
32196         /**
32197         * @cfg {Number} maxWidth
32198         * The maximum width of the quick tip (defaults to 300)
32199         */
32200        maxWidth : 300,
32201         /**
32202         * @cfg {Boolean} interceptTitles
32203         * True to automatically use the element's DOM title value if available (defaults to false)
32204         */
32205        interceptTitles : false,
32206         /**
32207         * @cfg {Boolean} trackMouse
32208         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32209         */
32210        trackMouse : false,
32211         /**
32212         * @cfg {Boolean} hideOnClick
32213         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32214         */
32215        hideOnClick : true,
32216         /**
32217         * @cfg {Number} showDelay
32218         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32219         */
32220        showDelay : 500,
32221         /**
32222         * @cfg {Number} hideDelay
32223         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32224         */
32225        hideDelay : 200,
32226         /**
32227         * @cfg {Boolean} autoHide
32228         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32229         * Used in conjunction with hideDelay.
32230         */
32231        autoHide : true,
32232         /**
32233         * @cfg {Boolean}
32234         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32235         * (defaults to true).  Used in conjunction with autoDismissDelay.
32236         */
32237        autoDismiss : true,
32238         /**
32239         * @cfg {Number}
32240         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32241         */
32242        autoDismissDelay : 5000,
32243        /**
32244         * @cfg {Boolean} animate
32245         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32246         */
32247        animate : false,
32248
32249        /**
32250         * @cfg {String} title
32251         * Title text to display (defaults to '').  This can be any valid HTML markup.
32252         */
32253         title: '',
32254        /**
32255         * @cfg {String} text
32256         * Body text to display (defaults to '').  This can be any valid HTML markup.
32257         */
32258         text : '',
32259        /**
32260         * @cfg {String} cls
32261         * A CSS class to apply to the base quick tip element (defaults to '').
32262         */
32263         cls : '',
32264        /**
32265         * @cfg {Number} width
32266         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32267         * minWidth or maxWidth.
32268         */
32269         width : null,
32270
32271     /**
32272      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32273      * or display QuickTips in a page.
32274      */
32275        init : function(){
32276           tm = Roo.QuickTips;
32277           cfg = tm.tagConfig;
32278           if(!inited){
32279               if(!Roo.isReady){ // allow calling of init() before onReady
32280                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32281                   return;
32282               }
32283               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32284               el.fxDefaults = {stopFx: true};
32285               // maximum custom styling
32286               //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>');
32287               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>');              
32288               tipTitle = el.child('h3');
32289               tipTitle.enableDisplayMode("block");
32290               tipBody = el.child('div.x-tip-bd');
32291               tipBodyText = el.child('div.x-tip-bd-inner');
32292               //bdLeft = el.child('div.x-tip-bd-left');
32293               //bdRight = el.child('div.x-tip-bd-right');
32294               close = el.child('div.x-tip-close');
32295               close.enableDisplayMode("block");
32296               close.on("click", hide);
32297               var d = Roo.get(document);
32298               d.on("mousedown", onDown);
32299               d.on("mouseover", onOver);
32300               d.on("mouseout", onOut);
32301               d.on("mousemove", onMove);
32302               esc = d.addKeyListener(27, hide);
32303               esc.disable();
32304               if(Roo.dd.DD){
32305                   dd = el.initDD("default", null, {
32306                       onDrag : function(){
32307                           el.sync();  
32308                       }
32309                   });
32310                   dd.setHandleElId(tipTitle.id);
32311                   dd.lock();
32312               }
32313               inited = true;
32314           }
32315           this.enable(); 
32316        },
32317
32318     /**
32319      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32320      * are supported:
32321      * <pre>
32322 Property    Type                   Description
32323 ----------  ---------------------  ------------------------------------------------------------------------
32324 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32325      * </ul>
32326      * @param {Object} config The config object
32327      */
32328        register : function(config){
32329            var cs = config instanceof Array ? config : arguments;
32330            for(var i = 0, len = cs.length; i < len; i++) {
32331                var c = cs[i];
32332                var target = c.target;
32333                if(target){
32334                    if(target instanceof Array){
32335                        for(var j = 0, jlen = target.length; j < jlen; j++){
32336                            tagEls[target[j]] = c;
32337                        }
32338                    }else{
32339                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32340                    }
32341                }
32342            }
32343        },
32344
32345     /**
32346      * Removes this quick tip from its element and destroys it.
32347      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32348      */
32349        unregister : function(el){
32350            delete tagEls[Roo.id(el)];
32351        },
32352
32353     /**
32354      * Enable this quick tip.
32355      */
32356        enable : function(){
32357            if(inited && disabled){
32358                locks.pop();
32359                if(locks.length < 1){
32360                    disabled = false;
32361                }
32362            }
32363        },
32364
32365     /**
32366      * Disable this quick tip.
32367      */
32368        disable : function(){
32369           disabled = true;
32370           clearTimeout(showProc);
32371           clearTimeout(hideProc);
32372           clearTimeout(dismissProc);
32373           if(ce){
32374               hide(true);
32375           }
32376           locks.push(1);
32377        },
32378
32379     /**
32380      * Returns true if the quick tip is enabled, else false.
32381      */
32382        isEnabled : function(){
32383             return !disabled;
32384        },
32385
32386         // private
32387        tagConfig : {
32388            namespace : "ext",
32389            attribute : "qtip",
32390            width : "width",
32391            target : "target",
32392            title : "qtitle",
32393            hide : "hide",
32394            cls : "qclass"
32395        }
32396    };
32397 }();
32398
32399 // backwards compat
32400 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32401  * Based on:
32402  * Ext JS Library 1.1.1
32403  * Copyright(c) 2006-2007, Ext JS, LLC.
32404  *
32405  * Originally Released Under LGPL - original licence link has changed is not relivant.
32406  *
32407  * Fork - LGPL
32408  * <script type="text/javascript">
32409  */
32410  
32411
32412 /**
32413  * @class Roo.tree.TreePanel
32414  * @extends Roo.data.Tree
32415
32416  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32417  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32418  * @cfg {Boolean} enableDD true to enable drag and drop
32419  * @cfg {Boolean} enableDrag true to enable just drag
32420  * @cfg {Boolean} enableDrop true to enable just drop
32421  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32422  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32423  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32424  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32425  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32426  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32427  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32428  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32429  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32430  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32431  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32432  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32433  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32434  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32435  * @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>
32436  * @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>
32437  * 
32438  * @constructor
32439  * @param {String/HTMLElement/Element} el The container element
32440  * @param {Object} config
32441  */
32442 Roo.tree.TreePanel = function(el, config){
32443     var root = false;
32444     var loader = false;
32445     if (config.root) {
32446         root = config.root;
32447         delete config.root;
32448     }
32449     if (config.loader) {
32450         loader = config.loader;
32451         delete config.loader;
32452     }
32453     
32454     Roo.apply(this, config);
32455     Roo.tree.TreePanel.superclass.constructor.call(this);
32456     this.el = Roo.get(el);
32457     this.el.addClass('x-tree');
32458     //console.log(root);
32459     if (root) {
32460         this.setRootNode( Roo.factory(root, Roo.tree));
32461     }
32462     if (loader) {
32463         this.loader = Roo.factory(loader, Roo.tree);
32464     }
32465    /**
32466     * Read-only. The id of the container element becomes this TreePanel's id.
32467     */
32468     this.id = this.el.id;
32469     this.addEvents({
32470         /**
32471         * @event beforeload
32472         * Fires before a node is loaded, return false to cancel
32473         * @param {Node} node The node being loaded
32474         */
32475         "beforeload" : true,
32476         /**
32477         * @event load
32478         * Fires when a node is loaded
32479         * @param {Node} node The node that was loaded
32480         */
32481         "load" : true,
32482         /**
32483         * @event textchange
32484         * Fires when the text for a node is changed
32485         * @param {Node} node The node
32486         * @param {String} text The new text
32487         * @param {String} oldText The old text
32488         */
32489         "textchange" : true,
32490         /**
32491         * @event beforeexpand
32492         * Fires before a node is expanded, return false to cancel.
32493         * @param {Node} node The node
32494         * @param {Boolean} deep
32495         * @param {Boolean} anim
32496         */
32497         "beforeexpand" : true,
32498         /**
32499         * @event beforecollapse
32500         * Fires before a node is collapsed, return false to cancel.
32501         * @param {Node} node The node
32502         * @param {Boolean} deep
32503         * @param {Boolean} anim
32504         */
32505         "beforecollapse" : true,
32506         /**
32507         * @event expand
32508         * Fires when a node is expanded
32509         * @param {Node} node The node
32510         */
32511         "expand" : true,
32512         /**
32513         * @event disabledchange
32514         * Fires when the disabled status of a node changes
32515         * @param {Node} node The node
32516         * @param {Boolean} disabled
32517         */
32518         "disabledchange" : true,
32519         /**
32520         * @event collapse
32521         * Fires when a node is collapsed
32522         * @param {Node} node The node
32523         */
32524         "collapse" : true,
32525         /**
32526         * @event beforeclick
32527         * Fires before click processing on a node. Return false to cancel the default action.
32528         * @param {Node} node The node
32529         * @param {Roo.EventObject} e The event object
32530         */
32531         "beforeclick":true,
32532         /**
32533         * @event checkchange
32534         * Fires when a node with a checkbox's checked property changes
32535         * @param {Node} this This node
32536         * @param {Boolean} checked
32537         */
32538         "checkchange":true,
32539         /**
32540         * @event click
32541         * Fires when a node is clicked
32542         * @param {Node} node The node
32543         * @param {Roo.EventObject} e The event object
32544         */
32545         "click":true,
32546         /**
32547         * @event dblclick
32548         * Fires when a node is double clicked
32549         * @param {Node} node The node
32550         * @param {Roo.EventObject} e The event object
32551         */
32552         "dblclick":true,
32553         /**
32554         * @event contextmenu
32555         * Fires when a node is right clicked
32556         * @param {Node} node The node
32557         * @param {Roo.EventObject} e The event object
32558         */
32559         "contextmenu":true,
32560         /**
32561         * @event beforechildrenrendered
32562         * Fires right before the child nodes for a node are rendered
32563         * @param {Node} node The node
32564         */
32565         "beforechildrenrendered":true,
32566         /**
32567         * @event startdrag
32568         * Fires when a node starts being dragged
32569         * @param {Roo.tree.TreePanel} this
32570         * @param {Roo.tree.TreeNode} node
32571         * @param {event} e The raw browser event
32572         */ 
32573        "startdrag" : true,
32574        /**
32575         * @event enddrag
32576         * Fires when a drag operation is complete
32577         * @param {Roo.tree.TreePanel} this
32578         * @param {Roo.tree.TreeNode} node
32579         * @param {event} e The raw browser event
32580         */
32581        "enddrag" : true,
32582        /**
32583         * @event dragdrop
32584         * Fires when a dragged node is dropped on a valid DD target
32585         * @param {Roo.tree.TreePanel} this
32586         * @param {Roo.tree.TreeNode} node
32587         * @param {DD} dd The dd it was dropped on
32588         * @param {event} e The raw browser event
32589         */
32590        "dragdrop" : true,
32591        /**
32592         * @event beforenodedrop
32593         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32594         * passed to handlers has the following properties:<br />
32595         * <ul style="padding:5px;padding-left:16px;">
32596         * <li>tree - The TreePanel</li>
32597         * <li>target - The node being targeted for the drop</li>
32598         * <li>data - The drag data from the drag source</li>
32599         * <li>point - The point of the drop - append, above or below</li>
32600         * <li>source - The drag source</li>
32601         * <li>rawEvent - Raw mouse event</li>
32602         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32603         * to be inserted by setting them on this object.</li>
32604         * <li>cancel - Set this to true to cancel the drop.</li>
32605         * </ul>
32606         * @param {Object} dropEvent
32607         */
32608        "beforenodedrop" : true,
32609        /**
32610         * @event nodedrop
32611         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32612         * passed to handlers has the following properties:<br />
32613         * <ul style="padding:5px;padding-left:16px;">
32614         * <li>tree - The TreePanel</li>
32615         * <li>target - The node being targeted for the drop</li>
32616         * <li>data - The drag data from the drag source</li>
32617         * <li>point - The point of the drop - append, above or below</li>
32618         * <li>source - The drag source</li>
32619         * <li>rawEvent - Raw mouse event</li>
32620         * <li>dropNode - Dropped node(s).</li>
32621         * </ul>
32622         * @param {Object} dropEvent
32623         */
32624        "nodedrop" : true,
32625         /**
32626         * @event nodedragover
32627         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32628         * passed to handlers has the following properties:<br />
32629         * <ul style="padding:5px;padding-left:16px;">
32630         * <li>tree - The TreePanel</li>
32631         * <li>target - The node being targeted for the drop</li>
32632         * <li>data - The drag data from the drag source</li>
32633         * <li>point - The point of the drop - append, above or below</li>
32634         * <li>source - The drag source</li>
32635         * <li>rawEvent - Raw mouse event</li>
32636         * <li>dropNode - Drop node(s) provided by the source.</li>
32637         * <li>cancel - Set this to true to signal drop not allowed.</li>
32638         * </ul>
32639         * @param {Object} dragOverEvent
32640         */
32641        "nodedragover" : true
32642         
32643     });
32644     if(this.singleExpand){
32645        this.on("beforeexpand", this.restrictExpand, this);
32646     }
32647     if (this.editor) {
32648         this.editor.tree = this;
32649         this.editor = Roo.factory(this.editor, Roo.tree);
32650     }
32651     
32652     if (this.selModel) {
32653         this.selModel = Roo.factory(this.selModel, Roo.tree);
32654     }
32655    
32656 };
32657 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32658     rootVisible : true,
32659     animate: Roo.enableFx,
32660     lines : true,
32661     enableDD : false,
32662     hlDrop : Roo.enableFx,
32663   
32664     renderer: false,
32665     
32666     rendererTip: false,
32667     // private
32668     restrictExpand : function(node){
32669         var p = node.parentNode;
32670         if(p){
32671             if(p.expandedChild && p.expandedChild.parentNode == p){
32672                 p.expandedChild.collapse();
32673             }
32674             p.expandedChild = node;
32675         }
32676     },
32677
32678     // private override
32679     setRootNode : function(node){
32680         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32681         if(!this.rootVisible){
32682             node.ui = new Roo.tree.RootTreeNodeUI(node);
32683         }
32684         return node;
32685     },
32686
32687     /**
32688      * Returns the container element for this TreePanel
32689      */
32690     getEl : function(){
32691         return this.el;
32692     },
32693
32694     /**
32695      * Returns the default TreeLoader for this TreePanel
32696      */
32697     getLoader : function(){
32698         return this.loader;
32699     },
32700
32701     /**
32702      * Expand all nodes
32703      */
32704     expandAll : function(){
32705         this.root.expand(true);
32706     },
32707
32708     /**
32709      * Collapse all nodes
32710      */
32711     collapseAll : function(){
32712         this.root.collapse(true);
32713     },
32714
32715     /**
32716      * Returns the selection model used by this TreePanel
32717      */
32718     getSelectionModel : function(){
32719         if(!this.selModel){
32720             this.selModel = new Roo.tree.DefaultSelectionModel();
32721         }
32722         return this.selModel;
32723     },
32724
32725     /**
32726      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32727      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32728      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32729      * @return {Array}
32730      */
32731     getChecked : function(a, startNode){
32732         startNode = startNode || this.root;
32733         var r = [];
32734         var f = function(){
32735             if(this.attributes.checked){
32736                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32737             }
32738         }
32739         startNode.cascade(f);
32740         return r;
32741     },
32742
32743     /**
32744      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32745      * @param {String} path
32746      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32747      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32748      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32749      */
32750     expandPath : function(path, attr, callback){
32751         attr = attr || "id";
32752         var keys = path.split(this.pathSeparator);
32753         var curNode = this.root;
32754         if(curNode.attributes[attr] != keys[1]){ // invalid root
32755             if(callback){
32756                 callback(false, null);
32757             }
32758             return;
32759         }
32760         var index = 1;
32761         var f = function(){
32762             if(++index == keys.length){
32763                 if(callback){
32764                     callback(true, curNode);
32765                 }
32766                 return;
32767             }
32768             var c = curNode.findChild(attr, keys[index]);
32769             if(!c){
32770                 if(callback){
32771                     callback(false, curNode);
32772                 }
32773                 return;
32774             }
32775             curNode = c;
32776             c.expand(false, false, f);
32777         };
32778         curNode.expand(false, false, f);
32779     },
32780
32781     /**
32782      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32783      * @param {String} path
32784      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32785      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32786      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32787      */
32788     selectPath : function(path, attr, callback){
32789         attr = attr || "id";
32790         var keys = path.split(this.pathSeparator);
32791         var v = keys.pop();
32792         if(keys.length > 0){
32793             var f = function(success, node){
32794                 if(success && node){
32795                     var n = node.findChild(attr, v);
32796                     if(n){
32797                         n.select();
32798                         if(callback){
32799                             callback(true, n);
32800                         }
32801                     }else if(callback){
32802                         callback(false, n);
32803                     }
32804                 }else{
32805                     if(callback){
32806                         callback(false, n);
32807                     }
32808                 }
32809             };
32810             this.expandPath(keys.join(this.pathSeparator), attr, f);
32811         }else{
32812             this.root.select();
32813             if(callback){
32814                 callback(true, this.root);
32815             }
32816         }
32817     },
32818
32819     getTreeEl : function(){
32820         return this.el;
32821     },
32822
32823     /**
32824      * Trigger rendering of this TreePanel
32825      */
32826     render : function(){
32827         if (this.innerCt) {
32828             return this; // stop it rendering more than once!!
32829         }
32830         
32831         this.innerCt = this.el.createChild({tag:"ul",
32832                cls:"x-tree-root-ct " +
32833                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32834
32835         if(this.containerScroll){
32836             Roo.dd.ScrollManager.register(this.el);
32837         }
32838         if((this.enableDD || this.enableDrop) && !this.dropZone){
32839            /**
32840             * The dropZone used by this tree if drop is enabled
32841             * @type Roo.tree.TreeDropZone
32842             */
32843              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32844                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32845            });
32846         }
32847         if((this.enableDD || this.enableDrag) && !this.dragZone){
32848            /**
32849             * The dragZone used by this tree if drag is enabled
32850             * @type Roo.tree.TreeDragZone
32851             */
32852             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32853                ddGroup: this.ddGroup || "TreeDD",
32854                scroll: this.ddScroll
32855            });
32856         }
32857         this.getSelectionModel().init(this);
32858         if (!this.root) {
32859             Roo.log("ROOT not set in tree");
32860             return this;
32861         }
32862         this.root.render();
32863         if(!this.rootVisible){
32864             this.root.renderChildren();
32865         }
32866         return this;
32867     }
32868 });/*
32869  * Based on:
32870  * Ext JS Library 1.1.1
32871  * Copyright(c) 2006-2007, Ext JS, LLC.
32872  *
32873  * Originally Released Under LGPL - original licence link has changed is not relivant.
32874  *
32875  * Fork - LGPL
32876  * <script type="text/javascript">
32877  */
32878  
32879
32880 /**
32881  * @class Roo.tree.DefaultSelectionModel
32882  * @extends Roo.util.Observable
32883  * The default single selection for a TreePanel.
32884  * @param {Object} cfg Configuration
32885  */
32886 Roo.tree.DefaultSelectionModel = function(cfg){
32887    this.selNode = null;
32888    
32889    
32890    
32891    this.addEvents({
32892        /**
32893         * @event selectionchange
32894         * Fires when the selected node changes
32895         * @param {DefaultSelectionModel} this
32896         * @param {TreeNode} node the new selection
32897         */
32898        "selectionchange" : true,
32899
32900        /**
32901         * @event beforeselect
32902         * Fires before the selected node changes, return false to cancel the change
32903         * @param {DefaultSelectionModel} this
32904         * @param {TreeNode} node the new selection
32905         * @param {TreeNode} node the old selection
32906         */
32907        "beforeselect" : true
32908    });
32909    
32910     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32911 };
32912
32913 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32914     init : function(tree){
32915         this.tree = tree;
32916         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32917         tree.on("click", this.onNodeClick, this);
32918     },
32919     
32920     onNodeClick : function(node, e){
32921         if (e.ctrlKey && this.selNode == node)  {
32922             this.unselect(node);
32923             return;
32924         }
32925         this.select(node);
32926     },
32927     
32928     /**
32929      * Select a node.
32930      * @param {TreeNode} node The node to select
32931      * @return {TreeNode} The selected node
32932      */
32933     select : function(node){
32934         var last = this.selNode;
32935         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32936             if(last){
32937                 last.ui.onSelectedChange(false);
32938             }
32939             this.selNode = node;
32940             node.ui.onSelectedChange(true);
32941             this.fireEvent("selectionchange", this, node, last);
32942         }
32943         return node;
32944     },
32945     
32946     /**
32947      * Deselect a node.
32948      * @param {TreeNode} node The node to unselect
32949      */
32950     unselect : function(node){
32951         if(this.selNode == node){
32952             this.clearSelections();
32953         }    
32954     },
32955     
32956     /**
32957      * Clear all selections
32958      */
32959     clearSelections : function(){
32960         var n = this.selNode;
32961         if(n){
32962             n.ui.onSelectedChange(false);
32963             this.selNode = null;
32964             this.fireEvent("selectionchange", this, null);
32965         }
32966         return n;
32967     },
32968     
32969     /**
32970      * Get the selected node
32971      * @return {TreeNode} The selected node
32972      */
32973     getSelectedNode : function(){
32974         return this.selNode;    
32975     },
32976     
32977     /**
32978      * Returns true if the node is selected
32979      * @param {TreeNode} node The node to check
32980      * @return {Boolean}
32981      */
32982     isSelected : function(node){
32983         return this.selNode == node;  
32984     },
32985
32986     /**
32987      * Selects the node above the selected node in the tree, intelligently walking the nodes
32988      * @return TreeNode The new selection
32989      */
32990     selectPrevious : function(){
32991         var s = this.selNode || this.lastSelNode;
32992         if(!s){
32993             return null;
32994         }
32995         var ps = s.previousSibling;
32996         if(ps){
32997             if(!ps.isExpanded() || ps.childNodes.length < 1){
32998                 return this.select(ps);
32999             } else{
33000                 var lc = ps.lastChild;
33001                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
33002                     lc = lc.lastChild;
33003                 }
33004                 return this.select(lc);
33005             }
33006         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
33007             return this.select(s.parentNode);
33008         }
33009         return null;
33010     },
33011
33012     /**
33013      * Selects the node above the selected node in the tree, intelligently walking the nodes
33014      * @return TreeNode The new selection
33015      */
33016     selectNext : function(){
33017         var s = this.selNode || this.lastSelNode;
33018         if(!s){
33019             return null;
33020         }
33021         if(s.firstChild && s.isExpanded()){
33022              return this.select(s.firstChild);
33023          }else if(s.nextSibling){
33024              return this.select(s.nextSibling);
33025          }else if(s.parentNode){
33026             var newS = null;
33027             s.parentNode.bubble(function(){
33028                 if(this.nextSibling){
33029                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
33030                     return false;
33031                 }
33032             });
33033             return newS;
33034          }
33035         return null;
33036     },
33037
33038     onKeyDown : function(e){
33039         var s = this.selNode || this.lastSelNode;
33040         // undesirable, but required
33041         var sm = this;
33042         if(!s){
33043             return;
33044         }
33045         var k = e.getKey();
33046         switch(k){
33047              case e.DOWN:
33048                  e.stopEvent();
33049                  this.selectNext();
33050              break;
33051              case e.UP:
33052                  e.stopEvent();
33053                  this.selectPrevious();
33054              break;
33055              case e.RIGHT:
33056                  e.preventDefault();
33057                  if(s.hasChildNodes()){
33058                      if(!s.isExpanded()){
33059                          s.expand();
33060                      }else if(s.firstChild){
33061                          this.select(s.firstChild, e);
33062                      }
33063                  }
33064              break;
33065              case e.LEFT:
33066                  e.preventDefault();
33067                  if(s.hasChildNodes() && s.isExpanded()){
33068                      s.collapse();
33069                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
33070                      this.select(s.parentNode, e);
33071                  }
33072              break;
33073         };
33074     }
33075 });
33076
33077 /**
33078  * @class Roo.tree.MultiSelectionModel
33079  * @extends Roo.util.Observable
33080  * Multi selection for a TreePanel.
33081  * @param {Object} cfg Configuration
33082  */
33083 Roo.tree.MultiSelectionModel = function(){
33084    this.selNodes = [];
33085    this.selMap = {};
33086    this.addEvents({
33087        /**
33088         * @event selectionchange
33089         * Fires when the selected nodes change
33090         * @param {MultiSelectionModel} this
33091         * @param {Array} nodes Array of the selected nodes
33092         */
33093        "selectionchange" : true
33094    });
33095    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33096    
33097 };
33098
33099 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33100     init : function(tree){
33101         this.tree = tree;
33102         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33103         tree.on("click", this.onNodeClick, this);
33104     },
33105     
33106     onNodeClick : function(node, e){
33107         this.select(node, e, e.ctrlKey);
33108     },
33109     
33110     /**
33111      * Select a node.
33112      * @param {TreeNode} node The node to select
33113      * @param {EventObject} e (optional) An event associated with the selection
33114      * @param {Boolean} keepExisting True to retain existing selections
33115      * @return {TreeNode} The selected node
33116      */
33117     select : function(node, e, keepExisting){
33118         if(keepExisting !== true){
33119             this.clearSelections(true);
33120         }
33121         if(this.isSelected(node)){
33122             this.lastSelNode = node;
33123             return node;
33124         }
33125         this.selNodes.push(node);
33126         this.selMap[node.id] = node;
33127         this.lastSelNode = node;
33128         node.ui.onSelectedChange(true);
33129         this.fireEvent("selectionchange", this, this.selNodes);
33130         return node;
33131     },
33132     
33133     /**
33134      * Deselect a node.
33135      * @param {TreeNode} node The node to unselect
33136      */
33137     unselect : function(node){
33138         if(this.selMap[node.id]){
33139             node.ui.onSelectedChange(false);
33140             var sn = this.selNodes;
33141             var index = -1;
33142             if(sn.indexOf){
33143                 index = sn.indexOf(node);
33144             }else{
33145                 for(var i = 0, len = sn.length; i < len; i++){
33146                     if(sn[i] == node){
33147                         index = i;
33148                         break;
33149                     }
33150                 }
33151             }
33152             if(index != -1){
33153                 this.selNodes.splice(index, 1);
33154             }
33155             delete this.selMap[node.id];
33156             this.fireEvent("selectionchange", this, this.selNodes);
33157         }
33158     },
33159     
33160     /**
33161      * Clear all selections
33162      */
33163     clearSelections : function(suppressEvent){
33164         var sn = this.selNodes;
33165         if(sn.length > 0){
33166             for(var i = 0, len = sn.length; i < len; i++){
33167                 sn[i].ui.onSelectedChange(false);
33168             }
33169             this.selNodes = [];
33170             this.selMap = {};
33171             if(suppressEvent !== true){
33172                 this.fireEvent("selectionchange", this, this.selNodes);
33173             }
33174         }
33175     },
33176     
33177     /**
33178      * Returns true if the node is selected
33179      * @param {TreeNode} node The node to check
33180      * @return {Boolean}
33181      */
33182     isSelected : function(node){
33183         return this.selMap[node.id] ? true : false;  
33184     },
33185     
33186     /**
33187      * Returns an array of the selected nodes
33188      * @return {Array}
33189      */
33190     getSelectedNodes : function(){
33191         return this.selNodes;    
33192     },
33193
33194     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33195
33196     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33197
33198     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33199 });/*
33200  * Based on:
33201  * Ext JS Library 1.1.1
33202  * Copyright(c) 2006-2007, Ext JS, LLC.
33203  *
33204  * Originally Released Under LGPL - original licence link has changed is not relivant.
33205  *
33206  * Fork - LGPL
33207  * <script type="text/javascript">
33208  */
33209  
33210 /**
33211  * @class Roo.tree.TreeNode
33212  * @extends Roo.data.Node
33213  * @cfg {String} text The text for this node
33214  * @cfg {Boolean} expanded true to start the node expanded
33215  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33216  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33217  * @cfg {Boolean} disabled true to start the node disabled
33218  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33219  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33220  * @cfg {String} cls A css class to be added to the node
33221  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33222  * @cfg {String} href URL of the link used for the node (defaults to #)
33223  * @cfg {String} hrefTarget target frame for the link
33224  * @cfg {String} qtip An Ext QuickTip for the node
33225  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33226  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33227  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33228  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33229  * (defaults to undefined with no checkbox rendered)
33230  * @constructor
33231  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33232  */
33233 Roo.tree.TreeNode = function(attributes){
33234     attributes = attributes || {};
33235     if(typeof attributes == "string"){
33236         attributes = {text: attributes};
33237     }
33238     this.childrenRendered = false;
33239     this.rendered = false;
33240     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33241     this.expanded = attributes.expanded === true;
33242     this.isTarget = attributes.isTarget !== false;
33243     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33244     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33245
33246     /**
33247      * Read-only. The text for this node. To change it use setText().
33248      * @type String
33249      */
33250     this.text = attributes.text;
33251     /**
33252      * True if this node is disabled.
33253      * @type Boolean
33254      */
33255     this.disabled = attributes.disabled === true;
33256
33257     this.addEvents({
33258         /**
33259         * @event textchange
33260         * Fires when the text for this node is changed
33261         * @param {Node} this This node
33262         * @param {String} text The new text
33263         * @param {String} oldText The old text
33264         */
33265         "textchange" : true,
33266         /**
33267         * @event beforeexpand
33268         * Fires before this node is expanded, return false to cancel.
33269         * @param {Node} this This node
33270         * @param {Boolean} deep
33271         * @param {Boolean} anim
33272         */
33273         "beforeexpand" : true,
33274         /**
33275         * @event beforecollapse
33276         * Fires before this node is collapsed, return false to cancel.
33277         * @param {Node} this This node
33278         * @param {Boolean} deep
33279         * @param {Boolean} anim
33280         */
33281         "beforecollapse" : true,
33282         /**
33283         * @event expand
33284         * Fires when this node is expanded
33285         * @param {Node} this This node
33286         */
33287         "expand" : true,
33288         /**
33289         * @event disabledchange
33290         * Fires when the disabled status of this node changes
33291         * @param {Node} this This node
33292         * @param {Boolean} disabled
33293         */
33294         "disabledchange" : true,
33295         /**
33296         * @event collapse
33297         * Fires when this node is collapsed
33298         * @param {Node} this This node
33299         */
33300         "collapse" : true,
33301         /**
33302         * @event beforeclick
33303         * Fires before click processing. Return false to cancel the default action.
33304         * @param {Node} this This node
33305         * @param {Roo.EventObject} e The event object
33306         */
33307         "beforeclick":true,
33308         /**
33309         * @event checkchange
33310         * Fires when a node with a checkbox's checked property changes
33311         * @param {Node} this This node
33312         * @param {Boolean} checked
33313         */
33314         "checkchange":true,
33315         /**
33316         * @event click
33317         * Fires when this node is clicked
33318         * @param {Node} this This node
33319         * @param {Roo.EventObject} e The event object
33320         */
33321         "click":true,
33322         /**
33323         * @event dblclick
33324         * Fires when this node is double clicked
33325         * @param {Node} this This node
33326         * @param {Roo.EventObject} e The event object
33327         */
33328         "dblclick":true,
33329         /**
33330         * @event contextmenu
33331         * Fires when this node is right clicked
33332         * @param {Node} this This node
33333         * @param {Roo.EventObject} e The event object
33334         */
33335         "contextmenu":true,
33336         /**
33337         * @event beforechildrenrendered
33338         * Fires right before the child nodes for this node are rendered
33339         * @param {Node} this This node
33340         */
33341         "beforechildrenrendered":true
33342     });
33343
33344     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33345
33346     /**
33347      * Read-only. The UI for this node
33348      * @type TreeNodeUI
33349      */
33350     this.ui = new uiClass(this);
33351     
33352     // finally support items[]
33353     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33354         return;
33355     }
33356     
33357     
33358     Roo.each(this.attributes.items, function(c) {
33359         this.appendChild(Roo.factory(c,Roo.Tree));
33360     }, this);
33361     delete this.attributes.items;
33362     
33363     
33364     
33365 };
33366 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33367     preventHScroll: true,
33368     /**
33369      * Returns true if this node is expanded
33370      * @return {Boolean}
33371      */
33372     isExpanded : function(){
33373         return this.expanded;
33374     },
33375
33376     /**
33377      * Returns the UI object for this node
33378      * @return {TreeNodeUI}
33379      */
33380     getUI : function(){
33381         return this.ui;
33382     },
33383
33384     // private override
33385     setFirstChild : function(node){
33386         var of = this.firstChild;
33387         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33388         if(this.childrenRendered && of && node != of){
33389             of.renderIndent(true, true);
33390         }
33391         if(this.rendered){
33392             this.renderIndent(true, true);
33393         }
33394     },
33395
33396     // private override
33397     setLastChild : function(node){
33398         var ol = this.lastChild;
33399         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33400         if(this.childrenRendered && ol && node != ol){
33401             ol.renderIndent(true, true);
33402         }
33403         if(this.rendered){
33404             this.renderIndent(true, true);
33405         }
33406     },
33407
33408     // these methods are overridden to provide lazy rendering support
33409     // private override
33410     appendChild : function()
33411     {
33412         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33413         if(node && this.childrenRendered){
33414             node.render();
33415         }
33416         this.ui.updateExpandIcon();
33417         return node;
33418     },
33419
33420     // private override
33421     removeChild : function(node){
33422         this.ownerTree.getSelectionModel().unselect(node);
33423         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33424         // if it's been rendered remove dom node
33425         if(this.childrenRendered){
33426             node.ui.remove();
33427         }
33428         if(this.childNodes.length < 1){
33429             this.collapse(false, false);
33430         }else{
33431             this.ui.updateExpandIcon();
33432         }
33433         if(!this.firstChild) {
33434             this.childrenRendered = false;
33435         }
33436         return node;
33437     },
33438
33439     // private override
33440     insertBefore : function(node, refNode){
33441         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33442         if(newNode && refNode && this.childrenRendered){
33443             node.render();
33444         }
33445         this.ui.updateExpandIcon();
33446         return newNode;
33447     },
33448
33449     /**
33450      * Sets the text for this node
33451      * @param {String} text
33452      */
33453     setText : function(text){
33454         var oldText = this.text;
33455         this.text = text;
33456         this.attributes.text = text;
33457         if(this.rendered){ // event without subscribing
33458             this.ui.onTextChange(this, text, oldText);
33459         }
33460         this.fireEvent("textchange", this, text, oldText);
33461     },
33462
33463     /**
33464      * Triggers selection of this node
33465      */
33466     select : function(){
33467         this.getOwnerTree().getSelectionModel().select(this);
33468     },
33469
33470     /**
33471      * Triggers deselection of this node
33472      */
33473     unselect : function(){
33474         this.getOwnerTree().getSelectionModel().unselect(this);
33475     },
33476
33477     /**
33478      * Returns true if this node is selected
33479      * @return {Boolean}
33480      */
33481     isSelected : function(){
33482         return this.getOwnerTree().getSelectionModel().isSelected(this);
33483     },
33484
33485     /**
33486      * Expand this node.
33487      * @param {Boolean} deep (optional) True to expand all children as well
33488      * @param {Boolean} anim (optional) false to cancel the default animation
33489      * @param {Function} callback (optional) A callback to be called when
33490      * expanding this node completes (does not wait for deep expand to complete).
33491      * Called with 1 parameter, this node.
33492      */
33493     expand : function(deep, anim, callback){
33494         if(!this.expanded){
33495             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33496                 return;
33497             }
33498             if(!this.childrenRendered){
33499                 this.renderChildren();
33500             }
33501             this.expanded = true;
33502             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33503                 this.ui.animExpand(function(){
33504                     this.fireEvent("expand", this);
33505                     if(typeof callback == "function"){
33506                         callback(this);
33507                     }
33508                     if(deep === true){
33509                         this.expandChildNodes(true);
33510                     }
33511                 }.createDelegate(this));
33512                 return;
33513             }else{
33514                 this.ui.expand();
33515                 this.fireEvent("expand", this);
33516                 if(typeof callback == "function"){
33517                     callback(this);
33518                 }
33519             }
33520         }else{
33521            if(typeof callback == "function"){
33522                callback(this);
33523            }
33524         }
33525         if(deep === true){
33526             this.expandChildNodes(true);
33527         }
33528     },
33529
33530     isHiddenRoot : function(){
33531         return this.isRoot && !this.getOwnerTree().rootVisible;
33532     },
33533
33534     /**
33535      * Collapse this node.
33536      * @param {Boolean} deep (optional) True to collapse all children as well
33537      * @param {Boolean} anim (optional) false to cancel the default animation
33538      */
33539     collapse : function(deep, anim){
33540         if(this.expanded && !this.isHiddenRoot()){
33541             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33542                 return;
33543             }
33544             this.expanded = false;
33545             if((this.getOwnerTree().animate && anim !== false) || anim){
33546                 this.ui.animCollapse(function(){
33547                     this.fireEvent("collapse", this);
33548                     if(deep === true){
33549                         this.collapseChildNodes(true);
33550                     }
33551                 }.createDelegate(this));
33552                 return;
33553             }else{
33554                 this.ui.collapse();
33555                 this.fireEvent("collapse", this);
33556             }
33557         }
33558         if(deep === true){
33559             var cs = this.childNodes;
33560             for(var i = 0, len = cs.length; i < len; i++) {
33561                 cs[i].collapse(true, false);
33562             }
33563         }
33564     },
33565
33566     // private
33567     delayedExpand : function(delay){
33568         if(!this.expandProcId){
33569             this.expandProcId = this.expand.defer(delay, this);
33570         }
33571     },
33572
33573     // private
33574     cancelExpand : function(){
33575         if(this.expandProcId){
33576             clearTimeout(this.expandProcId);
33577         }
33578         this.expandProcId = false;
33579     },
33580
33581     /**
33582      * Toggles expanded/collapsed state of the node
33583      */
33584     toggle : function(){
33585         if(this.expanded){
33586             this.collapse();
33587         }else{
33588             this.expand();
33589         }
33590     },
33591
33592     /**
33593      * Ensures all parent nodes are expanded
33594      */
33595     ensureVisible : function(callback){
33596         var tree = this.getOwnerTree();
33597         tree.expandPath(this.parentNode.getPath(), false, function(){
33598             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33599             Roo.callback(callback);
33600         }.createDelegate(this));
33601     },
33602
33603     /**
33604      * Expand all child nodes
33605      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33606      */
33607     expandChildNodes : function(deep){
33608         var cs = this.childNodes;
33609         for(var i = 0, len = cs.length; i < len; i++) {
33610                 cs[i].expand(deep);
33611         }
33612     },
33613
33614     /**
33615      * Collapse all child nodes
33616      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33617      */
33618     collapseChildNodes : function(deep){
33619         var cs = this.childNodes;
33620         for(var i = 0, len = cs.length; i < len; i++) {
33621                 cs[i].collapse(deep);
33622         }
33623     },
33624
33625     /**
33626      * Disables this node
33627      */
33628     disable : function(){
33629         this.disabled = true;
33630         this.unselect();
33631         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33632             this.ui.onDisableChange(this, true);
33633         }
33634         this.fireEvent("disabledchange", this, true);
33635     },
33636
33637     /**
33638      * Enables this node
33639      */
33640     enable : function(){
33641         this.disabled = false;
33642         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33643             this.ui.onDisableChange(this, false);
33644         }
33645         this.fireEvent("disabledchange", this, false);
33646     },
33647
33648     // private
33649     renderChildren : function(suppressEvent){
33650         if(suppressEvent !== false){
33651             this.fireEvent("beforechildrenrendered", this);
33652         }
33653         var cs = this.childNodes;
33654         for(var i = 0, len = cs.length; i < len; i++){
33655             cs[i].render(true);
33656         }
33657         this.childrenRendered = true;
33658     },
33659
33660     // private
33661     sort : function(fn, scope){
33662         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33663         if(this.childrenRendered){
33664             var cs = this.childNodes;
33665             for(var i = 0, len = cs.length; i < len; i++){
33666                 cs[i].render(true);
33667             }
33668         }
33669     },
33670
33671     // private
33672     render : function(bulkRender){
33673         this.ui.render(bulkRender);
33674         if(!this.rendered){
33675             this.rendered = true;
33676             if(this.expanded){
33677                 this.expanded = false;
33678                 this.expand(false, false);
33679             }
33680         }
33681     },
33682
33683     // private
33684     renderIndent : function(deep, refresh){
33685         if(refresh){
33686             this.ui.childIndent = null;
33687         }
33688         this.ui.renderIndent();
33689         if(deep === true && this.childrenRendered){
33690             var cs = this.childNodes;
33691             for(var i = 0, len = cs.length; i < len; i++){
33692                 cs[i].renderIndent(true, refresh);
33693             }
33694         }
33695     }
33696 });/*
33697  * Based on:
33698  * Ext JS Library 1.1.1
33699  * Copyright(c) 2006-2007, Ext JS, LLC.
33700  *
33701  * Originally Released Under LGPL - original licence link has changed is not relivant.
33702  *
33703  * Fork - LGPL
33704  * <script type="text/javascript">
33705  */
33706  
33707 /**
33708  * @class Roo.tree.AsyncTreeNode
33709  * @extends Roo.tree.TreeNode
33710  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33711  * @constructor
33712  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33713  */
33714  Roo.tree.AsyncTreeNode = function(config){
33715     this.loaded = false;
33716     this.loading = false;
33717     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33718     /**
33719     * @event beforeload
33720     * Fires before this node is loaded, return false to cancel
33721     * @param {Node} this This node
33722     */
33723     this.addEvents({'beforeload':true, 'load': true});
33724     /**
33725     * @event load
33726     * Fires when this node is loaded
33727     * @param {Node} this This node
33728     */
33729     /**
33730      * The loader used by this node (defaults to using the tree's defined loader)
33731      * @type TreeLoader
33732      * @property loader
33733      */
33734 };
33735 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33736     expand : function(deep, anim, callback){
33737         if(this.loading){ // if an async load is already running, waiting til it's done
33738             var timer;
33739             var f = function(){
33740                 if(!this.loading){ // done loading
33741                     clearInterval(timer);
33742                     this.expand(deep, anim, callback);
33743                 }
33744             }.createDelegate(this);
33745             timer = setInterval(f, 200);
33746             return;
33747         }
33748         if(!this.loaded){
33749             if(this.fireEvent("beforeload", this) === false){
33750                 return;
33751             }
33752             this.loading = true;
33753             this.ui.beforeLoad(this);
33754             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33755             if(loader){
33756                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33757                 return;
33758             }
33759         }
33760         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33761     },
33762     
33763     /**
33764      * Returns true if this node is currently loading
33765      * @return {Boolean}
33766      */
33767     isLoading : function(){
33768         return this.loading;  
33769     },
33770     
33771     loadComplete : function(deep, anim, callback){
33772         this.loading = false;
33773         this.loaded = true;
33774         this.ui.afterLoad(this);
33775         this.fireEvent("load", this);
33776         this.expand(deep, anim, callback);
33777     },
33778     
33779     /**
33780      * Returns true if this node has been loaded
33781      * @return {Boolean}
33782      */
33783     isLoaded : function(){
33784         return this.loaded;
33785     },
33786     
33787     hasChildNodes : function(){
33788         if(!this.isLeaf() && !this.loaded){
33789             return true;
33790         }else{
33791             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33792         }
33793     },
33794
33795     /**
33796      * Trigger a reload for this node
33797      * @param {Function} callback
33798      */
33799     reload : function(callback){
33800         this.collapse(false, false);
33801         while(this.firstChild){
33802             this.removeChild(this.firstChild);
33803         }
33804         this.childrenRendered = false;
33805         this.loaded = false;
33806         if(this.isHiddenRoot()){
33807             this.expanded = false;
33808         }
33809         this.expand(false, false, callback);
33810     }
33811 });/*
33812  * Based on:
33813  * Ext JS Library 1.1.1
33814  * Copyright(c) 2006-2007, Ext JS, LLC.
33815  *
33816  * Originally Released Under LGPL - original licence link has changed is not relivant.
33817  *
33818  * Fork - LGPL
33819  * <script type="text/javascript">
33820  */
33821  
33822 /**
33823  * @class Roo.tree.TreeNodeUI
33824  * @constructor
33825  * @param {Object} node The node to render
33826  * The TreeNode UI implementation is separate from the
33827  * tree implementation. Unless you are customizing the tree UI,
33828  * you should never have to use this directly.
33829  */
33830 Roo.tree.TreeNodeUI = function(node){
33831     this.node = node;
33832     this.rendered = false;
33833     this.animating = false;
33834     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33835 };
33836
33837 Roo.tree.TreeNodeUI.prototype = {
33838     removeChild : function(node){
33839         if(this.rendered){
33840             this.ctNode.removeChild(node.ui.getEl());
33841         }
33842     },
33843
33844     beforeLoad : function(){
33845          this.addClass("x-tree-node-loading");
33846     },
33847
33848     afterLoad : function(){
33849          this.removeClass("x-tree-node-loading");
33850     },
33851
33852     onTextChange : function(node, text, oldText){
33853         if(this.rendered){
33854             this.textNode.innerHTML = text;
33855         }
33856     },
33857
33858     onDisableChange : function(node, state){
33859         this.disabled = state;
33860         if(state){
33861             this.addClass("x-tree-node-disabled");
33862         }else{
33863             this.removeClass("x-tree-node-disabled");
33864         }
33865     },
33866
33867     onSelectedChange : function(state){
33868         if(state){
33869             this.focus();
33870             this.addClass("x-tree-selected");
33871         }else{
33872             //this.blur();
33873             this.removeClass("x-tree-selected");
33874         }
33875     },
33876
33877     onMove : function(tree, node, oldParent, newParent, index, refNode){
33878         this.childIndent = null;
33879         if(this.rendered){
33880             var targetNode = newParent.ui.getContainer();
33881             if(!targetNode){//target not rendered
33882                 this.holder = document.createElement("div");
33883                 this.holder.appendChild(this.wrap);
33884                 return;
33885             }
33886             var insertBefore = refNode ? refNode.ui.getEl() : null;
33887             if(insertBefore){
33888                 targetNode.insertBefore(this.wrap, insertBefore);
33889             }else{
33890                 targetNode.appendChild(this.wrap);
33891             }
33892             this.node.renderIndent(true);
33893         }
33894     },
33895
33896     addClass : function(cls){
33897         if(this.elNode){
33898             Roo.fly(this.elNode).addClass(cls);
33899         }
33900     },
33901
33902     removeClass : function(cls){
33903         if(this.elNode){
33904             Roo.fly(this.elNode).removeClass(cls);
33905         }
33906     },
33907
33908     remove : function(){
33909         if(this.rendered){
33910             this.holder = document.createElement("div");
33911             this.holder.appendChild(this.wrap);
33912         }
33913     },
33914
33915     fireEvent : function(){
33916         return this.node.fireEvent.apply(this.node, arguments);
33917     },
33918
33919     initEvents : function(){
33920         this.node.on("move", this.onMove, this);
33921         var E = Roo.EventManager;
33922         var a = this.anchor;
33923
33924         var el = Roo.fly(a, '_treeui');
33925
33926         if(Roo.isOpera){ // opera render bug ignores the CSS
33927             el.setStyle("text-decoration", "none");
33928         }
33929
33930         el.on("click", this.onClick, this);
33931         el.on("dblclick", this.onDblClick, this);
33932
33933         if(this.checkbox){
33934             Roo.EventManager.on(this.checkbox,
33935                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33936         }
33937
33938         el.on("contextmenu", this.onContextMenu, this);
33939
33940         var icon = Roo.fly(this.iconNode);
33941         icon.on("click", this.onClick, this);
33942         icon.on("dblclick", this.onDblClick, this);
33943         icon.on("contextmenu", this.onContextMenu, this);
33944         E.on(this.ecNode, "click", this.ecClick, this, true);
33945
33946         if(this.node.disabled){
33947             this.addClass("x-tree-node-disabled");
33948         }
33949         if(this.node.hidden){
33950             this.addClass("x-tree-node-disabled");
33951         }
33952         var ot = this.node.getOwnerTree();
33953         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33954         if(dd && (!this.node.isRoot || ot.rootVisible)){
33955             Roo.dd.Registry.register(this.elNode, {
33956                 node: this.node,
33957                 handles: this.getDDHandles(),
33958                 isHandle: false
33959             });
33960         }
33961     },
33962
33963     getDDHandles : function(){
33964         return [this.iconNode, this.textNode];
33965     },
33966
33967     hide : function(){
33968         if(this.rendered){
33969             this.wrap.style.display = "none";
33970         }
33971     },
33972
33973     show : function(){
33974         if(this.rendered){
33975             this.wrap.style.display = "";
33976         }
33977     },
33978
33979     onContextMenu : function(e){
33980         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33981             e.preventDefault();
33982             this.focus();
33983             this.fireEvent("contextmenu", this.node, e);
33984         }
33985     },
33986
33987     onClick : function(e){
33988         if(this.dropping){
33989             e.stopEvent();
33990             return;
33991         }
33992         if(this.fireEvent("beforeclick", this.node, e) !== false){
33993             if(!this.disabled && this.node.attributes.href){
33994                 this.fireEvent("click", this.node, e);
33995                 return;
33996             }
33997             e.preventDefault();
33998             if(this.disabled){
33999                 return;
34000             }
34001
34002             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
34003                 this.node.toggle();
34004             }
34005
34006             this.fireEvent("click", this.node, e);
34007         }else{
34008             e.stopEvent();
34009         }
34010     },
34011
34012     onDblClick : function(e){
34013         e.preventDefault();
34014         if(this.disabled){
34015             return;
34016         }
34017         if(this.checkbox){
34018             this.toggleCheck();
34019         }
34020         if(!this.animating && this.node.hasChildNodes()){
34021             this.node.toggle();
34022         }
34023         this.fireEvent("dblclick", this.node, e);
34024     },
34025
34026     onCheckChange : function(){
34027         var checked = this.checkbox.checked;
34028         this.node.attributes.checked = checked;
34029         this.fireEvent('checkchange', this.node, checked);
34030     },
34031
34032     ecClick : function(e){
34033         if(!this.animating && this.node.hasChildNodes()){
34034             this.node.toggle();
34035         }
34036     },
34037
34038     startDrop : function(){
34039         this.dropping = true;
34040     },
34041
34042     // delayed drop so the click event doesn't get fired on a drop
34043     endDrop : function(){
34044        setTimeout(function(){
34045            this.dropping = false;
34046        }.createDelegate(this), 50);
34047     },
34048
34049     expand : function(){
34050         this.updateExpandIcon();
34051         this.ctNode.style.display = "";
34052     },
34053
34054     focus : function(){
34055         if(!this.node.preventHScroll){
34056             try{this.anchor.focus();
34057             }catch(e){}
34058         }else if(!Roo.isIE){
34059             try{
34060                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
34061                 var l = noscroll.scrollLeft;
34062                 this.anchor.focus();
34063                 noscroll.scrollLeft = l;
34064             }catch(e){}
34065         }
34066     },
34067
34068     toggleCheck : function(value){
34069         var cb = this.checkbox;
34070         if(cb){
34071             cb.checked = (value === undefined ? !cb.checked : value);
34072         }
34073     },
34074
34075     blur : function(){
34076         try{
34077             this.anchor.blur();
34078         }catch(e){}
34079     },
34080
34081     animExpand : function(callback){
34082         var ct = Roo.get(this.ctNode);
34083         ct.stopFx();
34084         if(!this.node.hasChildNodes()){
34085             this.updateExpandIcon();
34086             this.ctNode.style.display = "";
34087             Roo.callback(callback);
34088             return;
34089         }
34090         this.animating = true;
34091         this.updateExpandIcon();
34092
34093         ct.slideIn('t', {
34094            callback : function(){
34095                this.animating = false;
34096                Roo.callback(callback);
34097             },
34098             scope: this,
34099             duration: this.node.ownerTree.duration || .25
34100         });
34101     },
34102
34103     highlight : function(){
34104         var tree = this.node.getOwnerTree();
34105         Roo.fly(this.wrap).highlight(
34106             tree.hlColor || "C3DAF9",
34107             {endColor: tree.hlBaseColor}
34108         );
34109     },
34110
34111     collapse : function(){
34112         this.updateExpandIcon();
34113         this.ctNode.style.display = "none";
34114     },
34115
34116     animCollapse : function(callback){
34117         var ct = Roo.get(this.ctNode);
34118         ct.enableDisplayMode('block');
34119         ct.stopFx();
34120
34121         this.animating = true;
34122         this.updateExpandIcon();
34123
34124         ct.slideOut('t', {
34125             callback : function(){
34126                this.animating = false;
34127                Roo.callback(callback);
34128             },
34129             scope: this,
34130             duration: this.node.ownerTree.duration || .25
34131         });
34132     },
34133
34134     getContainer : function(){
34135         return this.ctNode;
34136     },
34137
34138     getEl : function(){
34139         return this.wrap;
34140     },
34141
34142     appendDDGhost : function(ghostNode){
34143         ghostNode.appendChild(this.elNode.cloneNode(true));
34144     },
34145
34146     getDDRepairXY : function(){
34147         return Roo.lib.Dom.getXY(this.iconNode);
34148     },
34149
34150     onRender : function(){
34151         this.render();
34152     },
34153
34154     render : function(bulkRender){
34155         var n = this.node, a = n.attributes;
34156         var targetNode = n.parentNode ?
34157               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34158
34159         if(!this.rendered){
34160             this.rendered = true;
34161
34162             this.renderElements(n, a, targetNode, bulkRender);
34163
34164             if(a.qtip){
34165                if(this.textNode.setAttributeNS){
34166                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34167                    if(a.qtipTitle){
34168                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34169                    }
34170                }else{
34171                    this.textNode.setAttribute("ext:qtip", a.qtip);
34172                    if(a.qtipTitle){
34173                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34174                    }
34175                }
34176             }else if(a.qtipCfg){
34177                 a.qtipCfg.target = Roo.id(this.textNode);
34178                 Roo.QuickTips.register(a.qtipCfg);
34179             }
34180             this.initEvents();
34181             if(!this.node.expanded){
34182                 this.updateExpandIcon();
34183             }
34184         }else{
34185             if(bulkRender === true) {
34186                 targetNode.appendChild(this.wrap);
34187             }
34188         }
34189     },
34190
34191     renderElements : function(n, a, targetNode, bulkRender)
34192     {
34193         // add some indent caching, this helps performance when rendering a large tree
34194         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34195         var t = n.getOwnerTree();
34196         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34197         if (typeof(n.attributes.html) != 'undefined') {
34198             txt = n.attributes.html;
34199         }
34200         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34201         var cb = typeof a.checked == 'boolean';
34202         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34203         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34204             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34205             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34206             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34207             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34208             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34209              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34210                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34211             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34212             "</li>"];
34213
34214         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34215             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34216                                 n.nextSibling.ui.getEl(), buf.join(""));
34217         }else{
34218             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34219         }
34220
34221         this.elNode = this.wrap.childNodes[0];
34222         this.ctNode = this.wrap.childNodes[1];
34223         var cs = this.elNode.childNodes;
34224         this.indentNode = cs[0];
34225         this.ecNode = cs[1];
34226         this.iconNode = cs[2];
34227         var index = 3;
34228         if(cb){
34229             this.checkbox = cs[3];
34230             index++;
34231         }
34232         this.anchor = cs[index];
34233         this.textNode = cs[index].firstChild;
34234     },
34235
34236     getAnchor : function(){
34237         return this.anchor;
34238     },
34239
34240     getTextEl : function(){
34241         return this.textNode;
34242     },
34243
34244     getIconEl : function(){
34245         return this.iconNode;
34246     },
34247
34248     isChecked : function(){
34249         return this.checkbox ? this.checkbox.checked : false;
34250     },
34251
34252     updateExpandIcon : function(){
34253         if(this.rendered){
34254             var n = this.node, c1, c2;
34255             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34256             var hasChild = n.hasChildNodes();
34257             if(hasChild){
34258                 if(n.expanded){
34259                     cls += "-minus";
34260                     c1 = "x-tree-node-collapsed";
34261                     c2 = "x-tree-node-expanded";
34262                 }else{
34263                     cls += "-plus";
34264                     c1 = "x-tree-node-expanded";
34265                     c2 = "x-tree-node-collapsed";
34266                 }
34267                 if(this.wasLeaf){
34268                     this.removeClass("x-tree-node-leaf");
34269                     this.wasLeaf = false;
34270                 }
34271                 if(this.c1 != c1 || this.c2 != c2){
34272                     Roo.fly(this.elNode).replaceClass(c1, c2);
34273                     this.c1 = c1; this.c2 = c2;
34274                 }
34275             }else{
34276                 // this changes non-leafs into leafs if they have no children.
34277                 // it's not very rational behaviour..
34278                 
34279                 if(!this.wasLeaf && this.node.leaf){
34280                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34281                     delete this.c1;
34282                     delete this.c2;
34283                     this.wasLeaf = true;
34284                 }
34285             }
34286             var ecc = "x-tree-ec-icon "+cls;
34287             if(this.ecc != ecc){
34288                 this.ecNode.className = ecc;
34289                 this.ecc = ecc;
34290             }
34291         }
34292     },
34293
34294     getChildIndent : function(){
34295         if(!this.childIndent){
34296             var buf = [];
34297             var p = this.node;
34298             while(p){
34299                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34300                     if(!p.isLast()) {
34301                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34302                     } else {
34303                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34304                     }
34305                 }
34306                 p = p.parentNode;
34307             }
34308             this.childIndent = buf.join("");
34309         }
34310         return this.childIndent;
34311     },
34312
34313     renderIndent : function(){
34314         if(this.rendered){
34315             var indent = "";
34316             var p = this.node.parentNode;
34317             if(p){
34318                 indent = p.ui.getChildIndent();
34319             }
34320             if(this.indentMarkup != indent){ // don't rerender if not required
34321                 this.indentNode.innerHTML = indent;
34322                 this.indentMarkup = indent;
34323             }
34324             this.updateExpandIcon();
34325         }
34326     }
34327 };
34328
34329 Roo.tree.RootTreeNodeUI = function(){
34330     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34331 };
34332 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34333     render : function(){
34334         if(!this.rendered){
34335             var targetNode = this.node.ownerTree.innerCt.dom;
34336             this.node.expanded = true;
34337             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34338             this.wrap = this.ctNode = targetNode.firstChild;
34339         }
34340     },
34341     collapse : function(){
34342     },
34343     expand : function(){
34344     }
34345 });/*
34346  * Based on:
34347  * Ext JS Library 1.1.1
34348  * Copyright(c) 2006-2007, Ext JS, LLC.
34349  *
34350  * Originally Released Under LGPL - original licence link has changed is not relivant.
34351  *
34352  * Fork - LGPL
34353  * <script type="text/javascript">
34354  */
34355 /**
34356  * @class Roo.tree.TreeLoader
34357  * @extends Roo.util.Observable
34358  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34359  * nodes from a specified URL. The response must be a javascript Array definition
34360  * who's elements are node definition objects. eg:
34361  * <pre><code>
34362 {  success : true,
34363    data :      [
34364    
34365     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34366     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34367     ]
34368 }
34369
34370
34371 </code></pre>
34372  * <br><br>
34373  * The old style respose with just an array is still supported, but not recommended.
34374  * <br><br>
34375  *
34376  * A server request is sent, and child nodes are loaded only when a node is expanded.
34377  * The loading node's id is passed to the server under the parameter name "node" to
34378  * enable the server to produce the correct child nodes.
34379  * <br><br>
34380  * To pass extra parameters, an event handler may be attached to the "beforeload"
34381  * event, and the parameters specified in the TreeLoader's baseParams property:
34382  * <pre><code>
34383     myTreeLoader.on("beforeload", function(treeLoader, node) {
34384         this.baseParams.category = node.attributes.category;
34385     }, this);
34386 </code></pre><
34387  * This would pass an HTTP parameter called "category" to the server containing
34388  * the value of the Node's "category" attribute.
34389  * @constructor
34390  * Creates a new Treeloader.
34391  * @param {Object} config A config object containing config properties.
34392  */
34393 Roo.tree.TreeLoader = function(config){
34394     this.baseParams = {};
34395     this.requestMethod = "POST";
34396     Roo.apply(this, config);
34397
34398     this.addEvents({
34399     
34400         /**
34401          * @event beforeload
34402          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34403          * @param {Object} This TreeLoader object.
34404          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34405          * @param {Object} callback The callback function specified in the {@link #load} call.
34406          */
34407         beforeload : true,
34408         /**
34409          * @event load
34410          * Fires when the node has been successfuly loaded.
34411          * @param {Object} This TreeLoader object.
34412          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34413          * @param {Object} response The response object containing the data from the server.
34414          */
34415         load : true,
34416         /**
34417          * @event loadexception
34418          * Fires if the network request failed.
34419          * @param {Object} This TreeLoader object.
34420          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34421          * @param {Object} response The response object containing the data from the server.
34422          */
34423         loadexception : true,
34424         /**
34425          * @event create
34426          * Fires before a node is created, enabling you to return custom Node types 
34427          * @param {Object} This TreeLoader object.
34428          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34429          */
34430         create : true
34431     });
34432
34433     Roo.tree.TreeLoader.superclass.constructor.call(this);
34434 };
34435
34436 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34437     /**
34438     * @cfg {String} dataUrl The URL from which to request a Json string which
34439     * specifies an array of node definition object representing the child nodes
34440     * to be loaded.
34441     */
34442     /**
34443     * @cfg {String} requestMethod either GET or POST
34444     * defaults to POST (due to BC)
34445     * to be loaded.
34446     */
34447     /**
34448     * @cfg {Object} baseParams (optional) An object containing properties which
34449     * specify HTTP parameters to be passed to each request for child nodes.
34450     */
34451     /**
34452     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34453     * created by this loader. If the attributes sent by the server have an attribute in this object,
34454     * they take priority.
34455     */
34456     /**
34457     * @cfg {Object} uiProviders (optional) An object containing properties which
34458     * 
34459     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34460     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34461     * <i>uiProvider</i> attribute of a returned child node is a string rather
34462     * than a reference to a TreeNodeUI implementation, this that string value
34463     * is used as a property name in the uiProviders object. You can define the provider named
34464     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34465     */
34466     uiProviders : {},
34467
34468     /**
34469     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34470     * child nodes before loading.
34471     */
34472     clearOnLoad : true,
34473
34474     /**
34475     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34476     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34477     * Grid query { data : [ .....] }
34478     */
34479     
34480     root : false,
34481      /**
34482     * @cfg {String} queryParam (optional) 
34483     * Name of the query as it will be passed on the querystring (defaults to 'node')
34484     * eg. the request will be ?node=[id]
34485     */
34486     
34487     
34488     queryParam: false,
34489     
34490     /**
34491      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34492      * This is called automatically when a node is expanded, but may be used to reload
34493      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34494      * @param {Roo.tree.TreeNode} node
34495      * @param {Function} callback
34496      */
34497     load : function(node, callback){
34498         if(this.clearOnLoad){
34499             while(node.firstChild){
34500                 node.removeChild(node.firstChild);
34501             }
34502         }
34503         if(node.attributes.children){ // preloaded json children
34504             var cs = node.attributes.children;
34505             for(var i = 0, len = cs.length; i < len; i++){
34506                 node.appendChild(this.createNode(cs[i]));
34507             }
34508             if(typeof callback == "function"){
34509                 callback();
34510             }
34511         }else if(this.dataUrl){
34512             this.requestData(node, callback);
34513         }
34514     },
34515
34516     getParams: function(node){
34517         var buf = [], bp = this.baseParams;
34518         for(var key in bp){
34519             if(typeof bp[key] != "function"){
34520                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34521             }
34522         }
34523         var n = this.queryParam === false ? 'node' : this.queryParam;
34524         buf.push(n + "=", encodeURIComponent(node.id));
34525         return buf.join("");
34526     },
34527
34528     requestData : function(node, callback){
34529         if(this.fireEvent("beforeload", this, node, callback) !== false){
34530             this.transId = Roo.Ajax.request({
34531                 method:this.requestMethod,
34532                 url: this.dataUrl||this.url,
34533                 success: this.handleResponse,
34534                 failure: this.handleFailure,
34535                 scope: this,
34536                 argument: {callback: callback, node: node},
34537                 params: this.getParams(node)
34538             });
34539         }else{
34540             // if the load is cancelled, make sure we notify
34541             // the node that we are done
34542             if(typeof callback == "function"){
34543                 callback();
34544             }
34545         }
34546     },
34547
34548     isLoading : function(){
34549         return this.transId ? true : false;
34550     },
34551
34552     abort : function(){
34553         if(this.isLoading()){
34554             Roo.Ajax.abort(this.transId);
34555         }
34556     },
34557
34558     // private
34559     createNode : function(attr)
34560     {
34561         // apply baseAttrs, nice idea Corey!
34562         if(this.baseAttrs){
34563             Roo.applyIf(attr, this.baseAttrs);
34564         }
34565         if(this.applyLoader !== false){
34566             attr.loader = this;
34567         }
34568         // uiProvider = depreciated..
34569         
34570         if(typeof(attr.uiProvider) == 'string'){
34571            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34572                 /**  eval:var:attr */ eval(attr.uiProvider);
34573         }
34574         if(typeof(this.uiProviders['default']) != 'undefined') {
34575             attr.uiProvider = this.uiProviders['default'];
34576         }
34577         
34578         this.fireEvent('create', this, attr);
34579         
34580         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34581         return(attr.leaf ?
34582                         new Roo.tree.TreeNode(attr) :
34583                         new Roo.tree.AsyncTreeNode(attr));
34584     },
34585
34586     processResponse : function(response, node, callback)
34587     {
34588         var json = response.responseText;
34589         try {
34590             
34591             var o = Roo.decode(json);
34592             
34593             if (this.root === false && typeof(o.success) != undefined) {
34594                 this.root = 'data'; // the default behaviour for list like data..
34595                 }
34596                 
34597             if (this.root !== false &&  !o.success) {
34598                 // it's a failure condition.
34599                 var a = response.argument;
34600                 this.fireEvent("loadexception", this, a.node, response);
34601                 Roo.log("Load failed - should have a handler really");
34602                 return;
34603             }
34604             
34605             
34606             
34607             if (this.root !== false) {
34608                  o = o[this.root];
34609             }
34610             
34611             for(var i = 0, len = o.length; i < len; i++){
34612                 var n = this.createNode(o[i]);
34613                 if(n){
34614                     node.appendChild(n);
34615                 }
34616             }
34617             if(typeof callback == "function"){
34618                 callback(this, node);
34619             }
34620         }catch(e){
34621             this.handleFailure(response);
34622         }
34623     },
34624
34625     handleResponse : function(response){
34626         this.transId = false;
34627         var a = response.argument;
34628         this.processResponse(response, a.node, a.callback);
34629         this.fireEvent("load", this, a.node, response);
34630     },
34631
34632     handleFailure : function(response)
34633     {
34634         // should handle failure better..
34635         this.transId = false;
34636         var a = response.argument;
34637         this.fireEvent("loadexception", this, a.node, response);
34638         if(typeof a.callback == "function"){
34639             a.callback(this, a.node);
34640         }
34641     }
34642 });/*
34643  * Based on:
34644  * Ext JS Library 1.1.1
34645  * Copyright(c) 2006-2007, Ext JS, LLC.
34646  *
34647  * Originally Released Under LGPL - original licence link has changed is not relivant.
34648  *
34649  * Fork - LGPL
34650  * <script type="text/javascript">
34651  */
34652
34653 /**
34654 * @class Roo.tree.TreeFilter
34655 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34656 * @param {TreePanel} tree
34657 * @param {Object} config (optional)
34658  */
34659 Roo.tree.TreeFilter = function(tree, config){
34660     this.tree = tree;
34661     this.filtered = {};
34662     Roo.apply(this, config);
34663 };
34664
34665 Roo.tree.TreeFilter.prototype = {
34666     clearBlank:false,
34667     reverse:false,
34668     autoClear:false,
34669     remove:false,
34670
34671      /**
34672      * Filter the data by a specific attribute.
34673      * @param {String/RegExp} value Either string that the attribute value
34674      * should start with or a RegExp to test against the attribute
34675      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34676      * @param {TreeNode} startNode (optional) The node to start the filter at.
34677      */
34678     filter : function(value, attr, startNode){
34679         attr = attr || "text";
34680         var f;
34681         if(typeof value == "string"){
34682             var vlen = value.length;
34683             // auto clear empty filter
34684             if(vlen == 0 && this.clearBlank){
34685                 this.clear();
34686                 return;
34687             }
34688             value = value.toLowerCase();
34689             f = function(n){
34690                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34691             };
34692         }else if(value.exec){ // regex?
34693             f = function(n){
34694                 return value.test(n.attributes[attr]);
34695             };
34696         }else{
34697             throw 'Illegal filter type, must be string or regex';
34698         }
34699         this.filterBy(f, null, startNode);
34700         },
34701
34702     /**
34703      * Filter by a function. The passed function will be called with each
34704      * node in the tree (or from the startNode). If the function returns true, the node is kept
34705      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34706      * @param {Function} fn The filter function
34707      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34708      */
34709     filterBy : function(fn, scope, startNode){
34710         startNode = startNode || this.tree.root;
34711         if(this.autoClear){
34712             this.clear();
34713         }
34714         var af = this.filtered, rv = this.reverse;
34715         var f = function(n){
34716             if(n == startNode){
34717                 return true;
34718             }
34719             if(af[n.id]){
34720                 return false;
34721             }
34722             var m = fn.call(scope || n, n);
34723             if(!m || rv){
34724                 af[n.id] = n;
34725                 n.ui.hide();
34726                 return false;
34727             }
34728             return true;
34729         };
34730         startNode.cascade(f);
34731         if(this.remove){
34732            for(var id in af){
34733                if(typeof id != "function"){
34734                    var n = af[id];
34735                    if(n && n.parentNode){
34736                        n.parentNode.removeChild(n);
34737                    }
34738                }
34739            }
34740         }
34741     },
34742
34743     /**
34744      * Clears the current filter. Note: with the "remove" option
34745      * set a filter cannot be cleared.
34746      */
34747     clear : function(){
34748         var t = this.tree;
34749         var af = this.filtered;
34750         for(var id in af){
34751             if(typeof id != "function"){
34752                 var n = af[id];
34753                 if(n){
34754                     n.ui.show();
34755                 }
34756             }
34757         }
34758         this.filtered = {};
34759     }
34760 };
34761 /*
34762  * Based on:
34763  * Ext JS Library 1.1.1
34764  * Copyright(c) 2006-2007, Ext JS, LLC.
34765  *
34766  * Originally Released Under LGPL - original licence link has changed is not relivant.
34767  *
34768  * Fork - LGPL
34769  * <script type="text/javascript">
34770  */
34771  
34772
34773 /**
34774  * @class Roo.tree.TreeSorter
34775  * Provides sorting of nodes in a TreePanel
34776  * 
34777  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34778  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34779  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34780  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34781  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34782  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34783  * @constructor
34784  * @param {TreePanel} tree
34785  * @param {Object} config
34786  */
34787 Roo.tree.TreeSorter = function(tree, config){
34788     Roo.apply(this, config);
34789     tree.on("beforechildrenrendered", this.doSort, this);
34790     tree.on("append", this.updateSort, this);
34791     tree.on("insert", this.updateSort, this);
34792     
34793     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34794     var p = this.property || "text";
34795     var sortType = this.sortType;
34796     var fs = this.folderSort;
34797     var cs = this.caseSensitive === true;
34798     var leafAttr = this.leafAttr || 'leaf';
34799
34800     this.sortFn = function(n1, n2){
34801         if(fs){
34802             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34803                 return 1;
34804             }
34805             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34806                 return -1;
34807             }
34808         }
34809         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34810         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34811         if(v1 < v2){
34812                         return dsc ? +1 : -1;
34813                 }else if(v1 > v2){
34814                         return dsc ? -1 : +1;
34815         }else{
34816                 return 0;
34817         }
34818     };
34819 };
34820
34821 Roo.tree.TreeSorter.prototype = {
34822     doSort : function(node){
34823         node.sort(this.sortFn);
34824     },
34825     
34826     compareNodes : function(n1, n2){
34827         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34828     },
34829     
34830     updateSort : function(tree, node){
34831         if(node.childrenRendered){
34832             this.doSort.defer(1, this, [node]);
34833         }
34834     }
34835 };/*
34836  * Based on:
34837  * Ext JS Library 1.1.1
34838  * Copyright(c) 2006-2007, Ext JS, LLC.
34839  *
34840  * Originally Released Under LGPL - original licence link has changed is not relivant.
34841  *
34842  * Fork - LGPL
34843  * <script type="text/javascript">
34844  */
34845
34846 if(Roo.dd.DropZone){
34847     
34848 Roo.tree.TreeDropZone = function(tree, config){
34849     this.allowParentInsert = false;
34850     this.allowContainerDrop = false;
34851     this.appendOnly = false;
34852     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34853     this.tree = tree;
34854     this.lastInsertClass = "x-tree-no-status";
34855     this.dragOverData = {};
34856 };
34857
34858 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34859     ddGroup : "TreeDD",
34860     scroll:  true,
34861     
34862     expandDelay : 1000,
34863     
34864     expandNode : function(node){
34865         if(node.hasChildNodes() && !node.isExpanded()){
34866             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34867         }
34868     },
34869     
34870     queueExpand : function(node){
34871         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34872     },
34873     
34874     cancelExpand : function(){
34875         if(this.expandProcId){
34876             clearTimeout(this.expandProcId);
34877             this.expandProcId = false;
34878         }
34879     },
34880     
34881     isValidDropPoint : function(n, pt, dd, e, data){
34882         if(!n || !data){ return false; }
34883         var targetNode = n.node;
34884         var dropNode = data.node;
34885         // default drop rules
34886         if(!(targetNode && targetNode.isTarget && pt)){
34887             return false;
34888         }
34889         if(pt == "append" && targetNode.allowChildren === false){
34890             return false;
34891         }
34892         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34893             return false;
34894         }
34895         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34896             return false;
34897         }
34898         // reuse the object
34899         var overEvent = this.dragOverData;
34900         overEvent.tree = this.tree;
34901         overEvent.target = targetNode;
34902         overEvent.data = data;
34903         overEvent.point = pt;
34904         overEvent.source = dd;
34905         overEvent.rawEvent = e;
34906         overEvent.dropNode = dropNode;
34907         overEvent.cancel = false;  
34908         var result = this.tree.fireEvent("nodedragover", overEvent);
34909         return overEvent.cancel === false && result !== false;
34910     },
34911     
34912     getDropPoint : function(e, n, dd)
34913     {
34914         var tn = n.node;
34915         if(tn.isRoot){
34916             return tn.allowChildren !== false ? "append" : false; // always append for root
34917         }
34918         var dragEl = n.ddel;
34919         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34920         var y = Roo.lib.Event.getPageY(e);
34921         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34922         
34923         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34924         var noAppend = tn.allowChildren === false;
34925         if(this.appendOnly || tn.parentNode.allowChildren === false){
34926             return noAppend ? false : "append";
34927         }
34928         var noBelow = false;
34929         if(!this.allowParentInsert){
34930             noBelow = tn.hasChildNodes() && tn.isExpanded();
34931         }
34932         var q = (b - t) / (noAppend ? 2 : 3);
34933         if(y >= t && y < (t + q)){
34934             return "above";
34935         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34936             return "below";
34937         }else{
34938             return "append";
34939         }
34940     },
34941     
34942     onNodeEnter : function(n, dd, e, data)
34943     {
34944         this.cancelExpand();
34945     },
34946     
34947     onNodeOver : function(n, dd, e, data)
34948     {
34949        
34950         var pt = this.getDropPoint(e, n, dd);
34951         var node = n.node;
34952         
34953         // auto node expand check
34954         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34955             this.queueExpand(node);
34956         }else if(pt != "append"){
34957             this.cancelExpand();
34958         }
34959         
34960         // set the insert point style on the target node
34961         var returnCls = this.dropNotAllowed;
34962         if(this.isValidDropPoint(n, pt, dd, e, data)){
34963            if(pt){
34964                var el = n.ddel;
34965                var cls;
34966                if(pt == "above"){
34967                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34968                    cls = "x-tree-drag-insert-above";
34969                }else if(pt == "below"){
34970                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34971                    cls = "x-tree-drag-insert-below";
34972                }else{
34973                    returnCls = "x-tree-drop-ok-append";
34974                    cls = "x-tree-drag-append";
34975                }
34976                if(this.lastInsertClass != cls){
34977                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34978                    this.lastInsertClass = cls;
34979                }
34980            }
34981        }
34982        return returnCls;
34983     },
34984     
34985     onNodeOut : function(n, dd, e, data){
34986         
34987         this.cancelExpand();
34988         this.removeDropIndicators(n);
34989     },
34990     
34991     onNodeDrop : function(n, dd, e, data){
34992         var point = this.getDropPoint(e, n, dd);
34993         var targetNode = n.node;
34994         targetNode.ui.startDrop();
34995         if(!this.isValidDropPoint(n, point, dd, e, data)){
34996             targetNode.ui.endDrop();
34997             return false;
34998         }
34999         // first try to find the drop node
35000         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
35001         var dropEvent = {
35002             tree : this.tree,
35003             target: targetNode,
35004             data: data,
35005             point: point,
35006             source: dd,
35007             rawEvent: e,
35008             dropNode: dropNode,
35009             cancel: !dropNode   
35010         };
35011         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
35012         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
35013             targetNode.ui.endDrop();
35014             return false;
35015         }
35016         // allow target changing
35017         targetNode = dropEvent.target;
35018         if(point == "append" && !targetNode.isExpanded()){
35019             targetNode.expand(false, null, function(){
35020                 this.completeDrop(dropEvent);
35021             }.createDelegate(this));
35022         }else{
35023             this.completeDrop(dropEvent);
35024         }
35025         return true;
35026     },
35027     
35028     completeDrop : function(de){
35029         var ns = de.dropNode, p = de.point, t = de.target;
35030         if(!(ns instanceof Array)){
35031             ns = [ns];
35032         }
35033         var n;
35034         for(var i = 0, len = ns.length; i < len; i++){
35035             n = ns[i];
35036             if(p == "above"){
35037                 t.parentNode.insertBefore(n, t);
35038             }else if(p == "below"){
35039                 t.parentNode.insertBefore(n, t.nextSibling);
35040             }else{
35041                 t.appendChild(n);
35042             }
35043         }
35044         n.ui.focus();
35045         if(this.tree.hlDrop){
35046             n.ui.highlight();
35047         }
35048         t.ui.endDrop();
35049         this.tree.fireEvent("nodedrop", de);
35050     },
35051     
35052     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
35053         if(this.tree.hlDrop){
35054             dropNode.ui.focus();
35055             dropNode.ui.highlight();
35056         }
35057         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
35058     },
35059     
35060     getTree : function(){
35061         return this.tree;
35062     },
35063     
35064     removeDropIndicators : function(n){
35065         if(n && n.ddel){
35066             var el = n.ddel;
35067             Roo.fly(el).removeClass([
35068                     "x-tree-drag-insert-above",
35069                     "x-tree-drag-insert-below",
35070                     "x-tree-drag-append"]);
35071             this.lastInsertClass = "_noclass";
35072         }
35073     },
35074     
35075     beforeDragDrop : function(target, e, id){
35076         this.cancelExpand();
35077         return true;
35078     },
35079     
35080     afterRepair : function(data){
35081         if(data && Roo.enableFx){
35082             data.node.ui.highlight();
35083         }
35084         this.hideProxy();
35085     } 
35086     
35087 });
35088
35089 }
35090 /*
35091  * Based on:
35092  * Ext JS Library 1.1.1
35093  * Copyright(c) 2006-2007, Ext JS, LLC.
35094  *
35095  * Originally Released Under LGPL - original licence link has changed is not relivant.
35096  *
35097  * Fork - LGPL
35098  * <script type="text/javascript">
35099  */
35100  
35101
35102 if(Roo.dd.DragZone){
35103 Roo.tree.TreeDragZone = function(tree, config){
35104     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35105     this.tree = tree;
35106 };
35107
35108 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35109     ddGroup : "TreeDD",
35110    
35111     onBeforeDrag : function(data, e){
35112         var n = data.node;
35113         return n && n.draggable && !n.disabled;
35114     },
35115      
35116     
35117     onInitDrag : function(e){
35118         var data = this.dragData;
35119         this.tree.getSelectionModel().select(data.node);
35120         this.proxy.update("");
35121         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35122         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35123     },
35124     
35125     getRepairXY : function(e, data){
35126         return data.node.ui.getDDRepairXY();
35127     },
35128     
35129     onEndDrag : function(data, e){
35130         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35131         
35132         
35133     },
35134     
35135     onValidDrop : function(dd, e, id){
35136         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35137         this.hideProxy();
35138     },
35139     
35140     beforeInvalidDrop : function(e, id){
35141         // this scrolls the original position back into view
35142         var sm = this.tree.getSelectionModel();
35143         sm.clearSelections();
35144         sm.select(this.dragData.node);
35145     }
35146 });
35147 }/*
35148  * Based on:
35149  * Ext JS Library 1.1.1
35150  * Copyright(c) 2006-2007, Ext JS, LLC.
35151  *
35152  * Originally Released Under LGPL - original licence link has changed is not relivant.
35153  *
35154  * Fork - LGPL
35155  * <script type="text/javascript">
35156  */
35157 /**
35158  * @class Roo.tree.TreeEditor
35159  * @extends Roo.Editor
35160  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35161  * as the editor field.
35162  * @constructor
35163  * @param {Object} config (used to be the tree panel.)
35164  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35165  * 
35166  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35167  * @cfg {Roo.form.TextField|Object} field The field configuration
35168  *
35169  * 
35170  */
35171 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35172     var tree = config;
35173     var field;
35174     if (oldconfig) { // old style..
35175         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35176     } else {
35177         // new style..
35178         tree = config.tree;
35179         config.field = config.field  || {};
35180         config.field.xtype = 'TextField';
35181         field = Roo.factory(config.field, Roo.form);
35182     }
35183     config = config || {};
35184     
35185     
35186     this.addEvents({
35187         /**
35188          * @event beforenodeedit
35189          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35190          * false from the handler of this event.
35191          * @param {Editor} this
35192          * @param {Roo.tree.Node} node 
35193          */
35194         "beforenodeedit" : true
35195     });
35196     
35197     //Roo.log(config);
35198     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35199
35200     this.tree = tree;
35201
35202     tree.on('beforeclick', this.beforeNodeClick, this);
35203     tree.getTreeEl().on('mousedown', this.hide, this);
35204     this.on('complete', this.updateNode, this);
35205     this.on('beforestartedit', this.fitToTree, this);
35206     this.on('startedit', this.bindScroll, this, {delay:10});
35207     this.on('specialkey', this.onSpecialKey, this);
35208 };
35209
35210 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35211     /**
35212      * @cfg {String} alignment
35213      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35214      */
35215     alignment: "l-l",
35216     // inherit
35217     autoSize: false,
35218     /**
35219      * @cfg {Boolean} hideEl
35220      * True to hide the bound element while the editor is displayed (defaults to false)
35221      */
35222     hideEl : false,
35223     /**
35224      * @cfg {String} cls
35225      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35226      */
35227     cls: "x-small-editor x-tree-editor",
35228     /**
35229      * @cfg {Boolean} shim
35230      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35231      */
35232     shim:false,
35233     // inherit
35234     shadow:"frame",
35235     /**
35236      * @cfg {Number} maxWidth
35237      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35238      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35239      * scroll and client offsets into account prior to each edit.
35240      */
35241     maxWidth: 250,
35242
35243     editDelay : 350,
35244
35245     // private
35246     fitToTree : function(ed, el){
35247         var td = this.tree.getTreeEl().dom, nd = el.dom;
35248         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35249             td.scrollLeft = nd.offsetLeft;
35250         }
35251         var w = Math.min(
35252                 this.maxWidth,
35253                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35254         this.setSize(w, '');
35255         
35256         return this.fireEvent('beforenodeedit', this, this.editNode);
35257         
35258     },
35259
35260     // private
35261     triggerEdit : function(node){
35262         this.completeEdit();
35263         this.editNode = node;
35264         this.startEdit(node.ui.textNode, node.text);
35265     },
35266
35267     // private
35268     bindScroll : function(){
35269         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35270     },
35271
35272     // private
35273     beforeNodeClick : function(node, e){
35274         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35275         this.lastClick = new Date();
35276         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35277             e.stopEvent();
35278             this.triggerEdit(node);
35279             return false;
35280         }
35281         return true;
35282     },
35283
35284     // private
35285     updateNode : function(ed, value){
35286         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35287         this.editNode.setText(value);
35288     },
35289
35290     // private
35291     onHide : function(){
35292         Roo.tree.TreeEditor.superclass.onHide.call(this);
35293         if(this.editNode){
35294             this.editNode.ui.focus();
35295         }
35296     },
35297
35298     // private
35299     onSpecialKey : function(field, e){
35300         var k = e.getKey();
35301         if(k == e.ESC){
35302             e.stopEvent();
35303             this.cancelEdit();
35304         }else if(k == e.ENTER && !e.hasModifier()){
35305             e.stopEvent();
35306             this.completeEdit();
35307         }
35308     }
35309 });//<Script type="text/javascript">
35310 /*
35311  * Based on:
35312  * Ext JS Library 1.1.1
35313  * Copyright(c) 2006-2007, Ext JS, LLC.
35314  *
35315  * Originally Released Under LGPL - original licence link has changed is not relivant.
35316  *
35317  * Fork - LGPL
35318  * <script type="text/javascript">
35319  */
35320  
35321 /**
35322  * Not documented??? - probably should be...
35323  */
35324
35325 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35326     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35327     
35328     renderElements : function(n, a, targetNode, bulkRender){
35329         //consel.log("renderElements?");
35330         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35331
35332         var t = n.getOwnerTree();
35333         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35334         
35335         var cols = t.columns;
35336         var bw = t.borderWidth;
35337         var c = cols[0];
35338         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35339          var cb = typeof a.checked == "boolean";
35340         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35341         var colcls = 'x-t-' + tid + '-c0';
35342         var buf = [
35343             '<li class="x-tree-node">',
35344             
35345                 
35346                 '<div class="x-tree-node-el ', a.cls,'">',
35347                     // extran...
35348                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35349                 
35350                 
35351                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35352                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35353                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35354                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35355                            (a.iconCls ? ' '+a.iconCls : ''),
35356                            '" unselectable="on" />',
35357                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35358                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35359                              
35360                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35361                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35362                             '<span unselectable="on" qtip="' + tx + '">',
35363                              tx,
35364                              '</span></a>' ,
35365                     '</div>',
35366                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35367                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35368                  ];
35369         for(var i = 1, len = cols.length; i < len; i++){
35370             c = cols[i];
35371             colcls = 'x-t-' + tid + '-c' +i;
35372             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35373             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35374                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35375                       "</div>");
35376          }
35377          
35378          buf.push(
35379             '</a>',
35380             '<div class="x-clear"></div></div>',
35381             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35382             "</li>");
35383         
35384         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35385             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35386                                 n.nextSibling.ui.getEl(), buf.join(""));
35387         }else{
35388             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35389         }
35390         var el = this.wrap.firstChild;
35391         this.elRow = el;
35392         this.elNode = el.firstChild;
35393         this.ranchor = el.childNodes[1];
35394         this.ctNode = this.wrap.childNodes[1];
35395         var cs = el.firstChild.childNodes;
35396         this.indentNode = cs[0];
35397         this.ecNode = cs[1];
35398         this.iconNode = cs[2];
35399         var index = 3;
35400         if(cb){
35401             this.checkbox = cs[3];
35402             index++;
35403         }
35404         this.anchor = cs[index];
35405         
35406         this.textNode = cs[index].firstChild;
35407         
35408         //el.on("click", this.onClick, this);
35409         //el.on("dblclick", this.onDblClick, this);
35410         
35411         
35412        // console.log(this);
35413     },
35414     initEvents : function(){
35415         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35416         
35417             
35418         var a = this.ranchor;
35419
35420         var el = Roo.get(a);
35421
35422         if(Roo.isOpera){ // opera render bug ignores the CSS
35423             el.setStyle("text-decoration", "none");
35424         }
35425
35426         el.on("click", this.onClick, this);
35427         el.on("dblclick", this.onDblClick, this);
35428         el.on("contextmenu", this.onContextMenu, this);
35429         
35430     },
35431     
35432     /*onSelectedChange : function(state){
35433         if(state){
35434             this.focus();
35435             this.addClass("x-tree-selected");
35436         }else{
35437             //this.blur();
35438             this.removeClass("x-tree-selected");
35439         }
35440     },*/
35441     addClass : function(cls){
35442         if(this.elRow){
35443             Roo.fly(this.elRow).addClass(cls);
35444         }
35445         
35446     },
35447     
35448     
35449     removeClass : function(cls){
35450         if(this.elRow){
35451             Roo.fly(this.elRow).removeClass(cls);
35452         }
35453     }
35454
35455     
35456     
35457 });//<Script type="text/javascript">
35458
35459 /*
35460  * Based on:
35461  * Ext JS Library 1.1.1
35462  * Copyright(c) 2006-2007, Ext JS, LLC.
35463  *
35464  * Originally Released Under LGPL - original licence link has changed is not relivant.
35465  *
35466  * Fork - LGPL
35467  * <script type="text/javascript">
35468  */
35469  
35470
35471 /**
35472  * @class Roo.tree.ColumnTree
35473  * @extends Roo.data.TreePanel
35474  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35475  * @cfg {int} borderWidth  compined right/left border allowance
35476  * @constructor
35477  * @param {String/HTMLElement/Element} el The container element
35478  * @param {Object} config
35479  */
35480 Roo.tree.ColumnTree =  function(el, config)
35481 {
35482    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35483    this.addEvents({
35484         /**
35485         * @event resize
35486         * Fire this event on a container when it resizes
35487         * @param {int} w Width
35488         * @param {int} h Height
35489         */
35490        "resize" : true
35491     });
35492     this.on('resize', this.onResize, this);
35493 };
35494
35495 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35496     //lines:false,
35497     
35498     
35499     borderWidth: Roo.isBorderBox ? 0 : 2, 
35500     headEls : false,
35501     
35502     render : function(){
35503         // add the header.....
35504        
35505         Roo.tree.ColumnTree.superclass.render.apply(this);
35506         
35507         this.el.addClass('x-column-tree');
35508         
35509         this.headers = this.el.createChild(
35510             {cls:'x-tree-headers'},this.innerCt.dom);
35511    
35512         var cols = this.columns, c;
35513         var totalWidth = 0;
35514         this.headEls = [];
35515         var  len = cols.length;
35516         for(var i = 0; i < len; i++){
35517              c = cols[i];
35518              totalWidth += c.width;
35519             this.headEls.push(this.headers.createChild({
35520                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35521                  cn: {
35522                      cls:'x-tree-hd-text',
35523                      html: c.header
35524                  },
35525                  style:'width:'+(c.width-this.borderWidth)+'px;'
35526              }));
35527         }
35528         this.headers.createChild({cls:'x-clear'});
35529         // prevent floats from wrapping when clipped
35530         this.headers.setWidth(totalWidth);
35531         //this.innerCt.setWidth(totalWidth);
35532         this.innerCt.setStyle({ overflow: 'auto' });
35533         this.onResize(this.width, this.height);
35534              
35535         
35536     },
35537     onResize : function(w,h)
35538     {
35539         this.height = h;
35540         this.width = w;
35541         // resize cols..
35542         this.innerCt.setWidth(this.width);
35543         this.innerCt.setHeight(this.height-20);
35544         
35545         // headers...
35546         var cols = this.columns, c;
35547         var totalWidth = 0;
35548         var expEl = false;
35549         var len = cols.length;
35550         for(var i = 0; i < len; i++){
35551             c = cols[i];
35552             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35553                 // it's the expander..
35554                 expEl  = this.headEls[i];
35555                 continue;
35556             }
35557             totalWidth += c.width;
35558             
35559         }
35560         if (expEl) {
35561             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35562         }
35563         this.headers.setWidth(w-20);
35564
35565         
35566         
35567         
35568     }
35569 });
35570 /*
35571  * Based on:
35572  * Ext JS Library 1.1.1
35573  * Copyright(c) 2006-2007, Ext JS, LLC.
35574  *
35575  * Originally Released Under LGPL - original licence link has changed is not relivant.
35576  *
35577  * Fork - LGPL
35578  * <script type="text/javascript">
35579  */
35580  
35581 /**
35582  * @class Roo.menu.Menu
35583  * @extends Roo.util.Observable
35584  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35585  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35586  * @constructor
35587  * Creates a new Menu
35588  * @param {Object} config Configuration options
35589  */
35590 Roo.menu.Menu = function(config){
35591     Roo.apply(this, config);
35592     this.id = this.id || Roo.id();
35593     this.addEvents({
35594         /**
35595          * @event beforeshow
35596          * Fires before this menu is displayed
35597          * @param {Roo.menu.Menu} this
35598          */
35599         beforeshow : true,
35600         /**
35601          * @event beforehide
35602          * Fires before this menu is hidden
35603          * @param {Roo.menu.Menu} this
35604          */
35605         beforehide : true,
35606         /**
35607          * @event show
35608          * Fires after this menu is displayed
35609          * @param {Roo.menu.Menu} this
35610          */
35611         show : true,
35612         /**
35613          * @event hide
35614          * Fires after this menu is hidden
35615          * @param {Roo.menu.Menu} this
35616          */
35617         hide : true,
35618         /**
35619          * @event click
35620          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35621          * @param {Roo.menu.Menu} this
35622          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35623          * @param {Roo.EventObject} e
35624          */
35625         click : true,
35626         /**
35627          * @event mouseover
35628          * Fires when the mouse is hovering over this menu
35629          * @param {Roo.menu.Menu} this
35630          * @param {Roo.EventObject} e
35631          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35632          */
35633         mouseover : true,
35634         /**
35635          * @event mouseout
35636          * Fires when the mouse exits this menu
35637          * @param {Roo.menu.Menu} this
35638          * @param {Roo.EventObject} e
35639          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35640          */
35641         mouseout : true,
35642         /**
35643          * @event itemclick
35644          * Fires when a menu item contained in this menu is clicked
35645          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35646          * @param {Roo.EventObject} e
35647          */
35648         itemclick: true
35649     });
35650     if (this.registerMenu) {
35651         Roo.menu.MenuMgr.register(this);
35652     }
35653     
35654     var mis = this.items;
35655     this.items = new Roo.util.MixedCollection();
35656     if(mis){
35657         this.add.apply(this, mis);
35658     }
35659 };
35660
35661 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35662     /**
35663      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35664      */
35665     minWidth : 120,
35666     /**
35667      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35668      * for bottom-right shadow (defaults to "sides")
35669      */
35670     shadow : "sides",
35671     /**
35672      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35673      * this menu (defaults to "tl-tr?")
35674      */
35675     subMenuAlign : "tl-tr?",
35676     /**
35677      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35678      * relative to its element of origin (defaults to "tl-bl?")
35679      */
35680     defaultAlign : "tl-bl?",
35681     /**
35682      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35683      */
35684     allowOtherMenus : false,
35685     /**
35686      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35687      */
35688     registerMenu : true,
35689
35690     hidden:true,
35691
35692     // private
35693     render : function(){
35694         if(this.el){
35695             return;
35696         }
35697         var el = this.el = new Roo.Layer({
35698             cls: "x-menu",
35699             shadow:this.shadow,
35700             constrain: false,
35701             parentEl: this.parentEl || document.body,
35702             zindex:15000
35703         });
35704
35705         this.keyNav = new Roo.menu.MenuNav(this);
35706
35707         if(this.plain){
35708             el.addClass("x-menu-plain");
35709         }
35710         if(this.cls){
35711             el.addClass(this.cls);
35712         }
35713         // generic focus element
35714         this.focusEl = el.createChild({
35715             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35716         });
35717         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35718         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35719         
35720         ul.on("mouseover", this.onMouseOver, this);
35721         ul.on("mouseout", this.onMouseOut, this);
35722         this.items.each(function(item){
35723             if (item.hidden) {
35724                 return;
35725             }
35726             
35727             var li = document.createElement("li");
35728             li.className = "x-menu-list-item";
35729             ul.dom.appendChild(li);
35730             item.render(li, this);
35731         }, this);
35732         this.ul = ul;
35733         this.autoWidth();
35734     },
35735
35736     // private
35737     autoWidth : function(){
35738         var el = this.el, ul = this.ul;
35739         if(!el){
35740             return;
35741         }
35742         var w = this.width;
35743         if(w){
35744             el.setWidth(w);
35745         }else if(Roo.isIE){
35746             el.setWidth(this.minWidth);
35747             var t = el.dom.offsetWidth; // force recalc
35748             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35749         }
35750     },
35751
35752     // private
35753     delayAutoWidth : function(){
35754         if(this.rendered){
35755             if(!this.awTask){
35756                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35757             }
35758             this.awTask.delay(20);
35759         }
35760     },
35761
35762     // private
35763     findTargetItem : function(e){
35764         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35765         if(t && t.menuItemId){
35766             return this.items.get(t.menuItemId);
35767         }
35768     },
35769
35770     // private
35771     onClick : function(e){
35772         Roo.log("menu.onClick");
35773         var t = this.findTargetItem(e);
35774         if(!t){
35775             return;
35776         }
35777         Roo.log(e);
35778         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35779             if(t == this.activeItem && t.shouldDeactivate(e)){
35780                 this.activeItem.deactivate();
35781                 delete this.activeItem;
35782                 return;
35783             }
35784             if(t.canActivate){
35785                 this.setActiveItem(t, true);
35786             }
35787             return;
35788             
35789             
35790         }
35791         
35792         t.onClick(e);
35793         this.fireEvent("click", this, t, e);
35794     },
35795
35796     // private
35797     setActiveItem : function(item, autoExpand){
35798         if(item != this.activeItem){
35799             if(this.activeItem){
35800                 this.activeItem.deactivate();
35801             }
35802             this.activeItem = item;
35803             item.activate(autoExpand);
35804         }else if(autoExpand){
35805             item.expandMenu();
35806         }
35807     },
35808
35809     // private
35810     tryActivate : function(start, step){
35811         var items = this.items;
35812         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35813             var item = items.get(i);
35814             if(!item.disabled && item.canActivate){
35815                 this.setActiveItem(item, false);
35816                 return item;
35817             }
35818         }
35819         return false;
35820     },
35821
35822     // private
35823     onMouseOver : function(e){
35824         var t;
35825         if(t = this.findTargetItem(e)){
35826             if(t.canActivate && !t.disabled){
35827                 this.setActiveItem(t, true);
35828             }
35829         }
35830         this.fireEvent("mouseover", this, e, t);
35831     },
35832
35833     // private
35834     onMouseOut : function(e){
35835         var t;
35836         if(t = this.findTargetItem(e)){
35837             if(t == this.activeItem && t.shouldDeactivate(e)){
35838                 this.activeItem.deactivate();
35839                 delete this.activeItem;
35840             }
35841         }
35842         this.fireEvent("mouseout", this, e, t);
35843     },
35844
35845     /**
35846      * Read-only.  Returns true if the menu is currently displayed, else false.
35847      * @type Boolean
35848      */
35849     isVisible : function(){
35850         return this.el && !this.hidden;
35851     },
35852
35853     /**
35854      * Displays this menu relative to another element
35855      * @param {String/HTMLElement/Roo.Element} element The element to align to
35856      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35857      * the element (defaults to this.defaultAlign)
35858      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35859      */
35860     show : function(el, pos, parentMenu){
35861         this.parentMenu = parentMenu;
35862         if(!this.el){
35863             this.render();
35864         }
35865         this.fireEvent("beforeshow", this);
35866         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35867     },
35868
35869     /**
35870      * Displays this menu at a specific xy position
35871      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35872      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35873      */
35874     showAt : function(xy, parentMenu, /* private: */_e){
35875         this.parentMenu = parentMenu;
35876         if(!this.el){
35877             this.render();
35878         }
35879         if(_e !== false){
35880             this.fireEvent("beforeshow", this);
35881             xy = this.el.adjustForConstraints(xy);
35882         }
35883         this.el.setXY(xy);
35884         this.el.show();
35885         this.hidden = false;
35886         this.focus();
35887         this.fireEvent("show", this);
35888     },
35889
35890     focus : function(){
35891         if(!this.hidden){
35892             this.doFocus.defer(50, this);
35893         }
35894     },
35895
35896     doFocus : function(){
35897         if(!this.hidden){
35898             this.focusEl.focus();
35899         }
35900     },
35901
35902     /**
35903      * Hides this menu and optionally all parent menus
35904      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35905      */
35906     hide : function(deep){
35907         if(this.el && this.isVisible()){
35908             this.fireEvent("beforehide", this);
35909             if(this.activeItem){
35910                 this.activeItem.deactivate();
35911                 this.activeItem = null;
35912             }
35913             this.el.hide();
35914             this.hidden = true;
35915             this.fireEvent("hide", this);
35916         }
35917         if(deep === true && this.parentMenu){
35918             this.parentMenu.hide(true);
35919         }
35920     },
35921
35922     /**
35923      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35924      * Any of the following are valid:
35925      * <ul>
35926      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35927      * <li>An HTMLElement object which will be converted to a menu item</li>
35928      * <li>A menu item config object that will be created as a new menu item</li>
35929      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35930      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35931      * </ul>
35932      * Usage:
35933      * <pre><code>
35934 // Create the menu
35935 var menu = new Roo.menu.Menu();
35936
35937 // Create a menu item to add by reference
35938 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35939
35940 // Add a bunch of items at once using different methods.
35941 // Only the last item added will be returned.
35942 var item = menu.add(
35943     menuItem,                // add existing item by ref
35944     'Dynamic Item',          // new TextItem
35945     '-',                     // new separator
35946     { text: 'Config Item' }  // new item by config
35947 );
35948 </code></pre>
35949      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35950      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35951      */
35952     add : function(){
35953         var a = arguments, l = a.length, item;
35954         for(var i = 0; i < l; i++){
35955             var el = a[i];
35956             if ((typeof(el) == "object") && el.xtype && el.xns) {
35957                 el = Roo.factory(el, Roo.menu);
35958             }
35959             
35960             if(el.render){ // some kind of Item
35961                 item = this.addItem(el);
35962             }else if(typeof el == "string"){ // string
35963                 if(el == "separator" || el == "-"){
35964                     item = this.addSeparator();
35965                 }else{
35966                     item = this.addText(el);
35967                 }
35968             }else if(el.tagName || el.el){ // element
35969                 item = this.addElement(el);
35970             }else if(typeof el == "object"){ // must be menu item config?
35971                 item = this.addMenuItem(el);
35972             }
35973         }
35974         return item;
35975     },
35976
35977     /**
35978      * Returns this menu's underlying {@link Roo.Element} object
35979      * @return {Roo.Element} The element
35980      */
35981     getEl : function(){
35982         if(!this.el){
35983             this.render();
35984         }
35985         return this.el;
35986     },
35987
35988     /**
35989      * Adds a separator bar to the menu
35990      * @return {Roo.menu.Item} The menu item that was added
35991      */
35992     addSeparator : function(){
35993         return this.addItem(new Roo.menu.Separator());
35994     },
35995
35996     /**
35997      * Adds an {@link Roo.Element} object to the menu
35998      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35999      * @return {Roo.menu.Item} The menu item that was added
36000      */
36001     addElement : function(el){
36002         return this.addItem(new Roo.menu.BaseItem(el));
36003     },
36004
36005     /**
36006      * Adds an existing object based on {@link Roo.menu.Item} to the menu
36007      * @param {Roo.menu.Item} item The menu item to add
36008      * @return {Roo.menu.Item} The menu item that was added
36009      */
36010     addItem : function(item){
36011         this.items.add(item);
36012         if(this.ul){
36013             var li = document.createElement("li");
36014             li.className = "x-menu-list-item";
36015             this.ul.dom.appendChild(li);
36016             item.render(li, this);
36017             this.delayAutoWidth();
36018         }
36019         return item;
36020     },
36021
36022     /**
36023      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
36024      * @param {Object} config A MenuItem config object
36025      * @return {Roo.menu.Item} The menu item that was added
36026      */
36027     addMenuItem : function(config){
36028         if(!(config instanceof Roo.menu.Item)){
36029             if(typeof config.checked == "boolean"){ // must be check menu item config?
36030                 config = new Roo.menu.CheckItem(config);
36031             }else{
36032                 config = new Roo.menu.Item(config);
36033             }
36034         }
36035         return this.addItem(config);
36036     },
36037
36038     /**
36039      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
36040      * @param {String} text The text to display in the menu item
36041      * @return {Roo.menu.Item} The menu item that was added
36042      */
36043     addText : function(text){
36044         return this.addItem(new Roo.menu.TextItem({ text : text }));
36045     },
36046
36047     /**
36048      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
36049      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
36050      * @param {Roo.menu.Item} item The menu item to add
36051      * @return {Roo.menu.Item} The menu item that was added
36052      */
36053     insert : function(index, item){
36054         this.items.insert(index, item);
36055         if(this.ul){
36056             var li = document.createElement("li");
36057             li.className = "x-menu-list-item";
36058             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
36059             item.render(li, this);
36060             this.delayAutoWidth();
36061         }
36062         return item;
36063     },
36064
36065     /**
36066      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
36067      * @param {Roo.menu.Item} item The menu item to remove
36068      */
36069     remove : function(item){
36070         this.items.removeKey(item.id);
36071         item.destroy();
36072     },
36073
36074     /**
36075      * Removes and destroys all items in the menu
36076      */
36077     removeAll : function(){
36078         var f;
36079         while(f = this.items.first()){
36080             this.remove(f);
36081         }
36082     }
36083 });
36084
36085 // MenuNav is a private utility class used internally by the Menu
36086 Roo.menu.MenuNav = function(menu){
36087     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
36088     this.scope = this.menu = menu;
36089 };
36090
36091 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36092     doRelay : function(e, h){
36093         var k = e.getKey();
36094         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36095             this.menu.tryActivate(0, 1);
36096             return false;
36097         }
36098         return h.call(this.scope || this, e, this.menu);
36099     },
36100
36101     up : function(e, m){
36102         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36103             m.tryActivate(m.items.length-1, -1);
36104         }
36105     },
36106
36107     down : function(e, m){
36108         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36109             m.tryActivate(0, 1);
36110         }
36111     },
36112
36113     right : function(e, m){
36114         if(m.activeItem){
36115             m.activeItem.expandMenu(true);
36116         }
36117     },
36118
36119     left : function(e, m){
36120         m.hide();
36121         if(m.parentMenu && m.parentMenu.activeItem){
36122             m.parentMenu.activeItem.activate();
36123         }
36124     },
36125
36126     enter : function(e, m){
36127         if(m.activeItem){
36128             e.stopPropagation();
36129             m.activeItem.onClick(e);
36130             m.fireEvent("click", this, m.activeItem);
36131             return true;
36132         }
36133     }
36134 });/*
36135  * Based on:
36136  * Ext JS Library 1.1.1
36137  * Copyright(c) 2006-2007, Ext JS, LLC.
36138  *
36139  * Originally Released Under LGPL - original licence link has changed is not relivant.
36140  *
36141  * Fork - LGPL
36142  * <script type="text/javascript">
36143  */
36144  
36145 /**
36146  * @class Roo.menu.MenuMgr
36147  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36148  * @singleton
36149  */
36150 Roo.menu.MenuMgr = function(){
36151    var menus, active, groups = {}, attached = false, lastShow = new Date();
36152
36153    // private - called when first menu is created
36154    function init(){
36155        menus = {};
36156        active = new Roo.util.MixedCollection();
36157        Roo.get(document).addKeyListener(27, function(){
36158            if(active.length > 0){
36159                hideAll();
36160            }
36161        });
36162    }
36163
36164    // private
36165    function hideAll(){
36166        if(active && active.length > 0){
36167            var c = active.clone();
36168            c.each(function(m){
36169                m.hide();
36170            });
36171        }
36172    }
36173
36174    // private
36175    function onHide(m){
36176        active.remove(m);
36177        if(active.length < 1){
36178            Roo.get(document).un("mousedown", onMouseDown);
36179            attached = false;
36180        }
36181    }
36182
36183    // private
36184    function onShow(m){
36185        var last = active.last();
36186        lastShow = new Date();
36187        active.add(m);
36188        if(!attached){
36189            Roo.get(document).on("mousedown", onMouseDown);
36190            attached = true;
36191        }
36192        if(m.parentMenu){
36193           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36194           m.parentMenu.activeChild = m;
36195        }else if(last && last.isVisible()){
36196           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36197        }
36198    }
36199
36200    // private
36201    function onBeforeHide(m){
36202        if(m.activeChild){
36203            m.activeChild.hide();
36204        }
36205        if(m.autoHideTimer){
36206            clearTimeout(m.autoHideTimer);
36207            delete m.autoHideTimer;
36208        }
36209    }
36210
36211    // private
36212    function onBeforeShow(m){
36213        var pm = m.parentMenu;
36214        if(!pm && !m.allowOtherMenus){
36215            hideAll();
36216        }else if(pm && pm.activeChild && active != m){
36217            pm.activeChild.hide();
36218        }
36219    }
36220
36221    // private
36222    function onMouseDown(e){
36223        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36224            hideAll();
36225        }
36226    }
36227
36228    // private
36229    function onBeforeCheck(mi, state){
36230        if(state){
36231            var g = groups[mi.group];
36232            for(var i = 0, l = g.length; i < l; i++){
36233                if(g[i] != mi){
36234                    g[i].setChecked(false);
36235                }
36236            }
36237        }
36238    }
36239
36240    return {
36241
36242        /**
36243         * Hides all menus that are currently visible
36244         */
36245        hideAll : function(){
36246             hideAll();  
36247        },
36248
36249        // private
36250        register : function(menu){
36251            if(!menus){
36252                init();
36253            }
36254            menus[menu.id] = menu;
36255            menu.on("beforehide", onBeforeHide);
36256            menu.on("hide", onHide);
36257            menu.on("beforeshow", onBeforeShow);
36258            menu.on("show", onShow);
36259            var g = menu.group;
36260            if(g && menu.events["checkchange"]){
36261                if(!groups[g]){
36262                    groups[g] = [];
36263                }
36264                groups[g].push(menu);
36265                menu.on("checkchange", onCheck);
36266            }
36267        },
36268
36269         /**
36270          * Returns a {@link Roo.menu.Menu} object
36271          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36272          * be used to generate and return a new Menu instance.
36273          */
36274        get : function(menu){
36275            if(typeof menu == "string"){ // menu id
36276                return menus[menu];
36277            }else if(menu.events){  // menu instance
36278                return menu;
36279            }else if(typeof menu.length == 'number'){ // array of menu items?
36280                return new Roo.menu.Menu({items:menu});
36281            }else{ // otherwise, must be a config
36282                return new Roo.menu.Menu(menu);
36283            }
36284        },
36285
36286        // private
36287        unregister : function(menu){
36288            delete menus[menu.id];
36289            menu.un("beforehide", onBeforeHide);
36290            menu.un("hide", onHide);
36291            menu.un("beforeshow", onBeforeShow);
36292            menu.un("show", onShow);
36293            var g = menu.group;
36294            if(g && menu.events["checkchange"]){
36295                groups[g].remove(menu);
36296                menu.un("checkchange", onCheck);
36297            }
36298        },
36299
36300        // private
36301        registerCheckable : function(menuItem){
36302            var g = menuItem.group;
36303            if(g){
36304                if(!groups[g]){
36305                    groups[g] = [];
36306                }
36307                groups[g].push(menuItem);
36308                menuItem.on("beforecheckchange", onBeforeCheck);
36309            }
36310        },
36311
36312        // private
36313        unregisterCheckable : function(menuItem){
36314            var g = menuItem.group;
36315            if(g){
36316                groups[g].remove(menuItem);
36317                menuItem.un("beforecheckchange", onBeforeCheck);
36318            }
36319        }
36320    };
36321 }();/*
36322  * Based on:
36323  * Ext JS Library 1.1.1
36324  * Copyright(c) 2006-2007, Ext JS, LLC.
36325  *
36326  * Originally Released Under LGPL - original licence link has changed is not relivant.
36327  *
36328  * Fork - LGPL
36329  * <script type="text/javascript">
36330  */
36331  
36332
36333 /**
36334  * @class Roo.menu.BaseItem
36335  * @extends Roo.Component
36336  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36337  * management and base configuration options shared by all menu components.
36338  * @constructor
36339  * Creates a new BaseItem
36340  * @param {Object} config Configuration options
36341  */
36342 Roo.menu.BaseItem = function(config){
36343     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36344
36345     this.addEvents({
36346         /**
36347          * @event click
36348          * Fires when this item is clicked
36349          * @param {Roo.menu.BaseItem} this
36350          * @param {Roo.EventObject} e
36351          */
36352         click: true,
36353         /**
36354          * @event activate
36355          * Fires when this item is activated
36356          * @param {Roo.menu.BaseItem} this
36357          */
36358         activate : true,
36359         /**
36360          * @event deactivate
36361          * Fires when this item is deactivated
36362          * @param {Roo.menu.BaseItem} this
36363          */
36364         deactivate : true
36365     });
36366
36367     if(this.handler){
36368         this.on("click", this.handler, this.scope, true);
36369     }
36370 };
36371
36372 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36373     /**
36374      * @cfg {Function} handler
36375      * A function that will handle the click event of this menu item (defaults to undefined)
36376      */
36377     /**
36378      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36379      */
36380     canActivate : false,
36381     
36382      /**
36383      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36384      */
36385     hidden: false,
36386     
36387     /**
36388      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36389      */
36390     activeClass : "x-menu-item-active",
36391     /**
36392      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36393      */
36394     hideOnClick : true,
36395     /**
36396      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36397      */
36398     hideDelay : 100,
36399
36400     // private
36401     ctype: "Roo.menu.BaseItem",
36402
36403     // private
36404     actionMode : "container",
36405
36406     // private
36407     render : function(container, parentMenu){
36408         this.parentMenu = parentMenu;
36409         Roo.menu.BaseItem.superclass.render.call(this, container);
36410         this.container.menuItemId = this.id;
36411     },
36412
36413     // private
36414     onRender : function(container, position){
36415         this.el = Roo.get(this.el);
36416         container.dom.appendChild(this.el.dom);
36417     },
36418
36419     // private
36420     onClick : function(e){
36421         if(!this.disabled && this.fireEvent("click", this, e) !== false
36422                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36423             this.handleClick(e);
36424         }else{
36425             e.stopEvent();
36426         }
36427     },
36428
36429     // private
36430     activate : function(){
36431         if(this.disabled){
36432             return false;
36433         }
36434         var li = this.container;
36435         li.addClass(this.activeClass);
36436         this.region = li.getRegion().adjust(2, 2, -2, -2);
36437         this.fireEvent("activate", this);
36438         return true;
36439     },
36440
36441     // private
36442     deactivate : function(){
36443         this.container.removeClass(this.activeClass);
36444         this.fireEvent("deactivate", this);
36445     },
36446
36447     // private
36448     shouldDeactivate : function(e){
36449         return !this.region || !this.region.contains(e.getPoint());
36450     },
36451
36452     // private
36453     handleClick : function(e){
36454         if(this.hideOnClick){
36455             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36456         }
36457     },
36458
36459     // private
36460     expandMenu : function(autoActivate){
36461         // do nothing
36462     },
36463
36464     // private
36465     hideMenu : function(){
36466         // do nothing
36467     }
36468 });/*
36469  * Based on:
36470  * Ext JS Library 1.1.1
36471  * Copyright(c) 2006-2007, Ext JS, LLC.
36472  *
36473  * Originally Released Under LGPL - original licence link has changed is not relivant.
36474  *
36475  * Fork - LGPL
36476  * <script type="text/javascript">
36477  */
36478  
36479 /**
36480  * @class Roo.menu.Adapter
36481  * @extends Roo.menu.BaseItem
36482  * 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.
36483  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36484  * @constructor
36485  * Creates a new Adapter
36486  * @param {Object} config Configuration options
36487  */
36488 Roo.menu.Adapter = function(component, config){
36489     Roo.menu.Adapter.superclass.constructor.call(this, config);
36490     this.component = component;
36491 };
36492 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36493     // private
36494     canActivate : true,
36495
36496     // private
36497     onRender : function(container, position){
36498         this.component.render(container);
36499         this.el = this.component.getEl();
36500     },
36501
36502     // private
36503     activate : function(){
36504         if(this.disabled){
36505             return false;
36506         }
36507         this.component.focus();
36508         this.fireEvent("activate", this);
36509         return true;
36510     },
36511
36512     // private
36513     deactivate : function(){
36514         this.fireEvent("deactivate", this);
36515     },
36516
36517     // private
36518     disable : function(){
36519         this.component.disable();
36520         Roo.menu.Adapter.superclass.disable.call(this);
36521     },
36522
36523     // private
36524     enable : function(){
36525         this.component.enable();
36526         Roo.menu.Adapter.superclass.enable.call(this);
36527     }
36528 });/*
36529  * Based on:
36530  * Ext JS Library 1.1.1
36531  * Copyright(c) 2006-2007, Ext JS, LLC.
36532  *
36533  * Originally Released Under LGPL - original licence link has changed is not relivant.
36534  *
36535  * Fork - LGPL
36536  * <script type="text/javascript">
36537  */
36538
36539 /**
36540  * @class Roo.menu.TextItem
36541  * @extends Roo.menu.BaseItem
36542  * Adds a static text string to a menu, usually used as either a heading or group separator.
36543  * Note: old style constructor with text is still supported.
36544  * 
36545  * @constructor
36546  * Creates a new TextItem
36547  * @param {Object} cfg Configuration
36548  */
36549 Roo.menu.TextItem = function(cfg){
36550     if (typeof(cfg) == 'string') {
36551         this.text = cfg;
36552     } else {
36553         Roo.apply(this,cfg);
36554     }
36555     
36556     Roo.menu.TextItem.superclass.constructor.call(this);
36557 };
36558
36559 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36560     /**
36561      * @cfg {Boolean} text Text to show on item.
36562      */
36563     text : '',
36564     
36565     /**
36566      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36567      */
36568     hideOnClick : false,
36569     /**
36570      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36571      */
36572     itemCls : "x-menu-text",
36573
36574     // private
36575     onRender : function(){
36576         var s = document.createElement("span");
36577         s.className = this.itemCls;
36578         s.innerHTML = this.text;
36579         this.el = s;
36580         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36581     }
36582 });/*
36583  * Based on:
36584  * Ext JS Library 1.1.1
36585  * Copyright(c) 2006-2007, Ext JS, LLC.
36586  *
36587  * Originally Released Under LGPL - original licence link has changed is not relivant.
36588  *
36589  * Fork - LGPL
36590  * <script type="text/javascript">
36591  */
36592
36593 /**
36594  * @class Roo.menu.Separator
36595  * @extends Roo.menu.BaseItem
36596  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36597  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36598  * @constructor
36599  * @param {Object} config Configuration options
36600  */
36601 Roo.menu.Separator = function(config){
36602     Roo.menu.Separator.superclass.constructor.call(this, config);
36603 };
36604
36605 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36606     /**
36607      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36608      */
36609     itemCls : "x-menu-sep",
36610     /**
36611      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36612      */
36613     hideOnClick : false,
36614
36615     // private
36616     onRender : function(li){
36617         var s = document.createElement("span");
36618         s.className = this.itemCls;
36619         s.innerHTML = "&#160;";
36620         this.el = s;
36621         li.addClass("x-menu-sep-li");
36622         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36623     }
36624 });/*
36625  * Based on:
36626  * Ext JS Library 1.1.1
36627  * Copyright(c) 2006-2007, Ext JS, LLC.
36628  *
36629  * Originally Released Under LGPL - original licence link has changed is not relivant.
36630  *
36631  * Fork - LGPL
36632  * <script type="text/javascript">
36633  */
36634 /**
36635  * @class Roo.menu.Item
36636  * @extends Roo.menu.BaseItem
36637  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36638  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36639  * activation and click handling.
36640  * @constructor
36641  * Creates a new Item
36642  * @param {Object} config Configuration options
36643  */
36644 Roo.menu.Item = function(config){
36645     Roo.menu.Item.superclass.constructor.call(this, config);
36646     if(this.menu){
36647         this.menu = Roo.menu.MenuMgr.get(this.menu);
36648     }
36649 };
36650 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36651     
36652     /**
36653      * @cfg {String} text
36654      * The text to show on the menu item.
36655      */
36656     text: '',
36657      /**
36658      * @cfg {String} HTML to render in menu
36659      * The text to show on the menu item (HTML version).
36660      */
36661     html: '',
36662     /**
36663      * @cfg {String} icon
36664      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36665      */
36666     icon: undefined,
36667     /**
36668      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36669      */
36670     itemCls : "x-menu-item",
36671     /**
36672      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36673      */
36674     canActivate : true,
36675     /**
36676      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36677      */
36678     showDelay: 200,
36679     // doc'd in BaseItem
36680     hideDelay: 200,
36681
36682     // private
36683     ctype: "Roo.menu.Item",
36684     
36685     // private
36686     onRender : function(container, position){
36687         var el = document.createElement("a");
36688         el.hideFocus = true;
36689         el.unselectable = "on";
36690         el.href = this.href || "#";
36691         if(this.hrefTarget){
36692             el.target = this.hrefTarget;
36693         }
36694         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36695         
36696         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36697         
36698         el.innerHTML = String.format(
36699                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36700                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36701         this.el = el;
36702         Roo.menu.Item.superclass.onRender.call(this, container, position);
36703     },
36704
36705     /**
36706      * Sets the text to display in this menu item
36707      * @param {String} text The text to display
36708      * @param {Boolean} isHTML true to indicate text is pure html.
36709      */
36710     setText : function(text, isHTML){
36711         if (isHTML) {
36712             this.html = text;
36713         } else {
36714             this.text = text;
36715             this.html = '';
36716         }
36717         if(this.rendered){
36718             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36719      
36720             this.el.update(String.format(
36721                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36722                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36723             this.parentMenu.autoWidth();
36724         }
36725     },
36726
36727     // private
36728     handleClick : function(e){
36729         if(!this.href){ // if no link defined, stop the event automatically
36730             e.stopEvent();
36731         }
36732         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36733     },
36734
36735     // private
36736     activate : function(autoExpand){
36737         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36738             this.focus();
36739             if(autoExpand){
36740                 this.expandMenu();
36741             }
36742         }
36743         return true;
36744     },
36745
36746     // private
36747     shouldDeactivate : function(e){
36748         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36749             if(this.menu && this.menu.isVisible()){
36750                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36751             }
36752             return true;
36753         }
36754         return false;
36755     },
36756
36757     // private
36758     deactivate : function(){
36759         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36760         this.hideMenu();
36761     },
36762
36763     // private
36764     expandMenu : function(autoActivate){
36765         if(!this.disabled && this.menu){
36766             clearTimeout(this.hideTimer);
36767             delete this.hideTimer;
36768             if(!this.menu.isVisible() && !this.showTimer){
36769                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36770             }else if (this.menu.isVisible() && autoActivate){
36771                 this.menu.tryActivate(0, 1);
36772             }
36773         }
36774     },
36775
36776     // private
36777     deferExpand : function(autoActivate){
36778         delete this.showTimer;
36779         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36780         if(autoActivate){
36781             this.menu.tryActivate(0, 1);
36782         }
36783     },
36784
36785     // private
36786     hideMenu : function(){
36787         clearTimeout(this.showTimer);
36788         delete this.showTimer;
36789         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36790             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36791         }
36792     },
36793
36794     // private
36795     deferHide : function(){
36796         delete this.hideTimer;
36797         this.menu.hide();
36798     }
36799 });/*
36800  * Based on:
36801  * Ext JS Library 1.1.1
36802  * Copyright(c) 2006-2007, Ext JS, LLC.
36803  *
36804  * Originally Released Under LGPL - original licence link has changed is not relivant.
36805  *
36806  * Fork - LGPL
36807  * <script type="text/javascript">
36808  */
36809  
36810 /**
36811  * @class Roo.menu.CheckItem
36812  * @extends Roo.menu.Item
36813  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36814  * @constructor
36815  * Creates a new CheckItem
36816  * @param {Object} config Configuration options
36817  */
36818 Roo.menu.CheckItem = function(config){
36819     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36820     this.addEvents({
36821         /**
36822          * @event beforecheckchange
36823          * Fires before the checked value is set, providing an opportunity to cancel if needed
36824          * @param {Roo.menu.CheckItem} this
36825          * @param {Boolean} checked The new checked value that will be set
36826          */
36827         "beforecheckchange" : true,
36828         /**
36829          * @event checkchange
36830          * Fires after the checked value has been set
36831          * @param {Roo.menu.CheckItem} this
36832          * @param {Boolean} checked The checked value that was set
36833          */
36834         "checkchange" : true
36835     });
36836     if(this.checkHandler){
36837         this.on('checkchange', this.checkHandler, this.scope);
36838     }
36839 };
36840 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36841     /**
36842      * @cfg {String} group
36843      * All check items with the same group name will automatically be grouped into a single-select
36844      * radio button group (defaults to '')
36845      */
36846     /**
36847      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36848      */
36849     itemCls : "x-menu-item x-menu-check-item",
36850     /**
36851      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36852      */
36853     groupClass : "x-menu-group-item",
36854
36855     /**
36856      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36857      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36858      * initialized with checked = true will be rendered as checked.
36859      */
36860     checked: false,
36861
36862     // private
36863     ctype: "Roo.menu.CheckItem",
36864
36865     // private
36866     onRender : function(c){
36867         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36868         if(this.group){
36869             this.el.addClass(this.groupClass);
36870         }
36871         Roo.menu.MenuMgr.registerCheckable(this);
36872         if(this.checked){
36873             this.checked = false;
36874             this.setChecked(true, true);
36875         }
36876     },
36877
36878     // private
36879     destroy : function(){
36880         if(this.rendered){
36881             Roo.menu.MenuMgr.unregisterCheckable(this);
36882         }
36883         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36884     },
36885
36886     /**
36887      * Set the checked state of this item
36888      * @param {Boolean} checked The new checked value
36889      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36890      */
36891     setChecked : function(state, suppressEvent){
36892         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36893             if(this.container){
36894                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36895             }
36896             this.checked = state;
36897             if(suppressEvent !== true){
36898                 this.fireEvent("checkchange", this, state);
36899             }
36900         }
36901     },
36902
36903     // private
36904     handleClick : function(e){
36905        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36906            this.setChecked(!this.checked);
36907        }
36908        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36909     }
36910 });/*
36911  * Based on:
36912  * Ext JS Library 1.1.1
36913  * Copyright(c) 2006-2007, Ext JS, LLC.
36914  *
36915  * Originally Released Under LGPL - original licence link has changed is not relivant.
36916  *
36917  * Fork - LGPL
36918  * <script type="text/javascript">
36919  */
36920  
36921 /**
36922  * @class Roo.menu.DateItem
36923  * @extends Roo.menu.Adapter
36924  * A menu item that wraps the {@link Roo.DatPicker} component.
36925  * @constructor
36926  * Creates a new DateItem
36927  * @param {Object} config Configuration options
36928  */
36929 Roo.menu.DateItem = function(config){
36930     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36931     /** The Roo.DatePicker object @type Roo.DatePicker */
36932     this.picker = this.component;
36933     this.addEvents({select: true});
36934     
36935     this.picker.on("render", function(picker){
36936         picker.getEl().swallowEvent("click");
36937         picker.container.addClass("x-menu-date-item");
36938     });
36939
36940     this.picker.on("select", this.onSelect, this);
36941 };
36942
36943 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36944     // private
36945     onSelect : function(picker, date){
36946         this.fireEvent("select", this, date, picker);
36947         Roo.menu.DateItem.superclass.handleClick.call(this);
36948     }
36949 });/*
36950  * Based on:
36951  * Ext JS Library 1.1.1
36952  * Copyright(c) 2006-2007, Ext JS, LLC.
36953  *
36954  * Originally Released Under LGPL - original licence link has changed is not relivant.
36955  *
36956  * Fork - LGPL
36957  * <script type="text/javascript">
36958  */
36959  
36960 /**
36961  * @class Roo.menu.ColorItem
36962  * @extends Roo.menu.Adapter
36963  * A menu item that wraps the {@link Roo.ColorPalette} component.
36964  * @constructor
36965  * Creates a new ColorItem
36966  * @param {Object} config Configuration options
36967  */
36968 Roo.menu.ColorItem = function(config){
36969     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36970     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36971     this.palette = this.component;
36972     this.relayEvents(this.palette, ["select"]);
36973     if(this.selectHandler){
36974         this.on('select', this.selectHandler, this.scope);
36975     }
36976 };
36977 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36978  * Based on:
36979  * Ext JS Library 1.1.1
36980  * Copyright(c) 2006-2007, Ext JS, LLC.
36981  *
36982  * Originally Released Under LGPL - original licence link has changed is not relivant.
36983  *
36984  * Fork - LGPL
36985  * <script type="text/javascript">
36986  */
36987  
36988
36989 /**
36990  * @class Roo.menu.DateMenu
36991  * @extends Roo.menu.Menu
36992  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36993  * @constructor
36994  * Creates a new DateMenu
36995  * @param {Object} config Configuration options
36996  */
36997 Roo.menu.DateMenu = function(config){
36998     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36999     this.plain = true;
37000     var di = new Roo.menu.DateItem(config);
37001     this.add(di);
37002     /**
37003      * The {@link Roo.DatePicker} instance for this DateMenu
37004      * @type DatePicker
37005      */
37006     this.picker = di.picker;
37007     /**
37008      * @event select
37009      * @param {DatePicker} picker
37010      * @param {Date} date
37011      */
37012     this.relayEvents(di, ["select"]);
37013     this.on('beforeshow', function(){
37014         if(this.picker){
37015             this.picker.hideMonthPicker(false);
37016         }
37017     }, this);
37018 };
37019 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
37020     cls:'x-date-menu'
37021 });/*
37022  * Based on:
37023  * Ext JS Library 1.1.1
37024  * Copyright(c) 2006-2007, Ext JS, LLC.
37025  *
37026  * Originally Released Under LGPL - original licence link has changed is not relivant.
37027  *
37028  * Fork - LGPL
37029  * <script type="text/javascript">
37030  */
37031  
37032
37033 /**
37034  * @class Roo.menu.ColorMenu
37035  * @extends Roo.menu.Menu
37036  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
37037  * @constructor
37038  * Creates a new ColorMenu
37039  * @param {Object} config Configuration options
37040  */
37041 Roo.menu.ColorMenu = function(config){
37042     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
37043     this.plain = true;
37044     var ci = new Roo.menu.ColorItem(config);
37045     this.add(ci);
37046     /**
37047      * The {@link Roo.ColorPalette} instance for this ColorMenu
37048      * @type ColorPalette
37049      */
37050     this.palette = ci.palette;
37051     /**
37052      * @event select
37053      * @param {ColorPalette} palette
37054      * @param {String} color
37055      */
37056     this.relayEvents(ci, ["select"]);
37057 };
37058 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
37059  * Based on:
37060  * Ext JS Library 1.1.1
37061  * Copyright(c) 2006-2007, Ext JS, LLC.
37062  *
37063  * Originally Released Under LGPL - original licence link has changed is not relivant.
37064  *
37065  * Fork - LGPL
37066  * <script type="text/javascript">
37067  */
37068  
37069 /**
37070  * @class Roo.form.Field
37071  * @extends Roo.BoxComponent
37072  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
37073  * @constructor
37074  * Creates a new Field
37075  * @param {Object} config Configuration options
37076  */
37077 Roo.form.Field = function(config){
37078     Roo.form.Field.superclass.constructor.call(this, config);
37079 };
37080
37081 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
37082     /**
37083      * @cfg {String} fieldLabel Label to use when rendering a form.
37084      */
37085        /**
37086      * @cfg {String} qtip Mouse over tip
37087      */
37088      
37089     /**
37090      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37091      */
37092     invalidClass : "x-form-invalid",
37093     /**
37094      * @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")
37095      */
37096     invalidText : "The value in this field is invalid",
37097     /**
37098      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37099      */
37100     focusClass : "x-form-focus",
37101     /**
37102      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37103       automatic validation (defaults to "keyup").
37104      */
37105     validationEvent : "keyup",
37106     /**
37107      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37108      */
37109     validateOnBlur : true,
37110     /**
37111      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37112      */
37113     validationDelay : 250,
37114     /**
37115      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37116      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37117      */
37118     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
37119     /**
37120      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37121      */
37122     fieldClass : "x-form-field",
37123     /**
37124      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37125      *<pre>
37126 Value         Description
37127 -----------   ----------------------------------------------------------------------
37128 qtip          Display a quick tip when the user hovers over the field
37129 title         Display a default browser title attribute popup
37130 under         Add a block div beneath the field containing the error text
37131 side          Add an error icon to the right of the field with a popup on hover
37132 [element id]  Add the error text directly to the innerHTML of the specified element
37133 </pre>
37134      */
37135     msgTarget : 'qtip',
37136     /**
37137      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37138      */
37139     msgFx : 'normal',
37140
37141     /**
37142      * @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.
37143      */
37144     readOnly : false,
37145
37146     /**
37147      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37148      */
37149     disabled : false,
37150
37151     /**
37152      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37153      */
37154     inputType : undefined,
37155     
37156     /**
37157      * @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).
37158          */
37159         tabIndex : undefined,
37160         
37161     // private
37162     isFormField : true,
37163
37164     // private
37165     hasFocus : false,
37166     /**
37167      * @property {Roo.Element} fieldEl
37168      * Element Containing the rendered Field (with label etc.)
37169      */
37170     /**
37171      * @cfg {Mixed} value A value to initialize this field with.
37172      */
37173     value : undefined,
37174
37175     /**
37176      * @cfg {String} name The field's HTML name attribute.
37177      */
37178     /**
37179      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37180      */
37181
37182         // private ??
37183         initComponent : function(){
37184         Roo.form.Field.superclass.initComponent.call(this);
37185         this.addEvents({
37186             /**
37187              * @event focus
37188              * Fires when this field receives input focus.
37189              * @param {Roo.form.Field} this
37190              */
37191             focus : true,
37192             /**
37193              * @event blur
37194              * Fires when this field loses input focus.
37195              * @param {Roo.form.Field} this
37196              */
37197             blur : true,
37198             /**
37199              * @event specialkey
37200              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37201              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37202              * @param {Roo.form.Field} this
37203              * @param {Roo.EventObject} e The event object
37204              */
37205             specialkey : true,
37206             /**
37207              * @event change
37208              * Fires just before the field blurs if the field value has changed.
37209              * @param {Roo.form.Field} this
37210              * @param {Mixed} newValue The new value
37211              * @param {Mixed} oldValue The original value
37212              */
37213             change : true,
37214             /**
37215              * @event invalid
37216              * Fires after the field has been marked as invalid.
37217              * @param {Roo.form.Field} this
37218              * @param {String} msg The validation message
37219              */
37220             invalid : true,
37221             /**
37222              * @event valid
37223              * Fires after the field has been validated with no errors.
37224              * @param {Roo.form.Field} this
37225              */
37226             valid : true,
37227              /**
37228              * @event keyup
37229              * Fires after the key up
37230              * @param {Roo.form.Field} this
37231              * @param {Roo.EventObject}  e The event Object
37232              */
37233             keyup : true
37234         });
37235     },
37236
37237     /**
37238      * Returns the name attribute of the field if available
37239      * @return {String} name The field name
37240      */
37241     getName: function(){
37242          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37243     },
37244
37245     // private
37246     onRender : function(ct, position){
37247         Roo.form.Field.superclass.onRender.call(this, ct, position);
37248         if(!this.el){
37249             var cfg = this.getAutoCreate();
37250             if(!cfg.name){
37251                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37252             }
37253             if (!cfg.name.length) {
37254                 delete cfg.name;
37255             }
37256             if(this.inputType){
37257                 cfg.type = this.inputType;
37258             }
37259             this.el = ct.createChild(cfg, position);
37260         }
37261         var type = this.el.dom.type;
37262         if(type){
37263             if(type == 'password'){
37264                 type = 'text';
37265             }
37266             this.el.addClass('x-form-'+type);
37267         }
37268         if(this.readOnly){
37269             this.el.dom.readOnly = true;
37270         }
37271         if(this.tabIndex !== undefined){
37272             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37273         }
37274
37275         this.el.addClass([this.fieldClass, this.cls]);
37276         this.initValue();
37277     },
37278
37279     /**
37280      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37281      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37282      * @return {Roo.form.Field} this
37283      */
37284     applyTo : function(target){
37285         this.allowDomMove = false;
37286         this.el = Roo.get(target);
37287         this.render(this.el.dom.parentNode);
37288         return this;
37289     },
37290
37291     // private
37292     initValue : function(){
37293         if(this.value !== undefined){
37294             this.setValue(this.value);
37295         }else if(this.el.dom.value.length > 0){
37296             this.setValue(this.el.dom.value);
37297         }
37298     },
37299
37300     /**
37301      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37302      */
37303     isDirty : function() {
37304         if(this.disabled) {
37305             return false;
37306         }
37307         return String(this.getValue()) !== String(this.originalValue);
37308     },
37309
37310     // private
37311     afterRender : function(){
37312         Roo.form.Field.superclass.afterRender.call(this);
37313         this.initEvents();
37314     },
37315
37316     // private
37317     fireKey : function(e){
37318         //Roo.log('field ' + e.getKey());
37319         if(e.isNavKeyPress()){
37320             this.fireEvent("specialkey", this, e);
37321         }
37322     },
37323
37324     /**
37325      * Resets the current field value to the originally loaded value and clears any validation messages
37326      */
37327     reset : function(){
37328         this.setValue(this.resetValue);
37329         this.clearInvalid();
37330     },
37331
37332     // private
37333     initEvents : function(){
37334         // safari killled keypress - so keydown is now used..
37335         this.el.on("keydown" , this.fireKey,  this);
37336         this.el.on("focus", this.onFocus,  this);
37337         this.el.on("blur", this.onBlur,  this);
37338         this.el.relayEvent('keyup', this);
37339
37340         // reference to original value for reset
37341         this.originalValue = this.getValue();
37342         this.resetValue =  this.getValue();
37343     },
37344
37345     // private
37346     onFocus : function(){
37347         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37348             this.el.addClass(this.focusClass);
37349         }
37350         if(!this.hasFocus){
37351             this.hasFocus = true;
37352             this.startValue = this.getValue();
37353             this.fireEvent("focus", this);
37354         }
37355     },
37356
37357     beforeBlur : Roo.emptyFn,
37358
37359     // private
37360     onBlur : function(){
37361         this.beforeBlur();
37362         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37363             this.el.removeClass(this.focusClass);
37364         }
37365         this.hasFocus = false;
37366         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37367             this.validate();
37368         }
37369         var v = this.getValue();
37370         if(String(v) !== String(this.startValue)){
37371             this.fireEvent('change', this, v, this.startValue);
37372         }
37373         this.fireEvent("blur", this);
37374     },
37375
37376     /**
37377      * Returns whether or not the field value is currently valid
37378      * @param {Boolean} preventMark True to disable marking the field invalid
37379      * @return {Boolean} True if the value is valid, else false
37380      */
37381     isValid : function(preventMark){
37382         if(this.disabled){
37383             return true;
37384         }
37385         var restore = this.preventMark;
37386         this.preventMark = preventMark === true;
37387         var v = this.validateValue(this.processValue(this.getRawValue()));
37388         this.preventMark = restore;
37389         return v;
37390     },
37391
37392     /**
37393      * Validates the field value
37394      * @return {Boolean} True if the value is valid, else false
37395      */
37396     validate : function(){
37397         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37398             this.clearInvalid();
37399             return true;
37400         }
37401         return false;
37402     },
37403
37404     processValue : function(value){
37405         return value;
37406     },
37407
37408     // private
37409     // Subclasses should provide the validation implementation by overriding this
37410     validateValue : function(value){
37411         return true;
37412     },
37413
37414     /**
37415      * Mark this field as invalid
37416      * @param {String} msg The validation message
37417      */
37418     markInvalid : function(msg){
37419         if(!this.rendered || this.preventMark){ // not rendered
37420             return;
37421         }
37422         
37423         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37424         
37425         obj.el.addClass(this.invalidClass);
37426         msg = msg || this.invalidText;
37427         switch(this.msgTarget){
37428             case 'qtip':
37429                 obj.el.dom.qtip = msg;
37430                 obj.el.dom.qclass = 'x-form-invalid-tip';
37431                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37432                     Roo.QuickTips.enable();
37433                 }
37434                 break;
37435             case 'title':
37436                 this.el.dom.title = msg;
37437                 break;
37438             case 'under':
37439                 if(!this.errorEl){
37440                     var elp = this.el.findParent('.x-form-element', 5, true);
37441                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37442                     this.errorEl.setWidth(elp.getWidth(true)-20);
37443                 }
37444                 this.errorEl.update(msg);
37445                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37446                 break;
37447             case 'side':
37448                 if(!this.errorIcon){
37449                     var elp = this.el.findParent('.x-form-element', 5, true);
37450                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37451                 }
37452                 this.alignErrorIcon();
37453                 this.errorIcon.dom.qtip = msg;
37454                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37455                 this.errorIcon.show();
37456                 this.on('resize', this.alignErrorIcon, this);
37457                 break;
37458             default:
37459                 var t = Roo.getDom(this.msgTarget);
37460                 t.innerHTML = msg;
37461                 t.style.display = this.msgDisplay;
37462                 break;
37463         }
37464         this.fireEvent('invalid', this, msg);
37465     },
37466
37467     // private
37468     alignErrorIcon : function(){
37469         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37470     },
37471
37472     /**
37473      * Clear any invalid styles/messages for this field
37474      */
37475     clearInvalid : function(){
37476         if(!this.rendered || this.preventMark){ // not rendered
37477             return;
37478         }
37479         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37480         
37481         obj.el.removeClass(this.invalidClass);
37482         switch(this.msgTarget){
37483             case 'qtip':
37484                 obj.el.dom.qtip = '';
37485                 break;
37486             case 'title':
37487                 this.el.dom.title = '';
37488                 break;
37489             case 'under':
37490                 if(this.errorEl){
37491                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37492                 }
37493                 break;
37494             case 'side':
37495                 if(this.errorIcon){
37496                     this.errorIcon.dom.qtip = '';
37497                     this.errorIcon.hide();
37498                     this.un('resize', this.alignErrorIcon, this);
37499                 }
37500                 break;
37501             default:
37502                 var t = Roo.getDom(this.msgTarget);
37503                 t.innerHTML = '';
37504                 t.style.display = 'none';
37505                 break;
37506         }
37507         this.fireEvent('valid', this);
37508     },
37509
37510     /**
37511      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37512      * @return {Mixed} value The field value
37513      */
37514     getRawValue : function(){
37515         var v = this.el.getValue();
37516         
37517         return v;
37518     },
37519
37520     /**
37521      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37522      * @return {Mixed} value The field value
37523      */
37524     getValue : function(){
37525         var v = this.el.getValue();
37526          
37527         return v;
37528     },
37529
37530     /**
37531      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37532      * @param {Mixed} value The value to set
37533      */
37534     setRawValue : function(v){
37535         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37536     },
37537
37538     /**
37539      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37540      * @param {Mixed} value The value to set
37541      */
37542     setValue : function(v){
37543         this.value = v;
37544         if(this.rendered){
37545             this.el.dom.value = (v === null || v === undefined ? '' : v);
37546              this.validate();
37547         }
37548     },
37549
37550     adjustSize : function(w, h){
37551         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37552         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37553         return s;
37554     },
37555
37556     adjustWidth : function(tag, w){
37557         tag = tag.toLowerCase();
37558         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37559             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37560                 if(tag == 'input'){
37561                     return w + 2;
37562                 }
37563                 if(tag == 'textarea'){
37564                     return w-2;
37565                 }
37566             }else if(Roo.isOpera){
37567                 if(tag == 'input'){
37568                     return w + 2;
37569                 }
37570                 if(tag == 'textarea'){
37571                     return w-2;
37572                 }
37573             }
37574         }
37575         return w;
37576     }
37577 });
37578
37579
37580 // anything other than normal should be considered experimental
37581 Roo.form.Field.msgFx = {
37582     normal : {
37583         show: function(msgEl, f){
37584             msgEl.setDisplayed('block');
37585         },
37586
37587         hide : function(msgEl, f){
37588             msgEl.setDisplayed(false).update('');
37589         }
37590     },
37591
37592     slide : {
37593         show: function(msgEl, f){
37594             msgEl.slideIn('t', {stopFx:true});
37595         },
37596
37597         hide : function(msgEl, f){
37598             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37599         }
37600     },
37601
37602     slideRight : {
37603         show: function(msgEl, f){
37604             msgEl.fixDisplay();
37605             msgEl.alignTo(f.el, 'tl-tr');
37606             msgEl.slideIn('l', {stopFx:true});
37607         },
37608
37609         hide : function(msgEl, f){
37610             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37611         }
37612     }
37613 };/*
37614  * Based on:
37615  * Ext JS Library 1.1.1
37616  * Copyright(c) 2006-2007, Ext JS, LLC.
37617  *
37618  * Originally Released Under LGPL - original licence link has changed is not relivant.
37619  *
37620  * Fork - LGPL
37621  * <script type="text/javascript">
37622  */
37623  
37624
37625 /**
37626  * @class Roo.form.TextField
37627  * @extends Roo.form.Field
37628  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37629  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37630  * @constructor
37631  * Creates a new TextField
37632  * @param {Object} config Configuration options
37633  */
37634 Roo.form.TextField = function(config){
37635     Roo.form.TextField.superclass.constructor.call(this, config);
37636     this.addEvents({
37637         /**
37638          * @event autosize
37639          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37640          * according to the default logic, but this event provides a hook for the developer to apply additional
37641          * logic at runtime to resize the field if needed.
37642              * @param {Roo.form.Field} this This text field
37643              * @param {Number} width The new field width
37644              */
37645         autosize : true
37646     });
37647 };
37648
37649 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37650     /**
37651      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37652      */
37653     grow : false,
37654     /**
37655      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37656      */
37657     growMin : 30,
37658     /**
37659      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37660      */
37661     growMax : 800,
37662     /**
37663      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37664      */
37665     vtype : null,
37666     /**
37667      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37668      */
37669     maskRe : null,
37670     /**
37671      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37672      */
37673     disableKeyFilter : false,
37674     /**
37675      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37676      */
37677     allowBlank : true,
37678     /**
37679      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37680      */
37681     minLength : 0,
37682     /**
37683      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37684      */
37685     maxLength : Number.MAX_VALUE,
37686     /**
37687      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37688      */
37689     minLengthText : "The minimum length for this field is {0}",
37690     /**
37691      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37692      */
37693     maxLengthText : "The maximum length for this field is {0}",
37694     /**
37695      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37696      */
37697     selectOnFocus : false,
37698     /**
37699      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37700      */
37701     blankText : "This field is required",
37702     /**
37703      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37704      * If available, this function will be called only after the basic validators all return true, and will be passed the
37705      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37706      */
37707     validator : null,
37708     /**
37709      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37710      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37711      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37712      */
37713     regex : null,
37714     /**
37715      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37716      */
37717     regexText : "",
37718     /**
37719      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37720      */
37721     emptyText : null,
37722    
37723
37724     // private
37725     initEvents : function()
37726     {
37727         if (this.emptyText) {
37728             this.el.attr('placeholder', this.emptyText);
37729         }
37730         
37731         Roo.form.TextField.superclass.initEvents.call(this);
37732         if(this.validationEvent == 'keyup'){
37733             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37734             this.el.on('keyup', this.filterValidation, this);
37735         }
37736         else if(this.validationEvent !== false){
37737             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37738         }
37739         
37740         if(this.selectOnFocus){
37741             this.on("focus", this.preFocus, this);
37742             
37743         }
37744         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37745             this.el.on("keypress", this.filterKeys, this);
37746         }
37747         if(this.grow){
37748             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37749             this.el.on("click", this.autoSize,  this);
37750         }
37751         if(this.el.is('input[type=password]') && Roo.isSafari){
37752             this.el.on('keydown', this.SafariOnKeyDown, this);
37753         }
37754     },
37755
37756     processValue : function(value){
37757         if(this.stripCharsRe){
37758             var newValue = value.replace(this.stripCharsRe, '');
37759             if(newValue !== value){
37760                 this.setRawValue(newValue);
37761                 return newValue;
37762             }
37763         }
37764         return value;
37765     },
37766
37767     filterValidation : function(e){
37768         if(!e.isNavKeyPress()){
37769             this.validationTask.delay(this.validationDelay);
37770         }
37771     },
37772
37773     // private
37774     onKeyUp : function(e){
37775         if(!e.isNavKeyPress()){
37776             this.autoSize();
37777         }
37778     },
37779
37780     /**
37781      * Resets the current field value to the originally-loaded value and clears any validation messages.
37782      *  
37783      */
37784     reset : function(){
37785         Roo.form.TextField.superclass.reset.call(this);
37786        
37787     },
37788
37789     
37790     // private
37791     preFocus : function(){
37792         
37793         if(this.selectOnFocus){
37794             this.el.dom.select();
37795         }
37796     },
37797
37798     
37799     // private
37800     filterKeys : function(e){
37801         var k = e.getKey();
37802         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37803             return;
37804         }
37805         var c = e.getCharCode(), cc = String.fromCharCode(c);
37806         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37807             return;
37808         }
37809         if(!this.maskRe.test(cc)){
37810             e.stopEvent();
37811         }
37812     },
37813
37814     setValue : function(v){
37815         
37816         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37817         
37818         this.autoSize();
37819     },
37820
37821     /**
37822      * Validates a value according to the field's validation rules and marks the field as invalid
37823      * if the validation fails
37824      * @param {Mixed} value The value to validate
37825      * @return {Boolean} True if the value is valid, else false
37826      */
37827     validateValue : function(value){
37828         if(value.length < 1)  { // if it's blank
37829              if(this.allowBlank){
37830                 this.clearInvalid();
37831                 return true;
37832              }else{
37833                 this.markInvalid(this.blankText);
37834                 return false;
37835              }
37836         }
37837         if(value.length < this.minLength){
37838             this.markInvalid(String.format(this.minLengthText, this.minLength));
37839             return false;
37840         }
37841         if(value.length > this.maxLength){
37842             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37843             return false;
37844         }
37845         if(this.vtype){
37846             var vt = Roo.form.VTypes;
37847             if(!vt[this.vtype](value, this)){
37848                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37849                 return false;
37850             }
37851         }
37852         if(typeof this.validator == "function"){
37853             var msg = this.validator(value);
37854             if(msg !== true){
37855                 this.markInvalid(msg);
37856                 return false;
37857             }
37858         }
37859         if(this.regex && !this.regex.test(value)){
37860             this.markInvalid(this.regexText);
37861             return false;
37862         }
37863         return true;
37864     },
37865
37866     /**
37867      * Selects text in this field
37868      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37869      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37870      */
37871     selectText : function(start, end){
37872         var v = this.getRawValue();
37873         if(v.length > 0){
37874             start = start === undefined ? 0 : start;
37875             end = end === undefined ? v.length : end;
37876             var d = this.el.dom;
37877             if(d.setSelectionRange){
37878                 d.setSelectionRange(start, end);
37879             }else if(d.createTextRange){
37880                 var range = d.createTextRange();
37881                 range.moveStart("character", start);
37882                 range.moveEnd("character", v.length-end);
37883                 range.select();
37884             }
37885         }
37886     },
37887
37888     /**
37889      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37890      * This only takes effect if grow = true, and fires the autosize event.
37891      */
37892     autoSize : function(){
37893         if(!this.grow || !this.rendered){
37894             return;
37895         }
37896         if(!this.metrics){
37897             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37898         }
37899         var el = this.el;
37900         var v = el.dom.value;
37901         var d = document.createElement('div');
37902         d.appendChild(document.createTextNode(v));
37903         v = d.innerHTML;
37904         d = null;
37905         v += "&#160;";
37906         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37907         this.el.setWidth(w);
37908         this.fireEvent("autosize", this, w);
37909     },
37910     
37911     // private
37912     SafariOnKeyDown : function(event)
37913     {
37914         // this is a workaround for a password hang bug on chrome/ webkit.
37915         
37916         var isSelectAll = false;
37917         
37918         if(this.el.dom.selectionEnd > 0){
37919             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37920         }
37921         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37922             event.preventDefault();
37923             this.setValue('');
37924             return;
37925         }
37926         
37927         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
37928             
37929             event.preventDefault();
37930             // this is very hacky as keydown always get's upper case.
37931             
37932             var cc = String.fromCharCode(event.getCharCode());
37933             
37934             
37935             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37936             
37937         }
37938         
37939         
37940     }
37941 });/*
37942  * Based on:
37943  * Ext JS Library 1.1.1
37944  * Copyright(c) 2006-2007, Ext JS, LLC.
37945  *
37946  * Originally Released Under LGPL - original licence link has changed is not relivant.
37947  *
37948  * Fork - LGPL
37949  * <script type="text/javascript">
37950  */
37951  
37952 /**
37953  * @class Roo.form.Hidden
37954  * @extends Roo.form.TextField
37955  * Simple Hidden element used on forms 
37956  * 
37957  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37958  * 
37959  * @constructor
37960  * Creates a new Hidden form element.
37961  * @param {Object} config Configuration options
37962  */
37963
37964
37965
37966 // easy hidden field...
37967 Roo.form.Hidden = function(config){
37968     Roo.form.Hidden.superclass.constructor.call(this, config);
37969 };
37970   
37971 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37972     fieldLabel:      '',
37973     inputType:      'hidden',
37974     width:          50,
37975     allowBlank:     true,
37976     labelSeparator: '',
37977     hidden:         true,
37978     itemCls :       'x-form-item-display-none'
37979
37980
37981 });
37982
37983
37984 /*
37985  * Based on:
37986  * Ext JS Library 1.1.1
37987  * Copyright(c) 2006-2007, Ext JS, LLC.
37988  *
37989  * Originally Released Under LGPL - original licence link has changed is not relivant.
37990  *
37991  * Fork - LGPL
37992  * <script type="text/javascript">
37993  */
37994  
37995 /**
37996  * @class Roo.form.TriggerField
37997  * @extends Roo.form.TextField
37998  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37999  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
38000  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
38001  * for which you can provide a custom implementation.  For example:
38002  * <pre><code>
38003 var trigger = new Roo.form.TriggerField();
38004 trigger.onTriggerClick = myTriggerFn;
38005 trigger.applyTo('my-field');
38006 </code></pre>
38007  *
38008  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
38009  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
38010  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
38011  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
38012  * @constructor
38013  * Create a new TriggerField.
38014  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
38015  * to the base TextField)
38016  */
38017 Roo.form.TriggerField = function(config){
38018     this.mimicing = false;
38019     Roo.form.TriggerField.superclass.constructor.call(this, config);
38020 };
38021
38022 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
38023     /**
38024      * @cfg {String} triggerClass A CSS class to apply to the trigger
38025      */
38026     /**
38027      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38028      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
38029      */
38030     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
38031     /**
38032      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
38033      */
38034     hideTrigger:false,
38035
38036     /** @cfg {Boolean} grow @hide */
38037     /** @cfg {Number} growMin @hide */
38038     /** @cfg {Number} growMax @hide */
38039
38040     /**
38041      * @hide 
38042      * @method
38043      */
38044     autoSize: Roo.emptyFn,
38045     // private
38046     monitorTab : true,
38047     // private
38048     deferHeight : true,
38049
38050     
38051     actionMode : 'wrap',
38052     // private
38053     onResize : function(w, h){
38054         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
38055         if(typeof w == 'number'){
38056             var x = w - this.trigger.getWidth();
38057             this.el.setWidth(this.adjustWidth('input', x));
38058             this.trigger.setStyle('left', x+'px');
38059         }
38060     },
38061
38062     // private
38063     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38064
38065     // private
38066     getResizeEl : function(){
38067         return this.wrap;
38068     },
38069
38070     // private
38071     getPositionEl : function(){
38072         return this.wrap;
38073     },
38074
38075     // private
38076     alignErrorIcon : function(){
38077         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
38078     },
38079
38080     // private
38081     onRender : function(ct, position){
38082         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
38083         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
38084         this.trigger = this.wrap.createChild(this.triggerConfig ||
38085                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
38086         if(this.hideTrigger){
38087             this.trigger.setDisplayed(false);
38088         }
38089         this.initTrigger();
38090         if(!this.width){
38091             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38092         }
38093     },
38094
38095     // private
38096     initTrigger : function(){
38097         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38098         this.trigger.addClassOnOver('x-form-trigger-over');
38099         this.trigger.addClassOnClick('x-form-trigger-click');
38100     },
38101
38102     // private
38103     onDestroy : function(){
38104         if(this.trigger){
38105             this.trigger.removeAllListeners();
38106             this.trigger.remove();
38107         }
38108         if(this.wrap){
38109             this.wrap.remove();
38110         }
38111         Roo.form.TriggerField.superclass.onDestroy.call(this);
38112     },
38113
38114     // private
38115     onFocus : function(){
38116         Roo.form.TriggerField.superclass.onFocus.call(this);
38117         if(!this.mimicing){
38118             this.wrap.addClass('x-trigger-wrap-focus');
38119             this.mimicing = true;
38120             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38121             if(this.monitorTab){
38122                 this.el.on("keydown", this.checkTab, this);
38123             }
38124         }
38125     },
38126
38127     // private
38128     checkTab : function(e){
38129         if(e.getKey() == e.TAB){
38130             this.triggerBlur();
38131         }
38132     },
38133
38134     // private
38135     onBlur : function(){
38136         // do nothing
38137     },
38138
38139     // private
38140     mimicBlur : function(e, t){
38141         if(!this.wrap.contains(t) && this.validateBlur()){
38142             this.triggerBlur();
38143         }
38144     },
38145
38146     // private
38147     triggerBlur : function(){
38148         this.mimicing = false;
38149         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38150         if(this.monitorTab){
38151             this.el.un("keydown", this.checkTab, this);
38152         }
38153         this.wrap.removeClass('x-trigger-wrap-focus');
38154         Roo.form.TriggerField.superclass.onBlur.call(this);
38155     },
38156
38157     // private
38158     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38159     validateBlur : function(e, t){
38160         return true;
38161     },
38162
38163     // private
38164     onDisable : function(){
38165         Roo.form.TriggerField.superclass.onDisable.call(this);
38166         if(this.wrap){
38167             this.wrap.addClass('x-item-disabled');
38168         }
38169     },
38170
38171     // private
38172     onEnable : function(){
38173         Roo.form.TriggerField.superclass.onEnable.call(this);
38174         if(this.wrap){
38175             this.wrap.removeClass('x-item-disabled');
38176         }
38177     },
38178
38179     // private
38180     onShow : function(){
38181         var ae = this.getActionEl();
38182         
38183         if(ae){
38184             ae.dom.style.display = '';
38185             ae.dom.style.visibility = 'visible';
38186         }
38187     },
38188
38189     // private
38190     
38191     onHide : function(){
38192         var ae = this.getActionEl();
38193         ae.dom.style.display = 'none';
38194     },
38195
38196     /**
38197      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38198      * by an implementing function.
38199      * @method
38200      * @param {EventObject} e
38201      */
38202     onTriggerClick : Roo.emptyFn
38203 });
38204
38205 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38206 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38207 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38208 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38209     initComponent : function(){
38210         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38211
38212         this.triggerConfig = {
38213             tag:'span', cls:'x-form-twin-triggers', cn:[
38214             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38215             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38216         ]};
38217     },
38218
38219     getTrigger : function(index){
38220         return this.triggers[index];
38221     },
38222
38223     initTrigger : function(){
38224         var ts = this.trigger.select('.x-form-trigger', true);
38225         this.wrap.setStyle('overflow', 'hidden');
38226         var triggerField = this;
38227         ts.each(function(t, all, index){
38228             t.hide = function(){
38229                 var w = triggerField.wrap.getWidth();
38230                 this.dom.style.display = 'none';
38231                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38232             };
38233             t.show = function(){
38234                 var w = triggerField.wrap.getWidth();
38235                 this.dom.style.display = '';
38236                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38237             };
38238             var triggerIndex = 'Trigger'+(index+1);
38239
38240             if(this['hide'+triggerIndex]){
38241                 t.dom.style.display = 'none';
38242             }
38243             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38244             t.addClassOnOver('x-form-trigger-over');
38245             t.addClassOnClick('x-form-trigger-click');
38246         }, this);
38247         this.triggers = ts.elements;
38248     },
38249
38250     onTrigger1Click : Roo.emptyFn,
38251     onTrigger2Click : Roo.emptyFn
38252 });/*
38253  * Based on:
38254  * Ext JS Library 1.1.1
38255  * Copyright(c) 2006-2007, Ext JS, LLC.
38256  *
38257  * Originally Released Under LGPL - original licence link has changed is not relivant.
38258  *
38259  * Fork - LGPL
38260  * <script type="text/javascript">
38261  */
38262  
38263 /**
38264  * @class Roo.form.TextArea
38265  * @extends Roo.form.TextField
38266  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38267  * support for auto-sizing.
38268  * @constructor
38269  * Creates a new TextArea
38270  * @param {Object} config Configuration options
38271  */
38272 Roo.form.TextArea = function(config){
38273     Roo.form.TextArea.superclass.constructor.call(this, config);
38274     // these are provided exchanges for backwards compat
38275     // minHeight/maxHeight were replaced by growMin/growMax to be
38276     // compatible with TextField growing config values
38277     if(this.minHeight !== undefined){
38278         this.growMin = this.minHeight;
38279     }
38280     if(this.maxHeight !== undefined){
38281         this.growMax = this.maxHeight;
38282     }
38283 };
38284
38285 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38286     /**
38287      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38288      */
38289     growMin : 60,
38290     /**
38291      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38292      */
38293     growMax: 1000,
38294     /**
38295      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38296      * in the field (equivalent to setting overflow: hidden, defaults to false)
38297      */
38298     preventScrollbars: false,
38299     /**
38300      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38301      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38302      */
38303
38304     // private
38305     onRender : function(ct, position){
38306         if(!this.el){
38307             this.defaultAutoCreate = {
38308                 tag: "textarea",
38309                 style:"width:300px;height:60px;",
38310                 autocomplete: "new-password"
38311             };
38312         }
38313         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38314         if(this.grow){
38315             this.textSizeEl = Roo.DomHelper.append(document.body, {
38316                 tag: "pre", cls: "x-form-grow-sizer"
38317             });
38318             if(this.preventScrollbars){
38319                 this.el.setStyle("overflow", "hidden");
38320             }
38321             this.el.setHeight(this.growMin);
38322         }
38323     },
38324
38325     onDestroy : function(){
38326         if(this.textSizeEl){
38327             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38328         }
38329         Roo.form.TextArea.superclass.onDestroy.call(this);
38330     },
38331
38332     // private
38333     onKeyUp : function(e){
38334         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38335             this.autoSize();
38336         }
38337     },
38338
38339     /**
38340      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38341      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38342      */
38343     autoSize : function(){
38344         if(!this.grow || !this.textSizeEl){
38345             return;
38346         }
38347         var el = this.el;
38348         var v = el.dom.value;
38349         var ts = this.textSizeEl;
38350
38351         ts.innerHTML = '';
38352         ts.appendChild(document.createTextNode(v));
38353         v = ts.innerHTML;
38354
38355         Roo.fly(ts).setWidth(this.el.getWidth());
38356         if(v.length < 1){
38357             v = "&#160;&#160;";
38358         }else{
38359             if(Roo.isIE){
38360                 v = v.replace(/\n/g, '<p>&#160;</p>');
38361             }
38362             v += "&#160;\n&#160;";
38363         }
38364         ts.innerHTML = v;
38365         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38366         if(h != this.lastHeight){
38367             this.lastHeight = h;
38368             this.el.setHeight(h);
38369             this.fireEvent("autosize", this, h);
38370         }
38371     }
38372 });/*
38373  * Based on:
38374  * Ext JS Library 1.1.1
38375  * Copyright(c) 2006-2007, Ext JS, LLC.
38376  *
38377  * Originally Released Under LGPL - original licence link has changed is not relivant.
38378  *
38379  * Fork - LGPL
38380  * <script type="text/javascript">
38381  */
38382  
38383
38384 /**
38385  * @class Roo.form.NumberField
38386  * @extends Roo.form.TextField
38387  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38388  * @constructor
38389  * Creates a new NumberField
38390  * @param {Object} config Configuration options
38391  */
38392 Roo.form.NumberField = function(config){
38393     Roo.form.NumberField.superclass.constructor.call(this, config);
38394 };
38395
38396 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38397     /**
38398      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38399      */
38400     fieldClass: "x-form-field x-form-num-field",
38401     /**
38402      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38403      */
38404     allowDecimals : true,
38405     /**
38406      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38407      */
38408     decimalSeparator : ".",
38409     /**
38410      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38411      */
38412     decimalPrecision : 2,
38413     /**
38414      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38415      */
38416     allowNegative : true,
38417     /**
38418      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38419      */
38420     minValue : Number.NEGATIVE_INFINITY,
38421     /**
38422      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38423      */
38424     maxValue : Number.MAX_VALUE,
38425     /**
38426      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38427      */
38428     minText : "The minimum value for this field is {0}",
38429     /**
38430      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38431      */
38432     maxText : "The maximum value for this field is {0}",
38433     /**
38434      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38435      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38436      */
38437     nanText : "{0} is not a valid number",
38438
38439     // private
38440     initEvents : function(){
38441         Roo.form.NumberField.superclass.initEvents.call(this);
38442         var allowed = "0123456789";
38443         if(this.allowDecimals){
38444             allowed += this.decimalSeparator;
38445         }
38446         if(this.allowNegative){
38447             allowed += "-";
38448         }
38449         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38450         var keyPress = function(e){
38451             var k = e.getKey();
38452             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38453                 return;
38454             }
38455             var c = e.getCharCode();
38456             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38457                 e.stopEvent();
38458             }
38459         };
38460         this.el.on("keypress", keyPress, this);
38461     },
38462
38463     // private
38464     validateValue : function(value){
38465         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38466             return false;
38467         }
38468         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38469              return true;
38470         }
38471         var num = this.parseValue(value);
38472         if(isNaN(num)){
38473             this.markInvalid(String.format(this.nanText, value));
38474             return false;
38475         }
38476         if(num < this.minValue){
38477             this.markInvalid(String.format(this.minText, this.minValue));
38478             return false;
38479         }
38480         if(num > this.maxValue){
38481             this.markInvalid(String.format(this.maxText, this.maxValue));
38482             return false;
38483         }
38484         return true;
38485     },
38486
38487     getValue : function(){
38488         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38489     },
38490
38491     // private
38492     parseValue : function(value){
38493         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38494         return isNaN(value) ? '' : value;
38495     },
38496
38497     // private
38498     fixPrecision : function(value){
38499         var nan = isNaN(value);
38500         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38501             return nan ? '' : value;
38502         }
38503         return parseFloat(value).toFixed(this.decimalPrecision);
38504     },
38505
38506     setValue : function(v){
38507         v = this.fixPrecision(v);
38508         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38509     },
38510
38511     // private
38512     decimalPrecisionFcn : function(v){
38513         return Math.floor(v);
38514     },
38515
38516     beforeBlur : function(){
38517         var v = this.parseValue(this.getRawValue());
38518         if(v){
38519             this.setValue(v);
38520         }
38521     }
38522 });/*
38523  * Based on:
38524  * Ext JS Library 1.1.1
38525  * Copyright(c) 2006-2007, Ext JS, LLC.
38526  *
38527  * Originally Released Under LGPL - original licence link has changed is not relivant.
38528  *
38529  * Fork - LGPL
38530  * <script type="text/javascript">
38531  */
38532  
38533 /**
38534  * @class Roo.form.DateField
38535  * @extends Roo.form.TriggerField
38536  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38537 * @constructor
38538 * Create a new DateField
38539 * @param {Object} config
38540  */
38541 Roo.form.DateField = function(config){
38542     Roo.form.DateField.superclass.constructor.call(this, config);
38543     
38544       this.addEvents({
38545          
38546         /**
38547          * @event select
38548          * Fires when a date is selected
38549              * @param {Roo.form.DateField} combo This combo box
38550              * @param {Date} date The date selected
38551              */
38552         'select' : true
38553          
38554     });
38555     
38556     
38557     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38558     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38559     this.ddMatch = null;
38560     if(this.disabledDates){
38561         var dd = this.disabledDates;
38562         var re = "(?:";
38563         for(var i = 0; i < dd.length; i++){
38564             re += dd[i];
38565             if(i != dd.length-1) re += "|";
38566         }
38567         this.ddMatch = new RegExp(re + ")");
38568     }
38569 };
38570
38571 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38572     /**
38573      * @cfg {String} format
38574      * The default date format string which can be overriden for localization support.  The format must be
38575      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38576      */
38577     format : "m/d/y",
38578     /**
38579      * @cfg {String} altFormats
38580      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38581      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38582      */
38583     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38584     /**
38585      * @cfg {Array} disabledDays
38586      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38587      */
38588     disabledDays : null,
38589     /**
38590      * @cfg {String} disabledDaysText
38591      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38592      */
38593     disabledDaysText : "Disabled",
38594     /**
38595      * @cfg {Array} disabledDates
38596      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38597      * expression so they are very powerful. Some examples:
38598      * <ul>
38599      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38600      * <li>["03/08", "09/16"] would disable those days for every year</li>
38601      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38602      * <li>["03/../2006"] would disable every day in March 2006</li>
38603      * <li>["^03"] would disable every day in every March</li>
38604      * </ul>
38605      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38606      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38607      */
38608     disabledDates : null,
38609     /**
38610      * @cfg {String} disabledDatesText
38611      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38612      */
38613     disabledDatesText : "Disabled",
38614     /**
38615      * @cfg {Date/String} minValue
38616      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38617      * valid format (defaults to null).
38618      */
38619     minValue : null,
38620     /**
38621      * @cfg {Date/String} maxValue
38622      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38623      * valid format (defaults to null).
38624      */
38625     maxValue : null,
38626     /**
38627      * @cfg {String} minText
38628      * The error text to display when the date in the cell is before minValue (defaults to
38629      * 'The date in this field must be after {minValue}').
38630      */
38631     minText : "The date in this field must be equal to or after {0}",
38632     /**
38633      * @cfg {String} maxText
38634      * The error text to display when the date in the cell is after maxValue (defaults to
38635      * 'The date in this field must be before {maxValue}').
38636      */
38637     maxText : "The date in this field must be equal to or before {0}",
38638     /**
38639      * @cfg {String} invalidText
38640      * The error text to display when the date in the field is invalid (defaults to
38641      * '{value} is not a valid date - it must be in the format {format}').
38642      */
38643     invalidText : "{0} is not a valid date - it must be in the format {1}",
38644     /**
38645      * @cfg {String} triggerClass
38646      * An additional CSS class used to style the trigger button.  The trigger will always get the
38647      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38648      * which displays a calendar icon).
38649      */
38650     triggerClass : 'x-form-date-trigger',
38651     
38652
38653     /**
38654      * @cfg {Boolean} useIso
38655      * if enabled, then the date field will use a hidden field to store the 
38656      * real value as iso formated date. default (false)
38657      */ 
38658     useIso : false,
38659     /**
38660      * @cfg {String/Object} autoCreate
38661      * A DomHelper element spec, or true for a default element spec (defaults to
38662      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38663      */ 
38664     // private
38665     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38666     
38667     // private
38668     hiddenField: false,
38669     
38670     onRender : function(ct, position)
38671     {
38672         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38673         if (this.useIso) {
38674             //this.el.dom.removeAttribute('name'); 
38675             Roo.log("Changing name?");
38676             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38677             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38678                     'before', true);
38679             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38680             // prevent input submission
38681             this.hiddenName = this.name;
38682         }
38683             
38684             
38685     },
38686     
38687     // private
38688     validateValue : function(value)
38689     {
38690         value = this.formatDate(value);
38691         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38692             Roo.log('super failed');
38693             return false;
38694         }
38695         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38696              return true;
38697         }
38698         var svalue = value;
38699         value = this.parseDate(value);
38700         if(!value){
38701             Roo.log('parse date failed' + svalue);
38702             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38703             return false;
38704         }
38705         var time = value.getTime();
38706         if(this.minValue && time < this.minValue.getTime()){
38707             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38708             return false;
38709         }
38710         if(this.maxValue && time > this.maxValue.getTime()){
38711             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38712             return false;
38713         }
38714         if(this.disabledDays){
38715             var day = value.getDay();
38716             for(var i = 0; i < this.disabledDays.length; i++) {
38717                 if(day === this.disabledDays[i]){
38718                     this.markInvalid(this.disabledDaysText);
38719                     return false;
38720                 }
38721             }
38722         }
38723         var fvalue = this.formatDate(value);
38724         if(this.ddMatch && this.ddMatch.test(fvalue)){
38725             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38726             return false;
38727         }
38728         return true;
38729     },
38730
38731     // private
38732     // Provides logic to override the default TriggerField.validateBlur which just returns true
38733     validateBlur : function(){
38734         return !this.menu || !this.menu.isVisible();
38735     },
38736     
38737     getName: function()
38738     {
38739         // returns hidden if it's set..
38740         if (!this.rendered) {return ''};
38741         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38742         
38743     },
38744
38745     /**
38746      * Returns the current date value of the date field.
38747      * @return {Date} The date value
38748      */
38749     getValue : function(){
38750         
38751         return  this.hiddenField ?
38752                 this.hiddenField.value :
38753                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38754     },
38755
38756     /**
38757      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38758      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38759      * (the default format used is "m/d/y").
38760      * <br />Usage:
38761      * <pre><code>
38762 //All of these calls set the same date value (May 4, 2006)
38763
38764 //Pass a date object:
38765 var dt = new Date('5/4/06');
38766 dateField.setValue(dt);
38767
38768 //Pass a date string (default format):
38769 dateField.setValue('5/4/06');
38770
38771 //Pass a date string (custom format):
38772 dateField.format = 'Y-m-d';
38773 dateField.setValue('2006-5-4');
38774 </code></pre>
38775      * @param {String/Date} date The date or valid date string
38776      */
38777     setValue : function(date){
38778         if (this.hiddenField) {
38779             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38780         }
38781         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38782         // make sure the value field is always stored as a date..
38783         this.value = this.parseDate(date);
38784         
38785         
38786     },
38787
38788     // private
38789     parseDate : function(value){
38790         if(!value || value instanceof Date){
38791             return value;
38792         }
38793         var v = Date.parseDate(value, this.format);
38794          if (!v && this.useIso) {
38795             v = Date.parseDate(value, 'Y-m-d');
38796         }
38797         if(!v && this.altFormats){
38798             if(!this.altFormatsArray){
38799                 this.altFormatsArray = this.altFormats.split("|");
38800             }
38801             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38802                 v = Date.parseDate(value, this.altFormatsArray[i]);
38803             }
38804         }
38805         return v;
38806     },
38807
38808     // private
38809     formatDate : function(date, fmt){
38810         return (!date || !(date instanceof Date)) ?
38811                date : date.dateFormat(fmt || this.format);
38812     },
38813
38814     // private
38815     menuListeners : {
38816         select: function(m, d){
38817             
38818             this.setValue(d);
38819             this.fireEvent('select', this, d);
38820         },
38821         show : function(){ // retain focus styling
38822             this.onFocus();
38823         },
38824         hide : function(){
38825             this.focus.defer(10, this);
38826             var ml = this.menuListeners;
38827             this.menu.un("select", ml.select,  this);
38828             this.menu.un("show", ml.show,  this);
38829             this.menu.un("hide", ml.hide,  this);
38830         }
38831     },
38832
38833     // private
38834     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38835     onTriggerClick : function(){
38836         if(this.disabled){
38837             return;
38838         }
38839         if(this.menu == null){
38840             this.menu = new Roo.menu.DateMenu();
38841         }
38842         Roo.apply(this.menu.picker,  {
38843             showClear: this.allowBlank,
38844             minDate : this.minValue,
38845             maxDate : this.maxValue,
38846             disabledDatesRE : this.ddMatch,
38847             disabledDatesText : this.disabledDatesText,
38848             disabledDays : this.disabledDays,
38849             disabledDaysText : this.disabledDaysText,
38850             format : this.useIso ? 'Y-m-d' : this.format,
38851             minText : String.format(this.minText, this.formatDate(this.minValue)),
38852             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38853         });
38854         this.menu.on(Roo.apply({}, this.menuListeners, {
38855             scope:this
38856         }));
38857         this.menu.picker.setValue(this.getValue() || new Date());
38858         this.menu.show(this.el, "tl-bl?");
38859     },
38860
38861     beforeBlur : function(){
38862         var v = this.parseDate(this.getRawValue());
38863         if(v){
38864             this.setValue(v);
38865         }
38866     },
38867
38868     /*@
38869      * overide
38870      * 
38871      */
38872     isDirty : function() {
38873         if(this.disabled) {
38874             return false;
38875         }
38876         
38877         if(typeof(this.startValue) === 'undefined'){
38878             return false;
38879         }
38880         
38881         return String(this.getValue()) !== String(this.startValue);
38882         
38883     }
38884 });/*
38885  * Based on:
38886  * Ext JS Library 1.1.1
38887  * Copyright(c) 2006-2007, Ext JS, LLC.
38888  *
38889  * Originally Released Under LGPL - original licence link has changed is not relivant.
38890  *
38891  * Fork - LGPL
38892  * <script type="text/javascript">
38893  */
38894  
38895 /**
38896  * @class Roo.form.MonthField
38897  * @extends Roo.form.TriggerField
38898  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38899 * @constructor
38900 * Create a new MonthField
38901 * @param {Object} config
38902  */
38903 Roo.form.MonthField = function(config){
38904     
38905     Roo.form.MonthField.superclass.constructor.call(this, config);
38906     
38907       this.addEvents({
38908          
38909         /**
38910          * @event select
38911          * Fires when a date is selected
38912              * @param {Roo.form.MonthFieeld} combo This combo box
38913              * @param {Date} date The date selected
38914              */
38915         'select' : true
38916          
38917     });
38918     
38919     
38920     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38921     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38922     this.ddMatch = null;
38923     if(this.disabledDates){
38924         var dd = this.disabledDates;
38925         var re = "(?:";
38926         for(var i = 0; i < dd.length; i++){
38927             re += dd[i];
38928             if(i != dd.length-1) re += "|";
38929         }
38930         this.ddMatch = new RegExp(re + ")");
38931     }
38932 };
38933
38934 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38935     /**
38936      * @cfg {String} format
38937      * The default date format string which can be overriden for localization support.  The format must be
38938      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38939      */
38940     format : "M Y",
38941     /**
38942      * @cfg {String} altFormats
38943      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38944      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38945      */
38946     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38947     /**
38948      * @cfg {Array} disabledDays
38949      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38950      */
38951     disabledDays : [0,1,2,3,4,5,6],
38952     /**
38953      * @cfg {String} disabledDaysText
38954      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38955      */
38956     disabledDaysText : "Disabled",
38957     /**
38958      * @cfg {Array} disabledDates
38959      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38960      * expression so they are very powerful. Some examples:
38961      * <ul>
38962      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38963      * <li>["03/08", "09/16"] would disable those days for every year</li>
38964      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38965      * <li>["03/../2006"] would disable every day in March 2006</li>
38966      * <li>["^03"] would disable every day in every March</li>
38967      * </ul>
38968      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38969      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38970      */
38971     disabledDates : null,
38972     /**
38973      * @cfg {String} disabledDatesText
38974      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38975      */
38976     disabledDatesText : "Disabled",
38977     /**
38978      * @cfg {Date/String} minValue
38979      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38980      * valid format (defaults to null).
38981      */
38982     minValue : null,
38983     /**
38984      * @cfg {Date/String} maxValue
38985      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38986      * valid format (defaults to null).
38987      */
38988     maxValue : null,
38989     /**
38990      * @cfg {String} minText
38991      * The error text to display when the date in the cell is before minValue (defaults to
38992      * 'The date in this field must be after {minValue}').
38993      */
38994     minText : "The date in this field must be equal to or after {0}",
38995     /**
38996      * @cfg {String} maxTextf
38997      * The error text to display when the date in the cell is after maxValue (defaults to
38998      * 'The date in this field must be before {maxValue}').
38999      */
39000     maxText : "The date in this field must be equal to or before {0}",
39001     /**
39002      * @cfg {String} invalidText
39003      * The error text to display when the date in the field is invalid (defaults to
39004      * '{value} is not a valid date - it must be in the format {format}').
39005      */
39006     invalidText : "{0} is not a valid date - it must be in the format {1}",
39007     /**
39008      * @cfg {String} triggerClass
39009      * An additional CSS class used to style the trigger button.  The trigger will always get the
39010      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
39011      * which displays a calendar icon).
39012      */
39013     triggerClass : 'x-form-date-trigger',
39014     
39015
39016     /**
39017      * @cfg {Boolean} useIso
39018      * if enabled, then the date field will use a hidden field to store the 
39019      * real value as iso formated date. default (true)
39020      */ 
39021     useIso : true,
39022     /**
39023      * @cfg {String/Object} autoCreate
39024      * A DomHelper element spec, or true for a default element spec (defaults to
39025      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
39026      */ 
39027     // private
39028     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
39029     
39030     // private
39031     hiddenField: false,
39032     
39033     hideMonthPicker : false,
39034     
39035     onRender : function(ct, position)
39036     {
39037         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
39038         if (this.useIso) {
39039             this.el.dom.removeAttribute('name'); 
39040             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
39041                     'before', true);
39042             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
39043             // prevent input submission
39044             this.hiddenName = this.name;
39045         }
39046             
39047             
39048     },
39049     
39050     // private
39051     validateValue : function(value)
39052     {
39053         value = this.formatDate(value);
39054         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
39055             return false;
39056         }
39057         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39058              return true;
39059         }
39060         var svalue = value;
39061         value = this.parseDate(value);
39062         if(!value){
39063             this.markInvalid(String.format(this.invalidText, svalue, this.format));
39064             return false;
39065         }
39066         var time = value.getTime();
39067         if(this.minValue && time < this.minValue.getTime()){
39068             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
39069             return false;
39070         }
39071         if(this.maxValue && time > this.maxValue.getTime()){
39072             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
39073             return false;
39074         }
39075         /*if(this.disabledDays){
39076             var day = value.getDay();
39077             for(var i = 0; i < this.disabledDays.length; i++) {
39078                 if(day === this.disabledDays[i]){
39079                     this.markInvalid(this.disabledDaysText);
39080                     return false;
39081                 }
39082             }
39083         }
39084         */
39085         var fvalue = this.formatDate(value);
39086         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
39087             this.markInvalid(String.format(this.disabledDatesText, fvalue));
39088             return false;
39089         }
39090         */
39091         return true;
39092     },
39093
39094     // private
39095     // Provides logic to override the default TriggerField.validateBlur which just returns true
39096     validateBlur : function(){
39097         return !this.menu || !this.menu.isVisible();
39098     },
39099
39100     /**
39101      * Returns the current date value of the date field.
39102      * @return {Date} The date value
39103      */
39104     getValue : function(){
39105         
39106         
39107         
39108         return  this.hiddenField ?
39109                 this.hiddenField.value :
39110                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39111     },
39112
39113     /**
39114      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39115      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39116      * (the default format used is "m/d/y").
39117      * <br />Usage:
39118      * <pre><code>
39119 //All of these calls set the same date value (May 4, 2006)
39120
39121 //Pass a date object:
39122 var dt = new Date('5/4/06');
39123 monthField.setValue(dt);
39124
39125 //Pass a date string (default format):
39126 monthField.setValue('5/4/06');
39127
39128 //Pass a date string (custom format):
39129 monthField.format = 'Y-m-d';
39130 monthField.setValue('2006-5-4');
39131 </code></pre>
39132      * @param {String/Date} date The date or valid date string
39133      */
39134     setValue : function(date){
39135         Roo.log('month setValue' + date);
39136         // can only be first of month..
39137         
39138         var val = this.parseDate(date);
39139         
39140         if (this.hiddenField) {
39141             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39142         }
39143         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39144         this.value = this.parseDate(date);
39145     },
39146
39147     // private
39148     parseDate : function(value){
39149         if(!value || value instanceof Date){
39150             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39151             return value;
39152         }
39153         var v = Date.parseDate(value, this.format);
39154         if (!v && this.useIso) {
39155             v = Date.parseDate(value, 'Y-m-d');
39156         }
39157         if (v) {
39158             // 
39159             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39160         }
39161         
39162         
39163         if(!v && this.altFormats){
39164             if(!this.altFormatsArray){
39165                 this.altFormatsArray = this.altFormats.split("|");
39166             }
39167             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39168                 v = Date.parseDate(value, this.altFormatsArray[i]);
39169             }
39170         }
39171         return v;
39172     },
39173
39174     // private
39175     formatDate : function(date, fmt){
39176         return (!date || !(date instanceof Date)) ?
39177                date : date.dateFormat(fmt || this.format);
39178     },
39179
39180     // private
39181     menuListeners : {
39182         select: function(m, d){
39183             this.setValue(d);
39184             this.fireEvent('select', this, d);
39185         },
39186         show : function(){ // retain focus styling
39187             this.onFocus();
39188         },
39189         hide : function(){
39190             this.focus.defer(10, this);
39191             var ml = this.menuListeners;
39192             this.menu.un("select", ml.select,  this);
39193             this.menu.un("show", ml.show,  this);
39194             this.menu.un("hide", ml.hide,  this);
39195         }
39196     },
39197     // private
39198     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39199     onTriggerClick : function(){
39200         if(this.disabled){
39201             return;
39202         }
39203         if(this.menu == null){
39204             this.menu = new Roo.menu.DateMenu();
39205            
39206         }
39207         
39208         Roo.apply(this.menu.picker,  {
39209             
39210             showClear: this.allowBlank,
39211             minDate : this.minValue,
39212             maxDate : this.maxValue,
39213             disabledDatesRE : this.ddMatch,
39214             disabledDatesText : this.disabledDatesText,
39215             
39216             format : this.useIso ? 'Y-m-d' : this.format,
39217             minText : String.format(this.minText, this.formatDate(this.minValue)),
39218             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39219             
39220         });
39221          this.menu.on(Roo.apply({}, this.menuListeners, {
39222             scope:this
39223         }));
39224        
39225         
39226         var m = this.menu;
39227         var p = m.picker;
39228         
39229         // hide month picker get's called when we called by 'before hide';
39230         
39231         var ignorehide = true;
39232         p.hideMonthPicker  = function(disableAnim){
39233             if (ignorehide) {
39234                 return;
39235             }
39236              if(this.monthPicker){
39237                 Roo.log("hideMonthPicker called");
39238                 if(disableAnim === true){
39239                     this.monthPicker.hide();
39240                 }else{
39241                     this.monthPicker.slideOut('t', {duration:.2});
39242                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39243                     p.fireEvent("select", this, this.value);
39244                     m.hide();
39245                 }
39246             }
39247         }
39248         
39249         Roo.log('picker set value');
39250         Roo.log(this.getValue());
39251         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39252         m.show(this.el, 'tl-bl?');
39253         ignorehide  = false;
39254         // this will trigger hideMonthPicker..
39255         
39256         
39257         // hidden the day picker
39258         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39259         
39260         
39261         
39262       
39263         
39264         p.showMonthPicker.defer(100, p);
39265     
39266         
39267        
39268     },
39269
39270     beforeBlur : function(){
39271         var v = this.parseDate(this.getRawValue());
39272         if(v){
39273             this.setValue(v);
39274         }
39275     }
39276
39277     /** @cfg {Boolean} grow @hide */
39278     /** @cfg {Number} growMin @hide */
39279     /** @cfg {Number} growMax @hide */
39280     /**
39281      * @hide
39282      * @method autoSize
39283      */
39284 });/*
39285  * Based on:
39286  * Ext JS Library 1.1.1
39287  * Copyright(c) 2006-2007, Ext JS, LLC.
39288  *
39289  * Originally Released Under LGPL - original licence link has changed is not relivant.
39290  *
39291  * Fork - LGPL
39292  * <script type="text/javascript">
39293  */
39294  
39295
39296 /**
39297  * @class Roo.form.ComboBox
39298  * @extends Roo.form.TriggerField
39299  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39300  * @constructor
39301  * Create a new ComboBox.
39302  * @param {Object} config Configuration options
39303  */
39304 Roo.form.ComboBox = function(config){
39305     Roo.form.ComboBox.superclass.constructor.call(this, config);
39306     this.addEvents({
39307         /**
39308          * @event expand
39309          * Fires when the dropdown list is expanded
39310              * @param {Roo.form.ComboBox} combo This combo box
39311              */
39312         'expand' : true,
39313         /**
39314          * @event collapse
39315          * Fires when the dropdown list is collapsed
39316              * @param {Roo.form.ComboBox} combo This combo box
39317              */
39318         'collapse' : true,
39319         /**
39320          * @event beforeselect
39321          * Fires before a list item is selected. Return false to cancel the selection.
39322              * @param {Roo.form.ComboBox} combo This combo box
39323              * @param {Roo.data.Record} record The data record returned from the underlying store
39324              * @param {Number} index The index of the selected item in the dropdown list
39325              */
39326         'beforeselect' : true,
39327         /**
39328          * @event select
39329          * Fires when a list item is selected
39330              * @param {Roo.form.ComboBox} combo This combo box
39331              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39332              * @param {Number} index The index of the selected item in the dropdown list
39333              */
39334         'select' : true,
39335         /**
39336          * @event beforequery
39337          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39338          * The event object passed has these properties:
39339              * @param {Roo.form.ComboBox} combo This combo box
39340              * @param {String} query The query
39341              * @param {Boolean} forceAll true to force "all" query
39342              * @param {Boolean} cancel true to cancel the query
39343              * @param {Object} e The query event object
39344              */
39345         'beforequery': true,
39346          /**
39347          * @event add
39348          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39349              * @param {Roo.form.ComboBox} combo This combo box
39350              */
39351         'add' : true,
39352         /**
39353          * @event edit
39354          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39355              * @param {Roo.form.ComboBox} combo This combo box
39356              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39357              */
39358         'edit' : true
39359         
39360         
39361     });
39362     if(this.transform){
39363         this.allowDomMove = false;
39364         var s = Roo.getDom(this.transform);
39365         if(!this.hiddenName){
39366             this.hiddenName = s.name;
39367         }
39368         if(!this.store){
39369             this.mode = 'local';
39370             var d = [], opts = s.options;
39371             for(var i = 0, len = opts.length;i < len; i++){
39372                 var o = opts[i];
39373                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39374                 if(o.selected) {
39375                     this.value = value;
39376                 }
39377                 d.push([value, o.text]);
39378             }
39379             this.store = new Roo.data.SimpleStore({
39380                 'id': 0,
39381                 fields: ['value', 'text'],
39382                 data : d
39383             });
39384             this.valueField = 'value';
39385             this.displayField = 'text';
39386         }
39387         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39388         if(!this.lazyRender){
39389             this.target = true;
39390             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39391             s.parentNode.removeChild(s); // remove it
39392             this.render(this.el.parentNode);
39393         }else{
39394             s.parentNode.removeChild(s); // remove it
39395         }
39396
39397     }
39398     if (this.store) {
39399         this.store = Roo.factory(this.store, Roo.data);
39400     }
39401     
39402     this.selectedIndex = -1;
39403     if(this.mode == 'local'){
39404         if(config.queryDelay === undefined){
39405             this.queryDelay = 10;
39406         }
39407         if(config.minChars === undefined){
39408             this.minChars = 0;
39409         }
39410     }
39411 };
39412
39413 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39414     /**
39415      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39416      */
39417     /**
39418      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39419      * rendering into an Roo.Editor, defaults to false)
39420      */
39421     /**
39422      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39423      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39424      */
39425     /**
39426      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39427      */
39428     /**
39429      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39430      * the dropdown list (defaults to undefined, with no header element)
39431      */
39432
39433      /**
39434      * @cfg {String/Roo.Template} tpl The template to use to render the output
39435      */
39436      
39437     // private
39438     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39439     /**
39440      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39441      */
39442     listWidth: undefined,
39443     /**
39444      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39445      * mode = 'remote' or 'text' if mode = 'local')
39446      */
39447     displayField: undefined,
39448     /**
39449      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39450      * mode = 'remote' or 'value' if mode = 'local'). 
39451      * Note: use of a valueField requires the user make a selection
39452      * in order for a value to be mapped.
39453      */
39454     valueField: undefined,
39455     
39456     
39457     /**
39458      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39459      * field's data value (defaults to the underlying DOM element's name)
39460      */
39461     hiddenName: undefined,
39462     /**
39463      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39464      */
39465     listClass: '',
39466     /**
39467      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39468      */
39469     selectedClass: 'x-combo-selected',
39470     /**
39471      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39472      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39473      * which displays a downward arrow icon).
39474      */
39475     triggerClass : 'x-form-arrow-trigger',
39476     /**
39477      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39478      */
39479     shadow:'sides',
39480     /**
39481      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39482      * anchor positions (defaults to 'tl-bl')
39483      */
39484     listAlign: 'tl-bl?',
39485     /**
39486      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39487      */
39488     maxHeight: 300,
39489     /**
39490      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39491      * query specified by the allQuery config option (defaults to 'query')
39492      */
39493     triggerAction: 'query',
39494     /**
39495      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39496      * (defaults to 4, does not apply if editable = false)
39497      */
39498     minChars : 4,
39499     /**
39500      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39501      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39502      */
39503     typeAhead: false,
39504     /**
39505      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39506      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39507      */
39508     queryDelay: 500,
39509     /**
39510      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39511      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39512      */
39513     pageSize: 0,
39514     /**
39515      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39516      * when editable = true (defaults to false)
39517      */
39518     selectOnFocus:false,
39519     /**
39520      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39521      */
39522     queryParam: 'query',
39523     /**
39524      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39525      * when mode = 'remote' (defaults to 'Loading...')
39526      */
39527     loadingText: 'Loading...',
39528     /**
39529      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39530      */
39531     resizable: false,
39532     /**
39533      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39534      */
39535     handleHeight : 8,
39536     /**
39537      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39538      * traditional select (defaults to true)
39539      */
39540     editable: true,
39541     /**
39542      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39543      */
39544     allQuery: '',
39545     /**
39546      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39547      */
39548     mode: 'remote',
39549     /**
39550      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39551      * listWidth has a higher value)
39552      */
39553     minListWidth : 70,
39554     /**
39555      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39556      * allow the user to set arbitrary text into the field (defaults to false)
39557      */
39558     forceSelection:false,
39559     /**
39560      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39561      * if typeAhead = true (defaults to 250)
39562      */
39563     typeAheadDelay : 250,
39564     /**
39565      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39566      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39567      */
39568     valueNotFoundText : undefined,
39569     /**
39570      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39571      */
39572     blockFocus : false,
39573     
39574     /**
39575      * @cfg {Boolean} disableClear Disable showing of clear button.
39576      */
39577     disableClear : false,
39578     /**
39579      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39580      */
39581     alwaysQuery : false,
39582     
39583     //private
39584     addicon : false,
39585     editicon: false,
39586     
39587     // element that contains real text value.. (when hidden is used..)
39588      
39589     // private
39590     onRender : function(ct, position){
39591         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39592         if(this.hiddenName){
39593             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39594                     'before', true);
39595             this.hiddenField.value =
39596                 this.hiddenValue !== undefined ? this.hiddenValue :
39597                 this.value !== undefined ? this.value : '';
39598
39599             // prevent input submission
39600             this.el.dom.removeAttribute('name');
39601              
39602              
39603         }
39604         if(Roo.isGecko){
39605             this.el.dom.setAttribute('autocomplete', 'off');
39606         }
39607
39608         var cls = 'x-combo-list';
39609
39610         this.list = new Roo.Layer({
39611             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39612         });
39613
39614         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39615         this.list.setWidth(lw);
39616         this.list.swallowEvent('mousewheel');
39617         this.assetHeight = 0;
39618
39619         if(this.title){
39620             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39621             this.assetHeight += this.header.getHeight();
39622         }
39623
39624         this.innerList = this.list.createChild({cls:cls+'-inner'});
39625         this.innerList.on('mouseover', this.onViewOver, this);
39626         this.innerList.on('mousemove', this.onViewMove, this);
39627         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39628         
39629         if(this.allowBlank && !this.pageSize && !this.disableClear){
39630             this.footer = this.list.createChild({cls:cls+'-ft'});
39631             this.pageTb = new Roo.Toolbar(this.footer);
39632            
39633         }
39634         if(this.pageSize){
39635             this.footer = this.list.createChild({cls:cls+'-ft'});
39636             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39637                     {pageSize: this.pageSize});
39638             
39639         }
39640         
39641         if (this.pageTb && this.allowBlank && !this.disableClear) {
39642             var _this = this;
39643             this.pageTb.add(new Roo.Toolbar.Fill(), {
39644                 cls: 'x-btn-icon x-btn-clear',
39645                 text: '&#160;',
39646                 handler: function()
39647                 {
39648                     _this.collapse();
39649                     _this.clearValue();
39650                     _this.onSelect(false, -1);
39651                 }
39652             });
39653         }
39654         if (this.footer) {
39655             this.assetHeight += this.footer.getHeight();
39656         }
39657         
39658
39659         if(!this.tpl){
39660             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39661         }
39662
39663         this.view = new Roo.View(this.innerList, this.tpl, {
39664             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39665         });
39666
39667         this.view.on('click', this.onViewClick, this);
39668
39669         this.store.on('beforeload', this.onBeforeLoad, this);
39670         this.store.on('load', this.onLoad, this);
39671         this.store.on('loadexception', this.onLoadException, this);
39672
39673         if(this.resizable){
39674             this.resizer = new Roo.Resizable(this.list,  {
39675                pinned:true, handles:'se'
39676             });
39677             this.resizer.on('resize', function(r, w, h){
39678                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39679                 this.listWidth = w;
39680                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39681                 this.restrictHeight();
39682             }, this);
39683             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39684         }
39685         if(!this.editable){
39686             this.editable = true;
39687             this.setEditable(false);
39688         }  
39689         
39690         
39691         if (typeof(this.events.add.listeners) != 'undefined') {
39692             
39693             this.addicon = this.wrap.createChild(
39694                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39695        
39696             this.addicon.on('click', function(e) {
39697                 this.fireEvent('add', this);
39698             }, this);
39699         }
39700         if (typeof(this.events.edit.listeners) != 'undefined') {
39701             
39702             this.editicon = this.wrap.createChild(
39703                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39704             if (this.addicon) {
39705                 this.editicon.setStyle('margin-left', '40px');
39706             }
39707             this.editicon.on('click', function(e) {
39708                 
39709                 // we fire even  if inothing is selected..
39710                 this.fireEvent('edit', this, this.lastData );
39711                 
39712             }, this);
39713         }
39714         
39715         
39716         
39717     },
39718
39719     // private
39720     initEvents : function(){
39721         Roo.form.ComboBox.superclass.initEvents.call(this);
39722
39723         this.keyNav = new Roo.KeyNav(this.el, {
39724             "up" : function(e){
39725                 this.inKeyMode = true;
39726                 this.selectPrev();
39727             },
39728
39729             "down" : function(e){
39730                 if(!this.isExpanded()){
39731                     this.onTriggerClick();
39732                 }else{
39733                     this.inKeyMode = true;
39734                     this.selectNext();
39735                 }
39736             },
39737
39738             "enter" : function(e){
39739                 this.onViewClick();
39740                 //return true;
39741             },
39742
39743             "esc" : function(e){
39744                 this.collapse();
39745             },
39746
39747             "tab" : function(e){
39748                 this.onViewClick(false);
39749                 this.fireEvent("specialkey", this, e);
39750                 return true;
39751             },
39752
39753             scope : this,
39754
39755             doRelay : function(foo, bar, hname){
39756                 if(hname == 'down' || this.scope.isExpanded()){
39757                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39758                 }
39759                 return true;
39760             },
39761
39762             forceKeyDown: true
39763         });
39764         this.queryDelay = Math.max(this.queryDelay || 10,
39765                 this.mode == 'local' ? 10 : 250);
39766         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39767         if(this.typeAhead){
39768             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39769         }
39770         if(this.editable !== false){
39771             this.el.on("keyup", this.onKeyUp, this);
39772         }
39773         if(this.forceSelection){
39774             this.on('blur', this.doForce, this);
39775         }
39776     },
39777
39778     onDestroy : function(){
39779         if(this.view){
39780             this.view.setStore(null);
39781             this.view.el.removeAllListeners();
39782             this.view.el.remove();
39783             this.view.purgeListeners();
39784         }
39785         if(this.list){
39786             this.list.destroy();
39787         }
39788         if(this.store){
39789             this.store.un('beforeload', this.onBeforeLoad, this);
39790             this.store.un('load', this.onLoad, this);
39791             this.store.un('loadexception', this.onLoadException, this);
39792         }
39793         Roo.form.ComboBox.superclass.onDestroy.call(this);
39794     },
39795
39796     // private
39797     fireKey : function(e){
39798         if(e.isNavKeyPress() && !this.list.isVisible()){
39799             this.fireEvent("specialkey", this, e);
39800         }
39801     },
39802
39803     // private
39804     onResize: function(w, h){
39805         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39806         
39807         if(typeof w != 'number'){
39808             // we do not handle it!?!?
39809             return;
39810         }
39811         var tw = this.trigger.getWidth();
39812         tw += this.addicon ? this.addicon.getWidth() : 0;
39813         tw += this.editicon ? this.editicon.getWidth() : 0;
39814         var x = w - tw;
39815         this.el.setWidth( this.adjustWidth('input', x));
39816             
39817         this.trigger.setStyle('left', x+'px');
39818         
39819         if(this.list && this.listWidth === undefined){
39820             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39821             this.list.setWidth(lw);
39822             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39823         }
39824         
39825     
39826         
39827     },
39828
39829     /**
39830      * Allow or prevent the user from directly editing the field text.  If false is passed,
39831      * the user will only be able to select from the items defined in the dropdown list.  This method
39832      * is the runtime equivalent of setting the 'editable' config option at config time.
39833      * @param {Boolean} value True to allow the user to directly edit the field text
39834      */
39835     setEditable : function(value){
39836         if(value == this.editable){
39837             return;
39838         }
39839         this.editable = value;
39840         if(!value){
39841             this.el.dom.setAttribute('readOnly', true);
39842             this.el.on('mousedown', this.onTriggerClick,  this);
39843             this.el.addClass('x-combo-noedit');
39844         }else{
39845             this.el.dom.setAttribute('readOnly', false);
39846             this.el.un('mousedown', this.onTriggerClick,  this);
39847             this.el.removeClass('x-combo-noedit');
39848         }
39849     },
39850
39851     // private
39852     onBeforeLoad : function(){
39853         if(!this.hasFocus){
39854             return;
39855         }
39856         this.innerList.update(this.loadingText ?
39857                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39858         this.restrictHeight();
39859         this.selectedIndex = -1;
39860     },
39861
39862     // private
39863     onLoad : function(){
39864         if(!this.hasFocus){
39865             return;
39866         }
39867         if(this.store.getCount() > 0){
39868             this.expand();
39869             this.restrictHeight();
39870             if(this.lastQuery == this.allQuery){
39871                 if(this.editable){
39872                     this.el.dom.select();
39873                 }
39874                 if(!this.selectByValue(this.value, true)){
39875                     this.select(0, true);
39876                 }
39877             }else{
39878                 this.selectNext();
39879                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39880                     this.taTask.delay(this.typeAheadDelay);
39881                 }
39882             }
39883         }else{
39884             this.onEmptyResults();
39885         }
39886         //this.el.focus();
39887     },
39888     // private
39889     onLoadException : function()
39890     {
39891         this.collapse();
39892         Roo.log(this.store.reader.jsonData);
39893         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39894             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39895         }
39896         
39897         
39898     },
39899     // private
39900     onTypeAhead : function(){
39901         if(this.store.getCount() > 0){
39902             var r = this.store.getAt(0);
39903             var newValue = r.data[this.displayField];
39904             var len = newValue.length;
39905             var selStart = this.getRawValue().length;
39906             if(selStart != len){
39907                 this.setRawValue(newValue);
39908                 this.selectText(selStart, newValue.length);
39909             }
39910         }
39911     },
39912
39913     // private
39914     onSelect : function(record, index){
39915         if(this.fireEvent('beforeselect', this, record, index) !== false){
39916             this.setFromData(index > -1 ? record.data : false);
39917             this.collapse();
39918             this.fireEvent('select', this, record, index);
39919         }
39920     },
39921
39922     /**
39923      * Returns the currently selected field value or empty string if no value is set.
39924      * @return {String} value The selected value
39925      */
39926     getValue : function(){
39927         if(this.valueField){
39928             return typeof this.value != 'undefined' ? this.value : '';
39929         }
39930         return Roo.form.ComboBox.superclass.getValue.call(this);
39931     },
39932
39933     /**
39934      * Clears any text/value currently set in the field
39935      */
39936     clearValue : function(){
39937         if(this.hiddenField){
39938             this.hiddenField.value = '';
39939         }
39940         this.value = '';
39941         this.setRawValue('');
39942         this.lastSelectionText = '';
39943         
39944     },
39945
39946     /**
39947      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39948      * will be displayed in the field.  If the value does not match the data value of an existing item,
39949      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39950      * Otherwise the field will be blank (although the value will still be set).
39951      * @param {String} value The value to match
39952      */
39953     setValue : function(v){
39954         var text = v;
39955         if(this.valueField){
39956             var r = this.findRecord(this.valueField, v);
39957             if(r){
39958                 text = r.data[this.displayField];
39959             }else if(this.valueNotFoundText !== undefined){
39960                 text = this.valueNotFoundText;
39961             }
39962         }
39963         this.lastSelectionText = text;
39964         if(this.hiddenField){
39965             this.hiddenField.value = v;
39966         }
39967         Roo.form.ComboBox.superclass.setValue.call(this, text);
39968         this.value = v;
39969     },
39970     /**
39971      * @property {Object} the last set data for the element
39972      */
39973     
39974     lastData : false,
39975     /**
39976      * Sets the value of the field based on a object which is related to the record format for the store.
39977      * @param {Object} value the value to set as. or false on reset?
39978      */
39979     setFromData : function(o){
39980         var dv = ''; // display value
39981         var vv = ''; // value value..
39982         this.lastData = o;
39983         if (this.displayField) {
39984             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39985         } else {
39986             // this is an error condition!!!
39987             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39988         }
39989         
39990         if(this.valueField){
39991             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39992         }
39993         if(this.hiddenField){
39994             this.hiddenField.value = vv;
39995             
39996             this.lastSelectionText = dv;
39997             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39998             this.value = vv;
39999             return;
40000         }
40001         // no hidden field.. - we store the value in 'value', but still display
40002         // display field!!!!
40003         this.lastSelectionText = dv;
40004         Roo.form.ComboBox.superclass.setValue.call(this, dv);
40005         this.value = vv;
40006         
40007         
40008     },
40009     // private
40010     reset : function(){
40011         // overridden so that last data is reset..
40012         this.setValue(this.resetValue);
40013         this.clearInvalid();
40014         this.lastData = false;
40015         if (this.view) {
40016             this.view.clearSelections();
40017         }
40018     },
40019     // private
40020     findRecord : function(prop, value){
40021         var record;
40022         if(this.store.getCount() > 0){
40023             this.store.each(function(r){
40024                 if(r.data[prop] == value){
40025                     record = r;
40026                     return false;
40027                 }
40028                 return true;
40029             });
40030         }
40031         return record;
40032     },
40033     
40034     getName: function()
40035     {
40036         // returns hidden if it's set..
40037         if (!this.rendered) {return ''};
40038         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40039         
40040     },
40041     // private
40042     onViewMove : function(e, t){
40043         this.inKeyMode = false;
40044     },
40045
40046     // private
40047     onViewOver : function(e, t){
40048         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
40049             return;
40050         }
40051         var item = this.view.findItemFromChild(t);
40052         if(item){
40053             var index = this.view.indexOf(item);
40054             this.select(index, false);
40055         }
40056     },
40057
40058     // private
40059     onViewClick : function(doFocus)
40060     {
40061         var index = this.view.getSelectedIndexes()[0];
40062         var r = this.store.getAt(index);
40063         if(r){
40064             this.onSelect(r, index);
40065         }
40066         if(doFocus !== false && !this.blockFocus){
40067             this.el.focus();
40068         }
40069     },
40070
40071     // private
40072     restrictHeight : function(){
40073         this.innerList.dom.style.height = '';
40074         var inner = this.innerList.dom;
40075         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
40076         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
40077         this.list.beginUpdate();
40078         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
40079         this.list.alignTo(this.el, this.listAlign);
40080         this.list.endUpdate();
40081     },
40082
40083     // private
40084     onEmptyResults : function(){
40085         this.collapse();
40086     },
40087
40088     /**
40089      * Returns true if the dropdown list is expanded, else false.
40090      */
40091     isExpanded : function(){
40092         return this.list.isVisible();
40093     },
40094
40095     /**
40096      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40097      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40098      * @param {String} value The data value of the item to select
40099      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40100      * selected item if it is not currently in view (defaults to true)
40101      * @return {Boolean} True if the value matched an item in the list, else false
40102      */
40103     selectByValue : function(v, scrollIntoView){
40104         if(v !== undefined && v !== null){
40105             var r = this.findRecord(this.valueField || this.displayField, v);
40106             if(r){
40107                 this.select(this.store.indexOf(r), scrollIntoView);
40108                 return true;
40109             }
40110         }
40111         return false;
40112     },
40113
40114     /**
40115      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40116      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40117      * @param {Number} index The zero-based index of the list item to select
40118      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40119      * selected item if it is not currently in view (defaults to true)
40120      */
40121     select : function(index, scrollIntoView){
40122         this.selectedIndex = index;
40123         this.view.select(index);
40124         if(scrollIntoView !== false){
40125             var el = this.view.getNode(index);
40126             if(el){
40127                 this.innerList.scrollChildIntoView(el, false);
40128             }
40129         }
40130     },
40131
40132     // private
40133     selectNext : function(){
40134         var ct = this.store.getCount();
40135         if(ct > 0){
40136             if(this.selectedIndex == -1){
40137                 this.select(0);
40138             }else if(this.selectedIndex < ct-1){
40139                 this.select(this.selectedIndex+1);
40140             }
40141         }
40142     },
40143
40144     // private
40145     selectPrev : function(){
40146         var ct = this.store.getCount();
40147         if(ct > 0){
40148             if(this.selectedIndex == -1){
40149                 this.select(0);
40150             }else if(this.selectedIndex != 0){
40151                 this.select(this.selectedIndex-1);
40152             }
40153         }
40154     },
40155
40156     // private
40157     onKeyUp : function(e){
40158         if(this.editable !== false && !e.isSpecialKey()){
40159             this.lastKey = e.getKey();
40160             this.dqTask.delay(this.queryDelay);
40161         }
40162     },
40163
40164     // private
40165     validateBlur : function(){
40166         return !this.list || !this.list.isVisible();   
40167     },
40168
40169     // private
40170     initQuery : function(){
40171         this.doQuery(this.getRawValue());
40172     },
40173
40174     // private
40175     doForce : function(){
40176         if(this.el.dom.value.length > 0){
40177             this.el.dom.value =
40178                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40179              
40180         }
40181     },
40182
40183     /**
40184      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40185      * query allowing the query action to be canceled if needed.
40186      * @param {String} query The SQL query to execute
40187      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40188      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40189      * saved in the current store (defaults to false)
40190      */
40191     doQuery : function(q, forceAll){
40192         if(q === undefined || q === null){
40193             q = '';
40194         }
40195         var qe = {
40196             query: q,
40197             forceAll: forceAll,
40198             combo: this,
40199             cancel:false
40200         };
40201         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40202             return false;
40203         }
40204         q = qe.query;
40205         forceAll = qe.forceAll;
40206         if(forceAll === true || (q.length >= this.minChars)){
40207             if(this.lastQuery != q || this.alwaysQuery){
40208                 this.lastQuery = q;
40209                 if(this.mode == 'local'){
40210                     this.selectedIndex = -1;
40211                     if(forceAll){
40212                         this.store.clearFilter();
40213                     }else{
40214                         this.store.filter(this.displayField, q);
40215                     }
40216                     this.onLoad();
40217                 }else{
40218                     this.store.baseParams[this.queryParam] = q;
40219                     this.store.load({
40220                         params: this.getParams(q)
40221                     });
40222                     this.expand();
40223                 }
40224             }else{
40225                 this.selectedIndex = -1;
40226                 this.onLoad();   
40227             }
40228         }
40229     },
40230
40231     // private
40232     getParams : function(q){
40233         var p = {};
40234         //p[this.queryParam] = q;
40235         if(this.pageSize){
40236             p.start = 0;
40237             p.limit = this.pageSize;
40238         }
40239         return p;
40240     },
40241
40242     /**
40243      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40244      */
40245     collapse : function(){
40246         if(!this.isExpanded()){
40247             return;
40248         }
40249         this.list.hide();
40250         Roo.get(document).un('mousedown', this.collapseIf, this);
40251         Roo.get(document).un('mousewheel', this.collapseIf, this);
40252         if (!this.editable) {
40253             Roo.get(document).un('keydown', this.listKeyPress, this);
40254         }
40255         this.fireEvent('collapse', this);
40256     },
40257
40258     // private
40259     collapseIf : function(e){
40260         if(!e.within(this.wrap) && !e.within(this.list)){
40261             this.collapse();
40262         }
40263     },
40264
40265     /**
40266      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40267      */
40268     expand : function(){
40269         if(this.isExpanded() || !this.hasFocus){
40270             return;
40271         }
40272         this.list.alignTo(this.el, this.listAlign);
40273         this.list.show();
40274         Roo.get(document).on('mousedown', this.collapseIf, this);
40275         Roo.get(document).on('mousewheel', this.collapseIf, this);
40276         if (!this.editable) {
40277             Roo.get(document).on('keydown', this.listKeyPress, this);
40278         }
40279         
40280         this.fireEvent('expand', this);
40281     },
40282
40283     // private
40284     // Implements the default empty TriggerField.onTriggerClick function
40285     onTriggerClick : function(){
40286         if(this.disabled){
40287             return;
40288         }
40289         if(this.isExpanded()){
40290             this.collapse();
40291             if (!this.blockFocus) {
40292                 this.el.focus();
40293             }
40294             
40295         }else {
40296             this.hasFocus = true;
40297             if(this.triggerAction == 'all') {
40298                 this.doQuery(this.allQuery, true);
40299             } else {
40300                 this.doQuery(this.getRawValue());
40301             }
40302             if (!this.blockFocus) {
40303                 this.el.focus();
40304             }
40305         }
40306     },
40307     listKeyPress : function(e)
40308     {
40309         //Roo.log('listkeypress');
40310         // scroll to first matching element based on key pres..
40311         if (e.isSpecialKey()) {
40312             return false;
40313         }
40314         var k = String.fromCharCode(e.getKey()).toUpperCase();
40315         //Roo.log(k);
40316         var match  = false;
40317         var csel = this.view.getSelectedNodes();
40318         var cselitem = false;
40319         if (csel.length) {
40320             var ix = this.view.indexOf(csel[0]);
40321             cselitem  = this.store.getAt(ix);
40322             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40323                 cselitem = false;
40324             }
40325             
40326         }
40327         
40328         this.store.each(function(v) { 
40329             if (cselitem) {
40330                 // start at existing selection.
40331                 if (cselitem.id == v.id) {
40332                     cselitem = false;
40333                 }
40334                 return;
40335             }
40336                 
40337             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40338                 match = this.store.indexOf(v);
40339                 return false;
40340             }
40341         }, this);
40342         
40343         if (match === false) {
40344             return true; // no more action?
40345         }
40346         // scroll to?
40347         this.view.select(match);
40348         var sn = Roo.get(this.view.getSelectedNodes()[0])
40349         sn.scrollIntoView(sn.dom.parentNode, false);
40350     }
40351
40352     /** 
40353     * @cfg {Boolean} grow 
40354     * @hide 
40355     */
40356     /** 
40357     * @cfg {Number} growMin 
40358     * @hide 
40359     */
40360     /** 
40361     * @cfg {Number} growMax 
40362     * @hide 
40363     */
40364     /**
40365      * @hide
40366      * @method autoSize
40367      */
40368 });/*
40369  * Copyright(c) 2010-2012, Roo J Solutions Limited
40370  *
40371  * Licence LGPL
40372  *
40373  */
40374
40375 /**
40376  * @class Roo.form.ComboBoxArray
40377  * @extends Roo.form.TextField
40378  * A facebook style adder... for lists of email / people / countries  etc...
40379  * pick multiple items from a combo box, and shows each one.
40380  *
40381  *  Fred [x]  Brian [x]  [Pick another |v]
40382  *
40383  *
40384  *  For this to work: it needs various extra information
40385  *    - normal combo problay has
40386  *      name, hiddenName
40387  *    + displayField, valueField
40388  *
40389  *    For our purpose...
40390  *
40391  *
40392  *   If we change from 'extends' to wrapping...
40393  *   
40394  *  
40395  *
40396  
40397  
40398  * @constructor
40399  * Create a new ComboBoxArray.
40400  * @param {Object} config Configuration options
40401  */
40402  
40403
40404 Roo.form.ComboBoxArray = function(config)
40405 {
40406     this.addEvents({
40407         /**
40408          * @event beforeremove
40409          * Fires before remove the value from the list
40410              * @param {Roo.form.ComboBoxArray} _self This combo box array
40411              * @param {Roo.form.ComboBoxArray.Item} item removed item
40412              */
40413         'beforeremove' : true,
40414         /**
40415          * @event remove
40416          * Fires when remove the value from the list
40417              * @param {Roo.form.ComboBoxArray} _self This combo box array
40418              * @param {Roo.form.ComboBoxArray.Item} item removed item
40419              */
40420         'remove' : true
40421         
40422         
40423     });
40424     
40425     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40426     
40427     this.items = new Roo.util.MixedCollection(false);
40428     
40429     // construct the child combo...
40430     
40431     
40432     
40433     
40434    
40435     
40436 }
40437
40438  
40439 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40440
40441     /**
40442      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40443      */
40444     
40445     lastData : false,
40446     
40447     // behavies liek a hiddne field
40448     inputType:      'hidden',
40449     /**
40450      * @cfg {Number} width The width of the box that displays the selected element
40451      */ 
40452     width:          300,
40453
40454     
40455     
40456     /**
40457      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40458      */
40459     name : false,
40460     /**
40461      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40462      */
40463     hiddenName : false,
40464     
40465     
40466     // private the array of items that are displayed..
40467     items  : false,
40468     // private - the hidden field el.
40469     hiddenEl : false,
40470     // private - the filed el..
40471     el : false,
40472     
40473     //validateValue : function() { return true; }, // all values are ok!
40474     //onAddClick: function() { },
40475     
40476     onRender : function(ct, position) 
40477     {
40478         
40479         // create the standard hidden element
40480         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40481         
40482         
40483         // give fake names to child combo;
40484         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40485         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40486         
40487         this.combo = Roo.factory(this.combo, Roo.form);
40488         this.combo.onRender(ct, position);
40489         if (typeof(this.combo.width) != 'undefined') {
40490             this.combo.onResize(this.combo.width,0);
40491         }
40492         
40493         this.combo.initEvents();
40494         
40495         // assigned so form know we need to do this..
40496         this.store          = this.combo.store;
40497         this.valueField     = this.combo.valueField;
40498         this.displayField   = this.combo.displayField ;
40499         
40500         
40501         this.combo.wrap.addClass('x-cbarray-grp');
40502         
40503         var cbwrap = this.combo.wrap.createChild(
40504             {tag: 'div', cls: 'x-cbarray-cb'},
40505             this.combo.el.dom
40506         );
40507         
40508              
40509         this.hiddenEl = this.combo.wrap.createChild({
40510             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40511         });
40512         this.el = this.combo.wrap.createChild({
40513             tag: 'input',  type:'hidden' , name: this.name, value : ''
40514         });
40515          //   this.el.dom.removeAttribute("name");
40516         
40517         
40518         this.outerWrap = this.combo.wrap;
40519         this.wrap = cbwrap;
40520         
40521         this.outerWrap.setWidth(this.width);
40522         this.outerWrap.dom.removeChild(this.el.dom);
40523         
40524         this.wrap.dom.appendChild(this.el.dom);
40525         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40526         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40527         
40528         this.combo.trigger.setStyle('position','relative');
40529         this.combo.trigger.setStyle('left', '0px');
40530         this.combo.trigger.setStyle('top', '2px');
40531         
40532         this.combo.el.setStyle('vertical-align', 'text-bottom');
40533         
40534         //this.trigger.setStyle('vertical-align', 'top');
40535         
40536         // this should use the code from combo really... on('add' ....)
40537         if (this.adder) {
40538             
40539         
40540             this.adder = this.outerWrap.createChild(
40541                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40542             var _t = this;
40543             this.adder.on('click', function(e) {
40544                 _t.fireEvent('adderclick', this, e);
40545             }, _t);
40546         }
40547         //var _t = this;
40548         //this.adder.on('click', this.onAddClick, _t);
40549         
40550         
40551         this.combo.on('select', function(cb, rec, ix) {
40552             this.addItem(rec.data);
40553             
40554             cb.setValue('');
40555             cb.el.dom.value = '';
40556             //cb.lastData = rec.data;
40557             // add to list
40558             
40559         }, this);
40560         
40561         
40562     },
40563     
40564     
40565     getName: function()
40566     {
40567         // returns hidden if it's set..
40568         if (!this.rendered) {return ''};
40569         return  this.hiddenName ? this.hiddenName : this.name;
40570         
40571     },
40572     
40573     
40574     onResize: function(w, h){
40575         
40576         return;
40577         // not sure if this is needed..
40578         //this.combo.onResize(w,h);
40579         
40580         if(typeof w != 'number'){
40581             // we do not handle it!?!?
40582             return;
40583         }
40584         var tw = this.combo.trigger.getWidth();
40585         tw += this.addicon ? this.addicon.getWidth() : 0;
40586         tw += this.editicon ? this.editicon.getWidth() : 0;
40587         var x = w - tw;
40588         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40589             
40590         this.combo.trigger.setStyle('left', '0px');
40591         
40592         if(this.list && this.listWidth === undefined){
40593             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40594             this.list.setWidth(lw);
40595             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40596         }
40597         
40598     
40599         
40600     },
40601     
40602     addItem: function(rec)
40603     {
40604         var valueField = this.combo.valueField;
40605         var displayField = this.combo.displayField;
40606         if (this.items.indexOfKey(rec[valueField]) > -1) {
40607             //console.log("GOT " + rec.data.id);
40608             return;
40609         }
40610         
40611         var x = new Roo.form.ComboBoxArray.Item({
40612             //id : rec[this.idField],
40613             data : rec,
40614             displayField : displayField ,
40615             tipField : displayField ,
40616             cb : this
40617         });
40618         // use the 
40619         this.items.add(rec[valueField],x);
40620         // add it before the element..
40621         this.updateHiddenEl();
40622         x.render(this.outerWrap, this.wrap.dom);
40623         // add the image handler..
40624     },
40625     
40626     updateHiddenEl : function()
40627     {
40628         this.validate();
40629         if (!this.hiddenEl) {
40630             return;
40631         }
40632         var ar = [];
40633         var idField = this.combo.valueField;
40634         
40635         this.items.each(function(f) {
40636             ar.push(f.data[idField]);
40637            
40638         });
40639         this.hiddenEl.dom.value = ar.join(',');
40640         this.validate();
40641     },
40642     
40643     reset : function()
40644     {
40645         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40646         this.items.each(function(f) {
40647            f.remove(); 
40648         });
40649         this.el.dom.value = '';
40650         if (this.hiddenEl) {
40651             this.hiddenEl.dom.value = '';
40652         }
40653         
40654     },
40655     getValue: function()
40656     {
40657         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40658     },
40659     setValue: function(v) // not a valid action - must use addItems..
40660     {
40661          
40662         this.reset();
40663         
40664         
40665         
40666         if (this.store.isLocal && (typeof(v) == 'string')) {
40667             // then we can use the store to find the values..
40668             // comma seperated at present.. this needs to allow JSON based encoding..
40669             this.hiddenEl.value  = v;
40670             var v_ar = [];
40671             Roo.each(v.split(','), function(k) {
40672                 Roo.log("CHECK " + this.valueField + ',' + k);
40673                 var li = this.store.query(this.valueField, k);
40674                 if (!li.length) {
40675                     return;
40676                 }
40677                 var add = {};
40678                 add[this.valueField] = k;
40679                 add[this.displayField] = li.item(0).data[this.displayField];
40680                 
40681                 this.addItem(add);
40682             }, this) 
40683              
40684         }
40685         if (typeof(v) == 'object' ) {
40686             // then let's assume it's an array of objects..
40687             Roo.each(v, function(l) {
40688                 this.addItem(l);
40689             }, this);
40690              
40691         }
40692         
40693         
40694     },
40695     setFromData: function(v)
40696     {
40697         // this recieves an object, if setValues is called.
40698         this.reset();
40699         this.el.dom.value = v[this.displayField];
40700         this.hiddenEl.dom.value = v[this.valueField];
40701         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40702             return;
40703         }
40704         var kv = v[this.valueField];
40705         var dv = v[this.displayField];
40706         kv = typeof(kv) != 'string' ? '' : kv;
40707         dv = typeof(dv) != 'string' ? '' : dv;
40708         
40709         
40710         var keys = kv.split(',');
40711         var display = dv.split(',');
40712         for (var i = 0 ; i < keys.length; i++) {
40713             
40714             add = {};
40715             add[this.valueField] = keys[i];
40716             add[this.displayField] = display[i];
40717             this.addItem(add);
40718         }
40719       
40720         
40721     },
40722     
40723     /**
40724      * Validates the combox array value
40725      * @return {Boolean} True if the value is valid, else false
40726      */
40727     validate : function(){
40728         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40729             this.clearInvalid();
40730             return true;
40731         }
40732         return false;
40733     },
40734     
40735     validateValue : function(value){
40736         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40737         
40738     },
40739     
40740     /*@
40741      * overide
40742      * 
40743      */
40744     isDirty : function() {
40745         if(this.disabled) {
40746             return false;
40747         }
40748         
40749         try {
40750             var d = Roo.decode(String(this.originalValue));
40751         } catch (e) {
40752             return String(this.getValue()) !== String(this.originalValue);
40753         }
40754         
40755         var originalValue = [];
40756         
40757         for (var i = 0; i < d.length; i++){
40758             originalValue.push(d[i][this.valueField]);
40759         }
40760         
40761         return String(this.getValue()) !== String(originalValue.join(','));
40762         
40763     }
40764     
40765 });
40766
40767
40768
40769 /**
40770  * @class Roo.form.ComboBoxArray.Item
40771  * @extends Roo.BoxComponent
40772  * A selected item in the list
40773  *  Fred [x]  Brian [x]  [Pick another |v]
40774  * 
40775  * @constructor
40776  * Create a new item.
40777  * @param {Object} config Configuration options
40778  */
40779  
40780 Roo.form.ComboBoxArray.Item = function(config) {
40781     config.id = Roo.id();
40782     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40783 }
40784
40785 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40786     data : {},
40787     cb: false,
40788     displayField : false,
40789     tipField : false,
40790     
40791     
40792     defaultAutoCreate : {
40793         tag: 'div',
40794         cls: 'x-cbarray-item',
40795         cn : [ 
40796             { tag: 'div' },
40797             {
40798                 tag: 'img',
40799                 width:16,
40800                 height : 16,
40801                 src : Roo.BLANK_IMAGE_URL ,
40802                 align: 'center'
40803             }
40804         ]
40805         
40806     },
40807     
40808  
40809     onRender : function(ct, position)
40810     {
40811         Roo.form.Field.superclass.onRender.call(this, ct, position);
40812         
40813         if(!this.el){
40814             var cfg = this.getAutoCreate();
40815             this.el = ct.createChild(cfg, position);
40816         }
40817         
40818         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40819         
40820         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40821             this.cb.renderer(this.data) :
40822             String.format('{0}',this.data[this.displayField]);
40823         
40824             
40825         this.el.child('div').dom.setAttribute('qtip',
40826                         String.format('{0}',this.data[this.tipField])
40827         );
40828         
40829         this.el.child('img').on('click', this.remove, this);
40830         
40831     },
40832    
40833     remove : function()
40834     {
40835         if(this.cb.disabled){
40836             return;
40837         }
40838         
40839         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
40840             this.cb.items.remove(this);
40841             this.el.child('img').un('click', this.remove, this);
40842             this.el.remove();
40843             this.cb.updateHiddenEl();
40844
40845             this.cb.fireEvent('remove', this.cb, this);
40846         }
40847         
40848     }
40849 });/*
40850  * Based on:
40851  * Ext JS Library 1.1.1
40852  * Copyright(c) 2006-2007, Ext JS, LLC.
40853  *
40854  * Originally Released Under LGPL - original licence link has changed is not relivant.
40855  *
40856  * Fork - LGPL
40857  * <script type="text/javascript">
40858  */
40859 /**
40860  * @class Roo.form.Checkbox
40861  * @extends Roo.form.Field
40862  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40863  * @constructor
40864  * Creates a new Checkbox
40865  * @param {Object} config Configuration options
40866  */
40867 Roo.form.Checkbox = function(config){
40868     Roo.form.Checkbox.superclass.constructor.call(this, config);
40869     this.addEvents({
40870         /**
40871          * @event check
40872          * Fires when the checkbox is checked or unchecked.
40873              * @param {Roo.form.Checkbox} this This checkbox
40874              * @param {Boolean} checked The new checked value
40875              */
40876         check : true
40877     });
40878 };
40879
40880 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40881     /**
40882      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40883      */
40884     focusClass : undefined,
40885     /**
40886      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40887      */
40888     fieldClass: "x-form-field",
40889     /**
40890      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40891      */
40892     checked: false,
40893     /**
40894      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40895      * {tag: "input", type: "checkbox", autocomplete: "off"})
40896      */
40897     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40898     /**
40899      * @cfg {String} boxLabel The text that appears beside the checkbox
40900      */
40901     boxLabel : "",
40902     /**
40903      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40904      */  
40905     inputValue : '1',
40906     /**
40907      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40908      */
40909      valueOff: '0', // value when not checked..
40910
40911     actionMode : 'viewEl', 
40912     //
40913     // private
40914     itemCls : 'x-menu-check-item x-form-item',
40915     groupClass : 'x-menu-group-item',
40916     inputType : 'hidden',
40917     
40918     
40919     inSetChecked: false, // check that we are not calling self...
40920     
40921     inputElement: false, // real input element?
40922     basedOn: false, // ????
40923     
40924     isFormField: true, // not sure where this is needed!!!!
40925
40926     onResize : function(){
40927         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40928         if(!this.boxLabel){
40929             this.el.alignTo(this.wrap, 'c-c');
40930         }
40931     },
40932
40933     initEvents : function(){
40934         Roo.form.Checkbox.superclass.initEvents.call(this);
40935         this.el.on("click", this.onClick,  this);
40936         this.el.on("change", this.onClick,  this);
40937     },
40938
40939
40940     getResizeEl : function(){
40941         return this.wrap;
40942     },
40943
40944     getPositionEl : function(){
40945         return this.wrap;
40946     },
40947
40948     // private
40949     onRender : function(ct, position){
40950         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40951         /*
40952         if(this.inputValue !== undefined){
40953             this.el.dom.value = this.inputValue;
40954         }
40955         */
40956         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40957         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40958         var viewEl = this.wrap.createChild({ 
40959             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40960         this.viewEl = viewEl;   
40961         this.wrap.on('click', this.onClick,  this); 
40962         
40963         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40964         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40965         
40966         
40967         
40968         if(this.boxLabel){
40969             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40970         //    viewEl.on('click', this.onClick,  this); 
40971         }
40972         //if(this.checked){
40973             this.setChecked(this.checked);
40974         //}else{
40975             //this.checked = this.el.dom;
40976         //}
40977
40978     },
40979
40980     // private
40981     initValue : Roo.emptyFn,
40982
40983     /**
40984      * Returns the checked state of the checkbox.
40985      * @return {Boolean} True if checked, else false
40986      */
40987     getValue : function(){
40988         if(this.el){
40989             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40990         }
40991         return this.valueOff;
40992         
40993     },
40994
40995         // private
40996     onClick : function(){ 
40997         if (this.disabled) {
40998             return;
40999         }
41000         this.setChecked(!this.checked);
41001
41002         //if(this.el.dom.checked != this.checked){
41003         //    this.setValue(this.el.dom.checked);
41004        // }
41005     },
41006
41007     /**
41008      * Sets the checked state of the checkbox.
41009      * On is always based on a string comparison between inputValue and the param.
41010      * @param {Boolean/String} value - the value to set 
41011      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
41012      */
41013     setValue : function(v,suppressEvent){
41014         
41015         
41016         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
41017         //if(this.el && this.el.dom){
41018         //    this.el.dom.checked = this.checked;
41019         //    this.el.dom.defaultChecked = this.checked;
41020         //}
41021         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
41022         //this.fireEvent("check", this, this.checked);
41023     },
41024     // private..
41025     setChecked : function(state,suppressEvent)
41026     {
41027         if (this.inSetChecked) {
41028             this.checked = state;
41029             return;
41030         }
41031         
41032     
41033         if(this.wrap){
41034             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
41035         }
41036         this.checked = state;
41037         if(suppressEvent !== true){
41038             this.fireEvent('check', this, state);
41039         }
41040         this.inSetChecked = true;
41041         this.el.dom.value = state ? this.inputValue : this.valueOff;
41042         this.inSetChecked = false;
41043         
41044     },
41045     // handle setting of hidden value by some other method!!?!?
41046     setFromHidden: function()
41047     {
41048         if(!this.el){
41049             return;
41050         }
41051         //console.log("SET FROM HIDDEN");
41052         //alert('setFrom hidden');
41053         this.setValue(this.el.dom.value);
41054     },
41055     
41056     onDestroy : function()
41057     {
41058         if(this.viewEl){
41059             Roo.get(this.viewEl).remove();
41060         }
41061          
41062         Roo.form.Checkbox.superclass.onDestroy.call(this);
41063     }
41064
41065 });/*
41066  * Based on:
41067  * Ext JS Library 1.1.1
41068  * Copyright(c) 2006-2007, Ext JS, LLC.
41069  *
41070  * Originally Released Under LGPL - original licence link has changed is not relivant.
41071  *
41072  * Fork - LGPL
41073  * <script type="text/javascript">
41074  */
41075  
41076 /**
41077  * @class Roo.form.Radio
41078  * @extends Roo.form.Checkbox
41079  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
41080  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
41081  * @constructor
41082  * Creates a new Radio
41083  * @param {Object} config Configuration options
41084  */
41085 Roo.form.Radio = function(){
41086     Roo.form.Radio.superclass.constructor.apply(this, arguments);
41087 };
41088 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
41089     inputType: 'radio',
41090
41091     /**
41092      * If this radio is part of a group, it will return the selected value
41093      * @return {String}
41094      */
41095     getGroupValue : function(){
41096         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
41097     },
41098     
41099     
41100     onRender : function(ct, position){
41101         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41102         
41103         if(this.inputValue !== undefined){
41104             this.el.dom.value = this.inputValue;
41105         }
41106          
41107         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41108         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41109         //var viewEl = this.wrap.createChild({ 
41110         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41111         //this.viewEl = viewEl;   
41112         //this.wrap.on('click', this.onClick,  this); 
41113         
41114         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41115         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41116         
41117         
41118         
41119         if(this.boxLabel){
41120             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41121         //    viewEl.on('click', this.onClick,  this); 
41122         }
41123          if(this.checked){
41124             this.el.dom.checked =   'checked' ;
41125         }
41126          
41127     } 
41128     
41129     
41130 });//<script type="text/javascript">
41131
41132 /*
41133  * Based  Ext JS Library 1.1.1
41134  * Copyright(c) 2006-2007, Ext JS, LLC.
41135  * LGPL
41136  *
41137  */
41138  
41139 /**
41140  * @class Roo.HtmlEditorCore
41141  * @extends Roo.Component
41142  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41143  *
41144  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41145  */
41146
41147 Roo.HtmlEditorCore = function(config){
41148     
41149     
41150     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41151     
41152     
41153     this.addEvents({
41154         /**
41155          * @event initialize
41156          * Fires when the editor is fully initialized (including the iframe)
41157          * @param {Roo.HtmlEditorCore} this
41158          */
41159         initialize: true,
41160         /**
41161          * @event activate
41162          * Fires when the editor is first receives the focus. Any insertion must wait
41163          * until after this event.
41164          * @param {Roo.HtmlEditorCore} this
41165          */
41166         activate: true,
41167          /**
41168          * @event beforesync
41169          * Fires before the textarea is updated with content from the editor iframe. Return false
41170          * to cancel the sync.
41171          * @param {Roo.HtmlEditorCore} this
41172          * @param {String} html
41173          */
41174         beforesync: true,
41175          /**
41176          * @event beforepush
41177          * Fires before the iframe editor is updated with content from the textarea. Return false
41178          * to cancel the push.
41179          * @param {Roo.HtmlEditorCore} this
41180          * @param {String} html
41181          */
41182         beforepush: true,
41183          /**
41184          * @event sync
41185          * Fires when the textarea is updated with content from the editor iframe.
41186          * @param {Roo.HtmlEditorCore} this
41187          * @param {String} html
41188          */
41189         sync: true,
41190          /**
41191          * @event push
41192          * Fires when the iframe editor is updated with content from the textarea.
41193          * @param {Roo.HtmlEditorCore} this
41194          * @param {String} html
41195          */
41196         push: true,
41197         
41198         /**
41199          * @event editorevent
41200          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41201          * @param {Roo.HtmlEditorCore} this
41202          */
41203         editorevent: true
41204         
41205     });
41206     
41207     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41208     
41209     // defaults : white / black...
41210     this.applyBlacklists();
41211     
41212     
41213     
41214 };
41215
41216
41217 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41218
41219
41220      /**
41221      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41222      */
41223     
41224     owner : false,
41225     
41226      /**
41227      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41228      *                        Roo.resizable.
41229      */
41230     resizable : false,
41231      /**
41232      * @cfg {Number} height (in pixels)
41233      */   
41234     height: 300,
41235    /**
41236      * @cfg {Number} width (in pixels)
41237      */   
41238     width: 500,
41239     
41240     /**
41241      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41242      * 
41243      */
41244     stylesheets: false,
41245     
41246     // id of frame..
41247     frameId: false,
41248     
41249     // private properties
41250     validationEvent : false,
41251     deferHeight: true,
41252     initialized : false,
41253     activated : false,
41254     sourceEditMode : false,
41255     onFocus : Roo.emptyFn,
41256     iframePad:3,
41257     hideMode:'offsets',
41258     
41259     clearUp: true,
41260     
41261     // blacklist + whitelisted elements..
41262     black: false,
41263     white: false,
41264      
41265     
41266
41267     /**
41268      * Protected method that will not generally be called directly. It
41269      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41270      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41271      */
41272     getDocMarkup : function(){
41273         // body styles..
41274         var st = '';
41275         
41276         // inherit styels from page...?? 
41277         if (this.stylesheets === false) {
41278             
41279             Roo.get(document.head).select('style').each(function(node) {
41280                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41281             });
41282             
41283             Roo.get(document.head).select('link').each(function(node) { 
41284                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41285             });
41286             
41287         } else if (!this.stylesheets.length) {
41288                 // simple..
41289                 st = '<style type="text/css">' +
41290                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41291                    '</style>';
41292         } else { 
41293             
41294         }
41295         
41296         st +=  '<style type="text/css">' +
41297             'IMG { cursor: pointer } ' +
41298         '</style>';
41299
41300         
41301         return '<html><head>' + st  +
41302             //<style type="text/css">' +
41303             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41304             //'</style>' +
41305             ' </head><body class="roo-htmleditor-body"></body></html>';
41306     },
41307
41308     // private
41309     onRender : function(ct, position)
41310     {
41311         var _t = this;
41312         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41313         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41314         
41315         
41316         this.el.dom.style.border = '0 none';
41317         this.el.dom.setAttribute('tabIndex', -1);
41318         this.el.addClass('x-hidden hide');
41319         
41320         
41321         
41322         if(Roo.isIE){ // fix IE 1px bogus margin
41323             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41324         }
41325        
41326         
41327         this.frameId = Roo.id();
41328         
41329          
41330         
41331         var iframe = this.owner.wrap.createChild({
41332             tag: 'iframe',
41333             cls: 'form-control', // bootstrap..
41334             id: this.frameId,
41335             name: this.frameId,
41336             frameBorder : 'no',
41337             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41338         }, this.el
41339         );
41340         
41341         
41342         this.iframe = iframe.dom;
41343
41344          this.assignDocWin();
41345         
41346         this.doc.designMode = 'on';
41347        
41348         this.doc.open();
41349         this.doc.write(this.getDocMarkup());
41350         this.doc.close();
41351
41352         
41353         var task = { // must defer to wait for browser to be ready
41354             run : function(){
41355                 //console.log("run task?" + this.doc.readyState);
41356                 this.assignDocWin();
41357                 if(this.doc.body || this.doc.readyState == 'complete'){
41358                     try {
41359                         this.doc.designMode="on";
41360                     } catch (e) {
41361                         return;
41362                     }
41363                     Roo.TaskMgr.stop(task);
41364                     this.initEditor.defer(10, this);
41365                 }
41366             },
41367             interval : 10,
41368             duration: 10000,
41369             scope: this
41370         };
41371         Roo.TaskMgr.start(task);
41372
41373     },
41374
41375     // private
41376     onResize : function(w, h)
41377     {
41378          Roo.log('resize: ' +w + ',' + h );
41379         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41380         if(!this.iframe){
41381             return;
41382         }
41383         if(typeof w == 'number'){
41384             
41385             this.iframe.style.width = w + 'px';
41386         }
41387         if(typeof h == 'number'){
41388             
41389             this.iframe.style.height = h + 'px';
41390             if(this.doc){
41391                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41392             }
41393         }
41394         
41395     },
41396
41397     /**
41398      * Toggles the editor between standard and source edit mode.
41399      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41400      */
41401     toggleSourceEdit : function(sourceEditMode){
41402         
41403         this.sourceEditMode = sourceEditMode === true;
41404         
41405         if(this.sourceEditMode){
41406  
41407             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41408             
41409         }else{
41410             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41411             //this.iframe.className = '';
41412             this.deferFocus();
41413         }
41414         //this.setSize(this.owner.wrap.getSize());
41415         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41416     },
41417
41418     
41419   
41420
41421     /**
41422      * Protected method that will not generally be called directly. If you need/want
41423      * custom HTML cleanup, this is the method you should override.
41424      * @param {String} html The HTML to be cleaned
41425      * return {String} The cleaned HTML
41426      */
41427     cleanHtml : function(html){
41428         html = String(html);
41429         if(html.length > 5){
41430             if(Roo.isSafari){ // strip safari nonsense
41431                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41432             }
41433         }
41434         if(html == '&nbsp;'){
41435             html = '';
41436         }
41437         return html;
41438     },
41439
41440     /**
41441      * HTML Editor -> Textarea
41442      * Protected method that will not generally be called directly. Syncs the contents
41443      * of the editor iframe with the textarea.
41444      */
41445     syncValue : function(){
41446         if(this.initialized){
41447             var bd = (this.doc.body || this.doc.documentElement);
41448             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41449             var html = bd.innerHTML;
41450             if(Roo.isSafari){
41451                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41452                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41453                 if(m && m[1]){
41454                     html = '<div style="'+m[0]+'">' + html + '</div>';
41455                 }
41456             }
41457             html = this.cleanHtml(html);
41458             // fix up the special chars.. normaly like back quotes in word...
41459             // however we do not want to do this with chinese..
41460             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41461                 var cc = b.charCodeAt();
41462                 if (
41463                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41464                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41465                     (cc >= 0xf900 && cc < 0xfb00 )
41466                 ) {
41467                         return b;
41468                 }
41469                 return "&#"+cc+";" 
41470             });
41471             if(this.owner.fireEvent('beforesync', this, html) !== false){
41472                 this.el.dom.value = html;
41473                 this.owner.fireEvent('sync', this, html);
41474             }
41475         }
41476     },
41477
41478     /**
41479      * Protected method that will not generally be called directly. Pushes the value of the textarea
41480      * into the iframe editor.
41481      */
41482     pushValue : function(){
41483         if(this.initialized){
41484             var v = this.el.dom.value.trim();
41485             
41486 //            if(v.length < 1){
41487 //                v = '&#160;';
41488 //            }
41489             
41490             if(this.owner.fireEvent('beforepush', this, v) !== false){
41491                 var d = (this.doc.body || this.doc.documentElement);
41492                 d.innerHTML = v;
41493                 this.cleanUpPaste();
41494                 this.el.dom.value = d.innerHTML;
41495                 this.owner.fireEvent('push', this, v);
41496             }
41497         }
41498     },
41499
41500     // private
41501     deferFocus : function(){
41502         this.focus.defer(10, this);
41503     },
41504
41505     // doc'ed in Field
41506     focus : function(){
41507         if(this.win && !this.sourceEditMode){
41508             this.win.focus();
41509         }else{
41510             this.el.focus();
41511         }
41512     },
41513     
41514     assignDocWin: function()
41515     {
41516         var iframe = this.iframe;
41517         
41518          if(Roo.isIE){
41519             this.doc = iframe.contentWindow.document;
41520             this.win = iframe.contentWindow;
41521         } else {
41522 //            if (!Roo.get(this.frameId)) {
41523 //                return;
41524 //            }
41525 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41526 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41527             
41528             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41529                 return;
41530             }
41531             
41532             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41533             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41534         }
41535     },
41536     
41537     // private
41538     initEditor : function(){
41539         //console.log("INIT EDITOR");
41540         this.assignDocWin();
41541         
41542         
41543         
41544         this.doc.designMode="on";
41545         this.doc.open();
41546         this.doc.write(this.getDocMarkup());
41547         this.doc.close();
41548         
41549         var dbody = (this.doc.body || this.doc.documentElement);
41550         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41551         // this copies styles from the containing element into thsi one..
41552         // not sure why we need all of this..
41553         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41554         
41555         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41556         //ss['background-attachment'] = 'fixed'; // w3c
41557         dbody.bgProperties = 'fixed'; // ie
41558         //Roo.DomHelper.applyStyles(dbody, ss);
41559         Roo.EventManager.on(this.doc, {
41560             //'mousedown': this.onEditorEvent,
41561             'mouseup': this.onEditorEvent,
41562             'dblclick': this.onEditorEvent,
41563             'click': this.onEditorEvent,
41564             'keyup': this.onEditorEvent,
41565             buffer:100,
41566             scope: this
41567         });
41568         if(Roo.isGecko){
41569             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41570         }
41571         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41572             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41573         }
41574         this.initialized = true;
41575
41576         this.owner.fireEvent('initialize', this);
41577         this.pushValue();
41578     },
41579
41580     // private
41581     onDestroy : function(){
41582         
41583         
41584         
41585         if(this.rendered){
41586             
41587             //for (var i =0; i < this.toolbars.length;i++) {
41588             //    // fixme - ask toolbars for heights?
41589             //    this.toolbars[i].onDestroy();
41590            // }
41591             
41592             //this.wrap.dom.innerHTML = '';
41593             //this.wrap.remove();
41594         }
41595     },
41596
41597     // private
41598     onFirstFocus : function(){
41599         
41600         this.assignDocWin();
41601         
41602         
41603         this.activated = true;
41604          
41605     
41606         if(Roo.isGecko){ // prevent silly gecko errors
41607             this.win.focus();
41608             var s = this.win.getSelection();
41609             if(!s.focusNode || s.focusNode.nodeType != 3){
41610                 var r = s.getRangeAt(0);
41611                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41612                 r.collapse(true);
41613                 this.deferFocus();
41614             }
41615             try{
41616                 this.execCmd('useCSS', true);
41617                 this.execCmd('styleWithCSS', false);
41618             }catch(e){}
41619         }
41620         this.owner.fireEvent('activate', this);
41621     },
41622
41623     // private
41624     adjustFont: function(btn){
41625         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41626         //if(Roo.isSafari){ // safari
41627         //    adjust *= 2;
41628        // }
41629         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41630         if(Roo.isSafari){ // safari
41631             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41632             v =  (v < 10) ? 10 : v;
41633             v =  (v > 48) ? 48 : v;
41634             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41635             
41636         }
41637         
41638         
41639         v = Math.max(1, v+adjust);
41640         
41641         this.execCmd('FontSize', v  );
41642     },
41643
41644     onEditorEvent : function(e)
41645     {
41646         this.owner.fireEvent('editorevent', this, e);
41647       //  this.updateToolbar();
41648         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41649     },
41650
41651     insertTag : function(tg)
41652     {
41653         // could be a bit smarter... -> wrap the current selected tRoo..
41654         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41655             
41656             range = this.createRange(this.getSelection());
41657             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41658             wrappingNode.appendChild(range.extractContents());
41659             range.insertNode(wrappingNode);
41660
41661             return;
41662             
41663             
41664             
41665         }
41666         this.execCmd("formatblock",   tg);
41667         
41668     },
41669     
41670     insertText : function(txt)
41671     {
41672         
41673         
41674         var range = this.createRange();
41675         range.deleteContents();
41676                //alert(Sender.getAttribute('label'));
41677                
41678         range.insertNode(this.doc.createTextNode(txt));
41679     } ,
41680     
41681      
41682
41683     /**
41684      * Executes a Midas editor command on the editor document and performs necessary focus and
41685      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41686      * @param {String} cmd The Midas command
41687      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41688      */
41689     relayCmd : function(cmd, value){
41690         this.win.focus();
41691         this.execCmd(cmd, value);
41692         this.owner.fireEvent('editorevent', this);
41693         //this.updateToolbar();
41694         this.owner.deferFocus();
41695     },
41696
41697     /**
41698      * Executes a Midas editor command directly on the editor document.
41699      * For visual commands, you should use {@link #relayCmd} instead.
41700      * <b>This should only be called after the editor is initialized.</b>
41701      * @param {String} cmd The Midas command
41702      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41703      */
41704     execCmd : function(cmd, value){
41705         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41706         this.syncValue();
41707     },
41708  
41709  
41710    
41711     /**
41712      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41713      * to insert tRoo.
41714      * @param {String} text | dom node.. 
41715      */
41716     insertAtCursor : function(text)
41717     {
41718         
41719         
41720         
41721         if(!this.activated){
41722             return;
41723         }
41724         /*
41725         if(Roo.isIE){
41726             this.win.focus();
41727             var r = this.doc.selection.createRange();
41728             if(r){
41729                 r.collapse(true);
41730                 r.pasteHTML(text);
41731                 this.syncValue();
41732                 this.deferFocus();
41733             
41734             }
41735             return;
41736         }
41737         */
41738         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41739             this.win.focus();
41740             
41741             
41742             // from jquery ui (MIT licenced)
41743             var range, node;
41744             var win = this.win;
41745             
41746             if (win.getSelection && win.getSelection().getRangeAt) {
41747                 range = win.getSelection().getRangeAt(0);
41748                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41749                 range.insertNode(node);
41750             } else if (win.document.selection && win.document.selection.createRange) {
41751                 // no firefox support
41752                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41753                 win.document.selection.createRange().pasteHTML(txt);
41754             } else {
41755                 // no firefox support
41756                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41757                 this.execCmd('InsertHTML', txt);
41758             } 
41759             
41760             this.syncValue();
41761             
41762             this.deferFocus();
41763         }
41764     },
41765  // private
41766     mozKeyPress : function(e){
41767         if(e.ctrlKey){
41768             var c = e.getCharCode(), cmd;
41769           
41770             if(c > 0){
41771                 c = String.fromCharCode(c).toLowerCase();
41772                 switch(c){
41773                     case 'b':
41774                         cmd = 'bold';
41775                         break;
41776                     case 'i':
41777                         cmd = 'italic';
41778                         break;
41779                     
41780                     case 'u':
41781                         cmd = 'underline';
41782                         break;
41783                     
41784                     case 'v':
41785                         this.cleanUpPaste.defer(100, this);
41786                         return;
41787                         
41788                 }
41789                 if(cmd){
41790                     this.win.focus();
41791                     this.execCmd(cmd);
41792                     this.deferFocus();
41793                     e.preventDefault();
41794                 }
41795                 
41796             }
41797         }
41798     },
41799
41800     // private
41801     fixKeys : function(){ // load time branching for fastest keydown performance
41802         if(Roo.isIE){
41803             return function(e){
41804                 var k = e.getKey(), r;
41805                 if(k == e.TAB){
41806                     e.stopEvent();
41807                     r = this.doc.selection.createRange();
41808                     if(r){
41809                         r.collapse(true);
41810                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41811                         this.deferFocus();
41812                     }
41813                     return;
41814                 }
41815                 
41816                 if(k == e.ENTER){
41817                     r = this.doc.selection.createRange();
41818                     if(r){
41819                         var target = r.parentElement();
41820                         if(!target || target.tagName.toLowerCase() != 'li'){
41821                             e.stopEvent();
41822                             r.pasteHTML('<br />');
41823                             r.collapse(false);
41824                             r.select();
41825                         }
41826                     }
41827                 }
41828                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41829                     this.cleanUpPaste.defer(100, this);
41830                     return;
41831                 }
41832                 
41833                 
41834             };
41835         }else if(Roo.isOpera){
41836             return function(e){
41837                 var k = e.getKey();
41838                 if(k == e.TAB){
41839                     e.stopEvent();
41840                     this.win.focus();
41841                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41842                     this.deferFocus();
41843                 }
41844                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41845                     this.cleanUpPaste.defer(100, this);
41846                     return;
41847                 }
41848                 
41849             };
41850         }else if(Roo.isSafari){
41851             return function(e){
41852                 var k = e.getKey();
41853                 
41854                 if(k == e.TAB){
41855                     e.stopEvent();
41856                     this.execCmd('InsertText','\t');
41857                     this.deferFocus();
41858                     return;
41859                 }
41860                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41861                     this.cleanUpPaste.defer(100, this);
41862                     return;
41863                 }
41864                 
41865              };
41866         }
41867     }(),
41868     
41869     getAllAncestors: function()
41870     {
41871         var p = this.getSelectedNode();
41872         var a = [];
41873         if (!p) {
41874             a.push(p); // push blank onto stack..
41875             p = this.getParentElement();
41876         }
41877         
41878         
41879         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41880             a.push(p);
41881             p = p.parentNode;
41882         }
41883         a.push(this.doc.body);
41884         return a;
41885     },
41886     lastSel : false,
41887     lastSelNode : false,
41888     
41889     
41890     getSelection : function() 
41891     {
41892         this.assignDocWin();
41893         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41894     },
41895     
41896     getSelectedNode: function() 
41897     {
41898         // this may only work on Gecko!!!
41899         
41900         // should we cache this!!!!
41901         
41902         
41903         
41904          
41905         var range = this.createRange(this.getSelection()).cloneRange();
41906         
41907         if (Roo.isIE) {
41908             var parent = range.parentElement();
41909             while (true) {
41910                 var testRange = range.duplicate();
41911                 testRange.moveToElementText(parent);
41912                 if (testRange.inRange(range)) {
41913                     break;
41914                 }
41915                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41916                     break;
41917                 }
41918                 parent = parent.parentElement;
41919             }
41920             return parent;
41921         }
41922         
41923         // is ancestor a text element.
41924         var ac =  range.commonAncestorContainer;
41925         if (ac.nodeType == 3) {
41926             ac = ac.parentNode;
41927         }
41928         
41929         var ar = ac.childNodes;
41930          
41931         var nodes = [];
41932         var other_nodes = [];
41933         var has_other_nodes = false;
41934         for (var i=0;i<ar.length;i++) {
41935             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41936                 continue;
41937             }
41938             // fullly contained node.
41939             
41940             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41941                 nodes.push(ar[i]);
41942                 continue;
41943             }
41944             
41945             // probably selected..
41946             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41947                 other_nodes.push(ar[i]);
41948                 continue;
41949             }
41950             // outer..
41951             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41952                 continue;
41953             }
41954             
41955             
41956             has_other_nodes = true;
41957         }
41958         if (!nodes.length && other_nodes.length) {
41959             nodes= other_nodes;
41960         }
41961         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41962             return false;
41963         }
41964         
41965         return nodes[0];
41966     },
41967     createRange: function(sel)
41968     {
41969         // this has strange effects when using with 
41970         // top toolbar - not sure if it's a great idea.
41971         //this.editor.contentWindow.focus();
41972         if (typeof sel != "undefined") {
41973             try {
41974                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41975             } catch(e) {
41976                 return this.doc.createRange();
41977             }
41978         } else {
41979             return this.doc.createRange();
41980         }
41981     },
41982     getParentElement: function()
41983     {
41984         
41985         this.assignDocWin();
41986         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41987         
41988         var range = this.createRange(sel);
41989          
41990         try {
41991             var p = range.commonAncestorContainer;
41992             while (p.nodeType == 3) { // text node
41993                 p = p.parentNode;
41994             }
41995             return p;
41996         } catch (e) {
41997             return null;
41998         }
41999     
42000     },
42001     /***
42002      *
42003      * Range intersection.. the hard stuff...
42004      *  '-1' = before
42005      *  '0' = hits..
42006      *  '1' = after.
42007      *         [ -- selected range --- ]
42008      *   [fail]                        [fail]
42009      *
42010      *    basically..
42011      *      if end is before start or  hits it. fail.
42012      *      if start is after end or hits it fail.
42013      *
42014      *   if either hits (but other is outside. - then it's not 
42015      *   
42016      *    
42017      **/
42018     
42019     
42020     // @see http://www.thismuchiknow.co.uk/?p=64.
42021     rangeIntersectsNode : function(range, node)
42022     {
42023         var nodeRange = node.ownerDocument.createRange();
42024         try {
42025             nodeRange.selectNode(node);
42026         } catch (e) {
42027             nodeRange.selectNodeContents(node);
42028         }
42029     
42030         var rangeStartRange = range.cloneRange();
42031         rangeStartRange.collapse(true);
42032     
42033         var rangeEndRange = range.cloneRange();
42034         rangeEndRange.collapse(false);
42035     
42036         var nodeStartRange = nodeRange.cloneRange();
42037         nodeStartRange.collapse(true);
42038     
42039         var nodeEndRange = nodeRange.cloneRange();
42040         nodeEndRange.collapse(false);
42041     
42042         return rangeStartRange.compareBoundaryPoints(
42043                  Range.START_TO_START, nodeEndRange) == -1 &&
42044                rangeEndRange.compareBoundaryPoints(
42045                  Range.START_TO_START, nodeStartRange) == 1;
42046         
42047          
42048     },
42049     rangeCompareNode : function(range, node)
42050     {
42051         var nodeRange = node.ownerDocument.createRange();
42052         try {
42053             nodeRange.selectNode(node);
42054         } catch (e) {
42055             nodeRange.selectNodeContents(node);
42056         }
42057         
42058         
42059         range.collapse(true);
42060     
42061         nodeRange.collapse(true);
42062      
42063         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
42064         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
42065          
42066         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
42067         
42068         var nodeIsBefore   =  ss == 1;
42069         var nodeIsAfter    = ee == -1;
42070         
42071         if (nodeIsBefore && nodeIsAfter)
42072             return 0; // outer
42073         if (!nodeIsBefore && nodeIsAfter)
42074             return 1; //right trailed.
42075         
42076         if (nodeIsBefore && !nodeIsAfter)
42077             return 2;  // left trailed.
42078         // fully contined.
42079         return 3;
42080     },
42081
42082     // private? - in a new class?
42083     cleanUpPaste :  function()
42084     {
42085         // cleans up the whole document..
42086         Roo.log('cleanuppaste');
42087         
42088         this.cleanUpChildren(this.doc.body);
42089         var clean = this.cleanWordChars(this.doc.body.innerHTML);
42090         if (clean != this.doc.body.innerHTML) {
42091             this.doc.body.innerHTML = clean;
42092         }
42093         
42094     },
42095     
42096     cleanWordChars : function(input) {// change the chars to hex code
42097         var he = Roo.HtmlEditorCore;
42098         
42099         var output = input;
42100         Roo.each(he.swapCodes, function(sw) { 
42101             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42102             
42103             output = output.replace(swapper, sw[1]);
42104         });
42105         
42106         return output;
42107     },
42108     
42109     
42110     cleanUpChildren : function (n)
42111     {
42112         if (!n.childNodes.length) {
42113             return;
42114         }
42115         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42116            this.cleanUpChild(n.childNodes[i]);
42117         }
42118     },
42119     
42120     
42121         
42122     
42123     cleanUpChild : function (node)
42124     {
42125         var ed = this;
42126         //console.log(node);
42127         if (node.nodeName == "#text") {
42128             // clean up silly Windows -- stuff?
42129             return; 
42130         }
42131         if (node.nodeName == "#comment") {
42132             node.parentNode.removeChild(node);
42133             // clean up silly Windows -- stuff?
42134             return; 
42135         }
42136         var lcname = node.tagName.toLowerCase();
42137         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42138         // whitelist of tags..
42139         
42140         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42141             // remove node.
42142             node.parentNode.removeChild(node);
42143             return;
42144             
42145         }
42146         
42147         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42148         
42149         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42150         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42151         
42152         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42153         //    remove_keep_children = true;
42154         //}
42155         
42156         if (remove_keep_children) {
42157             this.cleanUpChildren(node);
42158             // inserts everything just before this node...
42159             while (node.childNodes.length) {
42160                 var cn = node.childNodes[0];
42161                 node.removeChild(cn);
42162                 node.parentNode.insertBefore(cn, node);
42163             }
42164             node.parentNode.removeChild(node);
42165             return;
42166         }
42167         
42168         if (!node.attributes || !node.attributes.length) {
42169             this.cleanUpChildren(node);
42170             return;
42171         }
42172         
42173         function cleanAttr(n,v)
42174         {
42175             
42176             if (v.match(/^\./) || v.match(/^\//)) {
42177                 return;
42178             }
42179             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42180                 return;
42181             }
42182             if (v.match(/^#/)) {
42183                 return;
42184             }
42185 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42186             node.removeAttribute(n);
42187             
42188         }
42189         
42190         var cwhite = this.cwhite;
42191         var cblack = this.cblack;
42192             
42193         function cleanStyle(n,v)
42194         {
42195             if (v.match(/expression/)) { //XSS?? should we even bother..
42196                 node.removeAttribute(n);
42197                 return;
42198             }
42199             
42200             var parts = v.split(/;/);
42201             var clean = [];
42202             
42203             Roo.each(parts, function(p) {
42204                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42205                 if (!p.length) {
42206                     return true;
42207                 }
42208                 var l = p.split(':').shift().replace(/\s+/g,'');
42209                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42210                 
42211                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42212 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42213                     //node.removeAttribute(n);
42214                     return true;
42215                 }
42216                 //Roo.log()
42217                 // only allow 'c whitelisted system attributes'
42218                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42219 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42220                     //node.removeAttribute(n);
42221                     return true;
42222                 }
42223                 
42224                 
42225                  
42226                 
42227                 clean.push(p);
42228                 return true;
42229             });
42230             if (clean.length) { 
42231                 node.setAttribute(n, clean.join(';'));
42232             } else {
42233                 node.removeAttribute(n);
42234             }
42235             
42236         }
42237         
42238         
42239         for (var i = node.attributes.length-1; i > -1 ; i--) {
42240             var a = node.attributes[i];
42241             //console.log(a);
42242             
42243             if (a.name.toLowerCase().substr(0,2)=='on')  {
42244                 node.removeAttribute(a.name);
42245                 continue;
42246             }
42247             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42248                 node.removeAttribute(a.name);
42249                 continue;
42250             }
42251             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42252                 cleanAttr(a.name,a.value); // fixme..
42253                 continue;
42254             }
42255             if (a.name == 'style') {
42256                 cleanStyle(a.name,a.value);
42257                 continue;
42258             }
42259             /// clean up MS crap..
42260             // tecnically this should be a list of valid class'es..
42261             
42262             
42263             if (a.name == 'class') {
42264                 if (a.value.match(/^Mso/)) {
42265                     node.className = '';
42266                 }
42267                 
42268                 if (a.value.match(/body/)) {
42269                     node.className = '';
42270                 }
42271                 continue;
42272             }
42273             
42274             // style cleanup!?
42275             // class cleanup?
42276             
42277         }
42278         
42279         
42280         this.cleanUpChildren(node);
42281         
42282         
42283     },
42284     
42285     /**
42286      * Clean up MS wordisms...
42287      */
42288     cleanWord : function(node)
42289     {
42290         
42291         
42292         if (!node) {
42293             this.cleanWord(this.doc.body);
42294             return;
42295         }
42296         if (node.nodeName == "#text") {
42297             // clean up silly Windows -- stuff?
42298             return; 
42299         }
42300         if (node.nodeName == "#comment") {
42301             node.parentNode.removeChild(node);
42302             // clean up silly Windows -- stuff?
42303             return; 
42304         }
42305         
42306         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42307             node.parentNode.removeChild(node);
42308             return;
42309         }
42310         
42311         // remove - but keep children..
42312         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42313             while (node.childNodes.length) {
42314                 var cn = node.childNodes[0];
42315                 node.removeChild(cn);
42316                 node.parentNode.insertBefore(cn, node);
42317             }
42318             node.parentNode.removeChild(node);
42319             this.iterateChildren(node, this.cleanWord);
42320             return;
42321         }
42322         // clean styles
42323         if (node.className.length) {
42324             
42325             var cn = node.className.split(/\W+/);
42326             var cna = [];
42327             Roo.each(cn, function(cls) {
42328                 if (cls.match(/Mso[a-zA-Z]+/)) {
42329                     return;
42330                 }
42331                 cna.push(cls);
42332             });
42333             node.className = cna.length ? cna.join(' ') : '';
42334             if (!cna.length) {
42335                 node.removeAttribute("class");
42336             }
42337         }
42338         
42339         if (node.hasAttribute("lang")) {
42340             node.removeAttribute("lang");
42341         }
42342         
42343         if (node.hasAttribute("style")) {
42344             
42345             var styles = node.getAttribute("style").split(";");
42346             var nstyle = [];
42347             Roo.each(styles, function(s) {
42348                 if (!s.match(/:/)) {
42349                     return;
42350                 }
42351                 var kv = s.split(":");
42352                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42353                     return;
42354                 }
42355                 // what ever is left... we allow.
42356                 nstyle.push(s);
42357             });
42358             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42359             if (!nstyle.length) {
42360                 node.removeAttribute('style');
42361             }
42362         }
42363         this.iterateChildren(node, this.cleanWord);
42364         
42365         
42366         
42367     },
42368     /**
42369      * iterateChildren of a Node, calling fn each time, using this as the scole..
42370      * @param {DomNode} node node to iterate children of.
42371      * @param {Function} fn method of this class to call on each item.
42372      */
42373     iterateChildren : function(node, fn)
42374     {
42375         if (!node.childNodes.length) {
42376                 return;
42377         }
42378         for (var i = node.childNodes.length-1; i > -1 ; i--) {
42379            fn.call(this, node.childNodes[i])
42380         }
42381     },
42382     
42383     
42384     /**
42385      * cleanTableWidths.
42386      *
42387      * Quite often pasting from word etc.. results in tables with column and widths.
42388      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
42389      *
42390      */
42391     cleanTableWidths : function(node)
42392     {
42393          
42394          
42395         if (!node) {
42396             this.cleanTableWidths(this.doc.body);
42397             return;
42398         }
42399         
42400         // ignore list...
42401         if (node.nodeName == "#text" || node.nodeName == "#comment") {
42402             return; 
42403         }
42404         Roo.log(node.tagName);
42405         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
42406             this.iterateChildren(node, this.cleanTableWidths);
42407             return;
42408         }
42409         if (node.hasAttribute('width')) {
42410             node.removeAttribute('width');
42411         }
42412         
42413          
42414         if (node.hasAttribute("style")) {
42415             // pretty basic...
42416             
42417             var styles = node.getAttribute("style").split(";");
42418             var nstyle = [];
42419             Roo.each(styles, function(s) {
42420                 if (!s.match(/:/)) {
42421                     return;
42422                 }
42423                 var kv = s.split(":");
42424                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
42425                     return;
42426                 }
42427                 // what ever is left... we allow.
42428                 nstyle.push(s);
42429             });
42430             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42431             if (!nstyle.length) {
42432                 node.removeAttribute('style');
42433             }
42434         }
42435         
42436         this.iterateChildren(node, this.cleanTableWidths);
42437         
42438         
42439     },
42440     
42441     
42442     
42443     
42444     domToHTML : function(currentElement, depth, nopadtext) {
42445         
42446         depth = depth || 0;
42447         nopadtext = nopadtext || false;
42448     
42449         if (!currentElement) {
42450             return this.domToHTML(this.doc.body);
42451         }
42452         
42453         //Roo.log(currentElement);
42454         var j;
42455         var allText = false;
42456         var nodeName = currentElement.nodeName;
42457         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42458         
42459         if  (nodeName == '#text') {
42460             
42461             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
42462         }
42463         
42464         
42465         var ret = '';
42466         if (nodeName != 'BODY') {
42467              
42468             var i = 0;
42469             // Prints the node tagName, such as <A>, <IMG>, etc
42470             if (tagName) {
42471                 var attr = [];
42472                 for(i = 0; i < currentElement.attributes.length;i++) {
42473                     // quoting?
42474                     var aname = currentElement.attributes.item(i).name;
42475                     if (!currentElement.attributes.item(i).value.length) {
42476                         continue;
42477                     }
42478                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42479                 }
42480                 
42481                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42482             } 
42483             else {
42484                 
42485                 // eack
42486             }
42487         } else {
42488             tagName = false;
42489         }
42490         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42491             return ret;
42492         }
42493         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42494             nopadtext = true;
42495         }
42496         
42497         
42498         // Traverse the tree
42499         i = 0;
42500         var currentElementChild = currentElement.childNodes.item(i);
42501         var allText = true;
42502         var innerHTML  = '';
42503         lastnode = '';
42504         while (currentElementChild) {
42505             // Formatting code (indent the tree so it looks nice on the screen)
42506             var nopad = nopadtext;
42507             if (lastnode == 'SPAN') {
42508                 nopad  = true;
42509             }
42510             // text
42511             if  (currentElementChild.nodeName == '#text') {
42512                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42513                 toadd = nopadtext ? toadd : toadd.trim();
42514                 if (!nopad && toadd.length > 80) {
42515                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42516                 }
42517                 innerHTML  += toadd;
42518                 
42519                 i++;
42520                 currentElementChild = currentElement.childNodes.item(i);
42521                 lastNode = '';
42522                 continue;
42523             }
42524             allText = false;
42525             
42526             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42527                 
42528             // Recursively traverse the tree structure of the child node
42529             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42530             lastnode = currentElementChild.nodeName;
42531             i++;
42532             currentElementChild=currentElement.childNodes.item(i);
42533         }
42534         
42535         ret += innerHTML;
42536         
42537         if (!allText) {
42538                 // The remaining code is mostly for formatting the tree
42539             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42540         }
42541         
42542         
42543         if (tagName) {
42544             ret+= "</"+tagName+">";
42545         }
42546         return ret;
42547         
42548     },
42549         
42550     applyBlacklists : function()
42551     {
42552         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42553         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42554         
42555         this.white = [];
42556         this.black = [];
42557         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42558             if (b.indexOf(tag) > -1) {
42559                 return;
42560             }
42561             this.white.push(tag);
42562             
42563         }, this);
42564         
42565         Roo.each(w, function(tag) {
42566             if (b.indexOf(tag) > -1) {
42567                 return;
42568             }
42569             if (this.white.indexOf(tag) > -1) {
42570                 return;
42571             }
42572             this.white.push(tag);
42573             
42574         }, this);
42575         
42576         
42577         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42578             if (w.indexOf(tag) > -1) {
42579                 return;
42580             }
42581             this.black.push(tag);
42582             
42583         }, this);
42584         
42585         Roo.each(b, function(tag) {
42586             if (w.indexOf(tag) > -1) {
42587                 return;
42588             }
42589             if (this.black.indexOf(tag) > -1) {
42590                 return;
42591             }
42592             this.black.push(tag);
42593             
42594         }, this);
42595         
42596         
42597         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42598         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42599         
42600         this.cwhite = [];
42601         this.cblack = [];
42602         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42603             if (b.indexOf(tag) > -1) {
42604                 return;
42605             }
42606             this.cwhite.push(tag);
42607             
42608         }, this);
42609         
42610         Roo.each(w, function(tag) {
42611             if (b.indexOf(tag) > -1) {
42612                 return;
42613             }
42614             if (this.cwhite.indexOf(tag) > -1) {
42615                 return;
42616             }
42617             this.cwhite.push(tag);
42618             
42619         }, this);
42620         
42621         
42622         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42623             if (w.indexOf(tag) > -1) {
42624                 return;
42625             }
42626             this.cblack.push(tag);
42627             
42628         }, this);
42629         
42630         Roo.each(b, function(tag) {
42631             if (w.indexOf(tag) > -1) {
42632                 return;
42633             }
42634             if (this.cblack.indexOf(tag) > -1) {
42635                 return;
42636             }
42637             this.cblack.push(tag);
42638             
42639         }, this);
42640     },
42641     
42642     setStylesheets : function(stylesheets)
42643     {
42644         if(typeof(stylesheets) == 'string'){
42645             Roo.get(this.iframe.contentDocument.head).createChild({
42646                 tag : 'link',
42647                 rel : 'stylesheet',
42648                 type : 'text/css',
42649                 href : stylesheets
42650             });
42651             
42652             return;
42653         }
42654         var _this = this;
42655      
42656         Roo.each(stylesheets, function(s) {
42657             if(!s.length){
42658                 return;
42659             }
42660             
42661             Roo.get(_this.iframe.contentDocument.head).createChild({
42662                 tag : 'link',
42663                 rel : 'stylesheet',
42664                 type : 'text/css',
42665                 href : s
42666             });
42667         });
42668
42669         
42670     },
42671     
42672     removeStylesheets : function()
42673     {
42674         var _this = this;
42675         
42676         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
42677             s.remove();
42678         });
42679     }
42680     
42681     // hide stuff that is not compatible
42682     /**
42683      * @event blur
42684      * @hide
42685      */
42686     /**
42687      * @event change
42688      * @hide
42689      */
42690     /**
42691      * @event focus
42692      * @hide
42693      */
42694     /**
42695      * @event specialkey
42696      * @hide
42697      */
42698     /**
42699      * @cfg {String} fieldClass @hide
42700      */
42701     /**
42702      * @cfg {String} focusClass @hide
42703      */
42704     /**
42705      * @cfg {String} autoCreate @hide
42706      */
42707     /**
42708      * @cfg {String} inputType @hide
42709      */
42710     /**
42711      * @cfg {String} invalidClass @hide
42712      */
42713     /**
42714      * @cfg {String} invalidText @hide
42715      */
42716     /**
42717      * @cfg {String} msgFx @hide
42718      */
42719     /**
42720      * @cfg {String} validateOnBlur @hide
42721      */
42722 });
42723
42724 Roo.HtmlEditorCore.white = [
42725         'area', 'br', 'img', 'input', 'hr', 'wbr',
42726         
42727        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42728        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42729        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42730        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42731        'table',   'ul',         'xmp', 
42732        
42733        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42734       'thead',   'tr', 
42735      
42736       'dir', 'menu', 'ol', 'ul', 'dl',
42737        
42738       'embed',  'object'
42739 ];
42740
42741
42742 Roo.HtmlEditorCore.black = [
42743     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42744         'applet', // 
42745         'base',   'basefont', 'bgsound', 'blink',  'body', 
42746         'frame',  'frameset', 'head',    'html',   'ilayer', 
42747         'iframe', 'layer',  'link',     'meta',    'object',   
42748         'script', 'style' ,'title',  'xml' // clean later..
42749 ];
42750 Roo.HtmlEditorCore.clean = [
42751     'script', 'style', 'title', 'xml'
42752 ];
42753 Roo.HtmlEditorCore.remove = [
42754     'font'
42755 ];
42756 // attributes..
42757
42758 Roo.HtmlEditorCore.ablack = [
42759     'on'
42760 ];
42761     
42762 Roo.HtmlEditorCore.aclean = [ 
42763     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42764 ];
42765
42766 // protocols..
42767 Roo.HtmlEditorCore.pwhite= [
42768         'http',  'https',  'mailto'
42769 ];
42770
42771 // white listed style attributes.
42772 Roo.HtmlEditorCore.cwhite= [
42773       //  'text-align', /// default is to allow most things..
42774       
42775          
42776 //        'font-size'//??
42777 ];
42778
42779 // black listed style attributes.
42780 Roo.HtmlEditorCore.cblack= [
42781       //  'font-size' -- this can be set by the project 
42782 ];
42783
42784
42785 Roo.HtmlEditorCore.swapCodes   =[ 
42786     [    8211, "--" ], 
42787     [    8212, "--" ], 
42788     [    8216,  "'" ],  
42789     [    8217, "'" ],  
42790     [    8220, '"' ],  
42791     [    8221, '"' ],  
42792     [    8226, "*" ],  
42793     [    8230, "..." ]
42794 ]; 
42795
42796     //<script type="text/javascript">
42797
42798 /*
42799  * Ext JS Library 1.1.1
42800  * Copyright(c) 2006-2007, Ext JS, LLC.
42801  * Licence LGPL
42802  * 
42803  */
42804  
42805  
42806 Roo.form.HtmlEditor = function(config){
42807     
42808     
42809     
42810     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42811     
42812     if (!this.toolbars) {
42813         this.toolbars = [];
42814     }
42815     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42816     
42817     
42818 };
42819
42820 /**
42821  * @class Roo.form.HtmlEditor
42822  * @extends Roo.form.Field
42823  * Provides a lightweight HTML Editor component.
42824  *
42825  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42826  * 
42827  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42828  * supported by this editor.</b><br/><br/>
42829  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42830  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42831  */
42832 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42833     /**
42834      * @cfg {Boolean} clearUp
42835      */
42836     clearUp : true,
42837       /**
42838      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42839      */
42840     toolbars : false,
42841    
42842      /**
42843      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42844      *                        Roo.resizable.
42845      */
42846     resizable : false,
42847      /**
42848      * @cfg {Number} height (in pixels)
42849      */   
42850     height: 300,
42851    /**
42852      * @cfg {Number} width (in pixels)
42853      */   
42854     width: 500,
42855     
42856     /**
42857      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42858      * 
42859      */
42860     stylesheets: false,
42861     
42862     
42863      /**
42864      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42865      * 
42866      */
42867     cblack: false,
42868     /**
42869      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42870      * 
42871      */
42872     cwhite: false,
42873     
42874      /**
42875      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42876      * 
42877      */
42878     black: false,
42879     /**
42880      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42881      * 
42882      */
42883     white: false,
42884     
42885     // id of frame..
42886     frameId: false,
42887     
42888     // private properties
42889     validationEvent : false,
42890     deferHeight: true,
42891     initialized : false,
42892     activated : false,
42893     
42894     onFocus : Roo.emptyFn,
42895     iframePad:3,
42896     hideMode:'offsets',
42897     
42898     actionMode : 'container', // defaults to hiding it...
42899     
42900     defaultAutoCreate : { // modified by initCompnoent..
42901         tag: "textarea",
42902         style:"width:500px;height:300px;",
42903         autocomplete: "new-password"
42904     },
42905
42906     // private
42907     initComponent : function(){
42908         this.addEvents({
42909             /**
42910              * @event initialize
42911              * Fires when the editor is fully initialized (including the iframe)
42912              * @param {HtmlEditor} this
42913              */
42914             initialize: true,
42915             /**
42916              * @event activate
42917              * Fires when the editor is first receives the focus. Any insertion must wait
42918              * until after this event.
42919              * @param {HtmlEditor} this
42920              */
42921             activate: true,
42922              /**
42923              * @event beforesync
42924              * Fires before the textarea is updated with content from the editor iframe. Return false
42925              * to cancel the sync.
42926              * @param {HtmlEditor} this
42927              * @param {String} html
42928              */
42929             beforesync: true,
42930              /**
42931              * @event beforepush
42932              * Fires before the iframe editor is updated with content from the textarea. Return false
42933              * to cancel the push.
42934              * @param {HtmlEditor} this
42935              * @param {String} html
42936              */
42937             beforepush: true,
42938              /**
42939              * @event sync
42940              * Fires when the textarea is updated with content from the editor iframe.
42941              * @param {HtmlEditor} this
42942              * @param {String} html
42943              */
42944             sync: true,
42945              /**
42946              * @event push
42947              * Fires when the iframe editor is updated with content from the textarea.
42948              * @param {HtmlEditor} this
42949              * @param {String} html
42950              */
42951             push: true,
42952              /**
42953              * @event editmodechange
42954              * Fires when the editor switches edit modes
42955              * @param {HtmlEditor} this
42956              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42957              */
42958             editmodechange: true,
42959             /**
42960              * @event editorevent
42961              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42962              * @param {HtmlEditor} this
42963              */
42964             editorevent: true,
42965             /**
42966              * @event firstfocus
42967              * Fires when on first focus - needed by toolbars..
42968              * @param {HtmlEditor} this
42969              */
42970             firstfocus: true,
42971             /**
42972              * @event autosave
42973              * Auto save the htmlEditor value as a file into Events
42974              * @param {HtmlEditor} this
42975              */
42976             autosave: true,
42977             /**
42978              * @event savedpreview
42979              * preview the saved version of htmlEditor
42980              * @param {HtmlEditor} this
42981              */
42982             savedpreview: true,
42983             
42984             /**
42985             * @event stylesheetsclick
42986             * Fires when press the Sytlesheets button
42987             * @param {Roo.HtmlEditorCore} this
42988             */
42989             stylesheetsclick: true
42990         });
42991         this.defaultAutoCreate =  {
42992             tag: "textarea",
42993             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42994             autocomplete: "new-password"
42995         };
42996     },
42997
42998     /**
42999      * Protected method that will not generally be called directly. It
43000      * is called when the editor creates its toolbar. Override this method if you need to
43001      * add custom toolbar buttons.
43002      * @param {HtmlEditor} editor
43003      */
43004     createToolbar : function(editor){
43005         Roo.log("create toolbars");
43006         if (!editor.toolbars || !editor.toolbars.length) {
43007             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
43008         }
43009         
43010         for (var i =0 ; i < editor.toolbars.length;i++) {
43011             editor.toolbars[i] = Roo.factory(
43012                     typeof(editor.toolbars[i]) == 'string' ?
43013                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
43014                 Roo.form.HtmlEditor);
43015             editor.toolbars[i].init(editor);
43016         }
43017          
43018         
43019     },
43020
43021      
43022     // private
43023     onRender : function(ct, position)
43024     {
43025         var _t = this;
43026         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
43027         
43028         this.wrap = this.el.wrap({
43029             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
43030         });
43031         
43032         this.editorcore.onRender(ct, position);
43033          
43034         if (this.resizable) {
43035             this.resizeEl = new Roo.Resizable(this.wrap, {
43036                 pinned : true,
43037                 wrap: true,
43038                 dynamic : true,
43039                 minHeight : this.height,
43040                 height: this.height,
43041                 handles : this.resizable,
43042                 width: this.width,
43043                 listeners : {
43044                     resize : function(r, w, h) {
43045                         _t.onResize(w,h); // -something
43046                     }
43047                 }
43048             });
43049             
43050         }
43051         this.createToolbar(this);
43052        
43053         
43054         if(!this.width){
43055             this.setSize(this.wrap.getSize());
43056         }
43057         if (this.resizeEl) {
43058             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
43059             // should trigger onReize..
43060         }
43061         
43062         this.keyNav = new Roo.KeyNav(this.el, {
43063             
43064             "tab" : function(e){
43065                 e.preventDefault();
43066                 
43067                 var value = this.getValue();
43068                 
43069                 var start = this.el.dom.selectionStart;
43070                 var end = this.el.dom.selectionEnd;
43071                 
43072                 if(!e.shiftKey){
43073                     
43074                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
43075                     this.el.dom.setSelectionRange(end + 1, end + 1);
43076                     return;
43077                 }
43078                 
43079                 var f = value.substring(0, start).split("\t");
43080                 
43081                 if(f.pop().length != 0){
43082                     return;
43083                 }
43084                 
43085                 this.setValue(f.join("\t") + value.substring(end));
43086                 this.el.dom.setSelectionRange(start - 1, start - 1);
43087                 
43088             },
43089             
43090             "home" : function(e){
43091                 e.preventDefault();
43092                 
43093                 var curr = this.el.dom.selectionStart;
43094                 var lines = this.getValue().split("\n");
43095                 
43096                 if(!lines.length){
43097                     return;
43098                 }
43099                 
43100                 if(e.ctrlKey){
43101                     this.el.dom.setSelectionRange(0, 0);
43102                     return;
43103                 }
43104                 
43105                 var pos = 0;
43106                 
43107                 for (var i = 0; i < lines.length;i++) {
43108                     pos += lines[i].length;
43109                     
43110                     if(i != 0){
43111                         pos += 1;
43112                     }
43113                     
43114                     if(pos < curr){
43115                         continue;
43116                     }
43117                     
43118                     pos -= lines[i].length;
43119                     
43120                     break;
43121                 }
43122                 
43123                 if(!e.shiftKey){
43124                     this.el.dom.setSelectionRange(pos, pos);
43125                     return;
43126                 }
43127                 
43128                 this.el.dom.selectionStart = pos;
43129                 this.el.dom.selectionEnd = curr;
43130             },
43131             
43132             "end" : function(e){
43133                 e.preventDefault();
43134                 
43135                 var curr = this.el.dom.selectionStart;
43136                 var lines = this.getValue().split("\n");
43137                 
43138                 if(!lines.length){
43139                     return;
43140                 }
43141                 
43142                 if(e.ctrlKey){
43143                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
43144                     return;
43145                 }
43146                 
43147                 var pos = 0;
43148                 
43149                 for (var i = 0; i < lines.length;i++) {
43150                     
43151                     pos += lines[i].length;
43152                     
43153                     if(i != 0){
43154                         pos += 1;
43155                     }
43156                     
43157                     if(pos < curr){
43158                         continue;
43159                     }
43160                     
43161                     break;
43162                 }
43163                 
43164                 if(!e.shiftKey){
43165                     this.el.dom.setSelectionRange(pos, pos);
43166                     return;
43167                 }
43168                 
43169                 this.el.dom.selectionStart = curr;
43170                 this.el.dom.selectionEnd = pos;
43171             },
43172
43173             scope : this,
43174
43175             doRelay : function(foo, bar, hname){
43176                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43177             },
43178
43179             forceKeyDown: true
43180         });
43181         
43182 //        if(this.autosave && this.w){
43183 //            this.autoSaveFn = setInterval(this.autosave, 1000);
43184 //        }
43185     },
43186
43187     // private
43188     onResize : function(w, h)
43189     {
43190         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
43191         var ew = false;
43192         var eh = false;
43193         
43194         if(this.el ){
43195             if(typeof w == 'number'){
43196                 var aw = w - this.wrap.getFrameWidth('lr');
43197                 this.el.setWidth(this.adjustWidth('textarea', aw));
43198                 ew = aw;
43199             }
43200             if(typeof h == 'number'){
43201                 var tbh = 0;
43202                 for (var i =0; i < this.toolbars.length;i++) {
43203                     // fixme - ask toolbars for heights?
43204                     tbh += this.toolbars[i].tb.el.getHeight();
43205                     if (this.toolbars[i].footer) {
43206                         tbh += this.toolbars[i].footer.el.getHeight();
43207                     }
43208                 }
43209                 
43210                 
43211                 
43212                 
43213                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
43214                 ah -= 5; // knock a few pixes off for look..
43215 //                Roo.log(ah);
43216                 this.el.setHeight(this.adjustWidth('textarea', ah));
43217                 var eh = ah;
43218             }
43219         }
43220         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
43221         this.editorcore.onResize(ew,eh);
43222         
43223     },
43224
43225     /**
43226      * Toggles the editor between standard and source edit mode.
43227      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43228      */
43229     toggleSourceEdit : function(sourceEditMode)
43230     {
43231         this.editorcore.toggleSourceEdit(sourceEditMode);
43232         
43233         if(this.editorcore.sourceEditMode){
43234             Roo.log('editor - showing textarea');
43235             
43236 //            Roo.log('in');
43237 //            Roo.log(this.syncValue());
43238             this.editorcore.syncValue();
43239             this.el.removeClass('x-hidden');
43240             this.el.dom.removeAttribute('tabIndex');
43241             this.el.focus();
43242             
43243             for (var i = 0; i < this.toolbars.length; i++) {
43244                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43245                     this.toolbars[i].tb.hide();
43246                     this.toolbars[i].footer.hide();
43247                 }
43248             }
43249             
43250         }else{
43251             Roo.log('editor - hiding textarea');
43252 //            Roo.log('out')
43253 //            Roo.log(this.pushValue()); 
43254             this.editorcore.pushValue();
43255             
43256             this.el.addClass('x-hidden');
43257             this.el.dom.setAttribute('tabIndex', -1);
43258             
43259             for (var i = 0; i < this.toolbars.length; i++) {
43260                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43261                     this.toolbars[i].tb.show();
43262                     this.toolbars[i].footer.show();
43263                 }
43264             }
43265             
43266             //this.deferFocus();
43267         }
43268         
43269         this.setSize(this.wrap.getSize());
43270         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
43271         
43272         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
43273     },
43274  
43275     // private (for BoxComponent)
43276     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43277
43278     // private (for BoxComponent)
43279     getResizeEl : function(){
43280         return this.wrap;
43281     },
43282
43283     // private (for BoxComponent)
43284     getPositionEl : function(){
43285         return this.wrap;
43286     },
43287
43288     // private
43289     initEvents : function(){
43290         this.originalValue = this.getValue();
43291     },
43292
43293     /**
43294      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43295      * @method
43296      */
43297     markInvalid : Roo.emptyFn,
43298     /**
43299      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43300      * @method
43301      */
43302     clearInvalid : Roo.emptyFn,
43303
43304     setValue : function(v){
43305         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
43306         this.editorcore.pushValue();
43307     },
43308
43309      
43310     // private
43311     deferFocus : function(){
43312         this.focus.defer(10, this);
43313     },
43314
43315     // doc'ed in Field
43316     focus : function(){
43317         this.editorcore.focus();
43318         
43319     },
43320       
43321
43322     // private
43323     onDestroy : function(){
43324         
43325         
43326         
43327         if(this.rendered){
43328             
43329             for (var i =0; i < this.toolbars.length;i++) {
43330                 // fixme - ask toolbars for heights?
43331                 this.toolbars[i].onDestroy();
43332             }
43333             
43334             this.wrap.dom.innerHTML = '';
43335             this.wrap.remove();
43336         }
43337     },
43338
43339     // private
43340     onFirstFocus : function(){
43341         //Roo.log("onFirstFocus");
43342         this.editorcore.onFirstFocus();
43343          for (var i =0; i < this.toolbars.length;i++) {
43344             this.toolbars[i].onFirstFocus();
43345         }
43346         
43347     },
43348     
43349     // private
43350     syncValue : function()
43351     {
43352         this.editorcore.syncValue();
43353     },
43354     
43355     pushValue : function()
43356     {
43357         this.editorcore.pushValue();
43358     },
43359     
43360     setStylesheets : function(stylesheets)
43361     {
43362         this.editorcore.setStylesheets(stylesheets);
43363     },
43364     
43365     removeStylesheets : function()
43366     {
43367         this.editorcore.removeStylesheets();
43368     }
43369      
43370     
43371     // hide stuff that is not compatible
43372     /**
43373      * @event blur
43374      * @hide
43375      */
43376     /**
43377      * @event change
43378      * @hide
43379      */
43380     /**
43381      * @event focus
43382      * @hide
43383      */
43384     /**
43385      * @event specialkey
43386      * @hide
43387      */
43388     /**
43389      * @cfg {String} fieldClass @hide
43390      */
43391     /**
43392      * @cfg {String} focusClass @hide
43393      */
43394     /**
43395      * @cfg {String} autoCreate @hide
43396      */
43397     /**
43398      * @cfg {String} inputType @hide
43399      */
43400     /**
43401      * @cfg {String} invalidClass @hide
43402      */
43403     /**
43404      * @cfg {String} invalidText @hide
43405      */
43406     /**
43407      * @cfg {String} msgFx @hide
43408      */
43409     /**
43410      * @cfg {String} validateOnBlur @hide
43411      */
43412 });
43413  
43414     // <script type="text/javascript">
43415 /*
43416  * Based on
43417  * Ext JS Library 1.1.1
43418  * Copyright(c) 2006-2007, Ext JS, LLC.
43419  *  
43420  
43421  */
43422
43423 /**
43424  * @class Roo.form.HtmlEditorToolbar1
43425  * Basic Toolbar
43426  * 
43427  * Usage:
43428  *
43429  new Roo.form.HtmlEditor({
43430     ....
43431     toolbars : [
43432         new Roo.form.HtmlEditorToolbar1({
43433             disable : { fonts: 1 , format: 1, ..., ... , ...],
43434             btns : [ .... ]
43435         })
43436     }
43437      
43438  * 
43439  * @cfg {Object} disable List of elements to disable..
43440  * @cfg {Array} btns List of additional buttons.
43441  * 
43442  * 
43443  * NEEDS Extra CSS? 
43444  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43445  */
43446  
43447 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43448 {
43449     
43450     Roo.apply(this, config);
43451     
43452     // default disabled, based on 'good practice'..
43453     this.disable = this.disable || {};
43454     Roo.applyIf(this.disable, {
43455         fontSize : true,
43456         colors : true,
43457         specialElements : true
43458     });
43459     
43460     
43461     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43462     // dont call parent... till later.
43463 }
43464
43465 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43466     
43467     tb: false,
43468     
43469     rendered: false,
43470     
43471     editor : false,
43472     editorcore : false,
43473     /**
43474      * @cfg {Object} disable  List of toolbar elements to disable
43475          
43476      */
43477     disable : false,
43478     
43479     
43480      /**
43481      * @cfg {String} createLinkText The default text for the create link prompt
43482      */
43483     createLinkText : 'Please enter the URL for the link:',
43484     /**
43485      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43486      */
43487     defaultLinkValue : 'http:/'+'/',
43488    
43489     
43490       /**
43491      * @cfg {Array} fontFamilies An array of available font families
43492      */
43493     fontFamilies : [
43494         'Arial',
43495         'Courier New',
43496         'Tahoma',
43497         'Times New Roman',
43498         'Verdana'
43499     ],
43500     
43501     specialChars : [
43502            "&#169;",
43503           "&#174;",     
43504           "&#8482;",    
43505           "&#163;" ,    
43506          // "&#8212;",    
43507           "&#8230;",    
43508           "&#247;" ,    
43509         //  "&#225;" ,     ?? a acute?
43510            "&#8364;"    , //Euro
43511        //   "&#8220;"    ,
43512         //  "&#8221;"    ,
43513         //  "&#8226;"    ,
43514           "&#176;"  //   , // degrees
43515
43516          // "&#233;"     , // e ecute
43517          // "&#250;"     , // u ecute?
43518     ],
43519     
43520     specialElements : [
43521         {
43522             text: "Insert Table",
43523             xtype: 'MenuItem',
43524             xns : Roo.Menu,
43525             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43526                 
43527         },
43528         {    
43529             text: "Insert Image",
43530             xtype: 'MenuItem',
43531             xns : Roo.Menu,
43532             ihtml : '<img src="about:blank"/>'
43533             
43534         }
43535         
43536          
43537     ],
43538     
43539     
43540     inputElements : [ 
43541             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43542             "input:submit", "input:button", "select", "textarea", "label" ],
43543     formats : [
43544         ["p"] ,  
43545         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43546         ["pre"],[ "code"], 
43547         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43548         ['div'],['span']
43549     ],
43550     
43551     cleanStyles : [
43552         "font-size"
43553     ],
43554      /**
43555      * @cfg {String} defaultFont default font to use.
43556      */
43557     defaultFont: 'tahoma',
43558    
43559     fontSelect : false,
43560     
43561     
43562     formatCombo : false,
43563     
43564     init : function(editor)
43565     {
43566         this.editor = editor;
43567         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43568         var editorcore = this.editorcore;
43569         
43570         var _t = this;
43571         
43572         var fid = editorcore.frameId;
43573         var etb = this;
43574         function btn(id, toggle, handler){
43575             var xid = fid + '-'+ id ;
43576             return {
43577                 id : xid,
43578                 cmd : id,
43579                 cls : 'x-btn-icon x-edit-'+id,
43580                 enableToggle:toggle !== false,
43581                 scope: _t, // was editor...
43582                 handler:handler||_t.relayBtnCmd,
43583                 clickEvent:'mousedown',
43584                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43585                 tabIndex:-1
43586             };
43587         }
43588         
43589         
43590         
43591         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43592         this.tb = tb;
43593          // stop form submits
43594         tb.el.on('click', function(e){
43595             e.preventDefault(); // what does this do?
43596         });
43597
43598         if(!this.disable.font) { // && !Roo.isSafari){
43599             /* why no safari for fonts 
43600             editor.fontSelect = tb.el.createChild({
43601                 tag:'select',
43602                 tabIndex: -1,
43603                 cls:'x-font-select',
43604                 html: this.createFontOptions()
43605             });
43606             
43607             editor.fontSelect.on('change', function(){
43608                 var font = editor.fontSelect.dom.value;
43609                 editor.relayCmd('fontname', font);
43610                 editor.deferFocus();
43611             }, editor);
43612             
43613             tb.add(
43614                 editor.fontSelect.dom,
43615                 '-'
43616             );
43617             */
43618             
43619         };
43620         if(!this.disable.formats){
43621             this.formatCombo = new Roo.form.ComboBox({
43622                 store: new Roo.data.SimpleStore({
43623                     id : 'tag',
43624                     fields: ['tag'],
43625                     data : this.formats // from states.js
43626                 }),
43627                 blockFocus : true,
43628                 name : '',
43629                 //autoCreate : {tag: "div",  size: "20"},
43630                 displayField:'tag',
43631                 typeAhead: false,
43632                 mode: 'local',
43633                 editable : false,
43634                 triggerAction: 'all',
43635                 emptyText:'Add tag',
43636                 selectOnFocus:true,
43637                 width:135,
43638                 listeners : {
43639                     'select': function(c, r, i) {
43640                         editorcore.insertTag(r.get('tag'));
43641                         editor.focus();
43642                     }
43643                 }
43644
43645             });
43646             tb.addField(this.formatCombo);
43647             
43648         }
43649         
43650         if(!this.disable.format){
43651             tb.add(
43652                 btn('bold'),
43653                 btn('italic'),
43654                 btn('underline')
43655             );
43656         };
43657         if(!this.disable.fontSize){
43658             tb.add(
43659                 '-',
43660                 
43661                 
43662                 btn('increasefontsize', false, editorcore.adjustFont),
43663                 btn('decreasefontsize', false, editorcore.adjustFont)
43664             );
43665         };
43666         
43667         
43668         if(!this.disable.colors){
43669             tb.add(
43670                 '-', {
43671                     id:editorcore.frameId +'-forecolor',
43672                     cls:'x-btn-icon x-edit-forecolor',
43673                     clickEvent:'mousedown',
43674                     tooltip: this.buttonTips['forecolor'] || undefined,
43675                     tabIndex:-1,
43676                     menu : new Roo.menu.ColorMenu({
43677                         allowReselect: true,
43678                         focus: Roo.emptyFn,
43679                         value:'000000',
43680                         plain:true,
43681                         selectHandler: function(cp, color){
43682                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43683                             editor.deferFocus();
43684                         },
43685                         scope: editorcore,
43686                         clickEvent:'mousedown'
43687                     })
43688                 }, {
43689                     id:editorcore.frameId +'backcolor',
43690                     cls:'x-btn-icon x-edit-backcolor',
43691                     clickEvent:'mousedown',
43692                     tooltip: this.buttonTips['backcolor'] || undefined,
43693                     tabIndex:-1,
43694                     menu : new Roo.menu.ColorMenu({
43695                         focus: Roo.emptyFn,
43696                         value:'FFFFFF',
43697                         plain:true,
43698                         allowReselect: true,
43699                         selectHandler: function(cp, color){
43700                             if(Roo.isGecko){
43701                                 editorcore.execCmd('useCSS', false);
43702                                 editorcore.execCmd('hilitecolor', color);
43703                                 editorcore.execCmd('useCSS', true);
43704                                 editor.deferFocus();
43705                             }else{
43706                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43707                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43708                                 editor.deferFocus();
43709                             }
43710                         },
43711                         scope:editorcore,
43712                         clickEvent:'mousedown'
43713                     })
43714                 }
43715             );
43716         };
43717         // now add all the items...
43718         
43719
43720         if(!this.disable.alignments){
43721             tb.add(
43722                 '-',
43723                 btn('justifyleft'),
43724                 btn('justifycenter'),
43725                 btn('justifyright')
43726             );
43727         };
43728
43729         //if(!Roo.isSafari){
43730             if(!this.disable.links){
43731                 tb.add(
43732                     '-',
43733                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43734                 );
43735             };
43736
43737             if(!this.disable.lists){
43738                 tb.add(
43739                     '-',
43740                     btn('insertorderedlist'),
43741                     btn('insertunorderedlist')
43742                 );
43743             }
43744             if(!this.disable.sourceEdit){
43745                 tb.add(
43746                     '-',
43747                     btn('sourceedit', true, function(btn){
43748                         this.toggleSourceEdit(btn.pressed);
43749                     })
43750                 );
43751             }
43752         //}
43753         
43754         var smenu = { };
43755         // special menu.. - needs to be tidied up..
43756         if (!this.disable.special) {
43757             smenu = {
43758                 text: "&#169;",
43759                 cls: 'x-edit-none',
43760                 
43761                 menu : {
43762                     items : []
43763                 }
43764             };
43765             for (var i =0; i < this.specialChars.length; i++) {
43766                 smenu.menu.items.push({
43767                     
43768                     html: this.specialChars[i],
43769                     handler: function(a,b) {
43770                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43771                         //editor.insertAtCursor(a.html);
43772                         
43773                     },
43774                     tabIndex:-1
43775                 });
43776             }
43777             
43778             
43779             tb.add(smenu);
43780             
43781             
43782         }
43783         
43784         var cmenu = { };
43785         if (!this.disable.cleanStyles) {
43786             cmenu = {
43787                 cls: 'x-btn-icon x-btn-clear',
43788                 
43789                 menu : {
43790                     items : []
43791                 }
43792             };
43793             for (var i =0; i < this.cleanStyles.length; i++) {
43794                 cmenu.menu.items.push({
43795                     actiontype : this.cleanStyles[i],
43796                     html: 'Remove ' + this.cleanStyles[i],
43797                     handler: function(a,b) {
43798 //                        Roo.log(a);
43799 //                        Roo.log(b);
43800                         var c = Roo.get(editorcore.doc.body);
43801                         c.select('[style]').each(function(s) {
43802                             s.dom.style.removeProperty(a.actiontype);
43803                         });
43804                         editorcore.syncValue();
43805                     },
43806                     tabIndex:-1
43807                 });
43808             }
43809              cmenu.menu.items.push({
43810                 actiontype : 'tablewidths',
43811                 html: 'Remove Table Widths',
43812                 handler: function(a,b) {
43813                     editorcore.cleanTableWidths();
43814                     editorcore.syncValue();
43815                 },
43816                 tabIndex:-1
43817             });
43818             cmenu.menu.items.push({
43819                 actiontype : 'word',
43820                 html: 'Remove MS Word Formating',
43821                 handler: function(a,b) {
43822                     editorcore.cleanWord();
43823                     editorcore.syncValue();
43824                 },
43825                 tabIndex:-1
43826             });
43827             
43828             cmenu.menu.items.push({
43829                 actiontype : 'all',
43830                 html: 'Remove All Styles',
43831                 handler: function(a,b) {
43832                     
43833                     var c = Roo.get(editorcore.doc.body);
43834                     c.select('[style]').each(function(s) {
43835                         s.dom.removeAttribute('style');
43836                     });
43837                     editorcore.syncValue();
43838                 },
43839                 tabIndex:-1
43840             });
43841             
43842             cmenu.menu.items.push({
43843                 actiontype : 'all',
43844                 html: 'Remove All CSS Classes',
43845                 handler: function(a,b) {
43846                     
43847                     var c = Roo.get(editorcore.doc.body);
43848                     c.select('[class]').each(function(s) {
43849                         s.dom.className = '';
43850                     });
43851                     editorcore.syncValue();
43852                 },
43853                 tabIndex:-1
43854             });
43855             
43856              cmenu.menu.items.push({
43857                 actiontype : 'tidy',
43858                 html: 'Tidy HTML Source',
43859                 handler: function(a,b) {
43860                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43861                     editorcore.syncValue();
43862                 },
43863                 tabIndex:-1
43864             });
43865             
43866             
43867             tb.add(cmenu);
43868         }
43869          
43870         if (!this.disable.specialElements) {
43871             var semenu = {
43872                 text: "Other;",
43873                 cls: 'x-edit-none',
43874                 menu : {
43875                     items : []
43876                 }
43877             };
43878             for (var i =0; i < this.specialElements.length; i++) {
43879                 semenu.menu.items.push(
43880                     Roo.apply({ 
43881                         handler: function(a,b) {
43882                             editor.insertAtCursor(this.ihtml);
43883                         }
43884                     }, this.specialElements[i])
43885                 );
43886                     
43887             }
43888             
43889             tb.add(semenu);
43890             
43891             
43892         }
43893          
43894         
43895         if (this.btns) {
43896             for(var i =0; i< this.btns.length;i++) {
43897                 var b = Roo.factory(this.btns[i],Roo.form);
43898                 b.cls =  'x-edit-none';
43899                 
43900                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
43901                     b.cls += ' x-init-enable';
43902                 }
43903                 
43904                 b.scope = editorcore;
43905                 tb.add(b);
43906             }
43907         
43908         }
43909         
43910         
43911         
43912         // disable everything...
43913         
43914         this.tb.items.each(function(item){
43915             
43916            if(
43917                 item.id != editorcore.frameId+ '-sourceedit' && 
43918                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
43919             ){
43920                 
43921                 item.disable();
43922             }
43923         });
43924         this.rendered = true;
43925         
43926         // the all the btns;
43927         editor.on('editorevent', this.updateToolbar, this);
43928         // other toolbars need to implement this..
43929         //editor.on('editmodechange', this.updateToolbar, this);
43930     },
43931     
43932     
43933     relayBtnCmd : function(btn) {
43934         this.editorcore.relayCmd(btn.cmd);
43935     },
43936     // private used internally
43937     createLink : function(){
43938         Roo.log("create link?");
43939         var url = prompt(this.createLinkText, this.defaultLinkValue);
43940         if(url && url != 'http:/'+'/'){
43941             this.editorcore.relayCmd('createlink', url);
43942         }
43943     },
43944
43945     
43946     /**
43947      * Protected method that will not generally be called directly. It triggers
43948      * a toolbar update by reading the markup state of the current selection in the editor.
43949      */
43950     updateToolbar: function(){
43951
43952         if(!this.editorcore.activated){
43953             this.editor.onFirstFocus();
43954             return;
43955         }
43956
43957         var btns = this.tb.items.map, 
43958             doc = this.editorcore.doc,
43959             frameId = this.editorcore.frameId;
43960
43961         if(!this.disable.font && !Roo.isSafari){
43962             /*
43963             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43964             if(name != this.fontSelect.dom.value){
43965                 this.fontSelect.dom.value = name;
43966             }
43967             */
43968         }
43969         if(!this.disable.format){
43970             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43971             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43972             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43973         }
43974         if(!this.disable.alignments){
43975             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43976             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43977             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43978         }
43979         if(!Roo.isSafari && !this.disable.lists){
43980             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43981             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43982         }
43983         
43984         var ans = this.editorcore.getAllAncestors();
43985         if (this.formatCombo) {
43986             
43987             
43988             var store = this.formatCombo.store;
43989             this.formatCombo.setValue("");
43990             for (var i =0; i < ans.length;i++) {
43991                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43992                     // select it..
43993                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43994                     break;
43995                 }
43996             }
43997         }
43998         
43999         
44000         
44001         // hides menus... - so this cant be on a menu...
44002         Roo.menu.MenuMgr.hideAll();
44003
44004         //this.editorsyncValue();
44005     },
44006    
44007     
44008     createFontOptions : function(){
44009         var buf = [], fs = this.fontFamilies, ff, lc;
44010         
44011         
44012         
44013         for(var i = 0, len = fs.length; i< len; i++){
44014             ff = fs[i];
44015             lc = ff.toLowerCase();
44016             buf.push(
44017                 '<option value="',lc,'" style="font-family:',ff,';"',
44018                     (this.defaultFont == lc ? ' selected="true">' : '>'),
44019                     ff,
44020                 '</option>'
44021             );
44022         }
44023         return buf.join('');
44024     },
44025     
44026     toggleSourceEdit : function(sourceEditMode){
44027         
44028         Roo.log("toolbar toogle");
44029         if(sourceEditMode === undefined){
44030             sourceEditMode = !this.sourceEditMode;
44031         }
44032         this.sourceEditMode = sourceEditMode === true;
44033         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
44034         // just toggle the button?
44035         if(btn.pressed !== this.sourceEditMode){
44036             btn.toggle(this.sourceEditMode);
44037             return;
44038         }
44039         
44040         if(sourceEditMode){
44041             Roo.log("disabling buttons");
44042             this.tb.items.each(function(item){
44043                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
44044                     item.disable();
44045                 }
44046             });
44047           
44048         }else{
44049             Roo.log("enabling buttons");
44050             if(this.editorcore.initialized){
44051                 this.tb.items.each(function(item){
44052                     item.enable();
44053                 });
44054             }
44055             
44056         }
44057         Roo.log("calling toggole on editor");
44058         // tell the editor that it's been pressed..
44059         this.editor.toggleSourceEdit(sourceEditMode);
44060        
44061     },
44062      /**
44063      * Object collection of toolbar tooltips for the buttons in the editor. The key
44064      * is the command id associated with that button and the value is a valid QuickTips object.
44065      * For example:
44066 <pre><code>
44067 {
44068     bold : {
44069         title: 'Bold (Ctrl+B)',
44070         text: 'Make the selected text bold.',
44071         cls: 'x-html-editor-tip'
44072     },
44073     italic : {
44074         title: 'Italic (Ctrl+I)',
44075         text: 'Make the selected text italic.',
44076         cls: 'x-html-editor-tip'
44077     },
44078     ...
44079 </code></pre>
44080     * @type Object
44081      */
44082     buttonTips : {
44083         bold : {
44084             title: 'Bold (Ctrl+B)',
44085             text: 'Make the selected text bold.',
44086             cls: 'x-html-editor-tip'
44087         },
44088         italic : {
44089             title: 'Italic (Ctrl+I)',
44090             text: 'Make the selected text italic.',
44091             cls: 'x-html-editor-tip'
44092         },
44093         underline : {
44094             title: 'Underline (Ctrl+U)',
44095             text: 'Underline the selected text.',
44096             cls: 'x-html-editor-tip'
44097         },
44098         increasefontsize : {
44099             title: 'Grow Text',
44100             text: 'Increase the font size.',
44101             cls: 'x-html-editor-tip'
44102         },
44103         decreasefontsize : {
44104             title: 'Shrink Text',
44105             text: 'Decrease the font size.',
44106             cls: 'x-html-editor-tip'
44107         },
44108         backcolor : {
44109             title: 'Text Highlight Color',
44110             text: 'Change the background color of the selected text.',
44111             cls: 'x-html-editor-tip'
44112         },
44113         forecolor : {
44114             title: 'Font Color',
44115             text: 'Change the color of the selected text.',
44116             cls: 'x-html-editor-tip'
44117         },
44118         justifyleft : {
44119             title: 'Align Text Left',
44120             text: 'Align text to the left.',
44121             cls: 'x-html-editor-tip'
44122         },
44123         justifycenter : {
44124             title: 'Center Text',
44125             text: 'Center text in the editor.',
44126             cls: 'x-html-editor-tip'
44127         },
44128         justifyright : {
44129             title: 'Align Text Right',
44130             text: 'Align text to the right.',
44131             cls: 'x-html-editor-tip'
44132         },
44133         insertunorderedlist : {
44134             title: 'Bullet List',
44135             text: 'Start a bulleted list.',
44136             cls: 'x-html-editor-tip'
44137         },
44138         insertorderedlist : {
44139             title: 'Numbered List',
44140             text: 'Start a numbered list.',
44141             cls: 'x-html-editor-tip'
44142         },
44143         createlink : {
44144             title: 'Hyperlink',
44145             text: 'Make the selected text a hyperlink.',
44146             cls: 'x-html-editor-tip'
44147         },
44148         sourceedit : {
44149             title: 'Source Edit',
44150             text: 'Switch to source editing mode.',
44151             cls: 'x-html-editor-tip'
44152         }
44153     },
44154     // private
44155     onDestroy : function(){
44156         if(this.rendered){
44157             
44158             this.tb.items.each(function(item){
44159                 if(item.menu){
44160                     item.menu.removeAll();
44161                     if(item.menu.el){
44162                         item.menu.el.destroy();
44163                     }
44164                 }
44165                 item.destroy();
44166             });
44167              
44168         }
44169     },
44170     onFirstFocus: function() {
44171         this.tb.items.each(function(item){
44172            item.enable();
44173         });
44174     }
44175 });
44176
44177
44178
44179
44180 // <script type="text/javascript">
44181 /*
44182  * Based on
44183  * Ext JS Library 1.1.1
44184  * Copyright(c) 2006-2007, Ext JS, LLC.
44185  *  
44186  
44187  */
44188
44189  
44190 /**
44191  * @class Roo.form.HtmlEditor.ToolbarContext
44192  * Context Toolbar
44193  * 
44194  * Usage:
44195  *
44196  new Roo.form.HtmlEditor({
44197     ....
44198     toolbars : [
44199         { xtype: 'ToolbarStandard', styles : {} }
44200         { xtype: 'ToolbarContext', disable : {} }
44201     ]
44202 })
44203
44204      
44205  * 
44206  * @config : {Object} disable List of elements to disable.. (not done yet.)
44207  * @config : {Object} styles  Map of styles available.
44208  * 
44209  */
44210
44211 Roo.form.HtmlEditor.ToolbarContext = function(config)
44212 {
44213     
44214     Roo.apply(this, config);
44215     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44216     // dont call parent... till later.
44217     this.styles = this.styles || {};
44218 }
44219
44220  
44221
44222 Roo.form.HtmlEditor.ToolbarContext.types = {
44223     'IMG' : {
44224         width : {
44225             title: "Width",
44226             width: 40
44227         },
44228         height:  {
44229             title: "Height",
44230             width: 40
44231         },
44232         align: {
44233             title: "Align",
44234             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
44235             width : 80
44236             
44237         },
44238         border: {
44239             title: "Border",
44240             width: 40
44241         },
44242         alt: {
44243             title: "Alt",
44244             width: 120
44245         },
44246         src : {
44247             title: "Src",
44248             width: 220
44249         }
44250         
44251     },
44252     'A' : {
44253         name : {
44254             title: "Name",
44255             width: 50
44256         },
44257         target:  {
44258             title: "Target",
44259             width: 120
44260         },
44261         href:  {
44262             title: "Href",
44263             width: 220
44264         } // border?
44265         
44266     },
44267     'TABLE' : {
44268         rows : {
44269             title: "Rows",
44270             width: 20
44271         },
44272         cols : {
44273             title: "Cols",
44274             width: 20
44275         },
44276         width : {
44277             title: "Width",
44278             width: 40
44279         },
44280         height : {
44281             title: "Height",
44282             width: 40
44283         },
44284         border : {
44285             title: "Border",
44286             width: 20
44287         }
44288     },
44289     'TD' : {
44290         width : {
44291             title: "Width",
44292             width: 40
44293         },
44294         height : {
44295             title: "Height",
44296             width: 40
44297         },   
44298         align: {
44299             title: "Align",
44300             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
44301             width: 80
44302         },
44303         valign: {
44304             title: "Valign",
44305             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
44306             width: 80
44307         },
44308         colspan: {
44309             title: "Colspan",
44310             width: 20
44311             
44312         },
44313          'font-family'  : {
44314             title : "Font",
44315             style : 'fontFamily',
44316             displayField: 'display',
44317             optname : 'font-family',
44318             width: 140
44319         }
44320     },
44321     'INPUT' : {
44322         name : {
44323             title: "name",
44324             width: 120
44325         },
44326         value : {
44327             title: "Value",
44328             width: 120
44329         },
44330         width : {
44331             title: "Width",
44332             width: 40
44333         }
44334     },
44335     'LABEL' : {
44336         'for' : {
44337             title: "For",
44338             width: 120
44339         }
44340     },
44341     'TEXTAREA' : {
44342           name : {
44343             title: "name",
44344             width: 120
44345         },
44346         rows : {
44347             title: "Rows",
44348             width: 20
44349         },
44350         cols : {
44351             title: "Cols",
44352             width: 20
44353         }
44354     },
44355     'SELECT' : {
44356         name : {
44357             title: "name",
44358             width: 120
44359         },
44360         selectoptions : {
44361             title: "Options",
44362             width: 200
44363         }
44364     },
44365     
44366     // should we really allow this??
44367     // should this just be 
44368     'BODY' : {
44369         title : {
44370             title: "Title",
44371             width: 200,
44372             disabled : true
44373         }
44374     },
44375     'SPAN' : {
44376         'font-family'  : {
44377             title : "Font",
44378             style : 'fontFamily',
44379             displayField: 'display',
44380             optname : 'font-family',
44381             width: 140
44382         }
44383     },
44384     'DIV' : {
44385         'font-family'  : {
44386             title : "Font",
44387             style : 'fontFamily',
44388             displayField: 'display',
44389             optname : 'font-family',
44390             width: 140
44391         }
44392     },
44393      'P' : {
44394         'font-family'  : {
44395             title : "Font",
44396             style : 'fontFamily',
44397             displayField: 'display',
44398             optname : 'font-family',
44399             width: 140
44400         }
44401     },
44402     
44403     '*' : {
44404         // empty..
44405     }
44406
44407 };
44408
44409 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44410 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44411
44412 Roo.form.HtmlEditor.ToolbarContext.options = {
44413         'font-family'  : [ 
44414                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44415                 [ 'Courier New', 'Courier New'],
44416                 [ 'Tahoma', 'Tahoma'],
44417                 [ 'Times New Roman,serif', 'Times'],
44418                 [ 'Verdana','Verdana' ]
44419         ]
44420 };
44421
44422 // fixme - these need to be configurable..
44423  
44424
44425 Roo.form.HtmlEditor.ToolbarContext.types
44426
44427
44428 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44429     
44430     tb: false,
44431     
44432     rendered: false,
44433     
44434     editor : false,
44435     editorcore : false,
44436     /**
44437      * @cfg {Object} disable  List of toolbar elements to disable
44438          
44439      */
44440     disable : false,
44441     /**
44442      * @cfg {Object} styles List of styles 
44443      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44444      *
44445      * These must be defined in the page, so they get rendered correctly..
44446      * .headline { }
44447      * TD.underline { }
44448      * 
44449      */
44450     styles : false,
44451     
44452     options: false,
44453     
44454     toolbars : false,
44455     
44456     init : function(editor)
44457     {
44458         this.editor = editor;
44459         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44460         var editorcore = this.editorcore;
44461         
44462         var fid = editorcore.frameId;
44463         var etb = this;
44464         function btn(id, toggle, handler){
44465             var xid = fid + '-'+ id ;
44466             return {
44467                 id : xid,
44468                 cmd : id,
44469                 cls : 'x-btn-icon x-edit-'+id,
44470                 enableToggle:toggle !== false,
44471                 scope: editorcore, // was editor...
44472                 handler:handler||editorcore.relayBtnCmd,
44473                 clickEvent:'mousedown',
44474                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44475                 tabIndex:-1
44476             };
44477         }
44478         // create a new element.
44479         var wdiv = editor.wrap.createChild({
44480                 tag: 'div'
44481             }, editor.wrap.dom.firstChild.nextSibling, true);
44482         
44483         // can we do this more than once??
44484         
44485          // stop form submits
44486       
44487  
44488         // disable everything...
44489         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44490         this.toolbars = {};
44491            
44492         for (var i in  ty) {
44493           
44494             this.toolbars[i] = this.buildToolbar(ty[i],i);
44495         }
44496         this.tb = this.toolbars.BODY;
44497         this.tb.el.show();
44498         this.buildFooter();
44499         this.footer.show();
44500         editor.on('hide', function( ) { this.footer.hide() }, this);
44501         editor.on('show', function( ) { this.footer.show() }, this);
44502         
44503          
44504         this.rendered = true;
44505         
44506         // the all the btns;
44507         editor.on('editorevent', this.updateToolbar, this);
44508         // other toolbars need to implement this..
44509         //editor.on('editmodechange', this.updateToolbar, this);
44510     },
44511     
44512     
44513     
44514     /**
44515      * Protected method that will not generally be called directly. It triggers
44516      * a toolbar update by reading the markup state of the current selection in the editor.
44517      *
44518      * Note you can force an update by calling on('editorevent', scope, false)
44519      */
44520     updateToolbar: function(editor,ev,sel){
44521
44522         //Roo.log(ev);
44523         // capture mouse up - this is handy for selecting images..
44524         // perhaps should go somewhere else...
44525         if(!this.editorcore.activated){
44526              this.editor.onFirstFocus();
44527             return;
44528         }
44529         
44530         
44531         
44532         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44533         // selectNode - might want to handle IE?
44534         if (ev &&
44535             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44536             ev.target && ev.target.tagName == 'IMG') {
44537             // they have click on an image...
44538             // let's see if we can change the selection...
44539             sel = ev.target;
44540          
44541               var nodeRange = sel.ownerDocument.createRange();
44542             try {
44543                 nodeRange.selectNode(sel);
44544             } catch (e) {
44545                 nodeRange.selectNodeContents(sel);
44546             }
44547             //nodeRange.collapse(true);
44548             var s = this.editorcore.win.getSelection();
44549             s.removeAllRanges();
44550             s.addRange(nodeRange);
44551         }  
44552         
44553       
44554         var updateFooter = sel ? false : true;
44555         
44556         
44557         var ans = this.editorcore.getAllAncestors();
44558         
44559         // pick
44560         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44561         
44562         if (!sel) { 
44563             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44564             sel = sel ? sel : this.editorcore.doc.body;
44565             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44566             
44567         }
44568         // pick a menu that exists..
44569         var tn = sel.tagName.toUpperCase();
44570         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44571         
44572         tn = sel.tagName.toUpperCase();
44573         
44574         var lastSel = this.tb.selectedNode
44575         
44576         this.tb.selectedNode = sel;
44577         
44578         // if current menu does not match..
44579         
44580         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
44581                 
44582             this.tb.el.hide();
44583             ///console.log("show: " + tn);
44584             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44585             this.tb.el.show();
44586             // update name
44587             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44588             
44589             
44590             // update attributes
44591             if (this.tb.fields) {
44592                 this.tb.fields.each(function(e) {
44593                     if (e.stylename) {
44594                         e.setValue(sel.style[e.stylename]);
44595                         return;
44596                     } 
44597                    e.setValue(sel.getAttribute(e.attrname));
44598                 });
44599             }
44600             
44601             var hasStyles = false;
44602             for(var i in this.styles) {
44603                 hasStyles = true;
44604                 break;
44605             }
44606             
44607             // update styles
44608             if (hasStyles) { 
44609                 var st = this.tb.fields.item(0);
44610                 
44611                 st.store.removeAll();
44612                
44613                 
44614                 var cn = sel.className.split(/\s+/);
44615                 
44616                 var avs = [];
44617                 if (this.styles['*']) {
44618                     
44619                     Roo.each(this.styles['*'], function(v) {
44620                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44621                     });
44622                 }
44623                 if (this.styles[tn]) { 
44624                     Roo.each(this.styles[tn], function(v) {
44625                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44626                     });
44627                 }
44628                 
44629                 st.store.loadData(avs);
44630                 st.collapse();
44631                 st.setValue(cn);
44632             }
44633             // flag our selected Node.
44634             this.tb.selectedNode = sel;
44635            
44636            
44637             Roo.menu.MenuMgr.hideAll();
44638
44639         }
44640         
44641         if (!updateFooter) {
44642             //this.footDisp.dom.innerHTML = ''; 
44643             return;
44644         }
44645         // update the footer
44646         //
44647         var html = '';
44648         
44649         this.footerEls = ans.reverse();
44650         Roo.each(this.footerEls, function(a,i) {
44651             if (!a) { return; }
44652             html += html.length ? ' &gt; '  :  '';
44653             
44654             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44655             
44656         });
44657        
44658         // 
44659         var sz = this.footDisp.up('td').getSize();
44660         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44661         this.footDisp.dom.style.marginLeft = '5px';
44662         
44663         this.footDisp.dom.style.overflow = 'hidden';
44664         
44665         this.footDisp.dom.innerHTML = html;
44666             
44667         //this.editorsyncValue();
44668     },
44669      
44670     
44671    
44672        
44673     // private
44674     onDestroy : function(){
44675         if(this.rendered){
44676             
44677             this.tb.items.each(function(item){
44678                 if(item.menu){
44679                     item.menu.removeAll();
44680                     if(item.menu.el){
44681                         item.menu.el.destroy();
44682                     }
44683                 }
44684                 item.destroy();
44685             });
44686              
44687         }
44688     },
44689     onFirstFocus: function() {
44690         // need to do this for all the toolbars..
44691         this.tb.items.each(function(item){
44692            item.enable();
44693         });
44694     },
44695     buildToolbar: function(tlist, nm)
44696     {
44697         var editor = this.editor;
44698         var editorcore = this.editorcore;
44699          // create a new element.
44700         var wdiv = editor.wrap.createChild({
44701                 tag: 'div'
44702             }, editor.wrap.dom.firstChild.nextSibling, true);
44703         
44704        
44705         var tb = new Roo.Toolbar(wdiv);
44706         // add the name..
44707         
44708         tb.add(nm+ ":&nbsp;");
44709         
44710         var styles = [];
44711         for(var i in this.styles) {
44712             styles.push(i);
44713         }
44714         
44715         // styles...
44716         if (styles && styles.length) {
44717             
44718             // this needs a multi-select checkbox...
44719             tb.addField( new Roo.form.ComboBox({
44720                 store: new Roo.data.SimpleStore({
44721                     id : 'val',
44722                     fields: ['val', 'selected'],
44723                     data : [] 
44724                 }),
44725                 name : '-roo-edit-className',
44726                 attrname : 'className',
44727                 displayField: 'val',
44728                 typeAhead: false,
44729                 mode: 'local',
44730                 editable : false,
44731                 triggerAction: 'all',
44732                 emptyText:'Select Style',
44733                 selectOnFocus:true,
44734                 width: 130,
44735                 listeners : {
44736                     'select': function(c, r, i) {
44737                         // initial support only for on class per el..
44738                         tb.selectedNode.className =  r ? r.get('val') : '';
44739                         editorcore.syncValue();
44740                     }
44741                 }
44742     
44743             }));
44744         }
44745         
44746         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44747         var tbops = tbc.options;
44748         
44749         for (var i in tlist) {
44750             
44751             var item = tlist[i];
44752             tb.add(item.title + ":&nbsp;");
44753             
44754             
44755             //optname == used so you can configure the options available..
44756             var opts = item.opts ? item.opts : false;
44757             if (item.optname) {
44758                 opts = tbops[item.optname];
44759            
44760             }
44761             
44762             if (opts) {
44763                 // opts == pulldown..
44764                 tb.addField( new Roo.form.ComboBox({
44765                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44766                         id : 'val',
44767                         fields: ['val', 'display'],
44768                         data : opts  
44769                     }),
44770                     name : '-roo-edit-' + i,
44771                     attrname : i,
44772                     stylename : item.style ? item.style : false,
44773                     displayField: item.displayField ? item.displayField : 'val',
44774                     valueField :  'val',
44775                     typeAhead: false,
44776                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44777                     editable : false,
44778                     triggerAction: 'all',
44779                     emptyText:'Select',
44780                     selectOnFocus:true,
44781                     width: item.width ? item.width  : 130,
44782                     listeners : {
44783                         'select': function(c, r, i) {
44784                             if (c.stylename) {
44785                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44786                                 return;
44787                             }
44788                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44789                         }
44790                     }
44791
44792                 }));
44793                 continue;
44794                     
44795                  
44796                 
44797                 tb.addField( new Roo.form.TextField({
44798                     name: i,
44799                     width: 100,
44800                     //allowBlank:false,
44801                     value: ''
44802                 }));
44803                 continue;
44804             }
44805             tb.addField( new Roo.form.TextField({
44806                 name: '-roo-edit-' + i,
44807                 attrname : i,
44808                 
44809                 width: item.width,
44810                 //allowBlank:true,
44811                 value: '',
44812                 listeners: {
44813                     'change' : function(f, nv, ov) {
44814                         tb.selectedNode.setAttribute(f.attrname, nv);
44815                     }
44816                 }
44817             }));
44818              
44819         }
44820         
44821         var _this = this;
44822         
44823         if(nm == 'BODY'){
44824             tb.addSeparator();
44825         
44826             tb.addButton( {
44827                 text: 'Stylesheets',
44828
44829                 listeners : {
44830                     click : function ()
44831                     {
44832                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
44833                     }
44834                 }
44835             });
44836         }
44837         
44838         tb.addFill();
44839         tb.addButton( {
44840             text: 'Remove Tag',
44841     
44842             listeners : {
44843                 click : function ()
44844                 {
44845                     // remove
44846                     // undo does not work.
44847                      
44848                     var sn = tb.selectedNode;
44849                     
44850                     var pn = sn.parentNode;
44851                     
44852                     var stn =  sn.childNodes[0];
44853                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44854                     while (sn.childNodes.length) {
44855                         var node = sn.childNodes[0];
44856                         sn.removeChild(node);
44857                         //Roo.log(node);
44858                         pn.insertBefore(node, sn);
44859                         
44860                     }
44861                     pn.removeChild(sn);
44862                     var range = editorcore.createRange();
44863         
44864                     range.setStart(stn,0);
44865                     range.setEnd(en,0); //????
44866                     //range.selectNode(sel);
44867                     
44868                     
44869                     var selection = editorcore.getSelection();
44870                     selection.removeAllRanges();
44871                     selection.addRange(range);
44872                     
44873                     
44874                     
44875                     //_this.updateToolbar(null, null, pn);
44876                     _this.updateToolbar(null, null, null);
44877                     _this.footDisp.dom.innerHTML = ''; 
44878                 }
44879             }
44880             
44881                     
44882                 
44883             
44884         });
44885         
44886         
44887         tb.el.on('click', function(e){
44888             e.preventDefault(); // what does this do?
44889         });
44890         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44891         tb.el.hide();
44892         tb.name = nm;
44893         // dont need to disable them... as they will get hidden
44894         return tb;
44895          
44896         
44897     },
44898     buildFooter : function()
44899     {
44900         
44901         var fel = this.editor.wrap.createChild();
44902         this.footer = new Roo.Toolbar(fel);
44903         // toolbar has scrolly on left / right?
44904         var footDisp= new Roo.Toolbar.Fill();
44905         var _t = this;
44906         this.footer.add(
44907             {
44908                 text : '&lt;',
44909                 xtype: 'Button',
44910                 handler : function() {
44911                     _t.footDisp.scrollTo('left',0,true)
44912                 }
44913             }
44914         );
44915         this.footer.add( footDisp );
44916         this.footer.add( 
44917             {
44918                 text : '&gt;',
44919                 xtype: 'Button',
44920                 handler : function() {
44921                     // no animation..
44922                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44923                 }
44924             }
44925         );
44926         var fel = Roo.get(footDisp.el);
44927         fel.addClass('x-editor-context');
44928         this.footDispWrap = fel; 
44929         this.footDispWrap.overflow  = 'hidden';
44930         
44931         this.footDisp = fel.createChild();
44932         this.footDispWrap.on('click', this.onContextClick, this)
44933         
44934         
44935     },
44936     onContextClick : function (ev,dom)
44937     {
44938         ev.preventDefault();
44939         var  cn = dom.className;
44940         //Roo.log(cn);
44941         if (!cn.match(/x-ed-loc-/)) {
44942             return;
44943         }
44944         var n = cn.split('-').pop();
44945         var ans = this.footerEls;
44946         var sel = ans[n];
44947         
44948          // pick
44949         var range = this.editorcore.createRange();
44950         
44951         range.selectNodeContents(sel);
44952         //range.selectNode(sel);
44953         
44954         
44955         var selection = this.editorcore.getSelection();
44956         selection.removeAllRanges();
44957         selection.addRange(range);
44958         
44959         
44960         
44961         this.updateToolbar(null, null, sel);
44962         
44963         
44964     }
44965     
44966     
44967     
44968     
44969     
44970 });
44971
44972
44973
44974
44975
44976 /*
44977  * Based on:
44978  * Ext JS Library 1.1.1
44979  * Copyright(c) 2006-2007, Ext JS, LLC.
44980  *
44981  * Originally Released Under LGPL - original licence link has changed is not relivant.
44982  *
44983  * Fork - LGPL
44984  * <script type="text/javascript">
44985  */
44986  
44987 /**
44988  * @class Roo.form.BasicForm
44989  * @extends Roo.util.Observable
44990  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44991  * @constructor
44992  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44993  * @param {Object} config Configuration options
44994  */
44995 Roo.form.BasicForm = function(el, config){
44996     this.allItems = [];
44997     this.childForms = [];
44998     Roo.apply(this, config);
44999     /*
45000      * The Roo.form.Field items in this form.
45001      * @type MixedCollection
45002      */
45003      
45004      
45005     this.items = new Roo.util.MixedCollection(false, function(o){
45006         return o.id || (o.id = Roo.id());
45007     });
45008     this.addEvents({
45009         /**
45010          * @event beforeaction
45011          * Fires before any action is performed. Return false to cancel the action.
45012          * @param {Form} this
45013          * @param {Action} action The action to be performed
45014          */
45015         beforeaction: true,
45016         /**
45017          * @event actionfailed
45018          * Fires when an action fails.
45019          * @param {Form} this
45020          * @param {Action} action The action that failed
45021          */
45022         actionfailed : true,
45023         /**
45024          * @event actioncomplete
45025          * Fires when an action is completed.
45026          * @param {Form} this
45027          * @param {Action} action The action that completed
45028          */
45029         actioncomplete : true
45030     });
45031     if(el){
45032         this.initEl(el);
45033     }
45034     Roo.form.BasicForm.superclass.constructor.call(this);
45035 };
45036
45037 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
45038     /**
45039      * @cfg {String} method
45040      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
45041      */
45042     /**
45043      * @cfg {DataReader} reader
45044      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
45045      * This is optional as there is built-in support for processing JSON.
45046      */
45047     /**
45048      * @cfg {DataReader} errorReader
45049      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
45050      * This is completely optional as there is built-in support for processing JSON.
45051      */
45052     /**
45053      * @cfg {String} url
45054      * The URL to use for form actions if one isn't supplied in the action options.
45055      */
45056     /**
45057      * @cfg {Boolean} fileUpload
45058      * Set to true if this form is a file upload.
45059      */
45060      
45061     /**
45062      * @cfg {Object} baseParams
45063      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
45064      */
45065      /**
45066      
45067     /**
45068      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
45069      */
45070     timeout: 30,
45071
45072     // private
45073     activeAction : null,
45074
45075     /**
45076      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
45077      * or setValues() data instead of when the form was first created.
45078      */
45079     trackResetOnLoad : false,
45080     
45081     
45082     /**
45083      * childForms - used for multi-tab forms
45084      * @type {Array}
45085      */
45086     childForms : false,
45087     
45088     /**
45089      * allItems - full list of fields.
45090      * @type {Array}
45091      */
45092     allItems : false,
45093     
45094     /**
45095      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
45096      * element by passing it or its id or mask the form itself by passing in true.
45097      * @type Mixed
45098      */
45099     waitMsgTarget : false,
45100
45101     // private
45102     initEl : function(el){
45103         this.el = Roo.get(el);
45104         this.id = this.el.id || Roo.id();
45105         this.el.on('submit', this.onSubmit, this);
45106         this.el.addClass('x-form');
45107     },
45108
45109     // private
45110     onSubmit : function(e){
45111         e.stopEvent();
45112     },
45113
45114     /**
45115      * Returns true if client-side validation on the form is successful.
45116      * @return Boolean
45117      */
45118     isValid : function(){
45119         var valid = true;
45120         this.items.each(function(f){
45121            if(!f.validate()){
45122                valid = false;
45123            }
45124         });
45125         return valid;
45126     },
45127
45128     /**
45129      * Returns true if any fields in this form have changed since their original load.
45130      * @return Boolean
45131      */
45132     isDirty : function(){
45133         var dirty = false;
45134         this.items.each(function(f){
45135            if(f.isDirty()){
45136                dirty = true;
45137                return false;
45138            }
45139         });
45140         return dirty;
45141     },
45142
45143     /**
45144      * Performs a predefined action (submit or load) or custom actions you define on this form.
45145      * @param {String} actionName The name of the action type
45146      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
45147      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
45148      * accept other config options):
45149      * <pre>
45150 Property          Type             Description
45151 ----------------  ---------------  ----------------------------------------------------------------------------------
45152 url               String           The url for the action (defaults to the form's url)
45153 method            String           The form method to use (defaults to the form's method, or POST if not defined)
45154 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
45155 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
45156                                    validate the form on the client (defaults to false)
45157      * </pre>
45158      * @return {BasicForm} this
45159      */
45160     doAction : function(action, options){
45161         if(typeof action == 'string'){
45162             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
45163         }
45164         if(this.fireEvent('beforeaction', this, action) !== false){
45165             this.beforeAction(action);
45166             action.run.defer(100, action);
45167         }
45168         return this;
45169     },
45170
45171     /**
45172      * Shortcut to do a submit action.
45173      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45174      * @return {BasicForm} this
45175      */
45176     submit : function(options){
45177         this.doAction('submit', options);
45178         return this;
45179     },
45180
45181     /**
45182      * Shortcut to do a load action.
45183      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45184      * @return {BasicForm} this
45185      */
45186     load : function(options){
45187         this.doAction('load', options);
45188         return this;
45189     },
45190
45191     /**
45192      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
45193      * @param {Record} record The record to edit
45194      * @return {BasicForm} this
45195      */
45196     updateRecord : function(record){
45197         record.beginEdit();
45198         var fs = record.fields;
45199         fs.each(function(f){
45200             var field = this.findField(f.name);
45201             if(field){
45202                 record.set(f.name, field.getValue());
45203             }
45204         }, this);
45205         record.endEdit();
45206         return this;
45207     },
45208
45209     /**
45210      * Loads an Roo.data.Record into this form.
45211      * @param {Record} record The record to load
45212      * @return {BasicForm} this
45213      */
45214     loadRecord : function(record){
45215         this.setValues(record.data);
45216         return this;
45217     },
45218
45219     // private
45220     beforeAction : function(action){
45221         var o = action.options;
45222         
45223        
45224         if(this.waitMsgTarget === true){
45225             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
45226         }else if(this.waitMsgTarget){
45227             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
45228             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
45229         }else {
45230             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
45231         }
45232          
45233     },
45234
45235     // private
45236     afterAction : function(action, success){
45237         this.activeAction = null;
45238         var o = action.options;
45239         
45240         if(this.waitMsgTarget === true){
45241             this.el.unmask();
45242         }else if(this.waitMsgTarget){
45243             this.waitMsgTarget.unmask();
45244         }else{
45245             Roo.MessageBox.updateProgress(1);
45246             Roo.MessageBox.hide();
45247         }
45248          
45249         if(success){
45250             if(o.reset){
45251                 this.reset();
45252             }
45253             Roo.callback(o.success, o.scope, [this, action]);
45254             this.fireEvent('actioncomplete', this, action);
45255             
45256         }else{
45257             
45258             // failure condition..
45259             // we have a scenario where updates need confirming.
45260             // eg. if a locking scenario exists..
45261             // we look for { errors : { needs_confirm : true }} in the response.
45262             if (
45263                 (typeof(action.result) != 'undefined')  &&
45264                 (typeof(action.result.errors) != 'undefined')  &&
45265                 (typeof(action.result.errors.needs_confirm) != 'undefined')
45266            ){
45267                 var _t = this;
45268                 Roo.MessageBox.confirm(
45269                     "Change requires confirmation",
45270                     action.result.errorMsg,
45271                     function(r) {
45272                         if (r != 'yes') {
45273                             return;
45274                         }
45275                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
45276                     }
45277                     
45278                 );
45279                 
45280                 
45281                 
45282                 return;
45283             }
45284             
45285             Roo.callback(o.failure, o.scope, [this, action]);
45286             // show an error message if no failed handler is set..
45287             if (!this.hasListener('actionfailed')) {
45288                 Roo.MessageBox.alert("Error",
45289                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
45290                         action.result.errorMsg :
45291                         "Saving Failed, please check your entries or try again"
45292                 );
45293             }
45294             
45295             this.fireEvent('actionfailed', this, action);
45296         }
45297         
45298     },
45299
45300     /**
45301      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
45302      * @param {String} id The value to search for
45303      * @return Field
45304      */
45305     findField : function(id){
45306         var field = this.items.get(id);
45307         if(!field){
45308             this.items.each(function(f){
45309                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
45310                     field = f;
45311                     return false;
45312                 }
45313             });
45314         }
45315         return field || null;
45316     },
45317
45318     /**
45319      * Add a secondary form to this one, 
45320      * Used to provide tabbed forms. One form is primary, with hidden values 
45321      * which mirror the elements from the other forms.
45322      * 
45323      * @param {Roo.form.Form} form to add.
45324      * 
45325      */
45326     addForm : function(form)
45327     {
45328        
45329         if (this.childForms.indexOf(form) > -1) {
45330             // already added..
45331             return;
45332         }
45333         this.childForms.push(form);
45334         var n = '';
45335         Roo.each(form.allItems, function (fe) {
45336             
45337             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
45338             if (this.findField(n)) { // already added..
45339                 return;
45340             }
45341             var add = new Roo.form.Hidden({
45342                 name : n
45343             });
45344             add.render(this.el);
45345             
45346             this.add( add );
45347         }, this);
45348         
45349     },
45350     /**
45351      * Mark fields in this form invalid in bulk.
45352      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
45353      * @return {BasicForm} this
45354      */
45355     markInvalid : function(errors){
45356         if(errors instanceof Array){
45357             for(var i = 0, len = errors.length; i < len; i++){
45358                 var fieldError = errors[i];
45359                 var f = this.findField(fieldError.id);
45360                 if(f){
45361                     f.markInvalid(fieldError.msg);
45362                 }
45363             }
45364         }else{
45365             var field, id;
45366             for(id in errors){
45367                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
45368                     field.markInvalid(errors[id]);
45369                 }
45370             }
45371         }
45372         Roo.each(this.childForms || [], function (f) {
45373             f.markInvalid(errors);
45374         });
45375         
45376         return this;
45377     },
45378
45379     /**
45380      * Set values for fields in this form in bulk.
45381      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
45382      * @return {BasicForm} this
45383      */
45384     setValues : function(values){
45385         if(values instanceof Array){ // array of objects
45386             for(var i = 0, len = values.length; i < len; i++){
45387                 var v = values[i];
45388                 var f = this.findField(v.id);
45389                 if(f){
45390                     f.setValue(v.value);
45391                     if(this.trackResetOnLoad){
45392                         f.originalValue = f.getValue();
45393                     }
45394                 }
45395             }
45396         }else{ // object hash
45397             var field, id;
45398             for(id in values){
45399                 if(typeof values[id] != 'function' && (field = this.findField(id))){
45400                     
45401                     if (field.setFromData && 
45402                         field.valueField && 
45403                         field.displayField &&
45404                         // combos' with local stores can 
45405                         // be queried via setValue()
45406                         // to set their value..
45407                         (field.store && !field.store.isLocal)
45408                         ) {
45409                         // it's a combo
45410                         var sd = { };
45411                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
45412                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
45413                         field.setFromData(sd);
45414                         
45415                     } else {
45416                         field.setValue(values[id]);
45417                     }
45418                     
45419                     
45420                     if(this.trackResetOnLoad){
45421                         field.originalValue = field.getValue();
45422                     }
45423                 }
45424             }
45425         }
45426          
45427         Roo.each(this.childForms || [], function (f) {
45428             f.setValues(values);
45429         });
45430                 
45431         return this;
45432     },
45433
45434     /**
45435      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45436      * they are returned as an array.
45437      * @param {Boolean} asString
45438      * @return {Object}
45439      */
45440     getValues : function(asString){
45441         if (this.childForms) {
45442             // copy values from the child forms
45443             Roo.each(this.childForms, function (f) {
45444                 this.setValues(f.getValues());
45445             }, this);
45446         }
45447         
45448         
45449         
45450         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45451         if(asString === true){
45452             return fs;
45453         }
45454         return Roo.urlDecode(fs);
45455     },
45456     
45457     /**
45458      * Returns the fields in this form as an object with key/value pairs. 
45459      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45460      * @return {Object}
45461      */
45462     getFieldValues : function(with_hidden)
45463     {
45464         if (this.childForms) {
45465             // copy values from the child forms
45466             // should this call getFieldValues - probably not as we do not currently copy
45467             // hidden fields when we generate..
45468             Roo.each(this.childForms, function (f) {
45469                 this.setValues(f.getValues());
45470             }, this);
45471         }
45472         
45473         var ret = {};
45474         this.items.each(function(f){
45475             if (!f.getName()) {
45476                 return;
45477             }
45478             var v = f.getValue();
45479             if (f.inputType =='radio') {
45480                 if (typeof(ret[f.getName()]) == 'undefined') {
45481                     ret[f.getName()] = ''; // empty..
45482                 }
45483                 
45484                 if (!f.el.dom.checked) {
45485                     return;
45486                     
45487                 }
45488                 v = f.el.dom.value;
45489                 
45490             }
45491             
45492             // not sure if this supported any more..
45493             if ((typeof(v) == 'object') && f.getRawValue) {
45494                 v = f.getRawValue() ; // dates..
45495             }
45496             // combo boxes where name != hiddenName...
45497             if (f.name != f.getName()) {
45498                 ret[f.name] = f.getRawValue();
45499             }
45500             ret[f.getName()] = v;
45501         });
45502         
45503         return ret;
45504     },
45505
45506     /**
45507      * Clears all invalid messages in this form.
45508      * @return {BasicForm} this
45509      */
45510     clearInvalid : function(){
45511         this.items.each(function(f){
45512            f.clearInvalid();
45513         });
45514         
45515         Roo.each(this.childForms || [], function (f) {
45516             f.clearInvalid();
45517         });
45518         
45519         
45520         return this;
45521     },
45522
45523     /**
45524      * Resets this form.
45525      * @return {BasicForm} this
45526      */
45527     reset : function(){
45528         this.items.each(function(f){
45529             f.reset();
45530         });
45531         
45532         Roo.each(this.childForms || [], function (f) {
45533             f.reset();
45534         });
45535        
45536         
45537         return this;
45538     },
45539
45540     /**
45541      * Add Roo.form components to this form.
45542      * @param {Field} field1
45543      * @param {Field} field2 (optional)
45544      * @param {Field} etc (optional)
45545      * @return {BasicForm} this
45546      */
45547     add : function(){
45548         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45549         return this;
45550     },
45551
45552
45553     /**
45554      * Removes a field from the items collection (does NOT remove its markup).
45555      * @param {Field} field
45556      * @return {BasicForm} this
45557      */
45558     remove : function(field){
45559         this.items.remove(field);
45560         return this;
45561     },
45562
45563     /**
45564      * Looks at the fields in this form, checks them for an id attribute,
45565      * and calls applyTo on the existing dom element with that id.
45566      * @return {BasicForm} this
45567      */
45568     render : function(){
45569         this.items.each(function(f){
45570             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45571                 f.applyTo(f.id);
45572             }
45573         });
45574         return this;
45575     },
45576
45577     /**
45578      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45579      * @param {Object} values
45580      * @return {BasicForm} this
45581      */
45582     applyToFields : function(o){
45583         this.items.each(function(f){
45584            Roo.apply(f, o);
45585         });
45586         return this;
45587     },
45588
45589     /**
45590      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45591      * @param {Object} values
45592      * @return {BasicForm} this
45593      */
45594     applyIfToFields : function(o){
45595         this.items.each(function(f){
45596            Roo.applyIf(f, o);
45597         });
45598         return this;
45599     }
45600 });
45601
45602 // back compat
45603 Roo.BasicForm = Roo.form.BasicForm;/*
45604  * Based on:
45605  * Ext JS Library 1.1.1
45606  * Copyright(c) 2006-2007, Ext JS, LLC.
45607  *
45608  * Originally Released Under LGPL - original licence link has changed is not relivant.
45609  *
45610  * Fork - LGPL
45611  * <script type="text/javascript">
45612  */
45613
45614 /**
45615  * @class Roo.form.Form
45616  * @extends Roo.form.BasicForm
45617  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45618  * @constructor
45619  * @param {Object} config Configuration options
45620  */
45621 Roo.form.Form = function(config){
45622     var xitems =  [];
45623     if (config.items) {
45624         xitems = config.items;
45625         delete config.items;
45626     }
45627    
45628     
45629     Roo.form.Form.superclass.constructor.call(this, null, config);
45630     this.url = this.url || this.action;
45631     if(!this.root){
45632         this.root = new Roo.form.Layout(Roo.applyIf({
45633             id: Roo.id()
45634         }, config));
45635     }
45636     this.active = this.root;
45637     /**
45638      * Array of all the buttons that have been added to this form via {@link addButton}
45639      * @type Array
45640      */
45641     this.buttons = [];
45642     this.allItems = [];
45643     this.addEvents({
45644         /**
45645          * @event clientvalidation
45646          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45647          * @param {Form} this
45648          * @param {Boolean} valid true if the form has passed client-side validation
45649          */
45650         clientvalidation: true,
45651         /**
45652          * @event rendered
45653          * Fires when the form is rendered
45654          * @param {Roo.form.Form} form
45655          */
45656         rendered : true
45657     });
45658     
45659     if (this.progressUrl) {
45660             // push a hidden field onto the list of fields..
45661             this.addxtype( {
45662                     xns: Roo.form, 
45663                     xtype : 'Hidden', 
45664                     name : 'UPLOAD_IDENTIFIER' 
45665             });
45666         }
45667         
45668     
45669     Roo.each(xitems, this.addxtype, this);
45670     
45671     
45672     
45673 };
45674
45675 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45676     /**
45677      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45678      */
45679     /**
45680      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45681      */
45682     /**
45683      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45684      */
45685     buttonAlign:'center',
45686
45687     /**
45688      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45689      */
45690     minButtonWidth:75,
45691
45692     /**
45693      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45694      * This property cascades to child containers if not set.
45695      */
45696     labelAlign:'left',
45697
45698     /**
45699      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45700      * fires a looping event with that state. This is required to bind buttons to the valid
45701      * state using the config value formBind:true on the button.
45702      */
45703     monitorValid : false,
45704
45705     /**
45706      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45707      */
45708     monitorPoll : 200,
45709     
45710     /**
45711      * @cfg {String} progressUrl - Url to return progress data 
45712      */
45713     
45714     progressUrl : false,
45715   
45716     /**
45717      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45718      * fields are added and the column is closed. If no fields are passed the column remains open
45719      * until end() is called.
45720      * @param {Object} config The config to pass to the column
45721      * @param {Field} field1 (optional)
45722      * @param {Field} field2 (optional)
45723      * @param {Field} etc (optional)
45724      * @return Column The column container object
45725      */
45726     column : function(c){
45727         var col = new Roo.form.Column(c);
45728         this.start(col);
45729         if(arguments.length > 1){ // duplicate code required because of Opera
45730             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45731             this.end();
45732         }
45733         return col;
45734     },
45735
45736     /**
45737      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45738      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45739      * until end() is called.
45740      * @param {Object} config The config to pass to the fieldset
45741      * @param {Field} field1 (optional)
45742      * @param {Field} field2 (optional)
45743      * @param {Field} etc (optional)
45744      * @return FieldSet The fieldset container object
45745      */
45746     fieldset : function(c){
45747         var fs = new Roo.form.FieldSet(c);
45748         this.start(fs);
45749         if(arguments.length > 1){ // duplicate code required because of Opera
45750             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45751             this.end();
45752         }
45753         return fs;
45754     },
45755
45756     /**
45757      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45758      * fields are added and the container is closed. If no fields are passed the container remains open
45759      * until end() is called.
45760      * @param {Object} config The config to pass to the Layout
45761      * @param {Field} field1 (optional)
45762      * @param {Field} field2 (optional)
45763      * @param {Field} etc (optional)
45764      * @return Layout The container object
45765      */
45766     container : function(c){
45767         var l = new Roo.form.Layout(c);
45768         this.start(l);
45769         if(arguments.length > 1){ // duplicate code required because of Opera
45770             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45771             this.end();
45772         }
45773         return l;
45774     },
45775
45776     /**
45777      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45778      * @param {Object} container A Roo.form.Layout or subclass of Layout
45779      * @return {Form} this
45780      */
45781     start : function(c){
45782         // cascade label info
45783         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45784         this.active.stack.push(c);
45785         c.ownerCt = this.active;
45786         this.active = c;
45787         return this;
45788     },
45789
45790     /**
45791      * Closes the current open container
45792      * @return {Form} this
45793      */
45794     end : function(){
45795         if(this.active == this.root){
45796             return this;
45797         }
45798         this.active = this.active.ownerCt;
45799         return this;
45800     },
45801
45802     /**
45803      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45804      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45805      * as the label of the field.
45806      * @param {Field} field1
45807      * @param {Field} field2 (optional)
45808      * @param {Field} etc. (optional)
45809      * @return {Form} this
45810      */
45811     add : function(){
45812         this.active.stack.push.apply(this.active.stack, arguments);
45813         this.allItems.push.apply(this.allItems,arguments);
45814         var r = [];
45815         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45816             if(a[i].isFormField){
45817                 r.push(a[i]);
45818             }
45819         }
45820         if(r.length > 0){
45821             Roo.form.Form.superclass.add.apply(this, r);
45822         }
45823         return this;
45824     },
45825     
45826
45827     
45828     
45829     
45830      /**
45831      * Find any element that has been added to a form, using it's ID or name
45832      * This can include framesets, columns etc. along with regular fields..
45833      * @param {String} id - id or name to find.
45834      
45835      * @return {Element} e - or false if nothing found.
45836      */
45837     findbyId : function(id)
45838     {
45839         var ret = false;
45840         if (!id) {
45841             return ret;
45842         }
45843         Roo.each(this.allItems, function(f){
45844             if (f.id == id || f.name == id ){
45845                 ret = f;
45846                 return false;
45847             }
45848         });
45849         return ret;
45850     },
45851
45852     
45853     
45854     /**
45855      * Render this form into the passed container. This should only be called once!
45856      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45857      * @return {Form} this
45858      */
45859     render : function(ct)
45860     {
45861         
45862         
45863         
45864         ct = Roo.get(ct);
45865         var o = this.autoCreate || {
45866             tag: 'form',
45867             method : this.method || 'POST',
45868             id : this.id || Roo.id()
45869         };
45870         this.initEl(ct.createChild(o));
45871
45872         this.root.render(this.el);
45873         
45874        
45875              
45876         this.items.each(function(f){
45877             f.render('x-form-el-'+f.id);
45878         });
45879
45880         if(this.buttons.length > 0){
45881             // tables are required to maintain order and for correct IE layout
45882             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45883                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45884                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45885             }}, null, true);
45886             var tr = tb.getElementsByTagName('tr')[0];
45887             for(var i = 0, len = this.buttons.length; i < len; i++) {
45888                 var b = this.buttons[i];
45889                 var td = document.createElement('td');
45890                 td.className = 'x-form-btn-td';
45891                 b.render(tr.appendChild(td));
45892             }
45893         }
45894         if(this.monitorValid){ // initialize after render
45895             this.startMonitoring();
45896         }
45897         this.fireEvent('rendered', this);
45898         return this;
45899     },
45900
45901     /**
45902      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45903      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45904      * object or a valid Roo.DomHelper element config
45905      * @param {Function} handler The function called when the button is clicked
45906      * @param {Object} scope (optional) The scope of the handler function
45907      * @return {Roo.Button}
45908      */
45909     addButton : function(config, handler, scope){
45910         var bc = {
45911             handler: handler,
45912             scope: scope,
45913             minWidth: this.minButtonWidth,
45914             hideParent:true
45915         };
45916         if(typeof config == "string"){
45917             bc.text = config;
45918         }else{
45919             Roo.apply(bc, config);
45920         }
45921         var btn = new Roo.Button(null, bc);
45922         this.buttons.push(btn);
45923         return btn;
45924     },
45925
45926      /**
45927      * Adds a series of form elements (using the xtype property as the factory method.
45928      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45929      * @param {Object} config 
45930      */
45931     
45932     addxtype : function()
45933     {
45934         var ar = Array.prototype.slice.call(arguments, 0);
45935         var ret = false;
45936         for(var i = 0; i < ar.length; i++) {
45937             if (!ar[i]) {
45938                 continue; // skip -- if this happends something invalid got sent, we 
45939                 // should ignore it, as basically that interface element will not show up
45940                 // and that should be pretty obvious!!
45941             }
45942             
45943             if (Roo.form[ar[i].xtype]) {
45944                 ar[i].form = this;
45945                 var fe = Roo.factory(ar[i], Roo.form);
45946                 if (!ret) {
45947                     ret = fe;
45948                 }
45949                 fe.form = this;
45950                 if (fe.store) {
45951                     fe.store.form = this;
45952                 }
45953                 if (fe.isLayout) {  
45954                          
45955                     this.start(fe);
45956                     this.allItems.push(fe);
45957                     if (fe.items && fe.addxtype) {
45958                         fe.addxtype.apply(fe, fe.items);
45959                         delete fe.items;
45960                     }
45961                      this.end();
45962                     continue;
45963                 }
45964                 
45965                 
45966                  
45967                 this.add(fe);
45968               //  console.log('adding ' + ar[i].xtype);
45969             }
45970             if (ar[i].xtype == 'Button') {  
45971                 //console.log('adding button');
45972                 //console.log(ar[i]);
45973                 this.addButton(ar[i]);
45974                 this.allItems.push(fe);
45975                 continue;
45976             }
45977             
45978             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45979                 alert('end is not supported on xtype any more, use items');
45980             //    this.end();
45981             //    //console.log('adding end');
45982             }
45983             
45984         }
45985         return ret;
45986     },
45987     
45988     /**
45989      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45990      * option "monitorValid"
45991      */
45992     startMonitoring : function(){
45993         if(!this.bound){
45994             this.bound = true;
45995             Roo.TaskMgr.start({
45996                 run : this.bindHandler,
45997                 interval : this.monitorPoll || 200,
45998                 scope: this
45999             });
46000         }
46001     },
46002
46003     /**
46004      * Stops monitoring of the valid state of this form
46005      */
46006     stopMonitoring : function(){
46007         this.bound = false;
46008     },
46009
46010     // private
46011     bindHandler : function(){
46012         if(!this.bound){
46013             return false; // stops binding
46014         }
46015         var valid = true;
46016         this.items.each(function(f){
46017             if(!f.isValid(true)){
46018                 valid = false;
46019                 return false;
46020             }
46021         });
46022         for(var i = 0, len = this.buttons.length; i < len; i++){
46023             var btn = this.buttons[i];
46024             if(btn.formBind === true && btn.disabled === valid){
46025                 btn.setDisabled(!valid);
46026             }
46027         }
46028         this.fireEvent('clientvalidation', this, valid);
46029     }
46030     
46031     
46032     
46033     
46034     
46035     
46036     
46037     
46038 });
46039
46040
46041 // back compat
46042 Roo.Form = Roo.form.Form;
46043 /*
46044  * Based on:
46045  * Ext JS Library 1.1.1
46046  * Copyright(c) 2006-2007, Ext JS, LLC.
46047  *
46048  * Originally Released Under LGPL - original licence link has changed is not relivant.
46049  *
46050  * Fork - LGPL
46051  * <script type="text/javascript">
46052  */
46053
46054 // as we use this in bootstrap.
46055 Roo.namespace('Roo.form');
46056  /**
46057  * @class Roo.form.Action
46058  * Internal Class used to handle form actions
46059  * @constructor
46060  * @param {Roo.form.BasicForm} el The form element or its id
46061  * @param {Object} config Configuration options
46062  */
46063
46064  
46065  
46066 // define the action interface
46067 Roo.form.Action = function(form, options){
46068     this.form = form;
46069     this.options = options || {};
46070 };
46071 /**
46072  * Client Validation Failed
46073  * @const 
46074  */
46075 Roo.form.Action.CLIENT_INVALID = 'client';
46076 /**
46077  * Server Validation Failed
46078  * @const 
46079  */
46080 Roo.form.Action.SERVER_INVALID = 'server';
46081  /**
46082  * Connect to Server Failed
46083  * @const 
46084  */
46085 Roo.form.Action.CONNECT_FAILURE = 'connect';
46086 /**
46087  * Reading Data from Server Failed
46088  * @const 
46089  */
46090 Roo.form.Action.LOAD_FAILURE = 'load';
46091
46092 Roo.form.Action.prototype = {
46093     type : 'default',
46094     failureType : undefined,
46095     response : undefined,
46096     result : undefined,
46097
46098     // interface method
46099     run : function(options){
46100
46101     },
46102
46103     // interface method
46104     success : function(response){
46105
46106     },
46107
46108     // interface method
46109     handleResponse : function(response){
46110
46111     },
46112
46113     // default connection failure
46114     failure : function(response){
46115         
46116         this.response = response;
46117         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46118         this.form.afterAction(this, false);
46119     },
46120
46121     processResponse : function(response){
46122         this.response = response;
46123         if(!response.responseText){
46124             return true;
46125         }
46126         this.result = this.handleResponse(response);
46127         return this.result;
46128     },
46129
46130     // utility functions used internally
46131     getUrl : function(appendParams){
46132         var url = this.options.url || this.form.url || this.form.el.dom.action;
46133         if(appendParams){
46134             var p = this.getParams();
46135             if(p){
46136                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
46137             }
46138         }
46139         return url;
46140     },
46141
46142     getMethod : function(){
46143         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
46144     },
46145
46146     getParams : function(){
46147         var bp = this.form.baseParams;
46148         var p = this.options.params;
46149         if(p){
46150             if(typeof p == "object"){
46151                 p = Roo.urlEncode(Roo.applyIf(p, bp));
46152             }else if(typeof p == 'string' && bp){
46153                 p += '&' + Roo.urlEncode(bp);
46154             }
46155         }else if(bp){
46156             p = Roo.urlEncode(bp);
46157         }
46158         return p;
46159     },
46160
46161     createCallback : function(){
46162         return {
46163             success: this.success,
46164             failure: this.failure,
46165             scope: this,
46166             timeout: (this.form.timeout*1000),
46167             upload: this.form.fileUpload ? this.success : undefined
46168         };
46169     }
46170 };
46171
46172 Roo.form.Action.Submit = function(form, options){
46173     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
46174 };
46175
46176 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
46177     type : 'submit',
46178
46179     haveProgress : false,
46180     uploadComplete : false,
46181     
46182     // uploadProgress indicator.
46183     uploadProgress : function()
46184     {
46185         if (!this.form.progressUrl) {
46186             return;
46187         }
46188         
46189         if (!this.haveProgress) {
46190             Roo.MessageBox.progress("Uploading", "Uploading");
46191         }
46192         if (this.uploadComplete) {
46193            Roo.MessageBox.hide();
46194            return;
46195         }
46196         
46197         this.haveProgress = true;
46198    
46199         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
46200         
46201         var c = new Roo.data.Connection();
46202         c.request({
46203             url : this.form.progressUrl,
46204             params: {
46205                 id : uid
46206             },
46207             method: 'GET',
46208             success : function(req){
46209                //console.log(data);
46210                 var rdata = false;
46211                 var edata;
46212                 try  {
46213                    rdata = Roo.decode(req.responseText)
46214                 } catch (e) {
46215                     Roo.log("Invalid data from server..");
46216                     Roo.log(edata);
46217                     return;
46218                 }
46219                 if (!rdata || !rdata.success) {
46220                     Roo.log(rdata);
46221                     Roo.MessageBox.alert(Roo.encode(rdata));
46222                     return;
46223                 }
46224                 var data = rdata.data;
46225                 
46226                 if (this.uploadComplete) {
46227                    Roo.MessageBox.hide();
46228                    return;
46229                 }
46230                    
46231                 if (data){
46232                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
46233                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
46234                     );
46235                 }
46236                 this.uploadProgress.defer(2000,this);
46237             },
46238        
46239             failure: function(data) {
46240                 Roo.log('progress url failed ');
46241                 Roo.log(data);
46242             },
46243             scope : this
46244         });
46245            
46246     },
46247     
46248     
46249     run : function()
46250     {
46251         // run get Values on the form, so it syncs any secondary forms.
46252         this.form.getValues();
46253         
46254         var o = this.options;
46255         var method = this.getMethod();
46256         var isPost = method == 'POST';
46257         if(o.clientValidation === false || this.form.isValid()){
46258             
46259             if (this.form.progressUrl) {
46260                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
46261                     (new Date() * 1) + '' + Math.random());
46262                     
46263             } 
46264             
46265             
46266             Roo.Ajax.request(Roo.apply(this.createCallback(), {
46267                 form:this.form.el.dom,
46268                 url:this.getUrl(!isPost),
46269                 method: method,
46270                 params:isPost ? this.getParams() : null,
46271                 isUpload: this.form.fileUpload
46272             }));
46273             
46274             this.uploadProgress();
46275
46276         }else if (o.clientValidation !== false){ // client validation failed
46277             this.failureType = Roo.form.Action.CLIENT_INVALID;
46278             this.form.afterAction(this, false);
46279         }
46280     },
46281
46282     success : function(response)
46283     {
46284         this.uploadComplete= true;
46285         if (this.haveProgress) {
46286             Roo.MessageBox.hide();
46287         }
46288         
46289         
46290         var result = this.processResponse(response);
46291         if(result === true || result.success){
46292             this.form.afterAction(this, true);
46293             return;
46294         }
46295         if(result.errors){
46296             this.form.markInvalid(result.errors);
46297             this.failureType = Roo.form.Action.SERVER_INVALID;
46298         }
46299         this.form.afterAction(this, false);
46300     },
46301     failure : function(response)
46302     {
46303         this.uploadComplete= true;
46304         if (this.haveProgress) {
46305             Roo.MessageBox.hide();
46306         }
46307         
46308         this.response = response;
46309         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46310         this.form.afterAction(this, false);
46311     },
46312     
46313     handleResponse : function(response){
46314         if(this.form.errorReader){
46315             var rs = this.form.errorReader.read(response);
46316             var errors = [];
46317             if(rs.records){
46318                 for(var i = 0, len = rs.records.length; i < len; i++) {
46319                     var r = rs.records[i];
46320                     errors[i] = r.data;
46321                 }
46322             }
46323             if(errors.length < 1){
46324                 errors = null;
46325             }
46326             return {
46327                 success : rs.success,
46328                 errors : errors
46329             };
46330         }
46331         var ret = false;
46332         try {
46333             ret = Roo.decode(response.responseText);
46334         } catch (e) {
46335             ret = {
46336                 success: false,
46337                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
46338                 errors : []
46339             };
46340         }
46341         return ret;
46342         
46343     }
46344 });
46345
46346
46347 Roo.form.Action.Load = function(form, options){
46348     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
46349     this.reader = this.form.reader;
46350 };
46351
46352 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
46353     type : 'load',
46354
46355     run : function(){
46356         
46357         Roo.Ajax.request(Roo.apply(
46358                 this.createCallback(), {
46359                     method:this.getMethod(),
46360                     url:this.getUrl(false),
46361                     params:this.getParams()
46362         }));
46363     },
46364
46365     success : function(response){
46366         
46367         var result = this.processResponse(response);
46368         if(result === true || !result.success || !result.data){
46369             this.failureType = Roo.form.Action.LOAD_FAILURE;
46370             this.form.afterAction(this, false);
46371             return;
46372         }
46373         this.form.clearInvalid();
46374         this.form.setValues(result.data);
46375         this.form.afterAction(this, true);
46376     },
46377
46378     handleResponse : function(response){
46379         if(this.form.reader){
46380             var rs = this.form.reader.read(response);
46381             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
46382             return {
46383                 success : rs.success,
46384                 data : data
46385             };
46386         }
46387         return Roo.decode(response.responseText);
46388     }
46389 });
46390
46391 Roo.form.Action.ACTION_TYPES = {
46392     'load' : Roo.form.Action.Load,
46393     'submit' : Roo.form.Action.Submit
46394 };/*
46395  * Based on:
46396  * Ext JS Library 1.1.1
46397  * Copyright(c) 2006-2007, Ext JS, LLC.
46398  *
46399  * Originally Released Under LGPL - original licence link has changed is not relivant.
46400  *
46401  * Fork - LGPL
46402  * <script type="text/javascript">
46403  */
46404  
46405 /**
46406  * @class Roo.form.Layout
46407  * @extends Roo.Component
46408  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
46409  * @constructor
46410  * @param {Object} config Configuration options
46411  */
46412 Roo.form.Layout = function(config){
46413     var xitems = [];
46414     if (config.items) {
46415         xitems = config.items;
46416         delete config.items;
46417     }
46418     Roo.form.Layout.superclass.constructor.call(this, config);
46419     this.stack = [];
46420     Roo.each(xitems, this.addxtype, this);
46421      
46422 };
46423
46424 Roo.extend(Roo.form.Layout, Roo.Component, {
46425     /**
46426      * @cfg {String/Object} autoCreate
46427      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46428      */
46429     /**
46430      * @cfg {String/Object/Function} style
46431      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46432      * a function which returns such a specification.
46433      */
46434     /**
46435      * @cfg {String} labelAlign
46436      * Valid values are "left," "top" and "right" (defaults to "left")
46437      */
46438     /**
46439      * @cfg {Number} labelWidth
46440      * Fixed width in pixels of all field labels (defaults to undefined)
46441      */
46442     /**
46443      * @cfg {Boolean} clear
46444      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46445      */
46446     clear : true,
46447     /**
46448      * @cfg {String} labelSeparator
46449      * The separator to use after field labels (defaults to ':')
46450      */
46451     labelSeparator : ':',
46452     /**
46453      * @cfg {Boolean} hideLabels
46454      * True to suppress the display of field labels in this layout (defaults to false)
46455      */
46456     hideLabels : false,
46457
46458     // private
46459     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46460     
46461     isLayout : true,
46462     
46463     // private
46464     onRender : function(ct, position){
46465         if(this.el){ // from markup
46466             this.el = Roo.get(this.el);
46467         }else {  // generate
46468             var cfg = this.getAutoCreate();
46469             this.el = ct.createChild(cfg, position);
46470         }
46471         if(this.style){
46472             this.el.applyStyles(this.style);
46473         }
46474         if(this.labelAlign){
46475             this.el.addClass('x-form-label-'+this.labelAlign);
46476         }
46477         if(this.hideLabels){
46478             this.labelStyle = "display:none";
46479             this.elementStyle = "padding-left:0;";
46480         }else{
46481             if(typeof this.labelWidth == 'number'){
46482                 this.labelStyle = "width:"+this.labelWidth+"px;";
46483                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46484             }
46485             if(this.labelAlign == 'top'){
46486                 this.labelStyle = "width:auto;";
46487                 this.elementStyle = "padding-left:0;";
46488             }
46489         }
46490         var stack = this.stack;
46491         var slen = stack.length;
46492         if(slen > 0){
46493             if(!this.fieldTpl){
46494                 var t = new Roo.Template(
46495                     '<div class="x-form-item {5}">',
46496                         '<label for="{0}" style="{2}">{1}{4}</label>',
46497                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46498                         '</div>',
46499                     '</div><div class="x-form-clear-left"></div>'
46500                 );
46501                 t.disableFormats = true;
46502                 t.compile();
46503                 Roo.form.Layout.prototype.fieldTpl = t;
46504             }
46505             for(var i = 0; i < slen; i++) {
46506                 if(stack[i].isFormField){
46507                     this.renderField(stack[i]);
46508                 }else{
46509                     this.renderComponent(stack[i]);
46510                 }
46511             }
46512         }
46513         if(this.clear){
46514             this.el.createChild({cls:'x-form-clear'});
46515         }
46516     },
46517
46518     // private
46519     renderField : function(f){
46520         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46521                f.id, //0
46522                f.fieldLabel, //1
46523                f.labelStyle||this.labelStyle||'', //2
46524                this.elementStyle||'', //3
46525                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46526                f.itemCls||this.itemCls||''  //5
46527        ], true).getPrevSibling());
46528     },
46529
46530     // private
46531     renderComponent : function(c){
46532         c.render(c.isLayout ? this.el : this.el.createChild());    
46533     },
46534     /**
46535      * Adds a object form elements (using the xtype property as the factory method.)
46536      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46537      * @param {Object} config 
46538      */
46539     addxtype : function(o)
46540     {
46541         // create the lement.
46542         o.form = this.form;
46543         var fe = Roo.factory(o, Roo.form);
46544         this.form.allItems.push(fe);
46545         this.stack.push(fe);
46546         
46547         if (fe.isFormField) {
46548             this.form.items.add(fe);
46549         }
46550          
46551         return fe;
46552     }
46553 });
46554
46555 /**
46556  * @class Roo.form.Column
46557  * @extends Roo.form.Layout
46558  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46559  * @constructor
46560  * @param {Object} config Configuration options
46561  */
46562 Roo.form.Column = function(config){
46563     Roo.form.Column.superclass.constructor.call(this, config);
46564 };
46565
46566 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46567     /**
46568      * @cfg {Number/String} width
46569      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46570      */
46571     /**
46572      * @cfg {String/Object} autoCreate
46573      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46574      */
46575
46576     // private
46577     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46578
46579     // private
46580     onRender : function(ct, position){
46581         Roo.form.Column.superclass.onRender.call(this, ct, position);
46582         if(this.width){
46583             this.el.setWidth(this.width);
46584         }
46585     }
46586 });
46587
46588
46589 /**
46590  * @class Roo.form.Row
46591  * @extends Roo.form.Layout
46592  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46593  * @constructor
46594  * @param {Object} config Configuration options
46595  */
46596
46597  
46598 Roo.form.Row = function(config){
46599     Roo.form.Row.superclass.constructor.call(this, config);
46600 };
46601  
46602 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46603       /**
46604      * @cfg {Number/String} width
46605      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46606      */
46607     /**
46608      * @cfg {Number/String} height
46609      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46610      */
46611     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46612     
46613     padWidth : 20,
46614     // private
46615     onRender : function(ct, position){
46616         //console.log('row render');
46617         if(!this.rowTpl){
46618             var t = new Roo.Template(
46619                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46620                     '<label for="{0}" style="{2}">{1}{4}</label>',
46621                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46622                     '</div>',
46623                 '</div>'
46624             );
46625             t.disableFormats = true;
46626             t.compile();
46627             Roo.form.Layout.prototype.rowTpl = t;
46628         }
46629         this.fieldTpl = this.rowTpl;
46630         
46631         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46632         var labelWidth = 100;
46633         
46634         if ((this.labelAlign != 'top')) {
46635             if (typeof this.labelWidth == 'number') {
46636                 labelWidth = this.labelWidth
46637             }
46638             this.padWidth =  20 + labelWidth;
46639             
46640         }
46641         
46642         Roo.form.Column.superclass.onRender.call(this, ct, position);
46643         if(this.width){
46644             this.el.setWidth(this.width);
46645         }
46646         if(this.height){
46647             this.el.setHeight(this.height);
46648         }
46649     },
46650     
46651     // private
46652     renderField : function(f){
46653         f.fieldEl = this.fieldTpl.append(this.el, [
46654                f.id, f.fieldLabel,
46655                f.labelStyle||this.labelStyle||'',
46656                this.elementStyle||'',
46657                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46658                f.itemCls||this.itemCls||'',
46659                f.width ? f.width + this.padWidth : 160 + this.padWidth
46660        ],true);
46661     }
46662 });
46663  
46664
46665 /**
46666  * @class Roo.form.FieldSet
46667  * @extends Roo.form.Layout
46668  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46669  * @constructor
46670  * @param {Object} config Configuration options
46671  */
46672 Roo.form.FieldSet = function(config){
46673     Roo.form.FieldSet.superclass.constructor.call(this, config);
46674 };
46675
46676 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46677     /**
46678      * @cfg {String} legend
46679      * The text to display as the legend for the FieldSet (defaults to '')
46680      */
46681     /**
46682      * @cfg {String/Object} autoCreate
46683      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46684      */
46685
46686     // private
46687     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46688
46689     // private
46690     onRender : function(ct, position){
46691         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46692         if(this.legend){
46693             this.setLegend(this.legend);
46694         }
46695     },
46696
46697     // private
46698     setLegend : function(text){
46699         if(this.rendered){
46700             this.el.child('legend').update(text);
46701         }
46702     }
46703 });/*
46704  * Based on:
46705  * Ext JS Library 1.1.1
46706  * Copyright(c) 2006-2007, Ext JS, LLC.
46707  *
46708  * Originally Released Under LGPL - original licence link has changed is not relivant.
46709  *
46710  * Fork - LGPL
46711  * <script type="text/javascript">
46712  */
46713 /**
46714  * @class Roo.form.VTypes
46715  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46716  * @singleton
46717  */
46718 Roo.form.VTypes = function(){
46719     // closure these in so they are only created once.
46720     var alpha = /^[a-zA-Z_]+$/;
46721     var alphanum = /^[a-zA-Z0-9_]+$/;
46722     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46723     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46724
46725     // All these messages and functions are configurable
46726     return {
46727         /**
46728          * The function used to validate email addresses
46729          * @param {String} value The email address
46730          */
46731         'email' : function(v){
46732             return email.test(v);
46733         },
46734         /**
46735          * The error text to display when the email validation function returns false
46736          * @type String
46737          */
46738         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46739         /**
46740          * The keystroke filter mask to be applied on email input
46741          * @type RegExp
46742          */
46743         'emailMask' : /[a-z0-9_\.\-@]/i,
46744
46745         /**
46746          * The function used to validate URLs
46747          * @param {String} value The URL
46748          */
46749         'url' : function(v){
46750             return url.test(v);
46751         },
46752         /**
46753          * The error text to display when the url validation function returns false
46754          * @type String
46755          */
46756         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46757         
46758         /**
46759          * The function used to validate alpha values
46760          * @param {String} value The value
46761          */
46762         'alpha' : function(v){
46763             return alpha.test(v);
46764         },
46765         /**
46766          * The error text to display when the alpha validation function returns false
46767          * @type String
46768          */
46769         'alphaText' : 'This field should only contain letters and _',
46770         /**
46771          * The keystroke filter mask to be applied on alpha input
46772          * @type RegExp
46773          */
46774         'alphaMask' : /[a-z_]/i,
46775
46776         /**
46777          * The function used to validate alphanumeric values
46778          * @param {String} value The value
46779          */
46780         'alphanum' : function(v){
46781             return alphanum.test(v);
46782         },
46783         /**
46784          * The error text to display when the alphanumeric validation function returns false
46785          * @type String
46786          */
46787         'alphanumText' : 'This field should only contain letters, numbers and _',
46788         /**
46789          * The keystroke filter mask to be applied on alphanumeric input
46790          * @type RegExp
46791          */
46792         'alphanumMask' : /[a-z0-9_]/i
46793     };
46794 }();//<script type="text/javascript">
46795
46796 /**
46797  * @class Roo.form.FCKeditor
46798  * @extends Roo.form.TextArea
46799  * Wrapper around the FCKEditor http://www.fckeditor.net
46800  * @constructor
46801  * Creates a new FCKeditor
46802  * @param {Object} config Configuration options
46803  */
46804 Roo.form.FCKeditor = function(config){
46805     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46806     this.addEvents({
46807          /**
46808          * @event editorinit
46809          * Fired when the editor is initialized - you can add extra handlers here..
46810          * @param {FCKeditor} this
46811          * @param {Object} the FCK object.
46812          */
46813         editorinit : true
46814     });
46815     
46816     
46817 };
46818 Roo.form.FCKeditor.editors = { };
46819 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46820 {
46821     //defaultAutoCreate : {
46822     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46823     //},
46824     // private
46825     /**
46826      * @cfg {Object} fck options - see fck manual for details.
46827      */
46828     fckconfig : false,
46829     
46830     /**
46831      * @cfg {Object} fck toolbar set (Basic or Default)
46832      */
46833     toolbarSet : 'Basic',
46834     /**
46835      * @cfg {Object} fck BasePath
46836      */ 
46837     basePath : '/fckeditor/',
46838     
46839     
46840     frame : false,
46841     
46842     value : '',
46843     
46844    
46845     onRender : function(ct, position)
46846     {
46847         if(!this.el){
46848             this.defaultAutoCreate = {
46849                 tag: "textarea",
46850                 style:"width:300px;height:60px;",
46851                 autocomplete: "new-password"
46852             };
46853         }
46854         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46855         /*
46856         if(this.grow){
46857             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46858             if(this.preventScrollbars){
46859                 this.el.setStyle("overflow", "hidden");
46860             }
46861             this.el.setHeight(this.growMin);
46862         }
46863         */
46864         //console.log('onrender' + this.getId() );
46865         Roo.form.FCKeditor.editors[this.getId()] = this;
46866          
46867
46868         this.replaceTextarea() ;
46869         
46870     },
46871     
46872     getEditor : function() {
46873         return this.fckEditor;
46874     },
46875     /**
46876      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46877      * @param {Mixed} value The value to set
46878      */
46879     
46880     
46881     setValue : function(value)
46882     {
46883         //console.log('setValue: ' + value);
46884         
46885         if(typeof(value) == 'undefined') { // not sure why this is happending...
46886             return;
46887         }
46888         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46889         
46890         //if(!this.el || !this.getEditor()) {
46891         //    this.value = value;
46892             //this.setValue.defer(100,this,[value]);    
46893         //    return;
46894         //} 
46895         
46896         if(!this.getEditor()) {
46897             return;
46898         }
46899         
46900         this.getEditor().SetData(value);
46901         
46902         //
46903
46904     },
46905
46906     /**
46907      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46908      * @return {Mixed} value The field value
46909      */
46910     getValue : function()
46911     {
46912         
46913         if (this.frame && this.frame.dom.style.display == 'none') {
46914             return Roo.form.FCKeditor.superclass.getValue.call(this);
46915         }
46916         
46917         if(!this.el || !this.getEditor()) {
46918            
46919            // this.getValue.defer(100,this); 
46920             return this.value;
46921         }
46922        
46923         
46924         var value=this.getEditor().GetData();
46925         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46926         return Roo.form.FCKeditor.superclass.getValue.call(this);
46927         
46928
46929     },
46930
46931     /**
46932      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46933      * @return {Mixed} value The field value
46934      */
46935     getRawValue : function()
46936     {
46937         if (this.frame && this.frame.dom.style.display == 'none') {
46938             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46939         }
46940         
46941         if(!this.el || !this.getEditor()) {
46942             //this.getRawValue.defer(100,this); 
46943             return this.value;
46944             return;
46945         }
46946         
46947         
46948         
46949         var value=this.getEditor().GetData();
46950         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46951         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46952          
46953     },
46954     
46955     setSize : function(w,h) {
46956         
46957         
46958         
46959         //if (this.frame && this.frame.dom.style.display == 'none') {
46960         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46961         //    return;
46962         //}
46963         //if(!this.el || !this.getEditor()) {
46964         //    this.setSize.defer(100,this, [w,h]); 
46965         //    return;
46966         //}
46967         
46968         
46969         
46970         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46971         
46972         this.frame.dom.setAttribute('width', w);
46973         this.frame.dom.setAttribute('height', h);
46974         this.frame.setSize(w,h);
46975         
46976     },
46977     
46978     toggleSourceEdit : function(value) {
46979         
46980       
46981          
46982         this.el.dom.style.display = value ? '' : 'none';
46983         this.frame.dom.style.display = value ?  'none' : '';
46984         
46985     },
46986     
46987     
46988     focus: function(tag)
46989     {
46990         if (this.frame.dom.style.display == 'none') {
46991             return Roo.form.FCKeditor.superclass.focus.call(this);
46992         }
46993         if(!this.el || !this.getEditor()) {
46994             this.focus.defer(100,this, [tag]); 
46995             return;
46996         }
46997         
46998         
46999         
47000         
47001         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
47002         this.getEditor().Focus();
47003         if (tgs.length) {
47004             if (!this.getEditor().Selection.GetSelection()) {
47005                 this.focus.defer(100,this, [tag]); 
47006                 return;
47007             }
47008             
47009             
47010             var r = this.getEditor().EditorDocument.createRange();
47011             r.setStart(tgs[0],0);
47012             r.setEnd(tgs[0],0);
47013             this.getEditor().Selection.GetSelection().removeAllRanges();
47014             this.getEditor().Selection.GetSelection().addRange(r);
47015             this.getEditor().Focus();
47016         }
47017         
47018     },
47019     
47020     
47021     
47022     replaceTextarea : function()
47023     {
47024         if ( document.getElementById( this.getId() + '___Frame' ) )
47025             return ;
47026         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
47027         //{
47028             // We must check the elements firstly using the Id and then the name.
47029         var oTextarea = document.getElementById( this.getId() );
47030         
47031         var colElementsByName = document.getElementsByName( this.getId() ) ;
47032          
47033         oTextarea.style.display = 'none' ;
47034
47035         if ( oTextarea.tabIndex ) {            
47036             this.TabIndex = oTextarea.tabIndex ;
47037         }
47038         
47039         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
47040         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
47041         this.frame = Roo.get(this.getId() + '___Frame')
47042     },
47043     
47044     _getConfigHtml : function()
47045     {
47046         var sConfig = '' ;
47047
47048         for ( var o in this.fckconfig ) {
47049             sConfig += sConfig.length > 0  ? '&amp;' : '';
47050             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
47051         }
47052
47053         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
47054     },
47055     
47056     
47057     _getIFrameHtml : function()
47058     {
47059         var sFile = 'fckeditor.html' ;
47060         /* no idea what this is about..
47061         try
47062         {
47063             if ( (/fcksource=true/i).test( window.top.location.search ) )
47064                 sFile = 'fckeditor.original.html' ;
47065         }
47066         catch (e) { 
47067         */
47068
47069         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
47070         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
47071         
47072         
47073         var html = '<iframe id="' + this.getId() +
47074             '___Frame" src="' + sLink +
47075             '" width="' + this.width +
47076             '" height="' + this.height + '"' +
47077             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
47078             ' frameborder="0" scrolling="no"></iframe>' ;
47079
47080         return html ;
47081     },
47082     
47083     _insertHtmlBefore : function( html, element )
47084     {
47085         if ( element.insertAdjacentHTML )       {
47086             // IE
47087             element.insertAdjacentHTML( 'beforeBegin', html ) ;
47088         } else { // Gecko
47089             var oRange = document.createRange() ;
47090             oRange.setStartBefore( element ) ;
47091             var oFragment = oRange.createContextualFragment( html );
47092             element.parentNode.insertBefore( oFragment, element ) ;
47093         }
47094     }
47095     
47096     
47097   
47098     
47099     
47100     
47101     
47102
47103 });
47104
47105 //Roo.reg('fckeditor', Roo.form.FCKeditor);
47106
47107 function FCKeditor_OnComplete(editorInstance){
47108     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
47109     f.fckEditor = editorInstance;
47110     //console.log("loaded");
47111     f.fireEvent('editorinit', f, editorInstance);
47112
47113   
47114
47115  
47116
47117
47118
47119
47120
47121
47122
47123
47124
47125
47126
47127
47128
47129
47130
47131 //<script type="text/javascript">
47132 /**
47133  * @class Roo.form.GridField
47134  * @extends Roo.form.Field
47135  * Embed a grid (or editable grid into a form)
47136  * STATUS ALPHA
47137  * 
47138  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
47139  * it needs 
47140  * xgrid.store = Roo.data.Store
47141  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
47142  * xgrid.store.reader = Roo.data.JsonReader 
47143  * 
47144  * 
47145  * @constructor
47146  * Creates a new GridField
47147  * @param {Object} config Configuration options
47148  */
47149 Roo.form.GridField = function(config){
47150     Roo.form.GridField.superclass.constructor.call(this, config);
47151      
47152 };
47153
47154 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
47155     /**
47156      * @cfg {Number} width  - used to restrict width of grid..
47157      */
47158     width : 100,
47159     /**
47160      * @cfg {Number} height - used to restrict height of grid..
47161      */
47162     height : 50,
47163      /**
47164      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
47165          * 
47166          *}
47167      */
47168     xgrid : false, 
47169     /**
47170      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47171      * {tag: "input", type: "checkbox", autocomplete: "off"})
47172      */
47173    // defaultAutoCreate : { tag: 'div' },
47174     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
47175     /**
47176      * @cfg {String} addTitle Text to include for adding a title.
47177      */
47178     addTitle : false,
47179     //
47180     onResize : function(){
47181         Roo.form.Field.superclass.onResize.apply(this, arguments);
47182     },
47183
47184     initEvents : function(){
47185         // Roo.form.Checkbox.superclass.initEvents.call(this);
47186         // has no events...
47187        
47188     },
47189
47190
47191     getResizeEl : function(){
47192         return this.wrap;
47193     },
47194
47195     getPositionEl : function(){
47196         return this.wrap;
47197     },
47198
47199     // private
47200     onRender : function(ct, position){
47201         
47202         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
47203         var style = this.style;
47204         delete this.style;
47205         
47206         Roo.form.GridField.superclass.onRender.call(this, ct, position);
47207         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
47208         this.viewEl = this.wrap.createChild({ tag: 'div' });
47209         if (style) {
47210             this.viewEl.applyStyles(style);
47211         }
47212         if (this.width) {
47213             this.viewEl.setWidth(this.width);
47214         }
47215         if (this.height) {
47216             this.viewEl.setHeight(this.height);
47217         }
47218         //if(this.inputValue !== undefined){
47219         //this.setValue(this.value);
47220         
47221         
47222         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
47223         
47224         
47225         this.grid.render();
47226         this.grid.getDataSource().on('remove', this.refreshValue, this);
47227         this.grid.getDataSource().on('update', this.refreshValue, this);
47228         this.grid.on('afteredit', this.refreshValue, this);
47229  
47230     },
47231      
47232     
47233     /**
47234      * Sets the value of the item. 
47235      * @param {String} either an object  or a string..
47236      */
47237     setValue : function(v){
47238         //this.value = v;
47239         v = v || []; // empty set..
47240         // this does not seem smart - it really only affects memoryproxy grids..
47241         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
47242             var ds = this.grid.getDataSource();
47243             // assumes a json reader..
47244             var data = {}
47245             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
47246             ds.loadData( data);
47247         }
47248         // clear selection so it does not get stale.
47249         if (this.grid.sm) { 
47250             this.grid.sm.clearSelections();
47251         }
47252         
47253         Roo.form.GridField.superclass.setValue.call(this, v);
47254         this.refreshValue();
47255         // should load data in the grid really....
47256     },
47257     
47258     // private
47259     refreshValue: function() {
47260          var val = [];
47261         this.grid.getDataSource().each(function(r) {
47262             val.push(r.data);
47263         });
47264         this.el.dom.value = Roo.encode(val);
47265     }
47266     
47267      
47268     
47269     
47270 });/*
47271  * Based on:
47272  * Ext JS Library 1.1.1
47273  * Copyright(c) 2006-2007, Ext JS, LLC.
47274  *
47275  * Originally Released Under LGPL - original licence link has changed is not relivant.
47276  *
47277  * Fork - LGPL
47278  * <script type="text/javascript">
47279  */
47280 /**
47281  * @class Roo.form.DisplayField
47282  * @extends Roo.form.Field
47283  * A generic Field to display non-editable data.
47284  * @constructor
47285  * Creates a new Display Field item.
47286  * @param {Object} config Configuration options
47287  */
47288 Roo.form.DisplayField = function(config){
47289     Roo.form.DisplayField.superclass.constructor.call(this, config);
47290     
47291 };
47292
47293 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
47294     inputType:      'hidden',
47295     allowBlank:     true,
47296     readOnly:         true,
47297     
47298  
47299     /**
47300      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47301      */
47302     focusClass : undefined,
47303     /**
47304      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47305      */
47306     fieldClass: 'x-form-field',
47307     
47308      /**
47309      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
47310      */
47311     valueRenderer: undefined,
47312     
47313     width: 100,
47314     /**
47315      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47316      * {tag: "input", type: "checkbox", autocomplete: "off"})
47317      */
47318      
47319  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
47320
47321     onResize : function(){
47322         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
47323         
47324     },
47325
47326     initEvents : function(){
47327         // Roo.form.Checkbox.superclass.initEvents.call(this);
47328         // has no events...
47329        
47330     },
47331
47332
47333     getResizeEl : function(){
47334         return this.wrap;
47335     },
47336
47337     getPositionEl : function(){
47338         return this.wrap;
47339     },
47340
47341     // private
47342     onRender : function(ct, position){
47343         
47344         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
47345         //if(this.inputValue !== undefined){
47346         this.wrap = this.el.wrap();
47347         
47348         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
47349         
47350         if (this.bodyStyle) {
47351             this.viewEl.applyStyles(this.bodyStyle);
47352         }
47353         //this.viewEl.setStyle('padding', '2px');
47354         
47355         this.setValue(this.value);
47356         
47357     },
47358 /*
47359     // private
47360     initValue : Roo.emptyFn,
47361
47362   */
47363
47364         // private
47365     onClick : function(){
47366         
47367     },
47368
47369     /**
47370      * Sets the checked state of the checkbox.
47371      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
47372      */
47373     setValue : function(v){
47374         this.value = v;
47375         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
47376         // this might be called before we have a dom element..
47377         if (!this.viewEl) {
47378             return;
47379         }
47380         this.viewEl.dom.innerHTML = html;
47381         Roo.form.DisplayField.superclass.setValue.call(this, v);
47382
47383     }
47384 });/*
47385  * 
47386  * Licence- LGPL
47387  * 
47388  */
47389
47390 /**
47391  * @class Roo.form.DayPicker
47392  * @extends Roo.form.Field
47393  * A Day picker show [M] [T] [W] ....
47394  * @constructor
47395  * Creates a new Day Picker
47396  * @param {Object} config Configuration options
47397  */
47398 Roo.form.DayPicker= function(config){
47399     Roo.form.DayPicker.superclass.constructor.call(this, config);
47400      
47401 };
47402
47403 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
47404     /**
47405      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47406      */
47407     focusClass : undefined,
47408     /**
47409      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47410      */
47411     fieldClass: "x-form-field",
47412    
47413     /**
47414      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47415      * {tag: "input", type: "checkbox", autocomplete: "off"})
47416      */
47417     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
47418     
47419    
47420     actionMode : 'viewEl', 
47421     //
47422     // private
47423  
47424     inputType : 'hidden',
47425     
47426      
47427     inputElement: false, // real input element?
47428     basedOn: false, // ????
47429     
47430     isFormField: true, // not sure where this is needed!!!!
47431
47432     onResize : function(){
47433         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47434         if(!this.boxLabel){
47435             this.el.alignTo(this.wrap, 'c-c');
47436         }
47437     },
47438
47439     initEvents : function(){
47440         Roo.form.Checkbox.superclass.initEvents.call(this);
47441         this.el.on("click", this.onClick,  this);
47442         this.el.on("change", this.onClick,  this);
47443     },
47444
47445
47446     getResizeEl : function(){
47447         return this.wrap;
47448     },
47449
47450     getPositionEl : function(){
47451         return this.wrap;
47452     },
47453
47454     
47455     // private
47456     onRender : function(ct, position){
47457         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47458        
47459         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47460         
47461         var r1 = '<table><tr>';
47462         var r2 = '<tr class="x-form-daypick-icons">';
47463         for (var i=0; i < 7; i++) {
47464             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47465             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47466         }
47467         
47468         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47469         viewEl.select('img').on('click', this.onClick, this);
47470         this.viewEl = viewEl;   
47471         
47472         
47473         // this will not work on Chrome!!!
47474         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47475         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47476         
47477         
47478           
47479
47480     },
47481
47482     // private
47483     initValue : Roo.emptyFn,
47484
47485     /**
47486      * Returns the checked state of the checkbox.
47487      * @return {Boolean} True if checked, else false
47488      */
47489     getValue : function(){
47490         return this.el.dom.value;
47491         
47492     },
47493
47494         // private
47495     onClick : function(e){ 
47496         //this.setChecked(!this.checked);
47497         Roo.get(e.target).toggleClass('x-menu-item-checked');
47498         this.refreshValue();
47499         //if(this.el.dom.checked != this.checked){
47500         //    this.setValue(this.el.dom.checked);
47501        // }
47502     },
47503     
47504     // private
47505     refreshValue : function()
47506     {
47507         var val = '';
47508         this.viewEl.select('img',true).each(function(e,i,n)  {
47509             val += e.is(".x-menu-item-checked") ? String(n) : '';
47510         });
47511         this.setValue(val, true);
47512     },
47513
47514     /**
47515      * Sets the checked state of the checkbox.
47516      * On is always based on a string comparison between inputValue and the param.
47517      * @param {Boolean/String} value - the value to set 
47518      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47519      */
47520     setValue : function(v,suppressEvent){
47521         if (!this.el.dom) {
47522             return;
47523         }
47524         var old = this.el.dom.value ;
47525         this.el.dom.value = v;
47526         if (suppressEvent) {
47527             return ;
47528         }
47529          
47530         // update display..
47531         this.viewEl.select('img',true).each(function(e,i,n)  {
47532             
47533             var on = e.is(".x-menu-item-checked");
47534             var newv = v.indexOf(String(n)) > -1;
47535             if (on != newv) {
47536                 e.toggleClass('x-menu-item-checked');
47537             }
47538             
47539         });
47540         
47541         
47542         this.fireEvent('change', this, v, old);
47543         
47544         
47545     },
47546    
47547     // handle setting of hidden value by some other method!!?!?
47548     setFromHidden: function()
47549     {
47550         if(!this.el){
47551             return;
47552         }
47553         //console.log("SET FROM HIDDEN");
47554         //alert('setFrom hidden');
47555         this.setValue(this.el.dom.value);
47556     },
47557     
47558     onDestroy : function()
47559     {
47560         if(this.viewEl){
47561             Roo.get(this.viewEl).remove();
47562         }
47563          
47564         Roo.form.DayPicker.superclass.onDestroy.call(this);
47565     }
47566
47567 });/*
47568  * RooJS Library 1.1.1
47569  * Copyright(c) 2008-2011  Alan Knowles
47570  *
47571  * License - LGPL
47572  */
47573  
47574
47575 /**
47576  * @class Roo.form.ComboCheck
47577  * @extends Roo.form.ComboBox
47578  * A combobox for multiple select items.
47579  *
47580  * FIXME - could do with a reset button..
47581  * 
47582  * @constructor
47583  * Create a new ComboCheck
47584  * @param {Object} config Configuration options
47585  */
47586 Roo.form.ComboCheck = function(config){
47587     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47588     // should verify some data...
47589     // like
47590     // hiddenName = required..
47591     // displayField = required
47592     // valudField == required
47593     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47594     var _t = this;
47595     Roo.each(req, function(e) {
47596         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47597             throw "Roo.form.ComboCheck : missing value for: " + e;
47598         }
47599     });
47600     
47601     
47602 };
47603
47604 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47605      
47606      
47607     editable : false,
47608      
47609     selectedClass: 'x-menu-item-checked', 
47610     
47611     // private
47612     onRender : function(ct, position){
47613         var _t = this;
47614         
47615         
47616         
47617         if(!this.tpl){
47618             var cls = 'x-combo-list';
47619
47620             
47621             this.tpl =  new Roo.Template({
47622                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47623                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47624                    '<span>{' + this.displayField + '}</span>' +
47625                     '</div>' 
47626                 
47627             });
47628         }
47629  
47630         
47631         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47632         this.view.singleSelect = false;
47633         this.view.multiSelect = true;
47634         this.view.toggleSelect = true;
47635         this.pageTb.add(new Roo.Toolbar.Fill(), {
47636             
47637             text: 'Done',
47638             handler: function()
47639             {
47640                 _t.collapse();
47641             }
47642         });
47643     },
47644     
47645     onViewOver : function(e, t){
47646         // do nothing...
47647         return;
47648         
47649     },
47650     
47651     onViewClick : function(doFocus,index){
47652         return;
47653         
47654     },
47655     select: function () {
47656         //Roo.log("SELECT CALLED");
47657     },
47658      
47659     selectByValue : function(xv, scrollIntoView){
47660         var ar = this.getValueArray();
47661         var sels = [];
47662         
47663         Roo.each(ar, function(v) {
47664             if(v === undefined || v === null){
47665                 return;
47666             }
47667             var r = this.findRecord(this.valueField, v);
47668             if(r){
47669                 sels.push(this.store.indexOf(r))
47670                 
47671             }
47672         },this);
47673         this.view.select(sels);
47674         return false;
47675     },
47676     
47677     
47678     
47679     onSelect : function(record, index){
47680        // Roo.log("onselect Called");
47681        // this is only called by the clear button now..
47682         this.view.clearSelections();
47683         this.setValue('[]');
47684         if (this.value != this.valueBefore) {
47685             this.fireEvent('change', this, this.value, this.valueBefore);
47686             this.valueBefore = this.value;
47687         }
47688     },
47689     getValueArray : function()
47690     {
47691         var ar = [] ;
47692         
47693         try {
47694             //Roo.log(this.value);
47695             if (typeof(this.value) == 'undefined') {
47696                 return [];
47697             }
47698             var ar = Roo.decode(this.value);
47699             return  ar instanceof Array ? ar : []; //?? valid?
47700             
47701         } catch(e) {
47702             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47703             return [];
47704         }
47705          
47706     },
47707     expand : function ()
47708     {
47709         
47710         Roo.form.ComboCheck.superclass.expand.call(this);
47711         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47712         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47713         
47714
47715     },
47716     
47717     collapse : function(){
47718         Roo.form.ComboCheck.superclass.collapse.call(this);
47719         var sl = this.view.getSelectedIndexes();
47720         var st = this.store;
47721         var nv = [];
47722         var tv = [];
47723         var r;
47724         Roo.each(sl, function(i) {
47725             r = st.getAt(i);
47726             nv.push(r.get(this.valueField));
47727         },this);
47728         this.setValue(Roo.encode(nv));
47729         if (this.value != this.valueBefore) {
47730
47731             this.fireEvent('change', this, this.value, this.valueBefore);
47732             this.valueBefore = this.value;
47733         }
47734         
47735     },
47736     
47737     setValue : function(v){
47738         // Roo.log(v);
47739         this.value = v;
47740         
47741         var vals = this.getValueArray();
47742         var tv = [];
47743         Roo.each(vals, function(k) {
47744             var r = this.findRecord(this.valueField, k);
47745             if(r){
47746                 tv.push(r.data[this.displayField]);
47747             }else if(this.valueNotFoundText !== undefined){
47748                 tv.push( this.valueNotFoundText );
47749             }
47750         },this);
47751        // Roo.log(tv);
47752         
47753         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47754         this.hiddenField.value = v;
47755         this.value = v;
47756     }
47757     
47758 });/*
47759  * Based on:
47760  * Ext JS Library 1.1.1
47761  * Copyright(c) 2006-2007, Ext JS, LLC.
47762  *
47763  * Originally Released Under LGPL - original licence link has changed is not relivant.
47764  *
47765  * Fork - LGPL
47766  * <script type="text/javascript">
47767  */
47768  
47769 /**
47770  * @class Roo.form.Signature
47771  * @extends Roo.form.Field
47772  * Signature field.  
47773  * @constructor
47774  * 
47775  * @param {Object} config Configuration options
47776  */
47777
47778 Roo.form.Signature = function(config){
47779     Roo.form.Signature.superclass.constructor.call(this, config);
47780     
47781     this.addEvents({// not in used??
47782          /**
47783          * @event confirm
47784          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47785              * @param {Roo.form.Signature} combo This combo box
47786              */
47787         'confirm' : true,
47788         /**
47789          * @event reset
47790          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47791              * @param {Roo.form.ComboBox} combo This combo box
47792              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47793              */
47794         'reset' : true
47795     });
47796 };
47797
47798 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47799     /**
47800      * @cfg {Object} labels Label to use when rendering a form.
47801      * defaults to 
47802      * labels : { 
47803      *      clear : "Clear",
47804      *      confirm : "Confirm"
47805      *  }
47806      */
47807     labels : { 
47808         clear : "Clear",
47809         confirm : "Confirm"
47810     },
47811     /**
47812      * @cfg {Number} width The signature panel width (defaults to 300)
47813      */
47814     width: 300,
47815     /**
47816      * @cfg {Number} height The signature panel height (defaults to 100)
47817      */
47818     height : 100,
47819     /**
47820      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47821      */
47822     allowBlank : false,
47823     
47824     //private
47825     // {Object} signPanel The signature SVG panel element (defaults to {})
47826     signPanel : {},
47827     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47828     isMouseDown : false,
47829     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47830     isConfirmed : false,
47831     // {String} signatureTmp SVG mapping string (defaults to empty string)
47832     signatureTmp : '',
47833     
47834     
47835     defaultAutoCreate : { // modified by initCompnoent..
47836         tag: "input",
47837         type:"hidden"
47838     },
47839
47840     // private
47841     onRender : function(ct, position){
47842         
47843         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47844         
47845         this.wrap = this.el.wrap({
47846             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47847         });
47848         
47849         this.createToolbar(this);
47850         this.signPanel = this.wrap.createChild({
47851                 tag: 'div',
47852                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47853             }, this.el
47854         );
47855             
47856         this.svgID = Roo.id();
47857         this.svgEl = this.signPanel.createChild({
47858               xmlns : 'http://www.w3.org/2000/svg',
47859               tag : 'svg',
47860               id : this.svgID + "-svg",
47861               width: this.width,
47862               height: this.height,
47863               viewBox: '0 0 '+this.width+' '+this.height,
47864               cn : [
47865                 {
47866                     tag: "rect",
47867                     id: this.svgID + "-svg-r",
47868                     width: this.width,
47869                     height: this.height,
47870                     fill: "#ffa"
47871                 },
47872                 {
47873                     tag: "line",
47874                     id: this.svgID + "-svg-l",
47875                     x1: "0", // start
47876                     y1: (this.height*0.8), // start set the line in 80% of height
47877                     x2: this.width, // end
47878                     y2: (this.height*0.8), // end set the line in 80% of height
47879                     'stroke': "#666",
47880                     'stroke-width': "1",
47881                     'stroke-dasharray': "3",
47882                     'shape-rendering': "crispEdges",
47883                     'pointer-events': "none"
47884                 },
47885                 {
47886                     tag: "path",
47887                     id: this.svgID + "-svg-p",
47888                     'stroke': "navy",
47889                     'stroke-width': "3",
47890                     'fill': "none",
47891                     'pointer-events': 'none'
47892                 }
47893               ]
47894         });
47895         this.createSVG();
47896         this.svgBox = this.svgEl.dom.getScreenCTM();
47897     },
47898     createSVG : function(){ 
47899         var svg = this.signPanel;
47900         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47901         var t = this;
47902
47903         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47904         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47905         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47906         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47907         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47908         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47909         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47910         
47911     },
47912     isTouchEvent : function(e){
47913         return e.type.match(/^touch/);
47914     },
47915     getCoords : function (e) {
47916         var pt    = this.svgEl.dom.createSVGPoint();
47917         pt.x = e.clientX; 
47918         pt.y = e.clientY;
47919         if (this.isTouchEvent(e)) {
47920             pt.x =  e.targetTouches[0].clientX 
47921             pt.y = e.targetTouches[0].clientY;
47922         }
47923         var a = this.svgEl.dom.getScreenCTM();
47924         var b = a.inverse();
47925         var mx = pt.matrixTransform(b);
47926         return mx.x + ',' + mx.y;
47927     },
47928     //mouse event headler 
47929     down : function (e) {
47930         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47931         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47932         
47933         this.isMouseDown = true;
47934         
47935         e.preventDefault();
47936     },
47937     move : function (e) {
47938         if (this.isMouseDown) {
47939             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47940             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47941         }
47942         
47943         e.preventDefault();
47944     },
47945     up : function (e) {
47946         this.isMouseDown = false;
47947         var sp = this.signatureTmp.split(' ');
47948         
47949         if(sp.length > 1){
47950             if(!sp[sp.length-2].match(/^L/)){
47951                 sp.pop();
47952                 sp.pop();
47953                 sp.push("");
47954                 this.signatureTmp = sp.join(" ");
47955             }
47956         }
47957         if(this.getValue() != this.signatureTmp){
47958             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47959             this.isConfirmed = false;
47960         }
47961         e.preventDefault();
47962     },
47963     
47964     /**
47965      * Protected method that will not generally be called directly. It
47966      * is called when the editor creates its toolbar. Override this method if you need to
47967      * add custom toolbar buttons.
47968      * @param {HtmlEditor} editor
47969      */
47970     createToolbar : function(editor){
47971          function btn(id, toggle, handler){
47972             var xid = fid + '-'+ id ;
47973             return {
47974                 id : xid,
47975                 cmd : id,
47976                 cls : 'x-btn-icon x-edit-'+id,
47977                 enableToggle:toggle !== false,
47978                 scope: editor, // was editor...
47979                 handler:handler||editor.relayBtnCmd,
47980                 clickEvent:'mousedown',
47981                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47982                 tabIndex:-1
47983             };
47984         }
47985         
47986         
47987         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47988         this.tb = tb;
47989         this.tb.add(
47990            {
47991                 cls : ' x-signature-btn x-signature-'+id,
47992                 scope: editor, // was editor...
47993                 handler: this.reset,
47994                 clickEvent:'mousedown',
47995                 text: this.labels.clear
47996             },
47997             {
47998                  xtype : 'Fill',
47999                  xns: Roo.Toolbar
48000             }, 
48001             {
48002                 cls : '  x-signature-btn x-signature-'+id,
48003                 scope: editor, // was editor...
48004                 handler: this.confirmHandler,
48005                 clickEvent:'mousedown',
48006                 text: this.labels.confirm
48007             }
48008         );
48009     
48010     },
48011     //public
48012     /**
48013      * when user is clicked confirm then show this image.....
48014      * 
48015      * @return {String} Image Data URI
48016      */
48017     getImageDataURI : function(){
48018         var svg = this.svgEl.dom.parentNode.innerHTML;
48019         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
48020         return src; 
48021     },
48022     /**
48023      * 
48024      * @return {Boolean} this.isConfirmed
48025      */
48026     getConfirmed : function(){
48027         return this.isConfirmed;
48028     },
48029     /**
48030      * 
48031      * @return {Number} this.width
48032      */
48033     getWidth : function(){
48034         return this.width;
48035     },
48036     /**
48037      * 
48038      * @return {Number} this.height
48039      */
48040     getHeight : function(){
48041         return this.height;
48042     },
48043     // private
48044     getSignature : function(){
48045         return this.signatureTmp;
48046     },
48047     // private
48048     reset : function(){
48049         this.signatureTmp = '';
48050         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48051         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
48052         this.isConfirmed = false;
48053         Roo.form.Signature.superclass.reset.call(this);
48054     },
48055     setSignature : function(s){
48056         this.signatureTmp = s;
48057         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48058         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
48059         this.setValue(s);
48060         this.isConfirmed = false;
48061         Roo.form.Signature.superclass.reset.call(this);
48062     }, 
48063     test : function(){
48064 //        Roo.log(this.signPanel.dom.contentWindow.up())
48065     },
48066     //private
48067     setConfirmed : function(){
48068         
48069         
48070         
48071 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
48072     },
48073     // private
48074     confirmHandler : function(){
48075         if(!this.getSignature()){
48076             return;
48077         }
48078         
48079         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
48080         this.setValue(this.getSignature());
48081         this.isConfirmed = true;
48082         
48083         this.fireEvent('confirm', this);
48084     },
48085     // private
48086     // Subclasses should provide the validation implementation by overriding this
48087     validateValue : function(value){
48088         if(this.allowBlank){
48089             return true;
48090         }
48091         
48092         if(this.isConfirmed){
48093             return true;
48094         }
48095         return false;
48096     }
48097 });/*
48098  * Based on:
48099  * Ext JS Library 1.1.1
48100  * Copyright(c) 2006-2007, Ext JS, LLC.
48101  *
48102  * Originally Released Under LGPL - original licence link has changed is not relivant.
48103  *
48104  * Fork - LGPL
48105  * <script type="text/javascript">
48106  */
48107  
48108
48109 /**
48110  * @class Roo.form.ComboBox
48111  * @extends Roo.form.TriggerField
48112  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
48113  * @constructor
48114  * Create a new ComboBox.
48115  * @param {Object} config Configuration options
48116  */
48117 Roo.form.Select = function(config){
48118     Roo.form.Select.superclass.constructor.call(this, config);
48119      
48120 };
48121
48122 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
48123     /**
48124      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
48125      */
48126     /**
48127      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
48128      * rendering into an Roo.Editor, defaults to false)
48129      */
48130     /**
48131      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
48132      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
48133      */
48134     /**
48135      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
48136      */
48137     /**
48138      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
48139      * the dropdown list (defaults to undefined, with no header element)
48140      */
48141
48142      /**
48143      * @cfg {String/Roo.Template} tpl The template to use to render the output
48144      */
48145      
48146     // private
48147     defaultAutoCreate : {tag: "select"  },
48148     /**
48149      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
48150      */
48151     listWidth: undefined,
48152     /**
48153      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
48154      * mode = 'remote' or 'text' if mode = 'local')
48155      */
48156     displayField: undefined,
48157     /**
48158      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
48159      * mode = 'remote' or 'value' if mode = 'local'). 
48160      * Note: use of a valueField requires the user make a selection
48161      * in order for a value to be mapped.
48162      */
48163     valueField: undefined,
48164     
48165     
48166     /**
48167      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
48168      * field's data value (defaults to the underlying DOM element's name)
48169      */
48170     hiddenName: undefined,
48171     /**
48172      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
48173      */
48174     listClass: '',
48175     /**
48176      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
48177      */
48178     selectedClass: 'x-combo-selected',
48179     /**
48180      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
48181      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
48182      * which displays a downward arrow icon).
48183      */
48184     triggerClass : 'x-form-arrow-trigger',
48185     /**
48186      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
48187      */
48188     shadow:'sides',
48189     /**
48190      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
48191      * anchor positions (defaults to 'tl-bl')
48192      */
48193     listAlign: 'tl-bl?',
48194     /**
48195      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
48196      */
48197     maxHeight: 300,
48198     /**
48199      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
48200      * query specified by the allQuery config option (defaults to 'query')
48201      */
48202     triggerAction: 'query',
48203     /**
48204      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
48205      * (defaults to 4, does not apply if editable = false)
48206      */
48207     minChars : 4,
48208     /**
48209      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
48210      * delay (typeAheadDelay) if it matches a known value (defaults to false)
48211      */
48212     typeAhead: false,
48213     /**
48214      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
48215      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
48216      */
48217     queryDelay: 500,
48218     /**
48219      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
48220      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
48221      */
48222     pageSize: 0,
48223     /**
48224      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
48225      * when editable = true (defaults to false)
48226      */
48227     selectOnFocus:false,
48228     /**
48229      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
48230      */
48231     queryParam: 'query',
48232     /**
48233      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
48234      * when mode = 'remote' (defaults to 'Loading...')
48235      */
48236     loadingText: 'Loading...',
48237     /**
48238      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
48239      */
48240     resizable: false,
48241     /**
48242      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
48243      */
48244     handleHeight : 8,
48245     /**
48246      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
48247      * traditional select (defaults to true)
48248      */
48249     editable: true,
48250     /**
48251      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
48252      */
48253     allQuery: '',
48254     /**
48255      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
48256      */
48257     mode: 'remote',
48258     /**
48259      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
48260      * listWidth has a higher value)
48261      */
48262     minListWidth : 70,
48263     /**
48264      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
48265      * allow the user to set arbitrary text into the field (defaults to false)
48266      */
48267     forceSelection:false,
48268     /**
48269      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
48270      * if typeAhead = true (defaults to 250)
48271      */
48272     typeAheadDelay : 250,
48273     /**
48274      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
48275      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
48276      */
48277     valueNotFoundText : undefined,
48278     
48279     /**
48280      * @cfg {String} defaultValue The value displayed after loading the store.
48281      */
48282     defaultValue: '',
48283     
48284     /**
48285      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
48286      */
48287     blockFocus : false,
48288     
48289     /**
48290      * @cfg {Boolean} disableClear Disable showing of clear button.
48291      */
48292     disableClear : false,
48293     /**
48294      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
48295      */
48296     alwaysQuery : false,
48297     
48298     //private
48299     addicon : false,
48300     editicon: false,
48301     
48302     // element that contains real text value.. (when hidden is used..)
48303      
48304     // private
48305     onRender : function(ct, position){
48306         Roo.form.Field.prototype.onRender.call(this, ct, position);
48307         
48308         if(this.store){
48309             this.store.on('beforeload', this.onBeforeLoad, this);
48310             this.store.on('load', this.onLoad, this);
48311             this.store.on('loadexception', this.onLoadException, this);
48312             this.store.load({});
48313         }
48314         
48315         
48316         
48317     },
48318
48319     // private
48320     initEvents : function(){
48321         //Roo.form.ComboBox.superclass.initEvents.call(this);
48322  
48323     },
48324
48325     onDestroy : function(){
48326        
48327         if(this.store){
48328             this.store.un('beforeload', this.onBeforeLoad, this);
48329             this.store.un('load', this.onLoad, this);
48330             this.store.un('loadexception', this.onLoadException, this);
48331         }
48332         //Roo.form.ComboBox.superclass.onDestroy.call(this);
48333     },
48334
48335     // private
48336     fireKey : function(e){
48337         if(e.isNavKeyPress() && !this.list.isVisible()){
48338             this.fireEvent("specialkey", this, e);
48339         }
48340     },
48341
48342     // private
48343     onResize: function(w, h){
48344         
48345         return; 
48346     
48347         
48348     },
48349
48350     /**
48351      * Allow or prevent the user from directly editing the field text.  If false is passed,
48352      * the user will only be able to select from the items defined in the dropdown list.  This method
48353      * is the runtime equivalent of setting the 'editable' config option at config time.
48354      * @param {Boolean} value True to allow the user to directly edit the field text
48355      */
48356     setEditable : function(value){
48357          
48358     },
48359
48360     // private
48361     onBeforeLoad : function(){
48362         
48363         Roo.log("Select before load");
48364         return;
48365     
48366         this.innerList.update(this.loadingText ?
48367                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
48368         //this.restrictHeight();
48369         this.selectedIndex = -1;
48370     },
48371
48372     // private
48373     onLoad : function(){
48374
48375     
48376         var dom = this.el.dom;
48377         dom.innerHTML = '';
48378          var od = dom.ownerDocument;
48379          
48380         if (this.emptyText) {
48381             var op = od.createElement('option');
48382             op.setAttribute('value', '');
48383             op.innerHTML = String.format('{0}', this.emptyText);
48384             dom.appendChild(op);
48385         }
48386         if(this.store.getCount() > 0){
48387            
48388             var vf = this.valueField;
48389             var df = this.displayField;
48390             this.store.data.each(function(r) {
48391                 // which colmsn to use... testing - cdoe / title..
48392                 var op = od.createElement('option');
48393                 op.setAttribute('value', r.data[vf]);
48394                 op.innerHTML = String.format('{0}', r.data[df]);
48395                 dom.appendChild(op);
48396             });
48397             if (typeof(this.defaultValue != 'undefined')) {
48398                 this.setValue(this.defaultValue);
48399             }
48400             
48401              
48402         }else{
48403             //this.onEmptyResults();
48404         }
48405         //this.el.focus();
48406     },
48407     // private
48408     onLoadException : function()
48409     {
48410         dom.innerHTML = '';
48411             
48412         Roo.log("Select on load exception");
48413         return;
48414     
48415         this.collapse();
48416         Roo.log(this.store.reader.jsonData);
48417         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48418             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48419         }
48420         
48421         
48422     },
48423     // private
48424     onTypeAhead : function(){
48425          
48426     },
48427
48428     // private
48429     onSelect : function(record, index){
48430         Roo.log('on select?');
48431         return;
48432         if(this.fireEvent('beforeselect', this, record, index) !== false){
48433             this.setFromData(index > -1 ? record.data : false);
48434             this.collapse();
48435             this.fireEvent('select', this, record, index);
48436         }
48437     },
48438
48439     /**
48440      * Returns the currently selected field value or empty string if no value is set.
48441      * @return {String} value The selected value
48442      */
48443     getValue : function(){
48444         var dom = this.el.dom;
48445         this.value = dom.options[dom.selectedIndex].value;
48446         return this.value;
48447         
48448     },
48449
48450     /**
48451      * Clears any text/value currently set in the field
48452      */
48453     clearValue : function(){
48454         this.value = '';
48455         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48456         
48457     },
48458
48459     /**
48460      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48461      * will be displayed in the field.  If the value does not match the data value of an existing item,
48462      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48463      * Otherwise the field will be blank (although the value will still be set).
48464      * @param {String} value The value to match
48465      */
48466     setValue : function(v){
48467         var d = this.el.dom;
48468         for (var i =0; i < d.options.length;i++) {
48469             if (v == d.options[i].value) {
48470                 d.selectedIndex = i;
48471                 this.value = v;
48472                 return;
48473             }
48474         }
48475         this.clearValue();
48476     },
48477     /**
48478      * @property {Object} the last set data for the element
48479      */
48480     
48481     lastData : false,
48482     /**
48483      * Sets the value of the field based on a object which is related to the record format for the store.
48484      * @param {Object} value the value to set as. or false on reset?
48485      */
48486     setFromData : function(o){
48487         Roo.log('setfrom data?');
48488          
48489         
48490         
48491     },
48492     // private
48493     reset : function(){
48494         this.clearValue();
48495     },
48496     // private
48497     findRecord : function(prop, value){
48498         
48499         return false;
48500     
48501         var record;
48502         if(this.store.getCount() > 0){
48503             this.store.each(function(r){
48504                 if(r.data[prop] == value){
48505                     record = r;
48506                     return false;
48507                 }
48508                 return true;
48509             });
48510         }
48511         return record;
48512     },
48513     
48514     getName: function()
48515     {
48516         // returns hidden if it's set..
48517         if (!this.rendered) {return ''};
48518         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48519         
48520     },
48521      
48522
48523     
48524
48525     // private
48526     onEmptyResults : function(){
48527         Roo.log('empty results');
48528         //this.collapse();
48529     },
48530
48531     /**
48532      * Returns true if the dropdown list is expanded, else false.
48533      */
48534     isExpanded : function(){
48535         return false;
48536     },
48537
48538     /**
48539      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48540      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48541      * @param {String} value The data value of the item to select
48542      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48543      * selected item if it is not currently in view (defaults to true)
48544      * @return {Boolean} True if the value matched an item in the list, else false
48545      */
48546     selectByValue : function(v, scrollIntoView){
48547         Roo.log('select By Value');
48548         return false;
48549     
48550         if(v !== undefined && v !== null){
48551             var r = this.findRecord(this.valueField || this.displayField, v);
48552             if(r){
48553                 this.select(this.store.indexOf(r), scrollIntoView);
48554                 return true;
48555             }
48556         }
48557         return false;
48558     },
48559
48560     /**
48561      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48562      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48563      * @param {Number} index The zero-based index of the list item to select
48564      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48565      * selected item if it is not currently in view (defaults to true)
48566      */
48567     select : function(index, scrollIntoView){
48568         Roo.log('select ');
48569         return  ;
48570         
48571         this.selectedIndex = index;
48572         this.view.select(index);
48573         if(scrollIntoView !== false){
48574             var el = this.view.getNode(index);
48575             if(el){
48576                 this.innerList.scrollChildIntoView(el, false);
48577             }
48578         }
48579     },
48580
48581       
48582
48583     // private
48584     validateBlur : function(){
48585         
48586         return;
48587         
48588     },
48589
48590     // private
48591     initQuery : function(){
48592         this.doQuery(this.getRawValue());
48593     },
48594
48595     // private
48596     doForce : function(){
48597         if(this.el.dom.value.length > 0){
48598             this.el.dom.value =
48599                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48600              
48601         }
48602     },
48603
48604     /**
48605      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48606      * query allowing the query action to be canceled if needed.
48607      * @param {String} query The SQL query to execute
48608      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48609      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48610      * saved in the current store (defaults to false)
48611      */
48612     doQuery : function(q, forceAll){
48613         
48614         Roo.log('doQuery?');
48615         if(q === undefined || q === null){
48616             q = '';
48617         }
48618         var qe = {
48619             query: q,
48620             forceAll: forceAll,
48621             combo: this,
48622             cancel:false
48623         };
48624         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48625             return false;
48626         }
48627         q = qe.query;
48628         forceAll = qe.forceAll;
48629         if(forceAll === true || (q.length >= this.minChars)){
48630             if(this.lastQuery != q || this.alwaysQuery){
48631                 this.lastQuery = q;
48632                 if(this.mode == 'local'){
48633                     this.selectedIndex = -1;
48634                     if(forceAll){
48635                         this.store.clearFilter();
48636                     }else{
48637                         this.store.filter(this.displayField, q);
48638                     }
48639                     this.onLoad();
48640                 }else{
48641                     this.store.baseParams[this.queryParam] = q;
48642                     this.store.load({
48643                         params: this.getParams(q)
48644                     });
48645                     this.expand();
48646                 }
48647             }else{
48648                 this.selectedIndex = -1;
48649                 this.onLoad();   
48650             }
48651         }
48652     },
48653
48654     // private
48655     getParams : function(q){
48656         var p = {};
48657         //p[this.queryParam] = q;
48658         if(this.pageSize){
48659             p.start = 0;
48660             p.limit = this.pageSize;
48661         }
48662         return p;
48663     },
48664
48665     /**
48666      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48667      */
48668     collapse : function(){
48669         
48670     },
48671
48672     // private
48673     collapseIf : function(e){
48674         
48675     },
48676
48677     /**
48678      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48679      */
48680     expand : function(){
48681         
48682     } ,
48683
48684     // private
48685      
48686
48687     /** 
48688     * @cfg {Boolean} grow 
48689     * @hide 
48690     */
48691     /** 
48692     * @cfg {Number} growMin 
48693     * @hide 
48694     */
48695     /** 
48696     * @cfg {Number} growMax 
48697     * @hide 
48698     */
48699     /**
48700      * @hide
48701      * @method autoSize
48702      */
48703     
48704     setWidth : function()
48705     {
48706         
48707     },
48708     getResizeEl : function(){
48709         return this.el;
48710     }
48711 });//<script type="text/javasscript">
48712  
48713
48714 /**
48715  * @class Roo.DDView
48716  * A DnD enabled version of Roo.View.
48717  * @param {Element/String} container The Element in which to create the View.
48718  * @param {String} tpl The template string used to create the markup for each element of the View
48719  * @param {Object} config The configuration properties. These include all the config options of
48720  * {@link Roo.View} plus some specific to this class.<br>
48721  * <p>
48722  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48723  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48724  * <p>
48725  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48726 .x-view-drag-insert-above {
48727         border-top:1px dotted #3366cc;
48728 }
48729 .x-view-drag-insert-below {
48730         border-bottom:1px dotted #3366cc;
48731 }
48732 </code></pre>
48733  * 
48734  */
48735  
48736 Roo.DDView = function(container, tpl, config) {
48737     Roo.DDView.superclass.constructor.apply(this, arguments);
48738     this.getEl().setStyle("outline", "0px none");
48739     this.getEl().unselectable();
48740     if (this.dragGroup) {
48741                 this.setDraggable(this.dragGroup.split(","));
48742     }
48743     if (this.dropGroup) {
48744                 this.setDroppable(this.dropGroup.split(","));
48745     }
48746     if (this.deletable) {
48747         this.setDeletable();
48748     }
48749     this.isDirtyFlag = false;
48750         this.addEvents({
48751                 "drop" : true
48752         });
48753 };
48754
48755 Roo.extend(Roo.DDView, Roo.View, {
48756 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48757 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48758 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48759 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48760
48761         isFormField: true,
48762
48763         reset: Roo.emptyFn,
48764         
48765         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48766
48767         validate: function() {
48768                 return true;
48769         },
48770         
48771         destroy: function() {
48772                 this.purgeListeners();
48773                 this.getEl.removeAllListeners();
48774                 this.getEl().remove();
48775                 if (this.dragZone) {
48776                         if (this.dragZone.destroy) {
48777                                 this.dragZone.destroy();
48778                         }
48779                 }
48780                 if (this.dropZone) {
48781                         if (this.dropZone.destroy) {
48782                                 this.dropZone.destroy();
48783                         }
48784                 }
48785         },
48786
48787 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48788         getName: function() {
48789                 return this.name;
48790         },
48791
48792 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48793         setValue: function(v) {
48794                 if (!this.store) {
48795                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48796                 }
48797                 var data = {};
48798                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48799                 this.store.proxy = new Roo.data.MemoryProxy(data);
48800                 this.store.load();
48801         },
48802
48803 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48804         getValue: function() {
48805                 var result = '(';
48806                 this.store.each(function(rec) {
48807                         result += rec.id + ',';
48808                 });
48809                 return result.substr(0, result.length - 1) + ')';
48810         },
48811         
48812         getIds: function() {
48813                 var i = 0, result = new Array(this.store.getCount());
48814                 this.store.each(function(rec) {
48815                         result[i++] = rec.id;
48816                 });
48817                 return result;
48818         },
48819         
48820         isDirty: function() {
48821                 return this.isDirtyFlag;
48822         },
48823
48824 /**
48825  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48826  *      whole Element becomes the target, and this causes the drop gesture to append.
48827  */
48828     getTargetFromEvent : function(e) {
48829                 var target = e.getTarget();
48830                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48831                 target = target.parentNode;
48832                 }
48833                 if (!target) {
48834                         target = this.el.dom.lastChild || this.el.dom;
48835                 }
48836                 return target;
48837     },
48838
48839 /**
48840  *      Create the drag data which consists of an object which has the property "ddel" as
48841  *      the drag proxy element. 
48842  */
48843     getDragData : function(e) {
48844         var target = this.findItemFromChild(e.getTarget());
48845                 if(target) {
48846                         this.handleSelection(e);
48847                         var selNodes = this.getSelectedNodes();
48848             var dragData = {
48849                 source: this,
48850                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48851                 nodes: selNodes,
48852                 records: []
48853                         };
48854                         var selectedIndices = this.getSelectedIndexes();
48855                         for (var i = 0; i < selectedIndices.length; i++) {
48856                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48857                         }
48858                         if (selNodes.length == 1) {
48859                                 dragData.ddel = target.cloneNode(true); // the div element
48860                         } else {
48861                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48862                                 div.className = 'multi-proxy';
48863                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48864                                         div.appendChild(selNodes[i].cloneNode(true));
48865                                 }
48866                                 dragData.ddel = div;
48867                         }
48868             //console.log(dragData)
48869             //console.log(dragData.ddel.innerHTML)
48870                         return dragData;
48871                 }
48872         //console.log('nodragData')
48873                 return false;
48874     },
48875     
48876 /**     Specify to which ddGroup items in this DDView may be dragged. */
48877     setDraggable: function(ddGroup) {
48878         if (ddGroup instanceof Array) {
48879                 Roo.each(ddGroup, this.setDraggable, this);
48880                 return;
48881         }
48882         if (this.dragZone) {
48883                 this.dragZone.addToGroup(ddGroup);
48884         } else {
48885                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48886                                 containerScroll: true,
48887                                 ddGroup: ddGroup 
48888
48889                         });
48890 //                      Draggability implies selection. DragZone's mousedown selects the element.
48891                         if (!this.multiSelect) { this.singleSelect = true; }
48892
48893 //                      Wire the DragZone's handlers up to methods in *this*
48894                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48895                 }
48896     },
48897
48898 /**     Specify from which ddGroup this DDView accepts drops. */
48899     setDroppable: function(ddGroup) {
48900         if (ddGroup instanceof Array) {
48901                 Roo.each(ddGroup, this.setDroppable, this);
48902                 return;
48903         }
48904         if (this.dropZone) {
48905                 this.dropZone.addToGroup(ddGroup);
48906         } else {
48907                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48908                                 containerScroll: true,
48909                                 ddGroup: ddGroup
48910                         });
48911
48912 //                      Wire the DropZone's handlers up to methods in *this*
48913                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48914                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48915                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48916                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48917                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48918                 }
48919     },
48920
48921 /**     Decide whether to drop above or below a View node. */
48922     getDropPoint : function(e, n, dd){
48923         if (n == this.el.dom) { return "above"; }
48924                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48925                 var c = t + (b - t) / 2;
48926                 var y = Roo.lib.Event.getPageY(e);
48927                 if(y <= c) {
48928                         return "above";
48929                 }else{
48930                         return "below";
48931                 }
48932     },
48933
48934     onNodeEnter : function(n, dd, e, data){
48935                 return false;
48936     },
48937     
48938     onNodeOver : function(n, dd, e, data){
48939                 var pt = this.getDropPoint(e, n, dd);
48940                 // set the insert point style on the target node
48941                 var dragElClass = this.dropNotAllowed;
48942                 if (pt) {
48943                         var targetElClass;
48944                         if (pt == "above"){
48945                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48946                                 targetElClass = "x-view-drag-insert-above";
48947                         } else {
48948                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48949                                 targetElClass = "x-view-drag-insert-below";
48950                         }
48951                         if (this.lastInsertClass != targetElClass){
48952                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48953                                 this.lastInsertClass = targetElClass;
48954                         }
48955                 }
48956                 return dragElClass;
48957         },
48958
48959     onNodeOut : function(n, dd, e, data){
48960                 this.removeDropIndicators(n);
48961     },
48962
48963     onNodeDrop : function(n, dd, e, data){
48964         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48965                 return false;
48966         }
48967         var pt = this.getDropPoint(e, n, dd);
48968                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48969                 if (pt == "below") { insertAt++; }
48970                 for (var i = 0; i < data.records.length; i++) {
48971                         var r = data.records[i];
48972                         var dup = this.store.getById(r.id);
48973                         if (dup && (dd != this.dragZone)) {
48974                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48975                         } else {
48976                                 if (data.copy) {
48977                                         this.store.insert(insertAt++, r.copy());
48978                                 } else {
48979                                         data.source.isDirtyFlag = true;
48980                                         r.store.remove(r);
48981                                         this.store.insert(insertAt++, r);
48982                                 }
48983                                 this.isDirtyFlag = true;
48984                         }
48985                 }
48986                 this.dragZone.cachedTarget = null;
48987                 return true;
48988     },
48989
48990     removeDropIndicators : function(n){
48991                 if(n){
48992                         Roo.fly(n).removeClass([
48993                                 "x-view-drag-insert-above",
48994                                 "x-view-drag-insert-below"]);
48995                         this.lastInsertClass = "_noclass";
48996                 }
48997     },
48998
48999 /**
49000  *      Utility method. Add a delete option to the DDView's context menu.
49001  *      @param {String} imageUrl The URL of the "delete" icon image.
49002  */
49003         setDeletable: function(imageUrl) {
49004                 if (!this.singleSelect && !this.multiSelect) {
49005                         this.singleSelect = true;
49006                 }
49007                 var c = this.getContextMenu();
49008                 this.contextMenu.on("itemclick", function(item) {
49009                         switch (item.id) {
49010                                 case "delete":
49011                                         this.remove(this.getSelectedIndexes());
49012                                         break;
49013                         }
49014                 }, this);
49015                 this.contextMenu.add({
49016                         icon: imageUrl,
49017                         id: "delete",
49018                         text: 'Delete'
49019                 });
49020         },
49021         
49022 /**     Return the context menu for this DDView. */
49023         getContextMenu: function() {
49024                 if (!this.contextMenu) {
49025 //                      Create the View's context menu
49026                         this.contextMenu = new Roo.menu.Menu({
49027                                 id: this.id + "-contextmenu"
49028                         });
49029                         this.el.on("contextmenu", this.showContextMenu, this);
49030                 }
49031                 return this.contextMenu;
49032         },
49033         
49034         disableContextMenu: function() {
49035                 if (this.contextMenu) {
49036                         this.el.un("contextmenu", this.showContextMenu, this);
49037                 }
49038         },
49039
49040         showContextMenu: function(e, item) {
49041         item = this.findItemFromChild(e.getTarget());
49042                 if (item) {
49043                         e.stopEvent();
49044                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
49045                         this.contextMenu.showAt(e.getXY());
49046             }
49047     },
49048
49049 /**
49050  *      Remove {@link Roo.data.Record}s at the specified indices.
49051  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
49052  */
49053     remove: function(selectedIndices) {
49054                 selectedIndices = [].concat(selectedIndices);
49055                 for (var i = 0; i < selectedIndices.length; i++) {
49056                         var rec = this.store.getAt(selectedIndices[i]);
49057                         this.store.remove(rec);
49058                 }
49059     },
49060
49061 /**
49062  *      Double click fires the event, but also, if this is draggable, and there is only one other
49063  *      related DropZone, it transfers the selected node.
49064  */
49065     onDblClick : function(e){
49066         var item = this.findItemFromChild(e.getTarget());
49067         if(item){
49068             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
49069                 return false;
49070             }
49071             if (this.dragGroup) {
49072                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
49073                     while (targets.indexOf(this.dropZone) > -1) {
49074                             targets.remove(this.dropZone);
49075                                 }
49076                     if (targets.length == 1) {
49077                                         this.dragZone.cachedTarget = null;
49078                         var el = Roo.get(targets[0].getEl());
49079                         var box = el.getBox(true);
49080                         targets[0].onNodeDrop(el.dom, {
49081                                 target: el.dom,
49082                                 xy: [box.x, box.y + box.height - 1]
49083                         }, null, this.getDragData(e));
49084                     }
49085                 }
49086         }
49087     },
49088     
49089     handleSelection: function(e) {
49090                 this.dragZone.cachedTarget = null;
49091         var item = this.findItemFromChild(e.getTarget());
49092         if (!item) {
49093                 this.clearSelections(true);
49094                 return;
49095         }
49096                 if (item && (this.multiSelect || this.singleSelect)){
49097                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
49098                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
49099                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
49100                                 this.unselect(item);
49101                         } else {
49102                                 this.select(item, this.multiSelect && e.ctrlKey);
49103                                 this.lastSelection = item;
49104                         }
49105                 }
49106     },
49107
49108     onItemClick : function(item, index, e){
49109                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
49110                         return false;
49111                 }
49112                 return true;
49113     },
49114
49115     unselect : function(nodeInfo, suppressEvent){
49116                 var node = this.getNode(nodeInfo);
49117                 if(node && this.isSelected(node)){
49118                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
49119                                 Roo.fly(node).removeClass(this.selectedClass);
49120                                 this.selections.remove(node);
49121                                 if(!suppressEvent){
49122                                         this.fireEvent("selectionchange", this, this.selections);
49123                                 }
49124                         }
49125                 }
49126     }
49127 });
49128 /*
49129  * Based on:
49130  * Ext JS Library 1.1.1
49131  * Copyright(c) 2006-2007, Ext JS, LLC.
49132  *
49133  * Originally Released Under LGPL - original licence link has changed is not relivant.
49134  *
49135  * Fork - LGPL
49136  * <script type="text/javascript">
49137  */
49138  
49139 /**
49140  * @class Roo.LayoutManager
49141  * @extends Roo.util.Observable
49142  * Base class for layout managers.
49143  */
49144 Roo.LayoutManager = function(container, config){
49145     Roo.LayoutManager.superclass.constructor.call(this);
49146     this.el = Roo.get(container);
49147     // ie scrollbar fix
49148     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
49149         document.body.scroll = "no";
49150     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
49151         this.el.position('relative');
49152     }
49153     this.id = this.el.id;
49154     this.el.addClass("x-layout-container");
49155     /** false to disable window resize monitoring @type Boolean */
49156     this.monitorWindowResize = true;
49157     this.regions = {};
49158     this.addEvents({
49159         /**
49160          * @event layout
49161          * Fires when a layout is performed. 
49162          * @param {Roo.LayoutManager} this
49163          */
49164         "layout" : true,
49165         /**
49166          * @event regionresized
49167          * Fires when the user resizes a region. 
49168          * @param {Roo.LayoutRegion} region The resized region
49169          * @param {Number} newSize The new size (width for east/west, height for north/south)
49170          */
49171         "regionresized" : true,
49172         /**
49173          * @event regioncollapsed
49174          * Fires when a region is collapsed. 
49175          * @param {Roo.LayoutRegion} region The collapsed region
49176          */
49177         "regioncollapsed" : true,
49178         /**
49179          * @event regionexpanded
49180          * Fires when a region is expanded.  
49181          * @param {Roo.LayoutRegion} region The expanded region
49182          */
49183         "regionexpanded" : true
49184     });
49185     this.updating = false;
49186     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49187 };
49188
49189 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
49190     /**
49191      * Returns true if this layout is currently being updated
49192      * @return {Boolean}
49193      */
49194     isUpdating : function(){
49195         return this.updating; 
49196     },
49197     
49198     /**
49199      * Suspend the LayoutManager from doing auto-layouts while
49200      * making multiple add or remove calls
49201      */
49202     beginUpdate : function(){
49203         this.updating = true;    
49204     },
49205     
49206     /**
49207      * Restore auto-layouts and optionally disable the manager from performing a layout
49208      * @param {Boolean} noLayout true to disable a layout update 
49209      */
49210     endUpdate : function(noLayout){
49211         this.updating = false;
49212         if(!noLayout){
49213             this.layout();
49214         }    
49215     },
49216     
49217     layout: function(){
49218         
49219     },
49220     
49221     onRegionResized : function(region, newSize){
49222         this.fireEvent("regionresized", region, newSize);
49223         this.layout();
49224     },
49225     
49226     onRegionCollapsed : function(region){
49227         this.fireEvent("regioncollapsed", region);
49228     },
49229     
49230     onRegionExpanded : function(region){
49231         this.fireEvent("regionexpanded", region);
49232     },
49233         
49234     /**
49235      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
49236      * performs box-model adjustments.
49237      * @return {Object} The size as an object {width: (the width), height: (the height)}
49238      */
49239     getViewSize : function(){
49240         var size;
49241         if(this.el.dom != document.body){
49242             size = this.el.getSize();
49243         }else{
49244             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
49245         }
49246         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
49247         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
49248         return size;
49249     },
49250     
49251     /**
49252      * Returns the Element this layout is bound to.
49253      * @return {Roo.Element}
49254      */
49255     getEl : function(){
49256         return this.el;
49257     },
49258     
49259     /**
49260      * Returns the specified region.
49261      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
49262      * @return {Roo.LayoutRegion}
49263      */
49264     getRegion : function(target){
49265         return this.regions[target.toLowerCase()];
49266     },
49267     
49268     onWindowResize : function(){
49269         if(this.monitorWindowResize){
49270             this.layout();
49271         }
49272     }
49273 });/*
49274  * Based on:
49275  * Ext JS Library 1.1.1
49276  * Copyright(c) 2006-2007, Ext JS, LLC.
49277  *
49278  * Originally Released Under LGPL - original licence link has changed is not relivant.
49279  *
49280  * Fork - LGPL
49281  * <script type="text/javascript">
49282  */
49283 /**
49284  * @class Roo.BorderLayout
49285  * @extends Roo.LayoutManager
49286  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
49287  * please see: <br><br>
49288  * <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>
49289  * <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>
49290  * Example:
49291  <pre><code>
49292  var layout = new Roo.BorderLayout(document.body, {
49293     north: {
49294         initialSize: 25,
49295         titlebar: false
49296     },
49297     west: {
49298         split:true,
49299         initialSize: 200,
49300         minSize: 175,
49301         maxSize: 400,
49302         titlebar: true,
49303         collapsible: true
49304     },
49305     east: {
49306         split:true,
49307         initialSize: 202,
49308         minSize: 175,
49309         maxSize: 400,
49310         titlebar: true,
49311         collapsible: true
49312     },
49313     south: {
49314         split:true,
49315         initialSize: 100,
49316         minSize: 100,
49317         maxSize: 200,
49318         titlebar: true,
49319         collapsible: true
49320     },
49321     center: {
49322         titlebar: true,
49323         autoScroll:true,
49324         resizeTabs: true,
49325         minTabWidth: 50,
49326         preferredTabWidth: 150
49327     }
49328 });
49329
49330 // shorthand
49331 var CP = Roo.ContentPanel;
49332
49333 layout.beginUpdate();
49334 layout.add("north", new CP("north", "North"));
49335 layout.add("south", new CP("south", {title: "South", closable: true}));
49336 layout.add("west", new CP("west", {title: "West"}));
49337 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
49338 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
49339 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
49340 layout.getRegion("center").showPanel("center1");
49341 layout.endUpdate();
49342 </code></pre>
49343
49344 <b>The container the layout is rendered into can be either the body element or any other element.
49345 If it is not the body element, the container needs to either be an absolute positioned element,
49346 or you will need to add "position:relative" to the css of the container.  You will also need to specify
49347 the container size if it is not the body element.</b>
49348
49349 * @constructor
49350 * Create a new BorderLayout
49351 * @param {String/HTMLElement/Element} container The container this layout is bound to
49352 * @param {Object} config Configuration options
49353  */
49354 Roo.BorderLayout = function(container, config){
49355     config = config || {};
49356     Roo.BorderLayout.superclass.constructor.call(this, container, config);
49357     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
49358     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
49359         var target = this.factory.validRegions[i];
49360         if(config[target]){
49361             this.addRegion(target, config[target]);
49362         }
49363     }
49364 };
49365
49366 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
49367     /**
49368      * Creates and adds a new region if it doesn't already exist.
49369      * @param {String} target The target region key (north, south, east, west or center).
49370      * @param {Object} config The regions config object
49371      * @return {BorderLayoutRegion} The new region
49372      */
49373     addRegion : function(target, config){
49374         if(!this.regions[target]){
49375             var r = this.factory.create(target, this, config);
49376             this.bindRegion(target, r);
49377         }
49378         return this.regions[target];
49379     },
49380
49381     // private (kinda)
49382     bindRegion : function(name, r){
49383         this.regions[name] = r;
49384         r.on("visibilitychange", this.layout, this);
49385         r.on("paneladded", this.layout, this);
49386         r.on("panelremoved", this.layout, this);
49387         r.on("invalidated", this.layout, this);
49388         r.on("resized", this.onRegionResized, this);
49389         r.on("collapsed", this.onRegionCollapsed, this);
49390         r.on("expanded", this.onRegionExpanded, this);
49391     },
49392
49393     /**
49394      * Performs a layout update.
49395      */
49396     layout : function(){
49397         if(this.updating) return;
49398         var size = this.getViewSize();
49399         var w = size.width;
49400         var h = size.height;
49401         var centerW = w;
49402         var centerH = h;
49403         var centerY = 0;
49404         var centerX = 0;
49405         //var x = 0, y = 0;
49406
49407         var rs = this.regions;
49408         var north = rs["north"];
49409         var south = rs["south"]; 
49410         var west = rs["west"];
49411         var east = rs["east"];
49412         var center = rs["center"];
49413         //if(this.hideOnLayout){ // not supported anymore
49414             //c.el.setStyle("display", "none");
49415         //}
49416         if(north && north.isVisible()){
49417             var b = north.getBox();
49418             var m = north.getMargins();
49419             b.width = w - (m.left+m.right);
49420             b.x = m.left;
49421             b.y = m.top;
49422             centerY = b.height + b.y + m.bottom;
49423             centerH -= centerY;
49424             north.updateBox(this.safeBox(b));
49425         }
49426         if(south && south.isVisible()){
49427             var b = south.getBox();
49428             var m = south.getMargins();
49429             b.width = w - (m.left+m.right);
49430             b.x = m.left;
49431             var totalHeight = (b.height + m.top + m.bottom);
49432             b.y = h - totalHeight + m.top;
49433             centerH -= totalHeight;
49434             south.updateBox(this.safeBox(b));
49435         }
49436         if(west && west.isVisible()){
49437             var b = west.getBox();
49438             var m = west.getMargins();
49439             b.height = centerH - (m.top+m.bottom);
49440             b.x = m.left;
49441             b.y = centerY + m.top;
49442             var totalWidth = (b.width + m.left + m.right);
49443             centerX += totalWidth;
49444             centerW -= totalWidth;
49445             west.updateBox(this.safeBox(b));
49446         }
49447         if(east && east.isVisible()){
49448             var b = east.getBox();
49449             var m = east.getMargins();
49450             b.height = centerH - (m.top+m.bottom);
49451             var totalWidth = (b.width + m.left + m.right);
49452             b.x = w - totalWidth + m.left;
49453             b.y = centerY + m.top;
49454             centerW -= totalWidth;
49455             east.updateBox(this.safeBox(b));
49456         }
49457         if(center){
49458             var m = center.getMargins();
49459             var centerBox = {
49460                 x: centerX + m.left,
49461                 y: centerY + m.top,
49462                 width: centerW - (m.left+m.right),
49463                 height: centerH - (m.top+m.bottom)
49464             };
49465             //if(this.hideOnLayout){
49466                 //center.el.setStyle("display", "block");
49467             //}
49468             center.updateBox(this.safeBox(centerBox));
49469         }
49470         this.el.repaint();
49471         this.fireEvent("layout", this);
49472     },
49473
49474     // private
49475     safeBox : function(box){
49476         box.width = Math.max(0, box.width);
49477         box.height = Math.max(0, box.height);
49478         return box;
49479     },
49480
49481     /**
49482      * Adds a ContentPanel (or subclass) to this layout.
49483      * @param {String} target The target region key (north, south, east, west or center).
49484      * @param {Roo.ContentPanel} panel The panel to add
49485      * @return {Roo.ContentPanel} The added panel
49486      */
49487     add : function(target, panel){
49488          
49489         target = target.toLowerCase();
49490         return this.regions[target].add(panel);
49491     },
49492
49493     /**
49494      * Remove a ContentPanel (or subclass) to this layout.
49495      * @param {String} target The target region key (north, south, east, west or center).
49496      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49497      * @return {Roo.ContentPanel} The removed panel
49498      */
49499     remove : function(target, panel){
49500         target = target.toLowerCase();
49501         return this.regions[target].remove(panel);
49502     },
49503
49504     /**
49505      * Searches all regions for a panel with the specified id
49506      * @param {String} panelId
49507      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49508      */
49509     findPanel : function(panelId){
49510         var rs = this.regions;
49511         for(var target in rs){
49512             if(typeof rs[target] != "function"){
49513                 var p = rs[target].getPanel(panelId);
49514                 if(p){
49515                     return p;
49516                 }
49517             }
49518         }
49519         return null;
49520     },
49521
49522     /**
49523      * Searches all regions for a panel with the specified id and activates (shows) it.
49524      * @param {String/ContentPanel} panelId The panels id or the panel itself
49525      * @return {Roo.ContentPanel} The shown panel or null
49526      */
49527     showPanel : function(panelId) {
49528       var rs = this.regions;
49529       for(var target in rs){
49530          var r = rs[target];
49531          if(typeof r != "function"){
49532             if(r.hasPanel(panelId)){
49533                return r.showPanel(panelId);
49534             }
49535          }
49536       }
49537       return null;
49538    },
49539
49540    /**
49541      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49542      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49543      */
49544     restoreState : function(provider){
49545         if(!provider){
49546             provider = Roo.state.Manager;
49547         }
49548         var sm = new Roo.LayoutStateManager();
49549         sm.init(this, provider);
49550     },
49551
49552     /**
49553      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49554      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49555      * a valid ContentPanel config object.  Example:
49556      * <pre><code>
49557 // Create the main layout
49558 var layout = new Roo.BorderLayout('main-ct', {
49559     west: {
49560         split:true,
49561         minSize: 175,
49562         titlebar: true
49563     },
49564     center: {
49565         title:'Components'
49566     }
49567 }, 'main-ct');
49568
49569 // Create and add multiple ContentPanels at once via configs
49570 layout.batchAdd({
49571    west: {
49572        id: 'source-files',
49573        autoCreate:true,
49574        title:'Ext Source Files',
49575        autoScroll:true,
49576        fitToFrame:true
49577    },
49578    center : {
49579        el: cview,
49580        autoScroll:true,
49581        fitToFrame:true,
49582        toolbar: tb,
49583        resizeEl:'cbody'
49584    }
49585 });
49586 </code></pre>
49587      * @param {Object} regions An object containing ContentPanel configs by region name
49588      */
49589     batchAdd : function(regions){
49590         this.beginUpdate();
49591         for(var rname in regions){
49592             var lr = this.regions[rname];
49593             if(lr){
49594                 this.addTypedPanels(lr, regions[rname]);
49595             }
49596         }
49597         this.endUpdate();
49598     },
49599
49600     // private
49601     addTypedPanels : function(lr, ps){
49602         if(typeof ps == 'string'){
49603             lr.add(new Roo.ContentPanel(ps));
49604         }
49605         else if(ps instanceof Array){
49606             for(var i =0, len = ps.length; i < len; i++){
49607                 this.addTypedPanels(lr, ps[i]);
49608             }
49609         }
49610         else if(!ps.events){ // raw config?
49611             var el = ps.el;
49612             delete ps.el; // prevent conflict
49613             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49614         }
49615         else {  // panel object assumed!
49616             lr.add(ps);
49617         }
49618     },
49619     /**
49620      * Adds a xtype elements to the layout.
49621      * <pre><code>
49622
49623 layout.addxtype({
49624        xtype : 'ContentPanel',
49625        region: 'west',
49626        items: [ .... ]
49627    }
49628 );
49629
49630 layout.addxtype({
49631         xtype : 'NestedLayoutPanel',
49632         region: 'west',
49633         layout: {
49634            center: { },
49635            west: { }   
49636         },
49637         items : [ ... list of content panels or nested layout panels.. ]
49638    }
49639 );
49640 </code></pre>
49641      * @param {Object} cfg Xtype definition of item to add.
49642      */
49643     addxtype : function(cfg)
49644     {
49645         // basically accepts a pannel...
49646         // can accept a layout region..!?!?
49647         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49648         
49649         if (!cfg.xtype.match(/Panel$/)) {
49650             return false;
49651         }
49652         var ret = false;
49653         
49654         if (typeof(cfg.region) == 'undefined') {
49655             Roo.log("Failed to add Panel, region was not set");
49656             Roo.log(cfg);
49657             return false;
49658         }
49659         var region = cfg.region;
49660         delete cfg.region;
49661         
49662           
49663         var xitems = [];
49664         if (cfg.items) {
49665             xitems = cfg.items;
49666             delete cfg.items;
49667         }
49668         var nb = false;
49669         
49670         switch(cfg.xtype) 
49671         {
49672             case 'ContentPanel':  // ContentPanel (el, cfg)
49673             case 'ScrollPanel':  // ContentPanel (el, cfg)
49674             case 'ViewPanel': 
49675                 if(cfg.autoCreate) {
49676                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49677                 } else {
49678                     var el = this.el.createChild();
49679                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49680                 }
49681                 
49682                 this.add(region, ret);
49683                 break;
49684             
49685             
49686             case 'TreePanel': // our new panel!
49687                 cfg.el = this.el.createChild();
49688                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49689                 this.add(region, ret);
49690                 break;
49691             
49692             case 'NestedLayoutPanel': 
49693                 // create a new Layout (which is  a Border Layout...
49694                 var el = this.el.createChild();
49695                 var clayout = cfg.layout;
49696                 delete cfg.layout;
49697                 clayout.items   = clayout.items  || [];
49698                 // replace this exitems with the clayout ones..
49699                 xitems = clayout.items;
49700                  
49701                 
49702                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49703                     cfg.background = false;
49704                 }
49705                 var layout = new Roo.BorderLayout(el, clayout);
49706                 
49707                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49708                 //console.log('adding nested layout panel '  + cfg.toSource());
49709                 this.add(region, ret);
49710                 nb = {}; /// find first...
49711                 break;
49712                 
49713             case 'GridPanel': 
49714             
49715                 // needs grid and region
49716                 
49717                 //var el = this.getRegion(region).el.createChild();
49718                 var el = this.el.createChild();
49719                 // create the grid first...
49720                 
49721                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49722                 delete cfg.grid;
49723                 if (region == 'center' && this.active ) {
49724                     cfg.background = false;
49725                 }
49726                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49727                 
49728                 this.add(region, ret);
49729                 if (cfg.background) {
49730                     ret.on('activate', function(gp) {
49731                         if (!gp.grid.rendered) {
49732                             gp.grid.render();
49733                         }
49734                     });
49735                 } else {
49736                     grid.render();
49737                 }
49738                 break;
49739            
49740            
49741            
49742                 
49743                 
49744                 
49745             default:
49746                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49747                     
49748                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49749                     this.add(region, ret);
49750                 } else {
49751                 
49752                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49753                     return null;
49754                 }
49755                 
49756              // GridPanel (grid, cfg)
49757             
49758         }
49759         this.beginUpdate();
49760         // add children..
49761         var region = '';
49762         var abn = {};
49763         Roo.each(xitems, function(i)  {
49764             region = nb && i.region ? i.region : false;
49765             
49766             var add = ret.addxtype(i);
49767            
49768             if (region) {
49769                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49770                 if (!i.background) {
49771                     abn[region] = nb[region] ;
49772                 }
49773             }
49774             
49775         });
49776         this.endUpdate();
49777
49778         // make the last non-background panel active..
49779         //if (nb) { Roo.log(abn); }
49780         if (nb) {
49781             
49782             for(var r in abn) {
49783                 region = this.getRegion(r);
49784                 if (region) {
49785                     // tried using nb[r], but it does not work..
49786                      
49787                     region.showPanel(abn[r]);
49788                    
49789                 }
49790             }
49791         }
49792         return ret;
49793         
49794     }
49795 });
49796
49797 /**
49798  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49799  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49800  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49801  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49802  * <pre><code>
49803 // shorthand
49804 var CP = Roo.ContentPanel;
49805
49806 var layout = Roo.BorderLayout.create({
49807     north: {
49808         initialSize: 25,
49809         titlebar: false,
49810         panels: [new CP("north", "North")]
49811     },
49812     west: {
49813         split:true,
49814         initialSize: 200,
49815         minSize: 175,
49816         maxSize: 400,
49817         titlebar: true,
49818         collapsible: true,
49819         panels: [new CP("west", {title: "West"})]
49820     },
49821     east: {
49822         split:true,
49823         initialSize: 202,
49824         minSize: 175,
49825         maxSize: 400,
49826         titlebar: true,
49827         collapsible: true,
49828         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49829     },
49830     south: {
49831         split:true,
49832         initialSize: 100,
49833         minSize: 100,
49834         maxSize: 200,
49835         titlebar: true,
49836         collapsible: true,
49837         panels: [new CP("south", {title: "South", closable: true})]
49838     },
49839     center: {
49840         titlebar: true,
49841         autoScroll:true,
49842         resizeTabs: true,
49843         minTabWidth: 50,
49844         preferredTabWidth: 150,
49845         panels: [
49846             new CP("center1", {title: "Close Me", closable: true}),
49847             new CP("center2", {title: "Center Panel", closable: false})
49848         ]
49849     }
49850 }, document.body);
49851
49852 layout.getRegion("center").showPanel("center1");
49853 </code></pre>
49854  * @param config
49855  * @param targetEl
49856  */
49857 Roo.BorderLayout.create = function(config, targetEl){
49858     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49859     layout.beginUpdate();
49860     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49861     for(var j = 0, jlen = regions.length; j < jlen; j++){
49862         var lr = regions[j];
49863         if(layout.regions[lr] && config[lr].panels){
49864             var r = layout.regions[lr];
49865             var ps = config[lr].panels;
49866             layout.addTypedPanels(r, ps);
49867         }
49868     }
49869     layout.endUpdate();
49870     return layout;
49871 };
49872
49873 // private
49874 Roo.BorderLayout.RegionFactory = {
49875     // private
49876     validRegions : ["north","south","east","west","center"],
49877
49878     // private
49879     create : function(target, mgr, config){
49880         target = target.toLowerCase();
49881         if(config.lightweight || config.basic){
49882             return new Roo.BasicLayoutRegion(mgr, config, target);
49883         }
49884         switch(target){
49885             case "north":
49886                 return new Roo.NorthLayoutRegion(mgr, config);
49887             case "south":
49888                 return new Roo.SouthLayoutRegion(mgr, config);
49889             case "east":
49890                 return new Roo.EastLayoutRegion(mgr, config);
49891             case "west":
49892                 return new Roo.WestLayoutRegion(mgr, config);
49893             case "center":
49894                 return new Roo.CenterLayoutRegion(mgr, config);
49895         }
49896         throw 'Layout region "'+target+'" not supported.';
49897     }
49898 };/*
49899  * Based on:
49900  * Ext JS Library 1.1.1
49901  * Copyright(c) 2006-2007, Ext JS, LLC.
49902  *
49903  * Originally Released Under LGPL - original licence link has changed is not relivant.
49904  *
49905  * Fork - LGPL
49906  * <script type="text/javascript">
49907  */
49908  
49909 /**
49910  * @class Roo.BasicLayoutRegion
49911  * @extends Roo.util.Observable
49912  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49913  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49914  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49915  */
49916 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49917     this.mgr = mgr;
49918     this.position  = pos;
49919     this.events = {
49920         /**
49921          * @scope Roo.BasicLayoutRegion
49922          */
49923         
49924         /**
49925          * @event beforeremove
49926          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49927          * @param {Roo.LayoutRegion} this
49928          * @param {Roo.ContentPanel} panel The panel
49929          * @param {Object} e The cancel event object
49930          */
49931         "beforeremove" : true,
49932         /**
49933          * @event invalidated
49934          * Fires when the layout for this region is changed.
49935          * @param {Roo.LayoutRegion} this
49936          */
49937         "invalidated" : true,
49938         /**
49939          * @event visibilitychange
49940          * Fires when this region is shown or hidden 
49941          * @param {Roo.LayoutRegion} this
49942          * @param {Boolean} visibility true or false
49943          */
49944         "visibilitychange" : true,
49945         /**
49946          * @event paneladded
49947          * Fires when a panel is added. 
49948          * @param {Roo.LayoutRegion} this
49949          * @param {Roo.ContentPanel} panel The panel
49950          */
49951         "paneladded" : true,
49952         /**
49953          * @event panelremoved
49954          * Fires when a panel is removed. 
49955          * @param {Roo.LayoutRegion} this
49956          * @param {Roo.ContentPanel} panel The panel
49957          */
49958         "panelremoved" : true,
49959         /**
49960          * @event collapsed
49961          * Fires when this region is collapsed.
49962          * @param {Roo.LayoutRegion} this
49963          */
49964         "collapsed" : true,
49965         /**
49966          * @event expanded
49967          * Fires when this region is expanded.
49968          * @param {Roo.LayoutRegion} this
49969          */
49970         "expanded" : true,
49971         /**
49972          * @event slideshow
49973          * Fires when this region is slid into view.
49974          * @param {Roo.LayoutRegion} this
49975          */
49976         "slideshow" : true,
49977         /**
49978          * @event slidehide
49979          * Fires when this region slides out of view. 
49980          * @param {Roo.LayoutRegion} this
49981          */
49982         "slidehide" : true,
49983         /**
49984          * @event panelactivated
49985          * Fires when a panel is activated. 
49986          * @param {Roo.LayoutRegion} this
49987          * @param {Roo.ContentPanel} panel The activated panel
49988          */
49989         "panelactivated" : true,
49990         /**
49991          * @event resized
49992          * Fires when the user resizes this region. 
49993          * @param {Roo.LayoutRegion} this
49994          * @param {Number} newSize The new size (width for east/west, height for north/south)
49995          */
49996         "resized" : true
49997     };
49998     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49999     this.panels = new Roo.util.MixedCollection();
50000     this.panels.getKey = this.getPanelId.createDelegate(this);
50001     this.box = null;
50002     this.activePanel = null;
50003     // ensure listeners are added...
50004     
50005     if (config.listeners || config.events) {
50006         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
50007             listeners : config.listeners || {},
50008             events : config.events || {}
50009         });
50010     }
50011     
50012     if(skipConfig !== true){
50013         this.applyConfig(config);
50014     }
50015 };
50016
50017 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
50018     getPanelId : function(p){
50019         return p.getId();
50020     },
50021     
50022     applyConfig : function(config){
50023         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50024         this.config = config;
50025         
50026     },
50027     
50028     /**
50029      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
50030      * the width, for horizontal (north, south) the height.
50031      * @param {Number} newSize The new width or height
50032      */
50033     resizeTo : function(newSize){
50034         var el = this.el ? this.el :
50035                  (this.activePanel ? this.activePanel.getEl() : null);
50036         if(el){
50037             switch(this.position){
50038                 case "east":
50039                 case "west":
50040                     el.setWidth(newSize);
50041                     this.fireEvent("resized", this, newSize);
50042                 break;
50043                 case "north":
50044                 case "south":
50045                     el.setHeight(newSize);
50046                     this.fireEvent("resized", this, newSize);
50047                 break;                
50048             }
50049         }
50050     },
50051     
50052     getBox : function(){
50053         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
50054     },
50055     
50056     getMargins : function(){
50057         return this.margins;
50058     },
50059     
50060     updateBox : function(box){
50061         this.box = box;
50062         var el = this.activePanel.getEl();
50063         el.dom.style.left = box.x + "px";
50064         el.dom.style.top = box.y + "px";
50065         this.activePanel.setSize(box.width, box.height);
50066     },
50067     
50068     /**
50069      * Returns the container element for this region.
50070      * @return {Roo.Element}
50071      */
50072     getEl : function(){
50073         return this.activePanel;
50074     },
50075     
50076     /**
50077      * Returns true if this region is currently visible.
50078      * @return {Boolean}
50079      */
50080     isVisible : function(){
50081         return this.activePanel ? true : false;
50082     },
50083     
50084     setActivePanel : function(panel){
50085         panel = this.getPanel(panel);
50086         if(this.activePanel && this.activePanel != panel){
50087             this.activePanel.setActiveState(false);
50088             this.activePanel.getEl().setLeftTop(-10000,-10000);
50089         }
50090         this.activePanel = panel;
50091         panel.setActiveState(true);
50092         if(this.box){
50093             panel.setSize(this.box.width, this.box.height);
50094         }
50095         this.fireEvent("panelactivated", this, panel);
50096         this.fireEvent("invalidated");
50097     },
50098     
50099     /**
50100      * Show the specified panel.
50101      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
50102      * @return {Roo.ContentPanel} The shown panel or null
50103      */
50104     showPanel : function(panel){
50105         if(panel = this.getPanel(panel)){
50106             this.setActivePanel(panel);
50107         }
50108         return panel;
50109     },
50110     
50111     /**
50112      * Get the active panel for this region.
50113      * @return {Roo.ContentPanel} The active panel or null
50114      */
50115     getActivePanel : function(){
50116         return this.activePanel;
50117     },
50118     
50119     /**
50120      * Add the passed ContentPanel(s)
50121      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50122      * @return {Roo.ContentPanel} The panel added (if only one was added)
50123      */
50124     add : function(panel){
50125         if(arguments.length > 1){
50126             for(var i = 0, len = arguments.length; i < len; i++) {
50127                 this.add(arguments[i]);
50128             }
50129             return null;
50130         }
50131         if(this.hasPanel(panel)){
50132             this.showPanel(panel);
50133             return panel;
50134         }
50135         var el = panel.getEl();
50136         if(el.dom.parentNode != this.mgr.el.dom){
50137             this.mgr.el.dom.appendChild(el.dom);
50138         }
50139         if(panel.setRegion){
50140             panel.setRegion(this);
50141         }
50142         this.panels.add(panel);
50143         el.setStyle("position", "absolute");
50144         if(!panel.background){
50145             this.setActivePanel(panel);
50146             if(this.config.initialSize && this.panels.getCount()==1){
50147                 this.resizeTo(this.config.initialSize);
50148             }
50149         }
50150         this.fireEvent("paneladded", this, panel);
50151         return panel;
50152     },
50153     
50154     /**
50155      * Returns true if the panel is in this region.
50156      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50157      * @return {Boolean}
50158      */
50159     hasPanel : function(panel){
50160         if(typeof panel == "object"){ // must be panel obj
50161             panel = panel.getId();
50162         }
50163         return this.getPanel(panel) ? true : false;
50164     },
50165     
50166     /**
50167      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50168      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50169      * @param {Boolean} preservePanel Overrides the config preservePanel option
50170      * @return {Roo.ContentPanel} The panel that was removed
50171      */
50172     remove : function(panel, preservePanel){
50173         panel = this.getPanel(panel);
50174         if(!panel){
50175             return null;
50176         }
50177         var e = {};
50178         this.fireEvent("beforeremove", this, panel, e);
50179         if(e.cancel === true){
50180             return null;
50181         }
50182         var panelId = panel.getId();
50183         this.panels.removeKey(panelId);
50184         return panel;
50185     },
50186     
50187     /**
50188      * Returns the panel specified or null if it's not in this region.
50189      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50190      * @return {Roo.ContentPanel}
50191      */
50192     getPanel : function(id){
50193         if(typeof id == "object"){ // must be panel obj
50194             return id;
50195         }
50196         return this.panels.get(id);
50197     },
50198     
50199     /**
50200      * Returns this regions position (north/south/east/west/center).
50201      * @return {String} 
50202      */
50203     getPosition: function(){
50204         return this.position;    
50205     }
50206 });/*
50207  * Based on:
50208  * Ext JS Library 1.1.1
50209  * Copyright(c) 2006-2007, Ext JS, LLC.
50210  *
50211  * Originally Released Under LGPL - original licence link has changed is not relivant.
50212  *
50213  * Fork - LGPL
50214  * <script type="text/javascript">
50215  */
50216  
50217 /**
50218  * @class Roo.LayoutRegion
50219  * @extends Roo.BasicLayoutRegion
50220  * This class represents a region in a layout manager.
50221  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
50222  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
50223  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
50224  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
50225  * @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})
50226  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
50227  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
50228  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
50229  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
50230  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
50231  * @cfg {String}    title           The title for the region (overrides panel titles)
50232  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
50233  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
50234  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
50235  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
50236  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
50237  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
50238  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
50239  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
50240  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
50241  * @cfg {Boolean}   showPin         True to show a pin button
50242  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
50243  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
50244  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
50245  * @cfg {Number}    width           For East/West panels
50246  * @cfg {Number}    height          For North/South panels
50247  * @cfg {Boolean}   split           To show the splitter
50248  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
50249  */
50250 Roo.LayoutRegion = function(mgr, config, pos){
50251     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
50252     var dh = Roo.DomHelper;
50253     /** This region's container element 
50254     * @type Roo.Element */
50255     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
50256     /** This region's title element 
50257     * @type Roo.Element */
50258
50259     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
50260         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
50261         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
50262     ]}, true);
50263     this.titleEl.enableDisplayMode();
50264     /** This region's title text element 
50265     * @type HTMLElement */
50266     this.titleTextEl = this.titleEl.dom.firstChild;
50267     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
50268     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
50269     this.closeBtn.enableDisplayMode();
50270     this.closeBtn.on("click", this.closeClicked, this);
50271     this.closeBtn.hide();
50272
50273     this.createBody(config);
50274     this.visible = true;
50275     this.collapsed = false;
50276
50277     if(config.hideWhenEmpty){
50278         this.hide();
50279         this.on("paneladded", this.validateVisibility, this);
50280         this.on("panelremoved", this.validateVisibility, this);
50281     }
50282     this.applyConfig(config);
50283 };
50284
50285 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
50286
50287     createBody : function(){
50288         /** This region's body element 
50289         * @type Roo.Element */
50290         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
50291     },
50292
50293     applyConfig : function(c){
50294         if(c.collapsible && this.position != "center" && !this.collapsedEl){
50295             var dh = Roo.DomHelper;
50296             if(c.titlebar !== false){
50297                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
50298                 this.collapseBtn.on("click", this.collapse, this);
50299                 this.collapseBtn.enableDisplayMode();
50300
50301                 if(c.showPin === true || this.showPin){
50302                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
50303                     this.stickBtn.enableDisplayMode();
50304                     this.stickBtn.on("click", this.expand, this);
50305                     this.stickBtn.hide();
50306                 }
50307             }
50308             /** This region's collapsed element
50309             * @type Roo.Element */
50310             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
50311                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
50312             ]}, true);
50313             if(c.floatable !== false){
50314                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
50315                this.collapsedEl.on("click", this.collapseClick, this);
50316             }
50317
50318             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
50319                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
50320                    id: "message", unselectable: "on", style:{"float":"left"}});
50321                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
50322              }
50323             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
50324             this.expandBtn.on("click", this.expand, this);
50325         }
50326         if(this.collapseBtn){
50327             this.collapseBtn.setVisible(c.collapsible == true);
50328         }
50329         this.cmargins = c.cmargins || this.cmargins ||
50330                          (this.position == "west" || this.position == "east" ?
50331                              {top: 0, left: 2, right:2, bottom: 0} :
50332                              {top: 2, left: 0, right:0, bottom: 2});
50333         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50334         this.bottomTabs = c.tabPosition != "top";
50335         this.autoScroll = c.autoScroll || false;
50336         if(this.autoScroll){
50337             this.bodyEl.setStyle("overflow", "auto");
50338         }else{
50339             this.bodyEl.setStyle("overflow", "hidden");
50340         }
50341         //if(c.titlebar !== false){
50342             if((!c.titlebar && !c.title) || c.titlebar === false){
50343                 this.titleEl.hide();
50344             }else{
50345                 this.titleEl.show();
50346                 if(c.title){
50347                     this.titleTextEl.innerHTML = c.title;
50348                 }
50349             }
50350         //}
50351         this.duration = c.duration || .30;
50352         this.slideDuration = c.slideDuration || .45;
50353         this.config = c;
50354         if(c.collapsed){
50355             this.collapse(true);
50356         }
50357         if(c.hidden){
50358             this.hide();
50359         }
50360     },
50361     /**
50362      * Returns true if this region is currently visible.
50363      * @return {Boolean}
50364      */
50365     isVisible : function(){
50366         return this.visible;
50367     },
50368
50369     /**
50370      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
50371      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
50372      */
50373     setCollapsedTitle : function(title){
50374         title = title || "&#160;";
50375         if(this.collapsedTitleTextEl){
50376             this.collapsedTitleTextEl.innerHTML = title;
50377         }
50378     },
50379
50380     getBox : function(){
50381         var b;
50382         if(!this.collapsed){
50383             b = this.el.getBox(false, true);
50384         }else{
50385             b = this.collapsedEl.getBox(false, true);
50386         }
50387         return b;
50388     },
50389
50390     getMargins : function(){
50391         return this.collapsed ? this.cmargins : this.margins;
50392     },
50393
50394     highlight : function(){
50395         this.el.addClass("x-layout-panel-dragover");
50396     },
50397
50398     unhighlight : function(){
50399         this.el.removeClass("x-layout-panel-dragover");
50400     },
50401
50402     updateBox : function(box){
50403         this.box = box;
50404         if(!this.collapsed){
50405             this.el.dom.style.left = box.x + "px";
50406             this.el.dom.style.top = box.y + "px";
50407             this.updateBody(box.width, box.height);
50408         }else{
50409             this.collapsedEl.dom.style.left = box.x + "px";
50410             this.collapsedEl.dom.style.top = box.y + "px";
50411             this.collapsedEl.setSize(box.width, box.height);
50412         }
50413         if(this.tabs){
50414             this.tabs.autoSizeTabs();
50415         }
50416     },
50417
50418     updateBody : function(w, h){
50419         if(w !== null){
50420             this.el.setWidth(w);
50421             w -= this.el.getBorderWidth("rl");
50422             if(this.config.adjustments){
50423                 w += this.config.adjustments[0];
50424             }
50425         }
50426         if(h !== null){
50427             this.el.setHeight(h);
50428             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50429             h -= this.el.getBorderWidth("tb");
50430             if(this.config.adjustments){
50431                 h += this.config.adjustments[1];
50432             }
50433             this.bodyEl.setHeight(h);
50434             if(this.tabs){
50435                 h = this.tabs.syncHeight(h);
50436             }
50437         }
50438         if(this.panelSize){
50439             w = w !== null ? w : this.panelSize.width;
50440             h = h !== null ? h : this.panelSize.height;
50441         }
50442         if(this.activePanel){
50443             var el = this.activePanel.getEl();
50444             w = w !== null ? w : el.getWidth();
50445             h = h !== null ? h : el.getHeight();
50446             this.panelSize = {width: w, height: h};
50447             this.activePanel.setSize(w, h);
50448         }
50449         if(Roo.isIE && this.tabs){
50450             this.tabs.el.repaint();
50451         }
50452     },
50453
50454     /**
50455      * Returns the container element for this region.
50456      * @return {Roo.Element}
50457      */
50458     getEl : function(){
50459         return this.el;
50460     },
50461
50462     /**
50463      * Hides this region.
50464      */
50465     hide : function(){
50466         if(!this.collapsed){
50467             this.el.dom.style.left = "-2000px";
50468             this.el.hide();
50469         }else{
50470             this.collapsedEl.dom.style.left = "-2000px";
50471             this.collapsedEl.hide();
50472         }
50473         this.visible = false;
50474         this.fireEvent("visibilitychange", this, false);
50475     },
50476
50477     /**
50478      * Shows this region if it was previously hidden.
50479      */
50480     show : function(){
50481         if(!this.collapsed){
50482             this.el.show();
50483         }else{
50484             this.collapsedEl.show();
50485         }
50486         this.visible = true;
50487         this.fireEvent("visibilitychange", this, true);
50488     },
50489
50490     closeClicked : function(){
50491         if(this.activePanel){
50492             this.remove(this.activePanel);
50493         }
50494     },
50495
50496     collapseClick : function(e){
50497         if(this.isSlid){
50498            e.stopPropagation();
50499            this.slideIn();
50500         }else{
50501            e.stopPropagation();
50502            this.slideOut();
50503         }
50504     },
50505
50506     /**
50507      * Collapses this region.
50508      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50509      */
50510     collapse : function(skipAnim){
50511         if(this.collapsed) return;
50512         this.collapsed = true;
50513         if(this.split){
50514             this.split.el.hide();
50515         }
50516         if(this.config.animate && skipAnim !== true){
50517             this.fireEvent("invalidated", this);
50518             this.animateCollapse();
50519         }else{
50520             this.el.setLocation(-20000,-20000);
50521             this.el.hide();
50522             this.collapsedEl.show();
50523             this.fireEvent("collapsed", this);
50524             this.fireEvent("invalidated", this);
50525         }
50526     },
50527
50528     animateCollapse : function(){
50529         // overridden
50530     },
50531
50532     /**
50533      * Expands this region if it was previously collapsed.
50534      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50535      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50536      */
50537     expand : function(e, skipAnim){
50538         if(e) e.stopPropagation();
50539         if(!this.collapsed || this.el.hasActiveFx()) return;
50540         if(this.isSlid){
50541             this.afterSlideIn();
50542             skipAnim = true;
50543         }
50544         this.collapsed = false;
50545         if(this.config.animate && skipAnim !== true){
50546             this.animateExpand();
50547         }else{
50548             this.el.show();
50549             if(this.split){
50550                 this.split.el.show();
50551             }
50552             this.collapsedEl.setLocation(-2000,-2000);
50553             this.collapsedEl.hide();
50554             this.fireEvent("invalidated", this);
50555             this.fireEvent("expanded", this);
50556         }
50557     },
50558
50559     animateExpand : function(){
50560         // overridden
50561     },
50562
50563     initTabs : function()
50564     {
50565         this.bodyEl.setStyle("overflow", "hidden");
50566         var ts = new Roo.TabPanel(
50567                 this.bodyEl.dom,
50568                 {
50569                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50570                     disableTooltips: this.config.disableTabTips,
50571                     toolbar : this.config.toolbar
50572                 }
50573         );
50574         if(this.config.hideTabs){
50575             ts.stripWrap.setDisplayed(false);
50576         }
50577         this.tabs = ts;
50578         ts.resizeTabs = this.config.resizeTabs === true;
50579         ts.minTabWidth = this.config.minTabWidth || 40;
50580         ts.maxTabWidth = this.config.maxTabWidth || 250;
50581         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50582         ts.monitorResize = false;
50583         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50584         ts.bodyEl.addClass('x-layout-tabs-body');
50585         this.panels.each(this.initPanelAsTab, this);
50586     },
50587
50588     initPanelAsTab : function(panel){
50589         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50590                     this.config.closeOnTab && panel.isClosable());
50591         if(panel.tabTip !== undefined){
50592             ti.setTooltip(panel.tabTip);
50593         }
50594         ti.on("activate", function(){
50595               this.setActivePanel(panel);
50596         }, this);
50597         if(this.config.closeOnTab){
50598             ti.on("beforeclose", function(t, e){
50599                 e.cancel = true;
50600                 this.remove(panel);
50601             }, this);
50602         }
50603         return ti;
50604     },
50605
50606     updatePanelTitle : function(panel, title){
50607         if(this.activePanel == panel){
50608             this.updateTitle(title);
50609         }
50610         if(this.tabs){
50611             var ti = this.tabs.getTab(panel.getEl().id);
50612             ti.setText(title);
50613             if(panel.tabTip !== undefined){
50614                 ti.setTooltip(panel.tabTip);
50615             }
50616         }
50617     },
50618
50619     updateTitle : function(title){
50620         if(this.titleTextEl && !this.config.title){
50621             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50622         }
50623     },
50624
50625     setActivePanel : function(panel){
50626         panel = this.getPanel(panel);
50627         if(this.activePanel && this.activePanel != panel){
50628             this.activePanel.setActiveState(false);
50629         }
50630         this.activePanel = panel;
50631         panel.setActiveState(true);
50632         if(this.panelSize){
50633             panel.setSize(this.panelSize.width, this.panelSize.height);
50634         }
50635         if(this.closeBtn){
50636             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50637         }
50638         this.updateTitle(panel.getTitle());
50639         if(this.tabs){
50640             this.fireEvent("invalidated", this);
50641         }
50642         this.fireEvent("panelactivated", this, panel);
50643     },
50644
50645     /**
50646      * Shows the specified panel.
50647      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50648      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50649      */
50650     showPanel : function(panel)
50651     {
50652         panel = this.getPanel(panel);
50653         if(panel){
50654             if(this.tabs){
50655                 var tab = this.tabs.getTab(panel.getEl().id);
50656                 if(tab.isHidden()){
50657                     this.tabs.unhideTab(tab.id);
50658                 }
50659                 tab.activate();
50660             }else{
50661                 this.setActivePanel(panel);
50662             }
50663         }
50664         return panel;
50665     },
50666
50667     /**
50668      * Get the active panel for this region.
50669      * @return {Roo.ContentPanel} The active panel or null
50670      */
50671     getActivePanel : function(){
50672         return this.activePanel;
50673     },
50674
50675     validateVisibility : function(){
50676         if(this.panels.getCount() < 1){
50677             this.updateTitle("&#160;");
50678             this.closeBtn.hide();
50679             this.hide();
50680         }else{
50681             if(!this.isVisible()){
50682                 this.show();
50683             }
50684         }
50685     },
50686
50687     /**
50688      * Adds the passed ContentPanel(s) to this region.
50689      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50690      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50691      */
50692     add : function(panel){
50693         if(arguments.length > 1){
50694             for(var i = 0, len = arguments.length; i < len; i++) {
50695                 this.add(arguments[i]);
50696             }
50697             return null;
50698         }
50699         if(this.hasPanel(panel)){
50700             this.showPanel(panel);
50701             return panel;
50702         }
50703         panel.setRegion(this);
50704         this.panels.add(panel);
50705         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50706             this.bodyEl.dom.appendChild(panel.getEl().dom);
50707             if(panel.background !== true){
50708                 this.setActivePanel(panel);
50709             }
50710             this.fireEvent("paneladded", this, panel);
50711             return panel;
50712         }
50713         if(!this.tabs){
50714             this.initTabs();
50715         }else{
50716             this.initPanelAsTab(panel);
50717         }
50718         if(panel.background !== true){
50719             this.tabs.activate(panel.getEl().id);
50720         }
50721         this.fireEvent("paneladded", this, panel);
50722         return panel;
50723     },
50724
50725     /**
50726      * Hides the tab for the specified panel.
50727      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50728      */
50729     hidePanel : function(panel){
50730         if(this.tabs && (panel = this.getPanel(panel))){
50731             this.tabs.hideTab(panel.getEl().id);
50732         }
50733     },
50734
50735     /**
50736      * Unhides the tab for a previously hidden panel.
50737      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50738      */
50739     unhidePanel : function(panel){
50740         if(this.tabs && (panel = this.getPanel(panel))){
50741             this.tabs.unhideTab(panel.getEl().id);
50742         }
50743     },
50744
50745     clearPanels : function(){
50746         while(this.panels.getCount() > 0){
50747              this.remove(this.panels.first());
50748         }
50749     },
50750
50751     /**
50752      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50753      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50754      * @param {Boolean} preservePanel Overrides the config preservePanel option
50755      * @return {Roo.ContentPanel} The panel that was removed
50756      */
50757     remove : function(panel, preservePanel){
50758         panel = this.getPanel(panel);
50759         if(!panel){
50760             return null;
50761         }
50762         var e = {};
50763         this.fireEvent("beforeremove", this, panel, e);
50764         if(e.cancel === true){
50765             return null;
50766         }
50767         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50768         var panelId = panel.getId();
50769         this.panels.removeKey(panelId);
50770         if(preservePanel){
50771             document.body.appendChild(panel.getEl().dom);
50772         }
50773         if(this.tabs){
50774             this.tabs.removeTab(panel.getEl().id);
50775         }else if (!preservePanel){
50776             this.bodyEl.dom.removeChild(panel.getEl().dom);
50777         }
50778         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50779             var p = this.panels.first();
50780             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50781             tempEl.appendChild(p.getEl().dom);
50782             this.bodyEl.update("");
50783             this.bodyEl.dom.appendChild(p.getEl().dom);
50784             tempEl = null;
50785             this.updateTitle(p.getTitle());
50786             this.tabs = null;
50787             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50788             this.setActivePanel(p);
50789         }
50790         panel.setRegion(null);
50791         if(this.activePanel == panel){
50792             this.activePanel = null;
50793         }
50794         if(this.config.autoDestroy !== false && preservePanel !== true){
50795             try{panel.destroy();}catch(e){}
50796         }
50797         this.fireEvent("panelremoved", this, panel);
50798         return panel;
50799     },
50800
50801     /**
50802      * Returns the TabPanel component used by this region
50803      * @return {Roo.TabPanel}
50804      */
50805     getTabs : function(){
50806         return this.tabs;
50807     },
50808
50809     createTool : function(parentEl, className){
50810         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50811             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50812         btn.addClassOnOver("x-layout-tools-button-over");
50813         return btn;
50814     }
50815 });/*
50816  * Based on:
50817  * Ext JS Library 1.1.1
50818  * Copyright(c) 2006-2007, Ext JS, LLC.
50819  *
50820  * Originally Released Under LGPL - original licence link has changed is not relivant.
50821  *
50822  * Fork - LGPL
50823  * <script type="text/javascript">
50824  */
50825  
50826
50827
50828 /**
50829  * @class Roo.SplitLayoutRegion
50830  * @extends Roo.LayoutRegion
50831  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50832  */
50833 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50834     this.cursor = cursor;
50835     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50836 };
50837
50838 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50839     splitTip : "Drag to resize.",
50840     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50841     useSplitTips : false,
50842
50843     applyConfig : function(config){
50844         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50845         if(config.split){
50846             if(!this.split){
50847                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50848                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50849                 /** The SplitBar for this region 
50850                 * @type Roo.SplitBar */
50851                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50852                 this.split.on("moved", this.onSplitMove, this);
50853                 this.split.useShim = config.useShim === true;
50854                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50855                 if(this.useSplitTips){
50856                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50857                 }
50858                 if(config.collapsible){
50859                     this.split.el.on("dblclick", this.collapse,  this);
50860                 }
50861             }
50862             if(typeof config.minSize != "undefined"){
50863                 this.split.minSize = config.minSize;
50864             }
50865             if(typeof config.maxSize != "undefined"){
50866                 this.split.maxSize = config.maxSize;
50867             }
50868             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50869                 this.hideSplitter();
50870             }
50871         }
50872     },
50873
50874     getHMaxSize : function(){
50875          var cmax = this.config.maxSize || 10000;
50876          var center = this.mgr.getRegion("center");
50877          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50878     },
50879
50880     getVMaxSize : function(){
50881          var cmax = this.config.maxSize || 10000;
50882          var center = this.mgr.getRegion("center");
50883          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50884     },
50885
50886     onSplitMove : function(split, newSize){
50887         this.fireEvent("resized", this, newSize);
50888     },
50889     
50890     /** 
50891      * Returns the {@link Roo.SplitBar} for this region.
50892      * @return {Roo.SplitBar}
50893      */
50894     getSplitBar : function(){
50895         return this.split;
50896     },
50897     
50898     hide : function(){
50899         this.hideSplitter();
50900         Roo.SplitLayoutRegion.superclass.hide.call(this);
50901     },
50902
50903     hideSplitter : function(){
50904         if(this.split){
50905             this.split.el.setLocation(-2000,-2000);
50906             this.split.el.hide();
50907         }
50908     },
50909
50910     show : function(){
50911         if(this.split){
50912             this.split.el.show();
50913         }
50914         Roo.SplitLayoutRegion.superclass.show.call(this);
50915     },
50916     
50917     beforeSlide: function(){
50918         if(Roo.isGecko){// firefox overflow auto bug workaround
50919             this.bodyEl.clip();
50920             if(this.tabs) this.tabs.bodyEl.clip();
50921             if(this.activePanel){
50922                 this.activePanel.getEl().clip();
50923                 
50924                 if(this.activePanel.beforeSlide){
50925                     this.activePanel.beforeSlide();
50926                 }
50927             }
50928         }
50929     },
50930     
50931     afterSlide : function(){
50932         if(Roo.isGecko){// firefox overflow auto bug workaround
50933             this.bodyEl.unclip();
50934             if(this.tabs) this.tabs.bodyEl.unclip();
50935             if(this.activePanel){
50936                 this.activePanel.getEl().unclip();
50937                 if(this.activePanel.afterSlide){
50938                     this.activePanel.afterSlide();
50939                 }
50940             }
50941         }
50942     },
50943
50944     initAutoHide : function(){
50945         if(this.autoHide !== false){
50946             if(!this.autoHideHd){
50947                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50948                 this.autoHideHd = {
50949                     "mouseout": function(e){
50950                         if(!e.within(this.el, true)){
50951                             st.delay(500);
50952                         }
50953                     },
50954                     "mouseover" : function(e){
50955                         st.cancel();
50956                     },
50957                     scope : this
50958                 };
50959             }
50960             this.el.on(this.autoHideHd);
50961         }
50962     },
50963
50964     clearAutoHide : function(){
50965         if(this.autoHide !== false){
50966             this.el.un("mouseout", this.autoHideHd.mouseout);
50967             this.el.un("mouseover", this.autoHideHd.mouseover);
50968         }
50969     },
50970
50971     clearMonitor : function(){
50972         Roo.get(document).un("click", this.slideInIf, this);
50973     },
50974
50975     // these names are backwards but not changed for compat
50976     slideOut : function(){
50977         if(this.isSlid || this.el.hasActiveFx()){
50978             return;
50979         }
50980         this.isSlid = true;
50981         if(this.collapseBtn){
50982             this.collapseBtn.hide();
50983         }
50984         this.closeBtnState = this.closeBtn.getStyle('display');
50985         this.closeBtn.hide();
50986         if(this.stickBtn){
50987             this.stickBtn.show();
50988         }
50989         this.el.show();
50990         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50991         this.beforeSlide();
50992         this.el.setStyle("z-index", 10001);
50993         this.el.slideIn(this.getSlideAnchor(), {
50994             callback: function(){
50995                 this.afterSlide();
50996                 this.initAutoHide();
50997                 Roo.get(document).on("click", this.slideInIf, this);
50998                 this.fireEvent("slideshow", this);
50999             },
51000             scope: this,
51001             block: true
51002         });
51003     },
51004
51005     afterSlideIn : function(){
51006         this.clearAutoHide();
51007         this.isSlid = false;
51008         this.clearMonitor();
51009         this.el.setStyle("z-index", "");
51010         if(this.collapseBtn){
51011             this.collapseBtn.show();
51012         }
51013         this.closeBtn.setStyle('display', this.closeBtnState);
51014         if(this.stickBtn){
51015             this.stickBtn.hide();
51016         }
51017         this.fireEvent("slidehide", this);
51018     },
51019
51020     slideIn : function(cb){
51021         if(!this.isSlid || this.el.hasActiveFx()){
51022             Roo.callback(cb);
51023             return;
51024         }
51025         this.isSlid = false;
51026         this.beforeSlide();
51027         this.el.slideOut(this.getSlideAnchor(), {
51028             callback: function(){
51029                 this.el.setLeftTop(-10000, -10000);
51030                 this.afterSlide();
51031                 this.afterSlideIn();
51032                 Roo.callback(cb);
51033             },
51034             scope: this,
51035             block: true
51036         });
51037     },
51038     
51039     slideInIf : function(e){
51040         if(!e.within(this.el)){
51041             this.slideIn();
51042         }
51043     },
51044
51045     animateCollapse : function(){
51046         this.beforeSlide();
51047         this.el.setStyle("z-index", 20000);
51048         var anchor = this.getSlideAnchor();
51049         this.el.slideOut(anchor, {
51050             callback : function(){
51051                 this.el.setStyle("z-index", "");
51052                 this.collapsedEl.slideIn(anchor, {duration:.3});
51053                 this.afterSlide();
51054                 this.el.setLocation(-10000,-10000);
51055                 this.el.hide();
51056                 this.fireEvent("collapsed", this);
51057             },
51058             scope: this,
51059             block: true
51060         });
51061     },
51062
51063     animateExpand : function(){
51064         this.beforeSlide();
51065         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
51066         this.el.setStyle("z-index", 20000);
51067         this.collapsedEl.hide({
51068             duration:.1
51069         });
51070         this.el.slideIn(this.getSlideAnchor(), {
51071             callback : function(){
51072                 this.el.setStyle("z-index", "");
51073                 this.afterSlide();
51074                 if(this.split){
51075                     this.split.el.show();
51076                 }
51077                 this.fireEvent("invalidated", this);
51078                 this.fireEvent("expanded", this);
51079             },
51080             scope: this,
51081             block: true
51082         });
51083     },
51084
51085     anchors : {
51086         "west" : "left",
51087         "east" : "right",
51088         "north" : "top",
51089         "south" : "bottom"
51090     },
51091
51092     sanchors : {
51093         "west" : "l",
51094         "east" : "r",
51095         "north" : "t",
51096         "south" : "b"
51097     },
51098
51099     canchors : {
51100         "west" : "tl-tr",
51101         "east" : "tr-tl",
51102         "north" : "tl-bl",
51103         "south" : "bl-tl"
51104     },
51105
51106     getAnchor : function(){
51107         return this.anchors[this.position];
51108     },
51109
51110     getCollapseAnchor : function(){
51111         return this.canchors[this.position];
51112     },
51113
51114     getSlideAnchor : function(){
51115         return this.sanchors[this.position];
51116     },
51117
51118     getAlignAdj : function(){
51119         var cm = this.cmargins;
51120         switch(this.position){
51121             case "west":
51122                 return [0, 0];
51123             break;
51124             case "east":
51125                 return [0, 0];
51126             break;
51127             case "north":
51128                 return [0, 0];
51129             break;
51130             case "south":
51131                 return [0, 0];
51132             break;
51133         }
51134     },
51135
51136     getExpandAdj : function(){
51137         var c = this.collapsedEl, cm = this.cmargins;
51138         switch(this.position){
51139             case "west":
51140                 return [-(cm.right+c.getWidth()+cm.left), 0];
51141             break;
51142             case "east":
51143                 return [cm.right+c.getWidth()+cm.left, 0];
51144             break;
51145             case "north":
51146                 return [0, -(cm.top+cm.bottom+c.getHeight())];
51147             break;
51148             case "south":
51149                 return [0, cm.top+cm.bottom+c.getHeight()];
51150             break;
51151         }
51152     }
51153 });/*
51154  * Based on:
51155  * Ext JS Library 1.1.1
51156  * Copyright(c) 2006-2007, Ext JS, LLC.
51157  *
51158  * Originally Released Under LGPL - original licence link has changed is not relivant.
51159  *
51160  * Fork - LGPL
51161  * <script type="text/javascript">
51162  */
51163 /*
51164  * These classes are private internal classes
51165  */
51166 Roo.CenterLayoutRegion = function(mgr, config){
51167     Roo.LayoutRegion.call(this, mgr, config, "center");
51168     this.visible = true;
51169     this.minWidth = config.minWidth || 20;
51170     this.minHeight = config.minHeight || 20;
51171 };
51172
51173 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
51174     hide : function(){
51175         // center panel can't be hidden
51176     },
51177     
51178     show : function(){
51179         // center panel can't be hidden
51180     },
51181     
51182     getMinWidth: function(){
51183         return this.minWidth;
51184     },
51185     
51186     getMinHeight: function(){
51187         return this.minHeight;
51188     }
51189 });
51190
51191
51192 Roo.NorthLayoutRegion = function(mgr, config){
51193     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
51194     if(this.split){
51195         this.split.placement = Roo.SplitBar.TOP;
51196         this.split.orientation = Roo.SplitBar.VERTICAL;
51197         this.split.el.addClass("x-layout-split-v");
51198     }
51199     var size = config.initialSize || config.height;
51200     if(typeof size != "undefined"){
51201         this.el.setHeight(size);
51202     }
51203 };
51204 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
51205     orientation: Roo.SplitBar.VERTICAL,
51206     getBox : function(){
51207         if(this.collapsed){
51208             return this.collapsedEl.getBox();
51209         }
51210         var box = this.el.getBox();
51211         if(this.split){
51212             box.height += this.split.el.getHeight();
51213         }
51214         return box;
51215     },
51216     
51217     updateBox : function(box){
51218         if(this.split && !this.collapsed){
51219             box.height -= this.split.el.getHeight();
51220             this.split.el.setLeft(box.x);
51221             this.split.el.setTop(box.y+box.height);
51222             this.split.el.setWidth(box.width);
51223         }
51224         if(this.collapsed){
51225             this.updateBody(box.width, null);
51226         }
51227         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51228     }
51229 });
51230
51231 Roo.SouthLayoutRegion = function(mgr, config){
51232     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
51233     if(this.split){
51234         this.split.placement = Roo.SplitBar.BOTTOM;
51235         this.split.orientation = Roo.SplitBar.VERTICAL;
51236         this.split.el.addClass("x-layout-split-v");
51237     }
51238     var size = config.initialSize || config.height;
51239     if(typeof size != "undefined"){
51240         this.el.setHeight(size);
51241     }
51242 };
51243 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
51244     orientation: Roo.SplitBar.VERTICAL,
51245     getBox : function(){
51246         if(this.collapsed){
51247             return this.collapsedEl.getBox();
51248         }
51249         var box = this.el.getBox();
51250         if(this.split){
51251             var sh = this.split.el.getHeight();
51252             box.height += sh;
51253             box.y -= sh;
51254         }
51255         return box;
51256     },
51257     
51258     updateBox : function(box){
51259         if(this.split && !this.collapsed){
51260             var sh = this.split.el.getHeight();
51261             box.height -= sh;
51262             box.y += sh;
51263             this.split.el.setLeft(box.x);
51264             this.split.el.setTop(box.y-sh);
51265             this.split.el.setWidth(box.width);
51266         }
51267         if(this.collapsed){
51268             this.updateBody(box.width, null);
51269         }
51270         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51271     }
51272 });
51273
51274 Roo.EastLayoutRegion = function(mgr, config){
51275     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
51276     if(this.split){
51277         this.split.placement = Roo.SplitBar.RIGHT;
51278         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51279         this.split.el.addClass("x-layout-split-h");
51280     }
51281     var size = config.initialSize || config.width;
51282     if(typeof size != "undefined"){
51283         this.el.setWidth(size);
51284     }
51285 };
51286 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
51287     orientation: Roo.SplitBar.HORIZONTAL,
51288     getBox : function(){
51289         if(this.collapsed){
51290             return this.collapsedEl.getBox();
51291         }
51292         var box = this.el.getBox();
51293         if(this.split){
51294             var sw = this.split.el.getWidth();
51295             box.width += sw;
51296             box.x -= sw;
51297         }
51298         return box;
51299     },
51300
51301     updateBox : function(box){
51302         if(this.split && !this.collapsed){
51303             var sw = this.split.el.getWidth();
51304             box.width -= sw;
51305             this.split.el.setLeft(box.x);
51306             this.split.el.setTop(box.y);
51307             this.split.el.setHeight(box.height);
51308             box.x += sw;
51309         }
51310         if(this.collapsed){
51311             this.updateBody(null, box.height);
51312         }
51313         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51314     }
51315 });
51316
51317 Roo.WestLayoutRegion = function(mgr, config){
51318     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
51319     if(this.split){
51320         this.split.placement = Roo.SplitBar.LEFT;
51321         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51322         this.split.el.addClass("x-layout-split-h");
51323     }
51324     var size = config.initialSize || config.width;
51325     if(typeof size != "undefined"){
51326         this.el.setWidth(size);
51327     }
51328 };
51329 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
51330     orientation: Roo.SplitBar.HORIZONTAL,
51331     getBox : function(){
51332         if(this.collapsed){
51333             return this.collapsedEl.getBox();
51334         }
51335         var box = this.el.getBox();
51336         if(this.split){
51337             box.width += this.split.el.getWidth();
51338         }
51339         return box;
51340     },
51341     
51342     updateBox : function(box){
51343         if(this.split && !this.collapsed){
51344             var sw = this.split.el.getWidth();
51345             box.width -= sw;
51346             this.split.el.setLeft(box.x+box.width);
51347             this.split.el.setTop(box.y);
51348             this.split.el.setHeight(box.height);
51349         }
51350         if(this.collapsed){
51351             this.updateBody(null, box.height);
51352         }
51353         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51354     }
51355 });
51356 /*
51357  * Based on:
51358  * Ext JS Library 1.1.1
51359  * Copyright(c) 2006-2007, Ext JS, LLC.
51360  *
51361  * Originally Released Under LGPL - original licence link has changed is not relivant.
51362  *
51363  * Fork - LGPL
51364  * <script type="text/javascript">
51365  */
51366  
51367  
51368 /*
51369  * Private internal class for reading and applying state
51370  */
51371 Roo.LayoutStateManager = function(layout){
51372      // default empty state
51373      this.state = {
51374         north: {},
51375         south: {},
51376         east: {},
51377         west: {}       
51378     };
51379 };
51380
51381 Roo.LayoutStateManager.prototype = {
51382     init : function(layout, provider){
51383         this.provider = provider;
51384         var state = provider.get(layout.id+"-layout-state");
51385         if(state){
51386             var wasUpdating = layout.isUpdating();
51387             if(!wasUpdating){
51388                 layout.beginUpdate();
51389             }
51390             for(var key in state){
51391                 if(typeof state[key] != "function"){
51392                     var rstate = state[key];
51393                     var r = layout.getRegion(key);
51394                     if(r && rstate){
51395                         if(rstate.size){
51396                             r.resizeTo(rstate.size);
51397                         }
51398                         if(rstate.collapsed == true){
51399                             r.collapse(true);
51400                         }else{
51401                             r.expand(null, true);
51402                         }
51403                     }
51404                 }
51405             }
51406             if(!wasUpdating){
51407                 layout.endUpdate();
51408             }
51409             this.state = state; 
51410         }
51411         this.layout = layout;
51412         layout.on("regionresized", this.onRegionResized, this);
51413         layout.on("regioncollapsed", this.onRegionCollapsed, this);
51414         layout.on("regionexpanded", this.onRegionExpanded, this);
51415     },
51416     
51417     storeState : function(){
51418         this.provider.set(this.layout.id+"-layout-state", this.state);
51419     },
51420     
51421     onRegionResized : function(region, newSize){
51422         this.state[region.getPosition()].size = newSize;
51423         this.storeState();
51424     },
51425     
51426     onRegionCollapsed : function(region){
51427         this.state[region.getPosition()].collapsed = true;
51428         this.storeState();
51429     },
51430     
51431     onRegionExpanded : function(region){
51432         this.state[region.getPosition()].collapsed = false;
51433         this.storeState();
51434     }
51435 };/*
51436  * Based on:
51437  * Ext JS Library 1.1.1
51438  * Copyright(c) 2006-2007, Ext JS, LLC.
51439  *
51440  * Originally Released Under LGPL - original licence link has changed is not relivant.
51441  *
51442  * Fork - LGPL
51443  * <script type="text/javascript">
51444  */
51445 /**
51446  * @class Roo.ContentPanel
51447  * @extends Roo.util.Observable
51448  * A basic ContentPanel element.
51449  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51450  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51451  * @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
51452  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51453  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51454  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51455  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51456  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51457  * @cfg {String} title          The title for this panel
51458  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51459  * @cfg {String} url            Calls {@link #setUrl} with this value
51460  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51461  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51462  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51463  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51464
51465  * @constructor
51466  * Create a new ContentPanel.
51467  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51468  * @param {String/Object} config A string to set only the title or a config object
51469  * @param {String} content (optional) Set the HTML content for this panel
51470  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51471  */
51472 Roo.ContentPanel = function(el, config, content){
51473     
51474      
51475     /*
51476     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51477         config = el;
51478         el = Roo.id();
51479     }
51480     if (config && config.parentLayout) { 
51481         el = config.parentLayout.el.createChild(); 
51482     }
51483     */
51484     if(el.autoCreate){ // xtype is available if this is called from factory
51485         config = el;
51486         el = Roo.id();
51487     }
51488     this.el = Roo.get(el);
51489     if(!this.el && config && config.autoCreate){
51490         if(typeof config.autoCreate == "object"){
51491             if(!config.autoCreate.id){
51492                 config.autoCreate.id = config.id||el;
51493             }
51494             this.el = Roo.DomHelper.append(document.body,
51495                         config.autoCreate, true);
51496         }else{
51497             this.el = Roo.DomHelper.append(document.body,
51498                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51499         }
51500     }
51501     this.closable = false;
51502     this.loaded = false;
51503     this.active = false;
51504     if(typeof config == "string"){
51505         this.title = config;
51506     }else{
51507         Roo.apply(this, config);
51508     }
51509     
51510     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51511         this.wrapEl = this.el.wrap();
51512         this.toolbar.container = this.el.insertSibling(false, 'before');
51513         this.toolbar = new Roo.Toolbar(this.toolbar);
51514     }
51515     
51516     // xtype created footer. - not sure if will work as we normally have to render first..
51517     if (this.footer && !this.footer.el && this.footer.xtype) {
51518         if (!this.wrapEl) {
51519             this.wrapEl = this.el.wrap();
51520         }
51521     
51522         this.footer.container = this.wrapEl.createChild();
51523          
51524         this.footer = Roo.factory(this.footer, Roo);
51525         
51526     }
51527     
51528     if(this.resizeEl){
51529         this.resizeEl = Roo.get(this.resizeEl, true);
51530     }else{
51531         this.resizeEl = this.el;
51532     }
51533     // handle view.xtype
51534     
51535  
51536     
51537     
51538     this.addEvents({
51539         /**
51540          * @event activate
51541          * Fires when this panel is activated. 
51542          * @param {Roo.ContentPanel} this
51543          */
51544         "activate" : true,
51545         /**
51546          * @event deactivate
51547          * Fires when this panel is activated. 
51548          * @param {Roo.ContentPanel} this
51549          */
51550         "deactivate" : true,
51551
51552         /**
51553          * @event resize
51554          * Fires when this panel is resized if fitToFrame is true.
51555          * @param {Roo.ContentPanel} this
51556          * @param {Number} width The width after any component adjustments
51557          * @param {Number} height The height after any component adjustments
51558          */
51559         "resize" : true,
51560         
51561          /**
51562          * @event render
51563          * Fires when this tab is created
51564          * @param {Roo.ContentPanel} this
51565          */
51566         "render" : true
51567         
51568         
51569         
51570     });
51571     
51572
51573     
51574     
51575     if(this.autoScroll){
51576         this.resizeEl.setStyle("overflow", "auto");
51577     } else {
51578         // fix randome scrolling
51579         this.el.on('scroll', function() {
51580             Roo.log('fix random scolling');
51581             this.scrollTo('top',0); 
51582         });
51583     }
51584     content = content || this.content;
51585     if(content){
51586         this.setContent(content);
51587     }
51588     if(config && config.url){
51589         this.setUrl(this.url, this.params, this.loadOnce);
51590     }
51591     
51592     
51593     
51594     Roo.ContentPanel.superclass.constructor.call(this);
51595     
51596     if (this.view && typeof(this.view.xtype) != 'undefined') {
51597         this.view.el = this.el.appendChild(document.createElement("div"));
51598         this.view = Roo.factory(this.view); 
51599         this.view.render  &&  this.view.render(false, '');  
51600     }
51601     
51602     
51603     this.fireEvent('render', this);
51604 };
51605
51606 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51607     tabTip:'',
51608     setRegion : function(region){
51609         this.region = region;
51610         if(region){
51611            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51612         }else{
51613            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51614         } 
51615     },
51616     
51617     /**
51618      * Returns the toolbar for this Panel if one was configured. 
51619      * @return {Roo.Toolbar} 
51620      */
51621     getToolbar : function(){
51622         return this.toolbar;
51623     },
51624     
51625     setActiveState : function(active){
51626         this.active = active;
51627         if(!active){
51628             this.fireEvent("deactivate", this);
51629         }else{
51630             this.fireEvent("activate", this);
51631         }
51632     },
51633     /**
51634      * Updates this panel's element
51635      * @param {String} content The new content
51636      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51637     */
51638     setContent : function(content, loadScripts){
51639         this.el.update(content, loadScripts);
51640     },
51641
51642     ignoreResize : function(w, h){
51643         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51644             return true;
51645         }else{
51646             this.lastSize = {width: w, height: h};
51647             return false;
51648         }
51649     },
51650     /**
51651      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51652      * @return {Roo.UpdateManager} The UpdateManager
51653      */
51654     getUpdateManager : function(){
51655         return this.el.getUpdateManager();
51656     },
51657      /**
51658      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51659      * @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:
51660 <pre><code>
51661 panel.load({
51662     url: "your-url.php",
51663     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51664     callback: yourFunction,
51665     scope: yourObject, //(optional scope)
51666     discardUrl: false,
51667     nocache: false,
51668     text: "Loading...",
51669     timeout: 30,
51670     scripts: false
51671 });
51672 </code></pre>
51673      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51674      * 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.
51675      * @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}
51676      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51677      * @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.
51678      * @return {Roo.ContentPanel} this
51679      */
51680     load : function(){
51681         var um = this.el.getUpdateManager();
51682         um.update.apply(um, arguments);
51683         return this;
51684     },
51685
51686
51687     /**
51688      * 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.
51689      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51690      * @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)
51691      * @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)
51692      * @return {Roo.UpdateManager} The UpdateManager
51693      */
51694     setUrl : function(url, params, loadOnce){
51695         if(this.refreshDelegate){
51696             this.removeListener("activate", this.refreshDelegate);
51697         }
51698         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51699         this.on("activate", this.refreshDelegate);
51700         return this.el.getUpdateManager();
51701     },
51702     
51703     _handleRefresh : function(url, params, loadOnce){
51704         if(!loadOnce || !this.loaded){
51705             var updater = this.el.getUpdateManager();
51706             updater.update(url, params, this._setLoaded.createDelegate(this));
51707         }
51708     },
51709     
51710     _setLoaded : function(){
51711         this.loaded = true;
51712     }, 
51713     
51714     /**
51715      * Returns this panel's id
51716      * @return {String} 
51717      */
51718     getId : function(){
51719         return this.el.id;
51720     },
51721     
51722     /** 
51723      * Returns this panel's element - used by regiosn to add.
51724      * @return {Roo.Element} 
51725      */
51726     getEl : function(){
51727         return this.wrapEl || this.el;
51728     },
51729     
51730     adjustForComponents : function(width, height)
51731     {
51732         //Roo.log('adjustForComponents ');
51733         if(this.resizeEl != this.el){
51734             width -= this.el.getFrameWidth('lr');
51735             height -= this.el.getFrameWidth('tb');
51736         }
51737         if(this.toolbar){
51738             var te = this.toolbar.getEl();
51739             height -= te.getHeight();
51740             te.setWidth(width);
51741         }
51742         if(this.footer){
51743             var te = this.footer.getEl();
51744             Roo.log("footer:" + te.getHeight());
51745             
51746             height -= te.getHeight();
51747             te.setWidth(width);
51748         }
51749         
51750         
51751         if(this.adjustments){
51752             width += this.adjustments[0];
51753             height += this.adjustments[1];
51754         }
51755         return {"width": width, "height": height};
51756     },
51757     
51758     setSize : function(width, height){
51759         if(this.fitToFrame && !this.ignoreResize(width, height)){
51760             if(this.fitContainer && this.resizeEl != this.el){
51761                 this.el.setSize(width, height);
51762             }
51763             var size = this.adjustForComponents(width, height);
51764             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51765             this.fireEvent('resize', this, size.width, size.height);
51766         }
51767     },
51768     
51769     /**
51770      * Returns this panel's title
51771      * @return {String} 
51772      */
51773     getTitle : function(){
51774         return this.title;
51775     },
51776     
51777     /**
51778      * Set this panel's title
51779      * @param {String} title
51780      */
51781     setTitle : function(title){
51782         this.title = title;
51783         if(this.region){
51784             this.region.updatePanelTitle(this, title);
51785         }
51786     },
51787     
51788     /**
51789      * Returns true is this panel was configured to be closable
51790      * @return {Boolean} 
51791      */
51792     isClosable : function(){
51793         return this.closable;
51794     },
51795     
51796     beforeSlide : function(){
51797         this.el.clip();
51798         this.resizeEl.clip();
51799     },
51800     
51801     afterSlide : function(){
51802         this.el.unclip();
51803         this.resizeEl.unclip();
51804     },
51805     
51806     /**
51807      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51808      *   Will fail silently if the {@link #setUrl} method has not been called.
51809      *   This does not activate the panel, just updates its content.
51810      */
51811     refresh : function(){
51812         if(this.refreshDelegate){
51813            this.loaded = false;
51814            this.refreshDelegate();
51815         }
51816     },
51817     
51818     /**
51819      * Destroys this panel
51820      */
51821     destroy : function(){
51822         this.el.removeAllListeners();
51823         var tempEl = document.createElement("span");
51824         tempEl.appendChild(this.el.dom);
51825         tempEl.innerHTML = "";
51826         this.el.remove();
51827         this.el = null;
51828     },
51829     
51830     /**
51831      * form - if the content panel contains a form - this is a reference to it.
51832      * @type {Roo.form.Form}
51833      */
51834     form : false,
51835     /**
51836      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51837      *    This contains a reference to it.
51838      * @type {Roo.View}
51839      */
51840     view : false,
51841     
51842       /**
51843      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51844      * <pre><code>
51845
51846 layout.addxtype({
51847        xtype : 'Form',
51848        items: [ .... ]
51849    }
51850 );
51851
51852 </code></pre>
51853      * @param {Object} cfg Xtype definition of item to add.
51854      */
51855     
51856     addxtype : function(cfg) {
51857         // add form..
51858         if (cfg.xtype.match(/^Form$/)) {
51859             
51860             var el;
51861             //if (this.footer) {
51862             //    el = this.footer.container.insertSibling(false, 'before');
51863             //} else {
51864                 el = this.el.createChild();
51865             //}
51866
51867             this.form = new  Roo.form.Form(cfg);
51868             
51869             
51870             if ( this.form.allItems.length) this.form.render(el.dom);
51871             return this.form;
51872         }
51873         // should only have one of theses..
51874         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51875             // views.. should not be just added - used named prop 'view''
51876             
51877             cfg.el = this.el.appendChild(document.createElement("div"));
51878             // factory?
51879             
51880             var ret = new Roo.factory(cfg);
51881              
51882              ret.render && ret.render(false, ''); // render blank..
51883             this.view = ret;
51884             return ret;
51885         }
51886         return false;
51887     }
51888 });
51889
51890 /**
51891  * @class Roo.GridPanel
51892  * @extends Roo.ContentPanel
51893  * @constructor
51894  * Create a new GridPanel.
51895  * @param {Roo.grid.Grid} grid The grid for this panel
51896  * @param {String/Object} config A string to set only the panel's title, or a config object
51897  */
51898 Roo.GridPanel = function(grid, config){
51899     
51900   
51901     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51902         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51903         
51904     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51905     
51906     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51907     
51908     if(this.toolbar){
51909         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51910     }
51911     // xtype created footer. - not sure if will work as we normally have to render first..
51912     if (this.footer && !this.footer.el && this.footer.xtype) {
51913         
51914         this.footer.container = this.grid.getView().getFooterPanel(true);
51915         this.footer.dataSource = this.grid.dataSource;
51916         this.footer = Roo.factory(this.footer, Roo);
51917         
51918     }
51919     
51920     grid.monitorWindowResize = false; // turn off autosizing
51921     grid.autoHeight = false;
51922     grid.autoWidth = false;
51923     this.grid = grid;
51924     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51925 };
51926
51927 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51928     getId : function(){
51929         return this.grid.id;
51930     },
51931     
51932     /**
51933      * Returns the grid for this panel
51934      * @return {Roo.grid.Grid} 
51935      */
51936     getGrid : function(){
51937         return this.grid;    
51938     },
51939     
51940     setSize : function(width, height){
51941         if(!this.ignoreResize(width, height)){
51942             var grid = this.grid;
51943             var size = this.adjustForComponents(width, height);
51944             grid.getGridEl().setSize(size.width, size.height);
51945             grid.autoSize();
51946         }
51947     },
51948     
51949     beforeSlide : function(){
51950         this.grid.getView().scroller.clip();
51951     },
51952     
51953     afterSlide : function(){
51954         this.grid.getView().scroller.unclip();
51955     },
51956     
51957     destroy : function(){
51958         this.grid.destroy();
51959         delete this.grid;
51960         Roo.GridPanel.superclass.destroy.call(this); 
51961     }
51962 });
51963
51964
51965 /**
51966  * @class Roo.NestedLayoutPanel
51967  * @extends Roo.ContentPanel
51968  * @constructor
51969  * Create a new NestedLayoutPanel.
51970  * 
51971  * 
51972  * @param {Roo.BorderLayout} layout The layout for this panel
51973  * @param {String/Object} config A string to set only the title or a config object
51974  */
51975 Roo.NestedLayoutPanel = function(layout, config)
51976 {
51977     // construct with only one argument..
51978     /* FIXME - implement nicer consturctors
51979     if (layout.layout) {
51980         config = layout;
51981         layout = config.layout;
51982         delete config.layout;
51983     }
51984     if (layout.xtype && !layout.getEl) {
51985         // then layout needs constructing..
51986         layout = Roo.factory(layout, Roo);
51987     }
51988     */
51989     
51990     
51991     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51992     
51993     layout.monitorWindowResize = false; // turn off autosizing
51994     this.layout = layout;
51995     this.layout.getEl().addClass("x-layout-nested-layout");
51996     
51997     
51998     
51999     
52000 };
52001
52002 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
52003
52004     setSize : function(width, height){
52005         if(!this.ignoreResize(width, height)){
52006             var size = this.adjustForComponents(width, height);
52007             var el = this.layout.getEl();
52008             el.setSize(size.width, size.height);
52009             var touch = el.dom.offsetWidth;
52010             this.layout.layout();
52011             // ie requires a double layout on the first pass
52012             if(Roo.isIE && !this.initialized){
52013                 this.initialized = true;
52014                 this.layout.layout();
52015             }
52016         }
52017     },
52018     
52019     // activate all subpanels if not currently active..
52020     
52021     setActiveState : function(active){
52022         this.active = active;
52023         if(!active){
52024             this.fireEvent("deactivate", this);
52025             return;
52026         }
52027         
52028         this.fireEvent("activate", this);
52029         // not sure if this should happen before or after..
52030         if (!this.layout) {
52031             return; // should not happen..
52032         }
52033         var reg = false;
52034         for (var r in this.layout.regions) {
52035             reg = this.layout.getRegion(r);
52036             if (reg.getActivePanel()) {
52037                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
52038                 reg.setActivePanel(reg.getActivePanel());
52039                 continue;
52040             }
52041             if (!reg.panels.length) {
52042                 continue;
52043             }
52044             reg.showPanel(reg.getPanel(0));
52045         }
52046         
52047         
52048         
52049         
52050     },
52051     
52052     /**
52053      * Returns the nested BorderLayout for this panel
52054      * @return {Roo.BorderLayout} 
52055      */
52056     getLayout : function(){
52057         return this.layout;
52058     },
52059     
52060      /**
52061      * Adds a xtype elements to the layout of the nested panel
52062      * <pre><code>
52063
52064 panel.addxtype({
52065        xtype : 'ContentPanel',
52066        region: 'west',
52067        items: [ .... ]
52068    }
52069 );
52070
52071 panel.addxtype({
52072         xtype : 'NestedLayoutPanel',
52073         region: 'west',
52074         layout: {
52075            center: { },
52076            west: { }   
52077         },
52078         items : [ ... list of content panels or nested layout panels.. ]
52079    }
52080 );
52081 </code></pre>
52082      * @param {Object} cfg Xtype definition of item to add.
52083      */
52084     addxtype : function(cfg) {
52085         return this.layout.addxtype(cfg);
52086     
52087     }
52088 });
52089
52090 Roo.ScrollPanel = function(el, config, content){
52091     config = config || {};
52092     config.fitToFrame = true;
52093     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
52094     
52095     this.el.dom.style.overflow = "hidden";
52096     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
52097     this.el.removeClass("x-layout-inactive-content");
52098     this.el.on("mousewheel", this.onWheel, this);
52099
52100     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
52101     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
52102     up.unselectable(); down.unselectable();
52103     up.on("click", this.scrollUp, this);
52104     down.on("click", this.scrollDown, this);
52105     up.addClassOnOver("x-scroller-btn-over");
52106     down.addClassOnOver("x-scroller-btn-over");
52107     up.addClassOnClick("x-scroller-btn-click");
52108     down.addClassOnClick("x-scroller-btn-click");
52109     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
52110
52111     this.resizeEl = this.el;
52112     this.el = wrap; this.up = up; this.down = down;
52113 };
52114
52115 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
52116     increment : 100,
52117     wheelIncrement : 5,
52118     scrollUp : function(){
52119         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
52120     },
52121
52122     scrollDown : function(){
52123         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
52124     },
52125
52126     afterScroll : function(){
52127         var el = this.resizeEl;
52128         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
52129         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52130         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52131     },
52132
52133     setSize : function(){
52134         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
52135         this.afterScroll();
52136     },
52137
52138     onWheel : function(e){
52139         var d = e.getWheelDelta();
52140         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
52141         this.afterScroll();
52142         e.stopEvent();
52143     },
52144
52145     setContent : function(content, loadScripts){
52146         this.resizeEl.update(content, loadScripts);
52147     }
52148
52149 });
52150
52151
52152
52153
52154
52155
52156
52157
52158
52159 /**
52160  * @class Roo.TreePanel
52161  * @extends Roo.ContentPanel
52162  * @constructor
52163  * Create a new TreePanel. - defaults to fit/scoll contents.
52164  * @param {String/Object} config A string to set only the panel's title, or a config object
52165  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
52166  */
52167 Roo.TreePanel = function(config){
52168     var el = config.el;
52169     var tree = config.tree;
52170     delete config.tree; 
52171     delete config.el; // hopefull!
52172     
52173     // wrapper for IE7 strict & safari scroll issue
52174     
52175     var treeEl = el.createChild();
52176     config.resizeEl = treeEl;
52177     
52178     
52179     
52180     Roo.TreePanel.superclass.constructor.call(this, el, config);
52181  
52182  
52183     this.tree = new Roo.tree.TreePanel(treeEl , tree);
52184     //console.log(tree);
52185     this.on('activate', function()
52186     {
52187         if (this.tree.rendered) {
52188             return;
52189         }
52190         //console.log('render tree');
52191         this.tree.render();
52192     });
52193     // this should not be needed.. - it's actually the 'el' that resizes?
52194     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
52195     
52196     //this.on('resize',  function (cp, w, h) {
52197     //        this.tree.innerCt.setWidth(w);
52198     //        this.tree.innerCt.setHeight(h);
52199     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
52200     //});
52201
52202         
52203     
52204 };
52205
52206 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
52207     fitToFrame : true,
52208     autoScroll : true
52209 });
52210
52211
52212
52213
52214
52215
52216
52217
52218
52219
52220
52221 /*
52222  * Based on:
52223  * Ext JS Library 1.1.1
52224  * Copyright(c) 2006-2007, Ext JS, LLC.
52225  *
52226  * Originally Released Under LGPL - original licence link has changed is not relivant.
52227  *
52228  * Fork - LGPL
52229  * <script type="text/javascript">
52230  */
52231  
52232
52233 /**
52234  * @class Roo.ReaderLayout
52235  * @extends Roo.BorderLayout
52236  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
52237  * center region containing two nested regions (a top one for a list view and one for item preview below),
52238  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
52239  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
52240  * expedites the setup of the overall layout and regions for this common application style.
52241  * Example:
52242  <pre><code>
52243 var reader = new Roo.ReaderLayout();
52244 var CP = Roo.ContentPanel;  // shortcut for adding
52245
52246 reader.beginUpdate();
52247 reader.add("north", new CP("north", "North"));
52248 reader.add("west", new CP("west", {title: "West"}));
52249 reader.add("east", new CP("east", {title: "East"}));
52250
52251 reader.regions.listView.add(new CP("listView", "List"));
52252 reader.regions.preview.add(new CP("preview", "Preview"));
52253 reader.endUpdate();
52254 </code></pre>
52255 * @constructor
52256 * Create a new ReaderLayout
52257 * @param {Object} config Configuration options
52258 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
52259 * document.body if omitted)
52260 */
52261 Roo.ReaderLayout = function(config, renderTo){
52262     var c = config || {size:{}};
52263     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
52264         north: c.north !== false ? Roo.apply({
52265             split:false,
52266             initialSize: 32,
52267             titlebar: false
52268         }, c.north) : false,
52269         west: c.west !== false ? Roo.apply({
52270             split:true,
52271             initialSize: 200,
52272             minSize: 175,
52273             maxSize: 400,
52274             titlebar: true,
52275             collapsible: true,
52276             animate: true,
52277             margins:{left:5,right:0,bottom:5,top:5},
52278             cmargins:{left:5,right:5,bottom:5,top:5}
52279         }, c.west) : false,
52280         east: c.east !== false ? Roo.apply({
52281             split:true,
52282             initialSize: 200,
52283             minSize: 175,
52284             maxSize: 400,
52285             titlebar: true,
52286             collapsible: true,
52287             animate: true,
52288             margins:{left:0,right:5,bottom:5,top:5},
52289             cmargins:{left:5,right:5,bottom:5,top:5}
52290         }, c.east) : false,
52291         center: Roo.apply({
52292             tabPosition: 'top',
52293             autoScroll:false,
52294             closeOnTab: true,
52295             titlebar:false,
52296             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
52297         }, c.center)
52298     });
52299
52300     this.el.addClass('x-reader');
52301
52302     this.beginUpdate();
52303
52304     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
52305         south: c.preview !== false ? Roo.apply({
52306             split:true,
52307             initialSize: 200,
52308             minSize: 100,
52309             autoScroll:true,
52310             collapsible:true,
52311             titlebar: true,
52312             cmargins:{top:5,left:0, right:0, bottom:0}
52313         }, c.preview) : false,
52314         center: Roo.apply({
52315             autoScroll:false,
52316             titlebar:false,
52317             minHeight:200
52318         }, c.listView)
52319     });
52320     this.add('center', new Roo.NestedLayoutPanel(inner,
52321             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
52322
52323     this.endUpdate();
52324
52325     this.regions.preview = inner.getRegion('south');
52326     this.regions.listView = inner.getRegion('center');
52327 };
52328
52329 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
52330  * Based on:
52331  * Ext JS Library 1.1.1
52332  * Copyright(c) 2006-2007, Ext JS, LLC.
52333  *
52334  * Originally Released Under LGPL - original licence link has changed is not relivant.
52335  *
52336  * Fork - LGPL
52337  * <script type="text/javascript">
52338  */
52339  
52340 /**
52341  * @class Roo.grid.Grid
52342  * @extends Roo.util.Observable
52343  * This class represents the primary interface of a component based grid control.
52344  * <br><br>Usage:<pre><code>
52345  var grid = new Roo.grid.Grid("my-container-id", {
52346      ds: myDataStore,
52347      cm: myColModel,
52348      selModel: mySelectionModel,
52349      autoSizeColumns: true,
52350      monitorWindowResize: false,
52351      trackMouseOver: true
52352  });
52353  // set any options
52354  grid.render();
52355  * </code></pre>
52356  * <b>Common Problems:</b><br/>
52357  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
52358  * element will correct this<br/>
52359  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
52360  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
52361  * are unpredictable.<br/>
52362  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
52363  * grid to calculate dimensions/offsets.<br/>
52364   * @constructor
52365  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52366  * The container MUST have some type of size defined for the grid to fill. The container will be
52367  * automatically set to position relative if it isn't already.
52368  * @param {Object} config A config object that sets properties on this grid.
52369  */
52370 Roo.grid.Grid = function(container, config){
52371         // initialize the container
52372         this.container = Roo.get(container);
52373         this.container.update("");
52374         this.container.setStyle("overflow", "hidden");
52375     this.container.addClass('x-grid-container');
52376
52377     this.id = this.container.id;
52378
52379     Roo.apply(this, config);
52380     // check and correct shorthanded configs
52381     if(this.ds){
52382         this.dataSource = this.ds;
52383         delete this.ds;
52384     }
52385     if(this.cm){
52386         this.colModel = this.cm;
52387         delete this.cm;
52388     }
52389     if(this.sm){
52390         this.selModel = this.sm;
52391         delete this.sm;
52392     }
52393
52394     if (this.selModel) {
52395         this.selModel = Roo.factory(this.selModel, Roo.grid);
52396         this.sm = this.selModel;
52397         this.sm.xmodule = this.xmodule || false;
52398     }
52399     if (typeof(this.colModel.config) == 'undefined') {
52400         this.colModel = new Roo.grid.ColumnModel(this.colModel);
52401         this.cm = this.colModel;
52402         this.cm.xmodule = this.xmodule || false;
52403     }
52404     if (this.dataSource) {
52405         this.dataSource= Roo.factory(this.dataSource, Roo.data);
52406         this.ds = this.dataSource;
52407         this.ds.xmodule = this.xmodule || false;
52408          
52409     }
52410     
52411     
52412     
52413     if(this.width){
52414         this.container.setWidth(this.width);
52415     }
52416
52417     if(this.height){
52418         this.container.setHeight(this.height);
52419     }
52420     /** @private */
52421         this.addEvents({
52422         // raw events
52423         /**
52424          * @event click
52425          * The raw click event for the entire grid.
52426          * @param {Roo.EventObject} e
52427          */
52428         "click" : true,
52429         /**
52430          * @event dblclick
52431          * The raw dblclick event for the entire grid.
52432          * @param {Roo.EventObject} e
52433          */
52434         "dblclick" : true,
52435         /**
52436          * @event contextmenu
52437          * The raw contextmenu event for the entire grid.
52438          * @param {Roo.EventObject} e
52439          */
52440         "contextmenu" : true,
52441         /**
52442          * @event mousedown
52443          * The raw mousedown event for the entire grid.
52444          * @param {Roo.EventObject} e
52445          */
52446         "mousedown" : true,
52447         /**
52448          * @event mouseup
52449          * The raw mouseup event for the entire grid.
52450          * @param {Roo.EventObject} e
52451          */
52452         "mouseup" : true,
52453         /**
52454          * @event mouseover
52455          * The raw mouseover event for the entire grid.
52456          * @param {Roo.EventObject} e
52457          */
52458         "mouseover" : true,
52459         /**
52460          * @event mouseout
52461          * The raw mouseout event for the entire grid.
52462          * @param {Roo.EventObject} e
52463          */
52464         "mouseout" : true,
52465         /**
52466          * @event keypress
52467          * The raw keypress event for the entire grid.
52468          * @param {Roo.EventObject} e
52469          */
52470         "keypress" : true,
52471         /**
52472          * @event keydown
52473          * The raw keydown event for the entire grid.
52474          * @param {Roo.EventObject} e
52475          */
52476         "keydown" : true,
52477
52478         // custom events
52479
52480         /**
52481          * @event cellclick
52482          * Fires when a cell is clicked
52483          * @param {Grid} this
52484          * @param {Number} rowIndex
52485          * @param {Number} columnIndex
52486          * @param {Roo.EventObject} e
52487          */
52488         "cellclick" : true,
52489         /**
52490          * @event celldblclick
52491          * Fires when a cell is double clicked
52492          * @param {Grid} this
52493          * @param {Number} rowIndex
52494          * @param {Number} columnIndex
52495          * @param {Roo.EventObject} e
52496          */
52497         "celldblclick" : true,
52498         /**
52499          * @event rowclick
52500          * Fires when a row is clicked
52501          * @param {Grid} this
52502          * @param {Number} rowIndex
52503          * @param {Roo.EventObject} e
52504          */
52505         "rowclick" : true,
52506         /**
52507          * @event rowdblclick
52508          * Fires when a row is double clicked
52509          * @param {Grid} this
52510          * @param {Number} rowIndex
52511          * @param {Roo.EventObject} e
52512          */
52513         "rowdblclick" : true,
52514         /**
52515          * @event headerclick
52516          * Fires when a header is clicked
52517          * @param {Grid} this
52518          * @param {Number} columnIndex
52519          * @param {Roo.EventObject} e
52520          */
52521         "headerclick" : true,
52522         /**
52523          * @event headerdblclick
52524          * Fires when a header cell is double clicked
52525          * @param {Grid} this
52526          * @param {Number} columnIndex
52527          * @param {Roo.EventObject} e
52528          */
52529         "headerdblclick" : true,
52530         /**
52531          * @event rowcontextmenu
52532          * Fires when a row is right clicked
52533          * @param {Grid} this
52534          * @param {Number} rowIndex
52535          * @param {Roo.EventObject} e
52536          */
52537         "rowcontextmenu" : true,
52538         /**
52539          * @event cellcontextmenu
52540          * Fires when a cell is right clicked
52541          * @param {Grid} this
52542          * @param {Number} rowIndex
52543          * @param {Number} cellIndex
52544          * @param {Roo.EventObject} e
52545          */
52546          "cellcontextmenu" : true,
52547         /**
52548          * @event headercontextmenu
52549          * Fires when a header is right clicked
52550          * @param {Grid} this
52551          * @param {Number} columnIndex
52552          * @param {Roo.EventObject} e
52553          */
52554         "headercontextmenu" : true,
52555         /**
52556          * @event bodyscroll
52557          * Fires when the body element is scrolled
52558          * @param {Number} scrollLeft
52559          * @param {Number} scrollTop
52560          */
52561         "bodyscroll" : true,
52562         /**
52563          * @event columnresize
52564          * Fires when the user resizes a column
52565          * @param {Number} columnIndex
52566          * @param {Number} newSize
52567          */
52568         "columnresize" : true,
52569         /**
52570          * @event columnmove
52571          * Fires when the user moves a column
52572          * @param {Number} oldIndex
52573          * @param {Number} newIndex
52574          */
52575         "columnmove" : true,
52576         /**
52577          * @event startdrag
52578          * Fires when row(s) start being dragged
52579          * @param {Grid} this
52580          * @param {Roo.GridDD} dd The drag drop object
52581          * @param {event} e The raw browser event
52582          */
52583         "startdrag" : true,
52584         /**
52585          * @event enddrag
52586          * Fires when a drag operation is complete
52587          * @param {Grid} this
52588          * @param {Roo.GridDD} dd The drag drop object
52589          * @param {event} e The raw browser event
52590          */
52591         "enddrag" : true,
52592         /**
52593          * @event dragdrop
52594          * Fires when dragged row(s) are dropped on a valid DD target
52595          * @param {Grid} this
52596          * @param {Roo.GridDD} dd The drag drop object
52597          * @param {String} targetId The target drag drop object
52598          * @param {event} e The raw browser event
52599          */
52600         "dragdrop" : true,
52601         /**
52602          * @event dragover
52603          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52604          * @param {Grid} this
52605          * @param {Roo.GridDD} dd The drag drop object
52606          * @param {String} targetId The target drag drop object
52607          * @param {event} e The raw browser event
52608          */
52609         "dragover" : true,
52610         /**
52611          * @event dragenter
52612          *  Fires when the dragged row(s) first cross another DD target while being dragged
52613          * @param {Grid} this
52614          * @param {Roo.GridDD} dd The drag drop object
52615          * @param {String} targetId The target drag drop object
52616          * @param {event} e The raw browser event
52617          */
52618         "dragenter" : true,
52619         /**
52620          * @event dragout
52621          * Fires when the dragged row(s) leave another DD target while being dragged
52622          * @param {Grid} this
52623          * @param {Roo.GridDD} dd The drag drop object
52624          * @param {String} targetId The target drag drop object
52625          * @param {event} e The raw browser event
52626          */
52627         "dragout" : true,
52628         /**
52629          * @event rowclass
52630          * Fires when a row is rendered, so you can change add a style to it.
52631          * @param {GridView} gridview   The grid view
52632          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52633          */
52634         'rowclass' : true,
52635
52636         /**
52637          * @event render
52638          * Fires when the grid is rendered
52639          * @param {Grid} grid
52640          */
52641         'render' : true
52642     });
52643
52644     Roo.grid.Grid.superclass.constructor.call(this);
52645 };
52646 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52647     
52648     /**
52649      * @cfg {String} ddGroup - drag drop group.
52650      */
52651
52652     /**
52653      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52654      */
52655     minColumnWidth : 25,
52656
52657     /**
52658      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52659      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52660      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52661      */
52662     autoSizeColumns : false,
52663
52664     /**
52665      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52666      */
52667     autoSizeHeaders : true,
52668
52669     /**
52670      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52671      */
52672     monitorWindowResize : true,
52673
52674     /**
52675      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52676      * rows measured to get a columns size. Default is 0 (all rows).
52677      */
52678     maxRowsToMeasure : 0,
52679
52680     /**
52681      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52682      */
52683     trackMouseOver : true,
52684
52685     /**
52686     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52687     */
52688     
52689     /**
52690     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52691     */
52692     enableDragDrop : false,
52693     
52694     /**
52695     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52696     */
52697     enableColumnMove : true,
52698     
52699     /**
52700     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52701     */
52702     enableColumnHide : true,
52703     
52704     /**
52705     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52706     */
52707     enableRowHeightSync : false,
52708     
52709     /**
52710     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52711     */
52712     stripeRows : true,
52713     
52714     /**
52715     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52716     */
52717     autoHeight : false,
52718
52719     /**
52720      * @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.
52721      */
52722     autoExpandColumn : false,
52723
52724     /**
52725     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52726     * Default is 50.
52727     */
52728     autoExpandMin : 50,
52729
52730     /**
52731     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52732     */
52733     autoExpandMax : 1000,
52734
52735     /**
52736     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52737     */
52738     view : null,
52739
52740     /**
52741     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52742     */
52743     loadMask : false,
52744     /**
52745     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52746     */
52747     dropTarget: false,
52748     
52749    
52750     
52751     // private
52752     rendered : false,
52753
52754     /**
52755     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52756     * of a fixed width. Default is false.
52757     */
52758     /**
52759     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52760     */
52761     /**
52762      * Called once after all setup has been completed and the grid is ready to be rendered.
52763      * @return {Roo.grid.Grid} this
52764      */
52765     render : function()
52766     {
52767         var c = this.container;
52768         // try to detect autoHeight/width mode
52769         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52770             this.autoHeight = true;
52771         }
52772         var view = this.getView();
52773         view.init(this);
52774
52775         c.on("click", this.onClick, this);
52776         c.on("dblclick", this.onDblClick, this);
52777         c.on("contextmenu", this.onContextMenu, this);
52778         c.on("keydown", this.onKeyDown, this);
52779         if (Roo.isTouch) {
52780             c.on("touchstart", this.onTouchStart, this);
52781         }
52782
52783         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52784
52785         this.getSelectionModel().init(this);
52786
52787         view.render();
52788
52789         if(this.loadMask){
52790             this.loadMask = new Roo.LoadMask(this.container,
52791                     Roo.apply({store:this.dataSource}, this.loadMask));
52792         }
52793         
52794         
52795         if (this.toolbar && this.toolbar.xtype) {
52796             this.toolbar.container = this.getView().getHeaderPanel(true);
52797             this.toolbar = new Roo.Toolbar(this.toolbar);
52798         }
52799         if (this.footer && this.footer.xtype) {
52800             this.footer.dataSource = this.getDataSource();
52801             this.footer.container = this.getView().getFooterPanel(true);
52802             this.footer = Roo.factory(this.footer, Roo);
52803         }
52804         if (this.dropTarget && this.dropTarget.xtype) {
52805             delete this.dropTarget.xtype;
52806             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52807         }
52808         
52809         
52810         this.rendered = true;
52811         this.fireEvent('render', this);
52812         return this;
52813     },
52814
52815         /**
52816          * Reconfigures the grid to use a different Store and Column Model.
52817          * The View will be bound to the new objects and refreshed.
52818          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52819          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52820          */
52821     reconfigure : function(dataSource, colModel){
52822         if(this.loadMask){
52823             this.loadMask.destroy();
52824             this.loadMask = new Roo.LoadMask(this.container,
52825                     Roo.apply({store:dataSource}, this.loadMask));
52826         }
52827         this.view.bind(dataSource, colModel);
52828         this.dataSource = dataSource;
52829         this.colModel = colModel;
52830         this.view.refresh(true);
52831     },
52832
52833     // private
52834     onKeyDown : function(e){
52835         this.fireEvent("keydown", e);
52836     },
52837
52838     /**
52839      * Destroy this grid.
52840      * @param {Boolean} removeEl True to remove the element
52841      */
52842     destroy : function(removeEl, keepListeners){
52843         if(this.loadMask){
52844             this.loadMask.destroy();
52845         }
52846         var c = this.container;
52847         c.removeAllListeners();
52848         this.view.destroy();
52849         this.colModel.purgeListeners();
52850         if(!keepListeners){
52851             this.purgeListeners();
52852         }
52853         c.update("");
52854         if(removeEl === true){
52855             c.remove();
52856         }
52857     },
52858
52859     // private
52860     processEvent : function(name, e){
52861         // does this fire select???
52862         //Roo.log('grid:processEvent '  + name);
52863         
52864         if (name != 'touchstart' ) {
52865             this.fireEvent(name, e);    
52866         }
52867         
52868         var t = e.getTarget();
52869         var v = this.view;
52870         var header = v.findHeaderIndex(t);
52871         if(header !== false){
52872             var ename = name == 'touchstart' ? 'click' : name;
52873              
52874             this.fireEvent("header" + ename, this, header, e);
52875         }else{
52876             var row = v.findRowIndex(t);
52877             var cell = v.findCellIndex(t);
52878             if (name == 'touchstart') {
52879                 // first touch is always a click.
52880                 // hopefull this happens after selection is updated.?
52881                 name = false;
52882                 
52883                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52884                     var cs = this.selModel.getSelectedCell();
52885                     if (row == cs[0] && cell == cs[1]){
52886                         name = 'dblclick';
52887                     }
52888                 }
52889                 if (typeof(this.selModel.getSelections) != 'undefined') {
52890                     var cs = this.selModel.getSelections();
52891                     var ds = this.dataSource;
52892                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52893                         name = 'dblclick';
52894                     }
52895                 }
52896                 if (!name) {
52897                     return;
52898                 }
52899             }
52900             
52901             
52902             if(row !== false){
52903                 this.fireEvent("row" + name, this, row, e);
52904                 if(cell !== false){
52905                     this.fireEvent("cell" + name, this, row, cell, e);
52906                 }
52907             }
52908         }
52909     },
52910
52911     // private
52912     onClick : function(e){
52913         this.processEvent("click", e);
52914     },
52915    // private
52916     onTouchStart : function(e){
52917         this.processEvent("touchstart", e);
52918     },
52919
52920     // private
52921     onContextMenu : function(e, t){
52922         this.processEvent("contextmenu", e);
52923     },
52924
52925     // private
52926     onDblClick : function(e){
52927         this.processEvent("dblclick", e);
52928     },
52929
52930     // private
52931     walkCells : function(row, col, step, fn, scope){
52932         var cm = this.colModel, clen = cm.getColumnCount();
52933         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52934         if(step < 0){
52935             if(col < 0){
52936                 row--;
52937                 first = false;
52938             }
52939             while(row >= 0){
52940                 if(!first){
52941                     col = clen-1;
52942                 }
52943                 first = false;
52944                 while(col >= 0){
52945                     if(fn.call(scope || this, row, col, cm) === true){
52946                         return [row, col];
52947                     }
52948                     col--;
52949                 }
52950                 row--;
52951             }
52952         } else {
52953             if(col >= clen){
52954                 row++;
52955                 first = false;
52956             }
52957             while(row < rlen){
52958                 if(!first){
52959                     col = 0;
52960                 }
52961                 first = false;
52962                 while(col < clen){
52963                     if(fn.call(scope || this, row, col, cm) === true){
52964                         return [row, col];
52965                     }
52966                     col++;
52967                 }
52968                 row++;
52969             }
52970         }
52971         return null;
52972     },
52973
52974     // private
52975     getSelections : function(){
52976         return this.selModel.getSelections();
52977     },
52978
52979     /**
52980      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52981      * but if manual update is required this method will initiate it.
52982      */
52983     autoSize : function(){
52984         if(this.rendered){
52985             this.view.layout();
52986             if(this.view.adjustForScroll){
52987                 this.view.adjustForScroll();
52988             }
52989         }
52990     },
52991
52992     /**
52993      * Returns the grid's underlying element.
52994      * @return {Element} The element
52995      */
52996     getGridEl : function(){
52997         return this.container;
52998     },
52999
53000     // private for compatibility, overridden by editor grid
53001     stopEditing : function(){},
53002
53003     /**
53004      * Returns the grid's SelectionModel.
53005      * @return {SelectionModel}
53006      */
53007     getSelectionModel : function(){
53008         if(!this.selModel){
53009             this.selModel = new Roo.grid.RowSelectionModel();
53010         }
53011         return this.selModel;
53012     },
53013
53014     /**
53015      * Returns the grid's DataSource.
53016      * @return {DataSource}
53017      */
53018     getDataSource : function(){
53019         return this.dataSource;
53020     },
53021
53022     /**
53023      * Returns the grid's ColumnModel.
53024      * @return {ColumnModel}
53025      */
53026     getColumnModel : function(){
53027         return this.colModel;
53028     },
53029
53030     /**
53031      * Returns the grid's GridView object.
53032      * @return {GridView}
53033      */
53034     getView : function(){
53035         if(!this.view){
53036             this.view = new Roo.grid.GridView(this.viewConfig);
53037         }
53038         return this.view;
53039     },
53040     /**
53041      * Called to get grid's drag proxy text, by default returns this.ddText.
53042      * @return {String}
53043      */
53044     getDragDropText : function(){
53045         var count = this.selModel.getCount();
53046         return String.format(this.ddText, count, count == 1 ? '' : 's');
53047     }
53048 });
53049 /**
53050  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
53051  * %0 is replaced with the number of selected rows.
53052  * @type String
53053  */
53054 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
53055  * Based on:
53056  * Ext JS Library 1.1.1
53057  * Copyright(c) 2006-2007, Ext JS, LLC.
53058  *
53059  * Originally Released Under LGPL - original licence link has changed is not relivant.
53060  *
53061  * Fork - LGPL
53062  * <script type="text/javascript">
53063  */
53064  
53065 Roo.grid.AbstractGridView = function(){
53066         this.grid = null;
53067         
53068         this.events = {
53069             "beforerowremoved" : true,
53070             "beforerowsinserted" : true,
53071             "beforerefresh" : true,
53072             "rowremoved" : true,
53073             "rowsinserted" : true,
53074             "rowupdated" : true,
53075             "refresh" : true
53076         };
53077     Roo.grid.AbstractGridView.superclass.constructor.call(this);
53078 };
53079
53080 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
53081     rowClass : "x-grid-row",
53082     cellClass : "x-grid-cell",
53083     tdClass : "x-grid-td",
53084     hdClass : "x-grid-hd",
53085     splitClass : "x-grid-hd-split",
53086     
53087     init: function(grid){
53088         this.grid = grid;
53089                 var cid = this.grid.getGridEl().id;
53090         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
53091         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
53092         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
53093         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
53094         },
53095         
53096     getColumnRenderers : function(){
53097         var renderers = [];
53098         var cm = this.grid.colModel;
53099         var colCount = cm.getColumnCount();
53100         for(var i = 0; i < colCount; i++){
53101             renderers[i] = cm.getRenderer(i);
53102         }
53103         return renderers;
53104     },
53105     
53106     getColumnIds : function(){
53107         var ids = [];
53108         var cm = this.grid.colModel;
53109         var colCount = cm.getColumnCount();
53110         for(var i = 0; i < colCount; i++){
53111             ids[i] = cm.getColumnId(i);
53112         }
53113         return ids;
53114     },
53115     
53116     getDataIndexes : function(){
53117         if(!this.indexMap){
53118             this.indexMap = this.buildIndexMap();
53119         }
53120         return this.indexMap.colToData;
53121     },
53122     
53123     getColumnIndexByDataIndex : function(dataIndex){
53124         if(!this.indexMap){
53125             this.indexMap = this.buildIndexMap();
53126         }
53127         return this.indexMap.dataToCol[dataIndex];
53128     },
53129     
53130     /**
53131      * Set a css style for a column dynamically. 
53132      * @param {Number} colIndex The index of the column
53133      * @param {String} name The css property name
53134      * @param {String} value The css value
53135      */
53136     setCSSStyle : function(colIndex, name, value){
53137         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
53138         Roo.util.CSS.updateRule(selector, name, value);
53139     },
53140     
53141     generateRules : function(cm){
53142         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
53143         Roo.util.CSS.removeStyleSheet(rulesId);
53144         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53145             var cid = cm.getColumnId(i);
53146             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
53147                          this.tdSelector, cid, " {\n}\n",
53148                          this.hdSelector, cid, " {\n}\n",
53149                          this.splitSelector, cid, " {\n}\n");
53150         }
53151         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53152     }
53153 });/*
53154  * Based on:
53155  * Ext JS Library 1.1.1
53156  * Copyright(c) 2006-2007, Ext JS, LLC.
53157  *
53158  * Originally Released Under LGPL - original licence link has changed is not relivant.
53159  *
53160  * Fork - LGPL
53161  * <script type="text/javascript">
53162  */
53163
53164 // private
53165 // This is a support class used internally by the Grid components
53166 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
53167     this.grid = grid;
53168     this.view = grid.getView();
53169     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53170     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
53171     if(hd2){
53172         this.setHandleElId(Roo.id(hd));
53173         this.setOuterHandleElId(Roo.id(hd2));
53174     }
53175     this.scroll = false;
53176 };
53177 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
53178     maxDragWidth: 120,
53179     getDragData : function(e){
53180         var t = Roo.lib.Event.getTarget(e);
53181         var h = this.view.findHeaderCell(t);
53182         if(h){
53183             return {ddel: h.firstChild, header:h};
53184         }
53185         return false;
53186     },
53187
53188     onInitDrag : function(e){
53189         this.view.headersDisabled = true;
53190         var clone = this.dragData.ddel.cloneNode(true);
53191         clone.id = Roo.id();
53192         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
53193         this.proxy.update(clone);
53194         return true;
53195     },
53196
53197     afterValidDrop : function(){
53198         var v = this.view;
53199         setTimeout(function(){
53200             v.headersDisabled = false;
53201         }, 50);
53202     },
53203
53204     afterInvalidDrop : function(){
53205         var v = this.view;
53206         setTimeout(function(){
53207             v.headersDisabled = false;
53208         }, 50);
53209     }
53210 });
53211 /*
53212  * Based on:
53213  * Ext JS Library 1.1.1
53214  * Copyright(c) 2006-2007, Ext JS, LLC.
53215  *
53216  * Originally Released Under LGPL - original licence link has changed is not relivant.
53217  *
53218  * Fork - LGPL
53219  * <script type="text/javascript">
53220  */
53221 // private
53222 // This is a support class used internally by the Grid components
53223 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
53224     this.grid = grid;
53225     this.view = grid.getView();
53226     // split the proxies so they don't interfere with mouse events
53227     this.proxyTop = Roo.DomHelper.append(document.body, {
53228         cls:"col-move-top", html:"&#160;"
53229     }, true);
53230     this.proxyBottom = Roo.DomHelper.append(document.body, {
53231         cls:"col-move-bottom", html:"&#160;"
53232     }, true);
53233     this.proxyTop.hide = this.proxyBottom.hide = function(){
53234         this.setLeftTop(-100,-100);
53235         this.setStyle("visibility", "hidden");
53236     };
53237     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53238     // temporarily disabled
53239     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
53240     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
53241 };
53242 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
53243     proxyOffsets : [-4, -9],
53244     fly: Roo.Element.fly,
53245
53246     getTargetFromEvent : function(e){
53247         var t = Roo.lib.Event.getTarget(e);
53248         var cindex = this.view.findCellIndex(t);
53249         if(cindex !== false){
53250             return this.view.getHeaderCell(cindex);
53251         }
53252         return null;
53253     },
53254
53255     nextVisible : function(h){
53256         var v = this.view, cm = this.grid.colModel;
53257         h = h.nextSibling;
53258         while(h){
53259             if(!cm.isHidden(v.getCellIndex(h))){
53260                 return h;
53261             }
53262             h = h.nextSibling;
53263         }
53264         return null;
53265     },
53266
53267     prevVisible : function(h){
53268         var v = this.view, cm = this.grid.colModel;
53269         h = h.prevSibling;
53270         while(h){
53271             if(!cm.isHidden(v.getCellIndex(h))){
53272                 return h;
53273             }
53274             h = h.prevSibling;
53275         }
53276         return null;
53277     },
53278
53279     positionIndicator : function(h, n, e){
53280         var x = Roo.lib.Event.getPageX(e);
53281         var r = Roo.lib.Dom.getRegion(n.firstChild);
53282         var px, pt, py = r.top + this.proxyOffsets[1];
53283         if((r.right - x) <= (r.right-r.left)/2){
53284             px = r.right+this.view.borderWidth;
53285             pt = "after";
53286         }else{
53287             px = r.left;
53288             pt = "before";
53289         }
53290         var oldIndex = this.view.getCellIndex(h);
53291         var newIndex = this.view.getCellIndex(n);
53292
53293         if(this.grid.colModel.isFixed(newIndex)){
53294             return false;
53295         }
53296
53297         var locked = this.grid.colModel.isLocked(newIndex);
53298
53299         if(pt == "after"){
53300             newIndex++;
53301         }
53302         if(oldIndex < newIndex){
53303             newIndex--;
53304         }
53305         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
53306             return false;
53307         }
53308         px +=  this.proxyOffsets[0];
53309         this.proxyTop.setLeftTop(px, py);
53310         this.proxyTop.show();
53311         if(!this.bottomOffset){
53312             this.bottomOffset = this.view.mainHd.getHeight();
53313         }
53314         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
53315         this.proxyBottom.show();
53316         return pt;
53317     },
53318
53319     onNodeEnter : function(n, dd, e, data){
53320         if(data.header != n){
53321             this.positionIndicator(data.header, n, e);
53322         }
53323     },
53324
53325     onNodeOver : function(n, dd, e, data){
53326         var result = false;
53327         if(data.header != n){
53328             result = this.positionIndicator(data.header, n, e);
53329         }
53330         if(!result){
53331             this.proxyTop.hide();
53332             this.proxyBottom.hide();
53333         }
53334         return result ? this.dropAllowed : this.dropNotAllowed;
53335     },
53336
53337     onNodeOut : function(n, dd, e, data){
53338         this.proxyTop.hide();
53339         this.proxyBottom.hide();
53340     },
53341
53342     onNodeDrop : function(n, dd, e, data){
53343         var h = data.header;
53344         if(h != n){
53345             var cm = this.grid.colModel;
53346             var x = Roo.lib.Event.getPageX(e);
53347             var r = Roo.lib.Dom.getRegion(n.firstChild);
53348             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
53349             var oldIndex = this.view.getCellIndex(h);
53350             var newIndex = this.view.getCellIndex(n);
53351             var locked = cm.isLocked(newIndex);
53352             if(pt == "after"){
53353                 newIndex++;
53354             }
53355             if(oldIndex < newIndex){
53356                 newIndex--;
53357             }
53358             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
53359                 return false;
53360             }
53361             cm.setLocked(oldIndex, locked, true);
53362             cm.moveColumn(oldIndex, newIndex);
53363             this.grid.fireEvent("columnmove", oldIndex, newIndex);
53364             return true;
53365         }
53366         return false;
53367     }
53368 });
53369 /*
53370  * Based on:
53371  * Ext JS Library 1.1.1
53372  * Copyright(c) 2006-2007, Ext JS, LLC.
53373  *
53374  * Originally Released Under LGPL - original licence link has changed is not relivant.
53375  *
53376  * Fork - LGPL
53377  * <script type="text/javascript">
53378  */
53379   
53380 /**
53381  * @class Roo.grid.GridView
53382  * @extends Roo.util.Observable
53383  *
53384  * @constructor
53385  * @param {Object} config
53386  */
53387 Roo.grid.GridView = function(config){
53388     Roo.grid.GridView.superclass.constructor.call(this);
53389     this.el = null;
53390
53391     Roo.apply(this, config);
53392 };
53393
53394 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
53395
53396     unselectable :  'unselectable="on"',
53397     unselectableCls :  'x-unselectable',
53398     
53399     
53400     rowClass : "x-grid-row",
53401
53402     cellClass : "x-grid-col",
53403
53404     tdClass : "x-grid-td",
53405
53406     hdClass : "x-grid-hd",
53407
53408     splitClass : "x-grid-split",
53409
53410     sortClasses : ["sort-asc", "sort-desc"],
53411
53412     enableMoveAnim : false,
53413
53414     hlColor: "C3DAF9",
53415
53416     dh : Roo.DomHelper,
53417
53418     fly : Roo.Element.fly,
53419
53420     css : Roo.util.CSS,
53421
53422     borderWidth: 1,
53423
53424     splitOffset: 3,
53425
53426     scrollIncrement : 22,
53427
53428     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53429
53430     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53431
53432     bind : function(ds, cm){
53433         if(this.ds){
53434             this.ds.un("load", this.onLoad, this);
53435             this.ds.un("datachanged", this.onDataChange, this);
53436             this.ds.un("add", this.onAdd, this);
53437             this.ds.un("remove", this.onRemove, this);
53438             this.ds.un("update", this.onUpdate, this);
53439             this.ds.un("clear", this.onClear, this);
53440         }
53441         if(ds){
53442             ds.on("load", this.onLoad, this);
53443             ds.on("datachanged", this.onDataChange, this);
53444             ds.on("add", this.onAdd, this);
53445             ds.on("remove", this.onRemove, this);
53446             ds.on("update", this.onUpdate, this);
53447             ds.on("clear", this.onClear, this);
53448         }
53449         this.ds = ds;
53450
53451         if(this.cm){
53452             this.cm.un("widthchange", this.onColWidthChange, this);
53453             this.cm.un("headerchange", this.onHeaderChange, this);
53454             this.cm.un("hiddenchange", this.onHiddenChange, this);
53455             this.cm.un("columnmoved", this.onColumnMove, this);
53456             this.cm.un("columnlockchange", this.onColumnLock, this);
53457         }
53458         if(cm){
53459             this.generateRules(cm);
53460             cm.on("widthchange", this.onColWidthChange, this);
53461             cm.on("headerchange", this.onHeaderChange, this);
53462             cm.on("hiddenchange", this.onHiddenChange, this);
53463             cm.on("columnmoved", this.onColumnMove, this);
53464             cm.on("columnlockchange", this.onColumnLock, this);
53465         }
53466         this.cm = cm;
53467     },
53468
53469     init: function(grid){
53470         Roo.grid.GridView.superclass.init.call(this, grid);
53471
53472         this.bind(grid.dataSource, grid.colModel);
53473
53474         grid.on("headerclick", this.handleHeaderClick, this);
53475
53476         if(grid.trackMouseOver){
53477             grid.on("mouseover", this.onRowOver, this);
53478             grid.on("mouseout", this.onRowOut, this);
53479         }
53480         grid.cancelTextSelection = function(){};
53481         this.gridId = grid.id;
53482
53483         var tpls = this.templates || {};
53484
53485         if(!tpls.master){
53486             tpls.master = new Roo.Template(
53487                '<div class="x-grid" hidefocus="true">',
53488                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53489                   '<div class="x-grid-topbar"></div>',
53490                   '<div class="x-grid-scroller"><div></div></div>',
53491                   '<div class="x-grid-locked">',
53492                       '<div class="x-grid-header">{lockedHeader}</div>',
53493                       '<div class="x-grid-body">{lockedBody}</div>',
53494                   "</div>",
53495                   '<div class="x-grid-viewport">',
53496                       '<div class="x-grid-header">{header}</div>',
53497                       '<div class="x-grid-body">{body}</div>',
53498                   "</div>",
53499                   '<div class="x-grid-bottombar"></div>',
53500                  
53501                   '<div class="x-grid-resize-proxy">&#160;</div>',
53502                "</div>"
53503             );
53504             tpls.master.disableformats = true;
53505         }
53506
53507         if(!tpls.header){
53508             tpls.header = new Roo.Template(
53509                '<table border="0" cellspacing="0" cellpadding="0">',
53510                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53511                "</table>{splits}"
53512             );
53513             tpls.header.disableformats = true;
53514         }
53515         tpls.header.compile();
53516
53517         if(!tpls.hcell){
53518             tpls.hcell = new Roo.Template(
53519                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53520                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53521                 "</div></td>"
53522              );
53523              tpls.hcell.disableFormats = true;
53524         }
53525         tpls.hcell.compile();
53526
53527         if(!tpls.hsplit){
53528             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53529                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53530             tpls.hsplit.disableFormats = true;
53531         }
53532         tpls.hsplit.compile();
53533
53534         if(!tpls.body){
53535             tpls.body = new Roo.Template(
53536                '<table border="0" cellspacing="0" cellpadding="0">',
53537                "<tbody>{rows}</tbody>",
53538                "</table>"
53539             );
53540             tpls.body.disableFormats = true;
53541         }
53542         tpls.body.compile();
53543
53544         if(!tpls.row){
53545             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53546             tpls.row.disableFormats = true;
53547         }
53548         tpls.row.compile();
53549
53550         if(!tpls.cell){
53551             tpls.cell = new Roo.Template(
53552                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53553                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53554                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53555                 "</td>"
53556             );
53557             tpls.cell.disableFormats = true;
53558         }
53559         tpls.cell.compile();
53560
53561         this.templates = tpls;
53562     },
53563
53564     // remap these for backwards compat
53565     onColWidthChange : function(){
53566         this.updateColumns.apply(this, arguments);
53567     },
53568     onHeaderChange : function(){
53569         this.updateHeaders.apply(this, arguments);
53570     }, 
53571     onHiddenChange : function(){
53572         this.handleHiddenChange.apply(this, arguments);
53573     },
53574     onColumnMove : function(){
53575         this.handleColumnMove.apply(this, arguments);
53576     },
53577     onColumnLock : function(){
53578         this.handleLockChange.apply(this, arguments);
53579     },
53580
53581     onDataChange : function(){
53582         this.refresh();
53583         this.updateHeaderSortState();
53584     },
53585
53586     onClear : function(){
53587         this.refresh();
53588     },
53589
53590     onUpdate : function(ds, record){
53591         this.refreshRow(record);
53592     },
53593
53594     refreshRow : function(record){
53595         var ds = this.ds, index;
53596         if(typeof record == 'number'){
53597             index = record;
53598             record = ds.getAt(index);
53599         }else{
53600             index = ds.indexOf(record);
53601         }
53602         this.insertRows(ds, index, index, true);
53603         this.onRemove(ds, record, index+1, true);
53604         this.syncRowHeights(index, index);
53605         this.layout();
53606         this.fireEvent("rowupdated", this, index, record);
53607     },
53608
53609     onAdd : function(ds, records, index){
53610         this.insertRows(ds, index, index + (records.length-1));
53611     },
53612
53613     onRemove : function(ds, record, index, isUpdate){
53614         if(isUpdate !== true){
53615             this.fireEvent("beforerowremoved", this, index, record);
53616         }
53617         var bt = this.getBodyTable(), lt = this.getLockedTable();
53618         if(bt.rows[index]){
53619             bt.firstChild.removeChild(bt.rows[index]);
53620         }
53621         if(lt.rows[index]){
53622             lt.firstChild.removeChild(lt.rows[index]);
53623         }
53624         if(isUpdate !== true){
53625             this.stripeRows(index);
53626             this.syncRowHeights(index, index);
53627             this.layout();
53628             this.fireEvent("rowremoved", this, index, record);
53629         }
53630     },
53631
53632     onLoad : function(){
53633         this.scrollToTop();
53634     },
53635
53636     /**
53637      * Scrolls the grid to the top
53638      */
53639     scrollToTop : function(){
53640         if(this.scroller){
53641             this.scroller.dom.scrollTop = 0;
53642             this.syncScroll();
53643         }
53644     },
53645
53646     /**
53647      * Gets a panel in the header of the grid that can be used for toolbars etc.
53648      * After modifying the contents of this panel a call to grid.autoSize() may be
53649      * required to register any changes in size.
53650      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53651      * @return Roo.Element
53652      */
53653     getHeaderPanel : function(doShow){
53654         if(doShow){
53655             this.headerPanel.show();
53656         }
53657         return this.headerPanel;
53658     },
53659
53660     /**
53661      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53662      * After modifying the contents of this panel a call to grid.autoSize() may be
53663      * required to register any changes in size.
53664      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53665      * @return Roo.Element
53666      */
53667     getFooterPanel : function(doShow){
53668         if(doShow){
53669             this.footerPanel.show();
53670         }
53671         return this.footerPanel;
53672     },
53673
53674     initElements : function(){
53675         var E = Roo.Element;
53676         var el = this.grid.getGridEl().dom.firstChild;
53677         var cs = el.childNodes;
53678
53679         this.el = new E(el);
53680         
53681          this.focusEl = new E(el.firstChild);
53682         this.focusEl.swallowEvent("click", true);
53683         
53684         this.headerPanel = new E(cs[1]);
53685         this.headerPanel.enableDisplayMode("block");
53686
53687         this.scroller = new E(cs[2]);
53688         this.scrollSizer = new E(this.scroller.dom.firstChild);
53689
53690         this.lockedWrap = new E(cs[3]);
53691         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53692         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53693
53694         this.mainWrap = new E(cs[4]);
53695         this.mainHd = new E(this.mainWrap.dom.firstChild);
53696         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53697
53698         this.footerPanel = new E(cs[5]);
53699         this.footerPanel.enableDisplayMode("block");
53700
53701         this.resizeProxy = new E(cs[6]);
53702
53703         this.headerSelector = String.format(
53704            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53705            this.lockedHd.id, this.mainHd.id
53706         );
53707
53708         this.splitterSelector = String.format(
53709            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53710            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53711         );
53712     },
53713     idToCssName : function(s)
53714     {
53715         return s.replace(/[^a-z0-9]+/ig, '-');
53716     },
53717
53718     getHeaderCell : function(index){
53719         return Roo.DomQuery.select(this.headerSelector)[index];
53720     },
53721
53722     getHeaderCellMeasure : function(index){
53723         return this.getHeaderCell(index).firstChild;
53724     },
53725
53726     getHeaderCellText : function(index){
53727         return this.getHeaderCell(index).firstChild.firstChild;
53728     },
53729
53730     getLockedTable : function(){
53731         return this.lockedBody.dom.firstChild;
53732     },
53733
53734     getBodyTable : function(){
53735         return this.mainBody.dom.firstChild;
53736     },
53737
53738     getLockedRow : function(index){
53739         return this.getLockedTable().rows[index];
53740     },
53741
53742     getRow : function(index){
53743         return this.getBodyTable().rows[index];
53744     },
53745
53746     getRowComposite : function(index){
53747         if(!this.rowEl){
53748             this.rowEl = new Roo.CompositeElementLite();
53749         }
53750         var els = [], lrow, mrow;
53751         if(lrow = this.getLockedRow(index)){
53752             els.push(lrow);
53753         }
53754         if(mrow = this.getRow(index)){
53755             els.push(mrow);
53756         }
53757         this.rowEl.elements = els;
53758         return this.rowEl;
53759     },
53760     /**
53761      * Gets the 'td' of the cell
53762      * 
53763      * @param {Integer} rowIndex row to select
53764      * @param {Integer} colIndex column to select
53765      * 
53766      * @return {Object} 
53767      */
53768     getCell : function(rowIndex, colIndex){
53769         var locked = this.cm.getLockedCount();
53770         var source;
53771         if(colIndex < locked){
53772             source = this.lockedBody.dom.firstChild;
53773         }else{
53774             source = this.mainBody.dom.firstChild;
53775             colIndex -= locked;
53776         }
53777         return source.rows[rowIndex].childNodes[colIndex];
53778     },
53779
53780     getCellText : function(rowIndex, colIndex){
53781         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53782     },
53783
53784     getCellBox : function(cell){
53785         var b = this.fly(cell).getBox();
53786         if(Roo.isOpera){ // opera fails to report the Y
53787             b.y = cell.offsetTop + this.mainBody.getY();
53788         }
53789         return b;
53790     },
53791
53792     getCellIndex : function(cell){
53793         var id = String(cell.className).match(this.cellRE);
53794         if(id){
53795             return parseInt(id[1], 10);
53796         }
53797         return 0;
53798     },
53799
53800     findHeaderIndex : function(n){
53801         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53802         return r ? this.getCellIndex(r) : false;
53803     },
53804
53805     findHeaderCell : function(n){
53806         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53807         return r ? r : false;
53808     },
53809
53810     findRowIndex : function(n){
53811         if(!n){
53812             return false;
53813         }
53814         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53815         return r ? r.rowIndex : false;
53816     },
53817
53818     findCellIndex : function(node){
53819         var stop = this.el.dom;
53820         while(node && node != stop){
53821             if(this.findRE.test(node.className)){
53822                 return this.getCellIndex(node);
53823             }
53824             node = node.parentNode;
53825         }
53826         return false;
53827     },
53828
53829     getColumnId : function(index){
53830         return this.cm.getColumnId(index);
53831     },
53832
53833     getSplitters : function()
53834     {
53835         if(this.splitterSelector){
53836            return Roo.DomQuery.select(this.splitterSelector);
53837         }else{
53838             return null;
53839       }
53840     },
53841
53842     getSplitter : function(index){
53843         return this.getSplitters()[index];
53844     },
53845
53846     onRowOver : function(e, t){
53847         var row;
53848         if((row = this.findRowIndex(t)) !== false){
53849             this.getRowComposite(row).addClass("x-grid-row-over");
53850         }
53851     },
53852
53853     onRowOut : function(e, t){
53854         var row;
53855         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53856             this.getRowComposite(row).removeClass("x-grid-row-over");
53857         }
53858     },
53859
53860     renderHeaders : function(){
53861         var cm = this.cm;
53862         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53863         var cb = [], lb = [], sb = [], lsb = [], p = {};
53864         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53865             p.cellId = "x-grid-hd-0-" + i;
53866             p.splitId = "x-grid-csplit-0-" + i;
53867             p.id = cm.getColumnId(i);
53868             p.title = cm.getColumnTooltip(i) || "";
53869             p.value = cm.getColumnHeader(i) || "";
53870             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53871             if(!cm.isLocked(i)){
53872                 cb[cb.length] = ct.apply(p);
53873                 sb[sb.length] = st.apply(p);
53874             }else{
53875                 lb[lb.length] = ct.apply(p);
53876                 lsb[lsb.length] = st.apply(p);
53877             }
53878         }
53879         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53880                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53881     },
53882
53883     updateHeaders : function(){
53884         var html = this.renderHeaders();
53885         this.lockedHd.update(html[0]);
53886         this.mainHd.update(html[1]);
53887     },
53888
53889     /**
53890      * Focuses the specified row.
53891      * @param {Number} row The row index
53892      */
53893     focusRow : function(row)
53894     {
53895         //Roo.log('GridView.focusRow');
53896         var x = this.scroller.dom.scrollLeft;
53897         this.focusCell(row, 0, false);
53898         this.scroller.dom.scrollLeft = x;
53899     },
53900
53901     /**
53902      * Focuses the specified cell.
53903      * @param {Number} row The row index
53904      * @param {Number} col The column index
53905      * @param {Boolean} hscroll false to disable horizontal scrolling
53906      */
53907     focusCell : function(row, col, hscroll)
53908     {
53909         //Roo.log('GridView.focusCell');
53910         var el = this.ensureVisible(row, col, hscroll);
53911         this.focusEl.alignTo(el, "tl-tl");
53912         if(Roo.isGecko){
53913             this.focusEl.focus();
53914         }else{
53915             this.focusEl.focus.defer(1, this.focusEl);
53916         }
53917     },
53918
53919     /**
53920      * Scrolls the specified cell into view
53921      * @param {Number} row The row index
53922      * @param {Number} col The column index
53923      * @param {Boolean} hscroll false to disable horizontal scrolling
53924      */
53925     ensureVisible : function(row, col, hscroll)
53926     {
53927         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53928         //return null; //disable for testing.
53929         if(typeof row != "number"){
53930             row = row.rowIndex;
53931         }
53932         if(row < 0 && row >= this.ds.getCount()){
53933             return  null;
53934         }
53935         col = (col !== undefined ? col : 0);
53936         var cm = this.grid.colModel;
53937         while(cm.isHidden(col)){
53938             col++;
53939         }
53940
53941         var el = this.getCell(row, col);
53942         if(!el){
53943             return null;
53944         }
53945         var c = this.scroller.dom;
53946
53947         var ctop = parseInt(el.offsetTop, 10);
53948         var cleft = parseInt(el.offsetLeft, 10);
53949         var cbot = ctop + el.offsetHeight;
53950         var cright = cleft + el.offsetWidth;
53951         
53952         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53953         var stop = parseInt(c.scrollTop, 10);
53954         var sleft = parseInt(c.scrollLeft, 10);
53955         var sbot = stop + ch;
53956         var sright = sleft + c.clientWidth;
53957         /*
53958         Roo.log('GridView.ensureVisible:' +
53959                 ' ctop:' + ctop +
53960                 ' c.clientHeight:' + c.clientHeight +
53961                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53962                 ' stop:' + stop +
53963                 ' cbot:' + cbot +
53964                 ' sbot:' + sbot +
53965                 ' ch:' + ch  
53966                 );
53967         */
53968         if(ctop < stop){
53969              c.scrollTop = ctop;
53970             //Roo.log("set scrolltop to ctop DISABLE?");
53971         }else if(cbot > sbot){
53972             //Roo.log("set scrolltop to cbot-ch");
53973             c.scrollTop = cbot-ch;
53974         }
53975         
53976         if(hscroll !== false){
53977             if(cleft < sleft){
53978                 c.scrollLeft = cleft;
53979             }else if(cright > sright){
53980                 c.scrollLeft = cright-c.clientWidth;
53981             }
53982         }
53983          
53984         return el;
53985     },
53986
53987     updateColumns : function(){
53988         this.grid.stopEditing();
53989         var cm = this.grid.colModel, colIds = this.getColumnIds();
53990         //var totalWidth = cm.getTotalWidth();
53991         var pos = 0;
53992         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53993             //if(cm.isHidden(i)) continue;
53994             var w = cm.getColumnWidth(i);
53995             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53996             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53997         }
53998         this.updateSplitters();
53999     },
54000
54001     generateRules : function(cm){
54002         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
54003         Roo.util.CSS.removeStyleSheet(rulesId);
54004         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54005             var cid = cm.getColumnId(i);
54006             var align = '';
54007             if(cm.config[i].align){
54008                 align = 'text-align:'+cm.config[i].align+';';
54009             }
54010             var hidden = '';
54011             if(cm.isHidden(i)){
54012                 hidden = 'display:none;';
54013             }
54014             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
54015             ruleBuf.push(
54016                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
54017                     this.hdSelector, cid, " {\n", align, width, "}\n",
54018                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
54019                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
54020         }
54021         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54022     },
54023
54024     updateSplitters : function(){
54025         var cm = this.cm, s = this.getSplitters();
54026         if(s){ // splitters not created yet
54027             var pos = 0, locked = true;
54028             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54029                 if(cm.isHidden(i)) continue;
54030                 var w = cm.getColumnWidth(i); // make sure it's a number
54031                 if(!cm.isLocked(i) && locked){
54032                     pos = 0;
54033                     locked = false;
54034                 }
54035                 pos += w;
54036                 s[i].style.left = (pos-this.splitOffset) + "px";
54037             }
54038         }
54039     },
54040
54041     handleHiddenChange : function(colModel, colIndex, hidden){
54042         if(hidden){
54043             this.hideColumn(colIndex);
54044         }else{
54045             this.unhideColumn(colIndex);
54046         }
54047     },
54048
54049     hideColumn : function(colIndex){
54050         var cid = this.getColumnId(colIndex);
54051         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
54052         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
54053         if(Roo.isSafari){
54054             this.updateHeaders();
54055         }
54056         this.updateSplitters();
54057         this.layout();
54058     },
54059
54060     unhideColumn : function(colIndex){
54061         var cid = this.getColumnId(colIndex);
54062         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
54063         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
54064
54065         if(Roo.isSafari){
54066             this.updateHeaders();
54067         }
54068         this.updateSplitters();
54069         this.layout();
54070     },
54071
54072     insertRows : function(dm, firstRow, lastRow, isUpdate){
54073         if(firstRow == 0 && lastRow == dm.getCount()-1){
54074             this.refresh();
54075         }else{
54076             if(!isUpdate){
54077                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
54078             }
54079             var s = this.getScrollState();
54080             var markup = this.renderRows(firstRow, lastRow);
54081             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
54082             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
54083             this.restoreScroll(s);
54084             if(!isUpdate){
54085                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
54086                 this.syncRowHeights(firstRow, lastRow);
54087                 this.stripeRows(firstRow);
54088                 this.layout();
54089             }
54090         }
54091     },
54092
54093     bufferRows : function(markup, target, index){
54094         var before = null, trows = target.rows, tbody = target.tBodies[0];
54095         if(index < trows.length){
54096             before = trows[index];
54097         }
54098         var b = document.createElement("div");
54099         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
54100         var rows = b.firstChild.rows;
54101         for(var i = 0, len = rows.length; i < len; i++){
54102             if(before){
54103                 tbody.insertBefore(rows[0], before);
54104             }else{
54105                 tbody.appendChild(rows[0]);
54106             }
54107         }
54108         b.innerHTML = "";
54109         b = null;
54110     },
54111
54112     deleteRows : function(dm, firstRow, lastRow){
54113         if(dm.getRowCount()<1){
54114             this.fireEvent("beforerefresh", this);
54115             this.mainBody.update("");
54116             this.lockedBody.update("");
54117             this.fireEvent("refresh", this);
54118         }else{
54119             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
54120             var bt = this.getBodyTable();
54121             var tbody = bt.firstChild;
54122             var rows = bt.rows;
54123             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
54124                 tbody.removeChild(rows[firstRow]);
54125             }
54126             this.stripeRows(firstRow);
54127             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
54128         }
54129     },
54130
54131     updateRows : function(dataSource, firstRow, lastRow){
54132         var s = this.getScrollState();
54133         this.refresh();
54134         this.restoreScroll(s);
54135     },
54136
54137     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
54138         if(!noRefresh){
54139            this.refresh();
54140         }
54141         this.updateHeaderSortState();
54142     },
54143
54144     getScrollState : function(){
54145         
54146         var sb = this.scroller.dom;
54147         return {left: sb.scrollLeft, top: sb.scrollTop};
54148     },
54149
54150     stripeRows : function(startRow){
54151         if(!this.grid.stripeRows || this.ds.getCount() < 1){
54152             return;
54153         }
54154         startRow = startRow || 0;
54155         var rows = this.getBodyTable().rows;
54156         var lrows = this.getLockedTable().rows;
54157         var cls = ' x-grid-row-alt ';
54158         for(var i = startRow, len = rows.length; i < len; i++){
54159             var row = rows[i], lrow = lrows[i];
54160             var isAlt = ((i+1) % 2 == 0);
54161             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
54162             if(isAlt == hasAlt){
54163                 continue;
54164             }
54165             if(isAlt){
54166                 row.className += " x-grid-row-alt";
54167             }else{
54168                 row.className = row.className.replace("x-grid-row-alt", "");
54169             }
54170             if(lrow){
54171                 lrow.className = row.className;
54172             }
54173         }
54174     },
54175
54176     restoreScroll : function(state){
54177         //Roo.log('GridView.restoreScroll');
54178         var sb = this.scroller.dom;
54179         sb.scrollLeft = state.left;
54180         sb.scrollTop = state.top;
54181         this.syncScroll();
54182     },
54183
54184     syncScroll : function(){
54185         //Roo.log('GridView.syncScroll');
54186         var sb = this.scroller.dom;
54187         var sh = this.mainHd.dom;
54188         var bs = this.mainBody.dom;
54189         var lv = this.lockedBody.dom;
54190         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
54191         lv.scrollTop = bs.scrollTop = sb.scrollTop;
54192     },
54193
54194     handleScroll : function(e){
54195         this.syncScroll();
54196         var sb = this.scroller.dom;
54197         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
54198         e.stopEvent();
54199     },
54200
54201     handleWheel : function(e){
54202         var d = e.getWheelDelta();
54203         this.scroller.dom.scrollTop -= d*22;
54204         // set this here to prevent jumpy scrolling on large tables
54205         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
54206         e.stopEvent();
54207     },
54208
54209     renderRows : function(startRow, endRow){
54210         // pull in all the crap needed to render rows
54211         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
54212         var colCount = cm.getColumnCount();
54213
54214         if(ds.getCount() < 1){
54215             return ["", ""];
54216         }
54217
54218         // build a map for all the columns
54219         var cs = [];
54220         for(var i = 0; i < colCount; i++){
54221             var name = cm.getDataIndex(i);
54222             cs[i] = {
54223                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
54224                 renderer : cm.getRenderer(i),
54225                 id : cm.getColumnId(i),
54226                 locked : cm.isLocked(i)
54227             };
54228         }
54229
54230         startRow = startRow || 0;
54231         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
54232
54233         // records to render
54234         var rs = ds.getRange(startRow, endRow);
54235
54236         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
54237     },
54238
54239     // As much as I hate to duplicate code, this was branched because FireFox really hates
54240     // [].join("") on strings. The performance difference was substantial enough to
54241     // branch this function
54242     doRender : Roo.isGecko ?
54243             function(cs, rs, ds, startRow, colCount, stripe){
54244                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54245                 // buffers
54246                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54247                 
54248                 var hasListener = this.grid.hasListener('rowclass');
54249                 var rowcfg = {};
54250                 for(var j = 0, len = rs.length; j < len; j++){
54251                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
54252                     for(var i = 0; i < colCount; i++){
54253                         c = cs[i];
54254                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54255                         p.id = c.id;
54256                         p.css = p.attr = "";
54257                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54258                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54259                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54260                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54261                         }
54262                         var markup = ct.apply(p);
54263                         if(!c.locked){
54264                             cb+= markup;
54265                         }else{
54266                             lcb+= markup;
54267                         }
54268                     }
54269                     var alt = [];
54270                     if(stripe && ((rowIndex+1) % 2 == 0)){
54271                         alt.push("x-grid-row-alt")
54272                     }
54273                     if(r.dirty){
54274                         alt.push(  " x-grid-dirty-row");
54275                     }
54276                     rp.cells = lcb;
54277                     if(this.getRowClass){
54278                         alt.push(this.getRowClass(r, rowIndex));
54279                     }
54280                     if (hasListener) {
54281                         rowcfg = {
54282                              
54283                             record: r,
54284                             rowIndex : rowIndex,
54285                             rowClass : ''
54286                         }
54287                         this.grid.fireEvent('rowclass', this, rowcfg);
54288                         alt.push(rowcfg.rowClass);
54289                     }
54290                     rp.alt = alt.join(" ");
54291                     lbuf+= rt.apply(rp);
54292                     rp.cells = cb;
54293                     buf+=  rt.apply(rp);
54294                 }
54295                 return [lbuf, buf];
54296             } :
54297             function(cs, rs, ds, startRow, colCount, stripe){
54298                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54299                 // buffers
54300                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54301                 var hasListener = this.grid.hasListener('rowclass');
54302  
54303                 var rowcfg = {};
54304                 for(var j = 0, len = rs.length; j < len; j++){
54305                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
54306                     for(var i = 0; i < colCount; i++){
54307                         c = cs[i];
54308                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54309                         p.id = c.id;
54310                         p.css = p.attr = "";
54311                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54312                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54313                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54314                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54315                         }
54316                         
54317                         var markup = ct.apply(p);
54318                         if(!c.locked){
54319                             cb[cb.length] = markup;
54320                         }else{
54321                             lcb[lcb.length] = markup;
54322                         }
54323                     }
54324                     var alt = [];
54325                     if(stripe && ((rowIndex+1) % 2 == 0)){
54326                         alt.push( "x-grid-row-alt");
54327                     }
54328                     if(r.dirty){
54329                         alt.push(" x-grid-dirty-row");
54330                     }
54331                     rp.cells = lcb;
54332                     if(this.getRowClass){
54333                         alt.push( this.getRowClass(r, rowIndex));
54334                     }
54335                     if (hasListener) {
54336                         rowcfg = {
54337                              
54338                             record: r,
54339                             rowIndex : rowIndex,
54340                             rowClass : ''
54341                         }
54342                         this.grid.fireEvent('rowclass', this, rowcfg);
54343                         alt.push(rowcfg.rowClass);
54344                     }
54345                     rp.alt = alt.join(" ");
54346                     rp.cells = lcb.join("");
54347                     lbuf[lbuf.length] = rt.apply(rp);
54348                     rp.cells = cb.join("");
54349                     buf[buf.length] =  rt.apply(rp);
54350                 }
54351                 return [lbuf.join(""), buf.join("")];
54352             },
54353
54354     renderBody : function(){
54355         var markup = this.renderRows();
54356         var bt = this.templates.body;
54357         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
54358     },
54359
54360     /**
54361      * Refreshes the grid
54362      * @param {Boolean} headersToo
54363      */
54364     refresh : function(headersToo){
54365         this.fireEvent("beforerefresh", this);
54366         this.grid.stopEditing();
54367         var result = this.renderBody();
54368         this.lockedBody.update(result[0]);
54369         this.mainBody.update(result[1]);
54370         if(headersToo === true){
54371             this.updateHeaders();
54372             this.updateColumns();
54373             this.updateSplitters();
54374             this.updateHeaderSortState();
54375         }
54376         this.syncRowHeights();
54377         this.layout();
54378         this.fireEvent("refresh", this);
54379     },
54380
54381     handleColumnMove : function(cm, oldIndex, newIndex){
54382         this.indexMap = null;
54383         var s = this.getScrollState();
54384         this.refresh(true);
54385         this.restoreScroll(s);
54386         this.afterMove(newIndex);
54387     },
54388
54389     afterMove : function(colIndex){
54390         if(this.enableMoveAnim && Roo.enableFx){
54391             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
54392         }
54393         // if multisort - fix sortOrder, and reload..
54394         if (this.grid.dataSource.multiSort) {
54395             // the we can call sort again..
54396             var dm = this.grid.dataSource;
54397             var cm = this.grid.colModel;
54398             var so = [];
54399             for(var i = 0; i < cm.config.length; i++ ) {
54400                 
54401                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
54402                     continue; // dont' bother, it's not in sort list or being set.
54403                 }
54404                 
54405                 so.push(cm.config[i].dataIndex);
54406             };
54407             dm.sortOrder = so;
54408             dm.load(dm.lastOptions);
54409             
54410             
54411         }
54412         
54413     },
54414
54415     updateCell : function(dm, rowIndex, dataIndex){
54416         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54417         if(typeof colIndex == "undefined"){ // not present in grid
54418             return;
54419         }
54420         var cm = this.grid.colModel;
54421         var cell = this.getCell(rowIndex, colIndex);
54422         var cellText = this.getCellText(rowIndex, colIndex);
54423
54424         var p = {
54425             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54426             id : cm.getColumnId(colIndex),
54427             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54428         };
54429         var renderer = cm.getRenderer(colIndex);
54430         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54431         if(typeof val == "undefined" || val === "") val = "&#160;";
54432         cellText.innerHTML = val;
54433         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54434         this.syncRowHeights(rowIndex, rowIndex);
54435     },
54436
54437     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54438         var maxWidth = 0;
54439         if(this.grid.autoSizeHeaders){
54440             var h = this.getHeaderCellMeasure(colIndex);
54441             maxWidth = Math.max(maxWidth, h.scrollWidth);
54442         }
54443         var tb, index;
54444         if(this.cm.isLocked(colIndex)){
54445             tb = this.getLockedTable();
54446             index = colIndex;
54447         }else{
54448             tb = this.getBodyTable();
54449             index = colIndex - this.cm.getLockedCount();
54450         }
54451         if(tb && tb.rows){
54452             var rows = tb.rows;
54453             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54454             for(var i = 0; i < stopIndex; i++){
54455                 var cell = rows[i].childNodes[index].firstChild;
54456                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54457             }
54458         }
54459         return maxWidth + /*margin for error in IE*/ 5;
54460     },
54461     /**
54462      * Autofit a column to its content.
54463      * @param {Number} colIndex
54464      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54465      */
54466      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54467          if(this.cm.isHidden(colIndex)){
54468              return; // can't calc a hidden column
54469          }
54470         if(forceMinSize){
54471             var cid = this.cm.getColumnId(colIndex);
54472             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54473            if(this.grid.autoSizeHeaders){
54474                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54475            }
54476         }
54477         var newWidth = this.calcColumnWidth(colIndex);
54478         this.cm.setColumnWidth(colIndex,
54479             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54480         if(!suppressEvent){
54481             this.grid.fireEvent("columnresize", colIndex, newWidth);
54482         }
54483     },
54484
54485     /**
54486      * Autofits all columns to their content and then expands to fit any extra space in the grid
54487      */
54488      autoSizeColumns : function(){
54489         var cm = this.grid.colModel;
54490         var colCount = cm.getColumnCount();
54491         for(var i = 0; i < colCount; i++){
54492             this.autoSizeColumn(i, true, true);
54493         }
54494         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54495             this.fitColumns();
54496         }else{
54497             this.updateColumns();
54498             this.layout();
54499         }
54500     },
54501
54502     /**
54503      * Autofits all columns to the grid's width proportionate with their current size
54504      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54505      */
54506     fitColumns : function(reserveScrollSpace){
54507         var cm = this.grid.colModel;
54508         var colCount = cm.getColumnCount();
54509         var cols = [];
54510         var width = 0;
54511         var i, w;
54512         for (i = 0; i < colCount; i++){
54513             if(!cm.isHidden(i) && !cm.isFixed(i)){
54514                 w = cm.getColumnWidth(i);
54515                 cols.push(i);
54516                 cols.push(w);
54517                 width += w;
54518             }
54519         }
54520         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54521         if(reserveScrollSpace){
54522             avail -= 17;
54523         }
54524         var frac = (avail - cm.getTotalWidth())/width;
54525         while (cols.length){
54526             w = cols.pop();
54527             i = cols.pop();
54528             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54529         }
54530         this.updateColumns();
54531         this.layout();
54532     },
54533
54534     onRowSelect : function(rowIndex){
54535         var row = this.getRowComposite(rowIndex);
54536         row.addClass("x-grid-row-selected");
54537     },
54538
54539     onRowDeselect : function(rowIndex){
54540         var row = this.getRowComposite(rowIndex);
54541         row.removeClass("x-grid-row-selected");
54542     },
54543
54544     onCellSelect : function(row, col){
54545         var cell = this.getCell(row, col);
54546         if(cell){
54547             Roo.fly(cell).addClass("x-grid-cell-selected");
54548         }
54549     },
54550
54551     onCellDeselect : function(row, col){
54552         var cell = this.getCell(row, col);
54553         if(cell){
54554             Roo.fly(cell).removeClass("x-grid-cell-selected");
54555         }
54556     },
54557
54558     updateHeaderSortState : function(){
54559         
54560         // sort state can be single { field: xxx, direction : yyy}
54561         // or   { xxx=>ASC , yyy : DESC ..... }
54562         
54563         var mstate = {};
54564         if (!this.ds.multiSort) { 
54565             var state = this.ds.getSortState();
54566             if(!state){
54567                 return;
54568             }
54569             mstate[state.field] = state.direction;
54570             // FIXME... - this is not used here.. but might be elsewhere..
54571             this.sortState = state;
54572             
54573         } else {
54574             mstate = this.ds.sortToggle;
54575         }
54576         //remove existing sort classes..
54577         
54578         var sc = this.sortClasses;
54579         var hds = this.el.select(this.headerSelector).removeClass(sc);
54580         
54581         for(var f in mstate) {
54582         
54583             var sortColumn = this.cm.findColumnIndex(f);
54584             
54585             if(sortColumn != -1){
54586                 var sortDir = mstate[f];        
54587                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54588             }
54589         }
54590         
54591          
54592         
54593     },
54594
54595
54596     handleHeaderClick : function(g, index,e){
54597         
54598         Roo.log("header click");
54599         
54600         if (Roo.isTouch) {
54601             // touch events on header are handled by context
54602             this.handleHdCtx(g,index,e);
54603             return;
54604         }
54605         
54606         
54607         if(this.headersDisabled){
54608             return;
54609         }
54610         var dm = g.dataSource, cm = g.colModel;
54611         if(!cm.isSortable(index)){
54612             return;
54613         }
54614         g.stopEditing();
54615         
54616         if (dm.multiSort) {
54617             // update the sortOrder
54618             var so = [];
54619             for(var i = 0; i < cm.config.length; i++ ) {
54620                 
54621                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54622                     continue; // dont' bother, it's not in sort list or being set.
54623                 }
54624                 
54625                 so.push(cm.config[i].dataIndex);
54626             };
54627             dm.sortOrder = so;
54628         }
54629         
54630         
54631         dm.sort(cm.getDataIndex(index));
54632     },
54633
54634
54635     destroy : function(){
54636         if(this.colMenu){
54637             this.colMenu.removeAll();
54638             Roo.menu.MenuMgr.unregister(this.colMenu);
54639             this.colMenu.getEl().remove();
54640             delete this.colMenu;
54641         }
54642         if(this.hmenu){
54643             this.hmenu.removeAll();
54644             Roo.menu.MenuMgr.unregister(this.hmenu);
54645             this.hmenu.getEl().remove();
54646             delete this.hmenu;
54647         }
54648         if(this.grid.enableColumnMove){
54649             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54650             if(dds){
54651                 for(var dd in dds){
54652                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54653                         var elid = dds[dd].dragElId;
54654                         dds[dd].unreg();
54655                         Roo.get(elid).remove();
54656                     } else if(dds[dd].config.isTarget){
54657                         dds[dd].proxyTop.remove();
54658                         dds[dd].proxyBottom.remove();
54659                         dds[dd].unreg();
54660                     }
54661                     if(Roo.dd.DDM.locationCache[dd]){
54662                         delete Roo.dd.DDM.locationCache[dd];
54663                     }
54664                 }
54665                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54666             }
54667         }
54668         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54669         this.bind(null, null);
54670         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54671     },
54672
54673     handleLockChange : function(){
54674         this.refresh(true);
54675     },
54676
54677     onDenyColumnLock : function(){
54678
54679     },
54680
54681     onDenyColumnHide : function(){
54682
54683     },
54684
54685     handleHdMenuClick : function(item){
54686         var index = this.hdCtxIndex;
54687         var cm = this.cm, ds = this.ds;
54688         switch(item.id){
54689             case "asc":
54690                 ds.sort(cm.getDataIndex(index), "ASC");
54691                 break;
54692             case "desc":
54693                 ds.sort(cm.getDataIndex(index), "DESC");
54694                 break;
54695             case "lock":
54696                 var lc = cm.getLockedCount();
54697                 if(cm.getColumnCount(true) <= lc+1){
54698                     this.onDenyColumnLock();
54699                     return;
54700                 }
54701                 if(lc != index){
54702                     cm.setLocked(index, true, true);
54703                     cm.moveColumn(index, lc);
54704                     this.grid.fireEvent("columnmove", index, lc);
54705                 }else{
54706                     cm.setLocked(index, true);
54707                 }
54708             break;
54709             case "unlock":
54710                 var lc = cm.getLockedCount();
54711                 if((lc-1) != index){
54712                     cm.setLocked(index, false, true);
54713                     cm.moveColumn(index, lc-1);
54714                     this.grid.fireEvent("columnmove", index, lc-1);
54715                 }else{
54716                     cm.setLocked(index, false);
54717                 }
54718             break;
54719             case 'wider': // used to expand cols on touch..
54720             case 'narrow':
54721                 var cw = cm.getColumnWidth(index);
54722                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54723                 cw = Math.max(0, cw);
54724                 cw = Math.min(cw,4000);
54725                 cm.setColumnWidth(index, cw);
54726                 break;
54727                 
54728             default:
54729                 index = cm.getIndexById(item.id.substr(4));
54730                 if(index != -1){
54731                     if(item.checked && cm.getColumnCount(true) <= 1){
54732                         this.onDenyColumnHide();
54733                         return false;
54734                     }
54735                     cm.setHidden(index, item.checked);
54736                 }
54737         }
54738         return true;
54739     },
54740
54741     beforeColMenuShow : function(){
54742         var cm = this.cm,  colCount = cm.getColumnCount();
54743         this.colMenu.removeAll();
54744         for(var i = 0; i < colCount; i++){
54745             this.colMenu.add(new Roo.menu.CheckItem({
54746                 id: "col-"+cm.getColumnId(i),
54747                 text: cm.getColumnHeader(i),
54748                 checked: !cm.isHidden(i),
54749                 hideOnClick:false
54750             }));
54751         }
54752     },
54753
54754     handleHdCtx : function(g, index, e){
54755         e.stopEvent();
54756         var hd = this.getHeaderCell(index);
54757         this.hdCtxIndex = index;
54758         var ms = this.hmenu.items, cm = this.cm;
54759         ms.get("asc").setDisabled(!cm.isSortable(index));
54760         ms.get("desc").setDisabled(!cm.isSortable(index));
54761         if(this.grid.enableColLock !== false){
54762             ms.get("lock").setDisabled(cm.isLocked(index));
54763             ms.get("unlock").setDisabled(!cm.isLocked(index));
54764         }
54765         this.hmenu.show(hd, "tl-bl");
54766     },
54767
54768     handleHdOver : function(e){
54769         var hd = this.findHeaderCell(e.getTarget());
54770         if(hd && !this.headersDisabled){
54771             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54772                this.fly(hd).addClass("x-grid-hd-over");
54773             }
54774         }
54775     },
54776
54777     handleHdOut : function(e){
54778         var hd = this.findHeaderCell(e.getTarget());
54779         if(hd){
54780             this.fly(hd).removeClass("x-grid-hd-over");
54781         }
54782     },
54783
54784     handleSplitDblClick : function(e, t){
54785         var i = this.getCellIndex(t);
54786         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54787             this.autoSizeColumn(i, true);
54788             this.layout();
54789         }
54790     },
54791
54792     render : function(){
54793
54794         var cm = this.cm;
54795         var colCount = cm.getColumnCount();
54796
54797         if(this.grid.monitorWindowResize === true){
54798             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54799         }
54800         var header = this.renderHeaders();
54801         var body = this.templates.body.apply({rows:""});
54802         var html = this.templates.master.apply({
54803             lockedBody: body,
54804             body: body,
54805             lockedHeader: header[0],
54806             header: header[1]
54807         });
54808
54809         //this.updateColumns();
54810
54811         this.grid.getGridEl().dom.innerHTML = html;
54812
54813         this.initElements();
54814         
54815         // a kludge to fix the random scolling effect in webkit
54816         this.el.on("scroll", function() {
54817             this.el.dom.scrollTop=0; // hopefully not recursive..
54818         },this);
54819
54820         this.scroller.on("scroll", this.handleScroll, this);
54821         this.lockedBody.on("mousewheel", this.handleWheel, this);
54822         this.mainBody.on("mousewheel", this.handleWheel, this);
54823
54824         this.mainHd.on("mouseover", this.handleHdOver, this);
54825         this.mainHd.on("mouseout", this.handleHdOut, this);
54826         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54827                 {delegate: "."+this.splitClass});
54828
54829         this.lockedHd.on("mouseover", this.handleHdOver, this);
54830         this.lockedHd.on("mouseout", this.handleHdOut, this);
54831         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54832                 {delegate: "."+this.splitClass});
54833
54834         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54835             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54836         }
54837
54838         this.updateSplitters();
54839
54840         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54841             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54842             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54843         }
54844
54845         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54846             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54847             this.hmenu.add(
54848                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54849                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54850             );
54851             if(this.grid.enableColLock !== false){
54852                 this.hmenu.add('-',
54853                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54854                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54855                 );
54856             }
54857             if (Roo.isTouch) {
54858                  this.hmenu.add('-',
54859                     {id:"wider", text: this.columnsWiderText},
54860                     {id:"narrow", text: this.columnsNarrowText }
54861                 );
54862                 
54863                  
54864             }
54865             
54866             if(this.grid.enableColumnHide !== false){
54867
54868                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54869                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54870                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54871
54872                 this.hmenu.add('-',
54873                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54874                 );
54875             }
54876             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54877
54878             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54879         }
54880
54881         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54882             this.dd = new Roo.grid.GridDragZone(this.grid, {
54883                 ddGroup : this.grid.ddGroup || 'GridDD'
54884             });
54885             
54886         }
54887
54888         /*
54889         for(var i = 0; i < colCount; i++){
54890             if(cm.isHidden(i)){
54891                 this.hideColumn(i);
54892             }
54893             if(cm.config[i].align){
54894                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54895                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54896             }
54897         }*/
54898         
54899         this.updateHeaderSortState();
54900
54901         this.beforeInitialResize();
54902         this.layout(true);
54903
54904         // two part rendering gives faster view to the user
54905         this.renderPhase2.defer(1, this);
54906     },
54907
54908     renderPhase2 : function(){
54909         // render the rows now
54910         this.refresh();
54911         if(this.grid.autoSizeColumns){
54912             this.autoSizeColumns();
54913         }
54914     },
54915
54916     beforeInitialResize : function(){
54917
54918     },
54919
54920     onColumnSplitterMoved : function(i, w){
54921         this.userResized = true;
54922         var cm = this.grid.colModel;
54923         cm.setColumnWidth(i, w, true);
54924         var cid = cm.getColumnId(i);
54925         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54926         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54927         this.updateSplitters();
54928         this.layout();
54929         this.grid.fireEvent("columnresize", i, w);
54930     },
54931
54932     syncRowHeights : function(startIndex, endIndex){
54933         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54934             startIndex = startIndex || 0;
54935             var mrows = this.getBodyTable().rows;
54936             var lrows = this.getLockedTable().rows;
54937             var len = mrows.length-1;
54938             endIndex = Math.min(endIndex || len, len);
54939             for(var i = startIndex; i <= endIndex; i++){
54940                 var m = mrows[i], l = lrows[i];
54941                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54942                 m.style.height = l.style.height = h + "px";
54943             }
54944         }
54945     },
54946
54947     layout : function(initialRender, is2ndPass){
54948         var g = this.grid;
54949         var auto = g.autoHeight;
54950         var scrollOffset = 16;
54951         var c = g.getGridEl(), cm = this.cm,
54952                 expandCol = g.autoExpandColumn,
54953                 gv = this;
54954         //c.beginMeasure();
54955
54956         if(!c.dom.offsetWidth){ // display:none?
54957             if(initialRender){
54958                 this.lockedWrap.show();
54959                 this.mainWrap.show();
54960             }
54961             return;
54962         }
54963
54964         var hasLock = this.cm.isLocked(0);
54965
54966         var tbh = this.headerPanel.getHeight();
54967         var bbh = this.footerPanel.getHeight();
54968
54969         if(auto){
54970             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54971             var newHeight = ch + c.getBorderWidth("tb");
54972             if(g.maxHeight){
54973                 newHeight = Math.min(g.maxHeight, newHeight);
54974             }
54975             c.setHeight(newHeight);
54976         }
54977
54978         if(g.autoWidth){
54979             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54980         }
54981
54982         var s = this.scroller;
54983
54984         var csize = c.getSize(true);
54985
54986         this.el.setSize(csize.width, csize.height);
54987
54988         this.headerPanel.setWidth(csize.width);
54989         this.footerPanel.setWidth(csize.width);
54990
54991         var hdHeight = this.mainHd.getHeight();
54992         var vw = csize.width;
54993         var vh = csize.height - (tbh + bbh);
54994
54995         s.setSize(vw, vh);
54996
54997         var bt = this.getBodyTable();
54998         var ltWidth = hasLock ?
54999                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
55000
55001         var scrollHeight = bt.offsetHeight;
55002         var scrollWidth = ltWidth + bt.offsetWidth;
55003         var vscroll = false, hscroll = false;
55004
55005         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
55006
55007         var lw = this.lockedWrap, mw = this.mainWrap;
55008         var lb = this.lockedBody, mb = this.mainBody;
55009
55010         setTimeout(function(){
55011             var t = s.dom.offsetTop;
55012             var w = s.dom.clientWidth,
55013                 h = s.dom.clientHeight;
55014
55015             lw.setTop(t);
55016             lw.setSize(ltWidth, h);
55017
55018             mw.setLeftTop(ltWidth, t);
55019             mw.setSize(w-ltWidth, h);
55020
55021             lb.setHeight(h-hdHeight);
55022             mb.setHeight(h-hdHeight);
55023
55024             if(is2ndPass !== true && !gv.userResized && expandCol){
55025                 // high speed resize without full column calculation
55026                 
55027                 var ci = cm.getIndexById(expandCol);
55028                 if (ci < 0) {
55029                     ci = cm.findColumnIndex(expandCol);
55030                 }
55031                 ci = Math.max(0, ci); // make sure it's got at least the first col.
55032                 var expandId = cm.getColumnId(ci);
55033                 var  tw = cm.getTotalWidth(false);
55034                 var currentWidth = cm.getColumnWidth(ci);
55035                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
55036                 if(currentWidth != cw){
55037                     cm.setColumnWidth(ci, cw, true);
55038                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55039                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55040                     gv.updateSplitters();
55041                     gv.layout(false, true);
55042                 }
55043             }
55044
55045             if(initialRender){
55046                 lw.show();
55047                 mw.show();
55048             }
55049             //c.endMeasure();
55050         }, 10);
55051     },
55052
55053     onWindowResize : function(){
55054         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
55055             return;
55056         }
55057         this.layout();
55058     },
55059
55060     appendFooter : function(parentEl){
55061         return null;
55062     },
55063
55064     sortAscText : "Sort Ascending",
55065     sortDescText : "Sort Descending",
55066     lockText : "Lock Column",
55067     unlockText : "Unlock Column",
55068     columnsText : "Columns",
55069  
55070     columnsWiderText : "Wider",
55071     columnsNarrowText : "Thinner"
55072 });
55073
55074
55075 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
55076     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
55077     this.proxy.el.addClass('x-grid3-col-dd');
55078 };
55079
55080 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
55081     handleMouseDown : function(e){
55082
55083     },
55084
55085     callHandleMouseDown : function(e){
55086         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
55087     }
55088 });
55089 /*
55090  * Based on:
55091  * Ext JS Library 1.1.1
55092  * Copyright(c) 2006-2007, Ext JS, LLC.
55093  *
55094  * Originally Released Under LGPL - original licence link has changed is not relivant.
55095  *
55096  * Fork - LGPL
55097  * <script type="text/javascript">
55098  */
55099  
55100 // private
55101 // This is a support class used internally by the Grid components
55102 Roo.grid.SplitDragZone = function(grid, hd, hd2){
55103     this.grid = grid;
55104     this.view = grid.getView();
55105     this.proxy = this.view.resizeProxy;
55106     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
55107         "gridSplitters" + this.grid.getGridEl().id, {
55108         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
55109     });
55110     this.setHandleElId(Roo.id(hd));
55111     this.setOuterHandleElId(Roo.id(hd2));
55112     this.scroll = false;
55113 };
55114 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
55115     fly: Roo.Element.fly,
55116
55117     b4StartDrag : function(x, y){
55118         this.view.headersDisabled = true;
55119         this.proxy.setHeight(this.view.mainWrap.getHeight());
55120         var w = this.cm.getColumnWidth(this.cellIndex);
55121         var minw = Math.max(w-this.grid.minColumnWidth, 0);
55122         this.resetConstraints();
55123         this.setXConstraint(minw, 1000);
55124         this.setYConstraint(0, 0);
55125         this.minX = x - minw;
55126         this.maxX = x + 1000;
55127         this.startPos = x;
55128         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
55129     },
55130
55131
55132     handleMouseDown : function(e){
55133         ev = Roo.EventObject.setEvent(e);
55134         var t = this.fly(ev.getTarget());
55135         if(t.hasClass("x-grid-split")){
55136             this.cellIndex = this.view.getCellIndex(t.dom);
55137             this.split = t.dom;
55138             this.cm = this.grid.colModel;
55139             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
55140                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
55141             }
55142         }
55143     },
55144
55145     endDrag : function(e){
55146         this.view.headersDisabled = false;
55147         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
55148         var diff = endX - this.startPos;
55149         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
55150     },
55151
55152     autoOffset : function(){
55153         this.setDelta(0,0);
55154     }
55155 });/*
55156  * Based on:
55157  * Ext JS Library 1.1.1
55158  * Copyright(c) 2006-2007, Ext JS, LLC.
55159  *
55160  * Originally Released Under LGPL - original licence link has changed is not relivant.
55161  *
55162  * Fork - LGPL
55163  * <script type="text/javascript">
55164  */
55165  
55166 // private
55167 // This is a support class used internally by the Grid components
55168 Roo.grid.GridDragZone = function(grid, config){
55169     this.view = grid.getView();
55170     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
55171     if(this.view.lockedBody){
55172         this.setHandleElId(Roo.id(this.view.mainBody.dom));
55173         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
55174     }
55175     this.scroll = false;
55176     this.grid = grid;
55177     this.ddel = document.createElement('div');
55178     this.ddel.className = 'x-grid-dd-wrap';
55179 };
55180
55181 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
55182     ddGroup : "GridDD",
55183
55184     getDragData : function(e){
55185         var t = Roo.lib.Event.getTarget(e);
55186         var rowIndex = this.view.findRowIndex(t);
55187         var sm = this.grid.selModel;
55188             
55189         //Roo.log(rowIndex);
55190         
55191         if (sm.getSelectedCell) {
55192             // cell selection..
55193             if (!sm.getSelectedCell()) {
55194                 return false;
55195             }
55196             if (rowIndex != sm.getSelectedCell()[0]) {
55197                 return false;
55198             }
55199         
55200         }
55201         
55202         if(rowIndex !== false){
55203             
55204             // if editorgrid.. 
55205             
55206             
55207             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
55208                
55209             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
55210               //  
55211             //}
55212             if (e.hasModifier()){
55213                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
55214             }
55215             
55216             Roo.log("getDragData");
55217             
55218             return {
55219                 grid: this.grid,
55220                 ddel: this.ddel,
55221                 rowIndex: rowIndex,
55222                 selections:sm.getSelections ? sm.getSelections() : (
55223                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
55224                 )
55225             };
55226         }
55227         return false;
55228     },
55229
55230     onInitDrag : function(e){
55231         var data = this.dragData;
55232         this.ddel.innerHTML = this.grid.getDragDropText();
55233         this.proxy.update(this.ddel);
55234         // fire start drag?
55235     },
55236
55237     afterRepair : function(){
55238         this.dragging = false;
55239     },
55240
55241     getRepairXY : function(e, data){
55242         return false;
55243     },
55244
55245     onEndDrag : function(data, e){
55246         // fire end drag?
55247     },
55248
55249     onValidDrop : function(dd, e, id){
55250         // fire drag drop?
55251         this.hideProxy();
55252     },
55253
55254     beforeInvalidDrop : function(e, id){
55255
55256     }
55257 });/*
55258  * Based on:
55259  * Ext JS Library 1.1.1
55260  * Copyright(c) 2006-2007, Ext JS, LLC.
55261  *
55262  * Originally Released Under LGPL - original licence link has changed is not relivant.
55263  *
55264  * Fork - LGPL
55265  * <script type="text/javascript">
55266  */
55267  
55268
55269 /**
55270  * @class Roo.grid.ColumnModel
55271  * @extends Roo.util.Observable
55272  * This is the default implementation of a ColumnModel used by the Grid. It defines
55273  * the columns in the grid.
55274  * <br>Usage:<br>
55275  <pre><code>
55276  var colModel = new Roo.grid.ColumnModel([
55277         {header: "Ticker", width: 60, sortable: true, locked: true},
55278         {header: "Company Name", width: 150, sortable: true},
55279         {header: "Market Cap.", width: 100, sortable: true},
55280         {header: "$ Sales", width: 100, sortable: true, renderer: money},
55281         {header: "Employees", width: 100, sortable: true, resizable: false}
55282  ]);
55283  </code></pre>
55284  * <p>
55285  
55286  * The config options listed for this class are options which may appear in each
55287  * individual column definition.
55288  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
55289  * @constructor
55290  * @param {Object} config An Array of column config objects. See this class's
55291  * config objects for details.
55292 */
55293 Roo.grid.ColumnModel = function(config){
55294         /**
55295      * The config passed into the constructor
55296      */
55297     this.config = config;
55298     this.lookup = {};
55299
55300     // if no id, create one
55301     // if the column does not have a dataIndex mapping,
55302     // map it to the order it is in the config
55303     for(var i = 0, len = config.length; i < len; i++){
55304         var c = config[i];
55305         if(typeof c.dataIndex == "undefined"){
55306             c.dataIndex = i;
55307         }
55308         if(typeof c.renderer == "string"){
55309             c.renderer = Roo.util.Format[c.renderer];
55310         }
55311         if(typeof c.id == "undefined"){
55312             c.id = Roo.id();
55313         }
55314         if(c.editor && c.editor.xtype){
55315             c.editor  = Roo.factory(c.editor, Roo.grid);
55316         }
55317         if(c.editor && c.editor.isFormField){
55318             c.editor = new Roo.grid.GridEditor(c.editor);
55319         }
55320         this.lookup[c.id] = c;
55321     }
55322
55323     /**
55324      * The width of columns which have no width specified (defaults to 100)
55325      * @type Number
55326      */
55327     this.defaultWidth = 100;
55328
55329     /**
55330      * Default sortable of columns which have no sortable specified (defaults to false)
55331      * @type Boolean
55332      */
55333     this.defaultSortable = false;
55334
55335     this.addEvents({
55336         /**
55337              * @event widthchange
55338              * Fires when the width of a column changes.
55339              * @param {ColumnModel} this
55340              * @param {Number} columnIndex The column index
55341              * @param {Number} newWidth The new width
55342              */
55343             "widthchange": true,
55344         /**
55345              * @event headerchange
55346              * Fires when the text of a header changes.
55347              * @param {ColumnModel} this
55348              * @param {Number} columnIndex The column index
55349              * @param {Number} newText The new header text
55350              */
55351             "headerchange": true,
55352         /**
55353              * @event hiddenchange
55354              * Fires when a column is hidden or "unhidden".
55355              * @param {ColumnModel} this
55356              * @param {Number} columnIndex The column index
55357              * @param {Boolean} hidden true if hidden, false otherwise
55358              */
55359             "hiddenchange": true,
55360             /**
55361          * @event columnmoved
55362          * Fires when a column is moved.
55363          * @param {ColumnModel} this
55364          * @param {Number} oldIndex
55365          * @param {Number} newIndex
55366          */
55367         "columnmoved" : true,
55368         /**
55369          * @event columlockchange
55370          * Fires when a column's locked state is changed
55371          * @param {ColumnModel} this
55372          * @param {Number} colIndex
55373          * @param {Boolean} locked true if locked
55374          */
55375         "columnlockchange" : true
55376     });
55377     Roo.grid.ColumnModel.superclass.constructor.call(this);
55378 };
55379 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
55380     /**
55381      * @cfg {String} header The header text to display in the Grid view.
55382      */
55383     /**
55384      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
55385      * {@link Roo.data.Record} definition from which to draw the column's value. If not
55386      * specified, the column's index is used as an index into the Record's data Array.
55387      */
55388     /**
55389      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
55390      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
55391      */
55392     /**
55393      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
55394      * Defaults to the value of the {@link #defaultSortable} property.
55395      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
55396      */
55397     /**
55398      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
55399      */
55400     /**
55401      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
55402      */
55403     /**
55404      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
55405      */
55406     /**
55407      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
55408      */
55409     /**
55410      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
55411      * given the cell's data value. See {@link #setRenderer}. If not specified, the
55412      * default renderer uses the raw data value. If an object is returned (bootstrap only)
55413      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
55414      */
55415        /**
55416      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55417      */
55418     /**
55419      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55420      */
55421     /**
55422      * @cfg {String} cursor (Optional)
55423      */
55424     /**
55425      * @cfg {String} tooltip (Optional)
55426      */
55427     /**
55428      * Returns the id of the column at the specified index.
55429      * @param {Number} index The column index
55430      * @return {String} the id
55431      */
55432     getColumnId : function(index){
55433         return this.config[index].id;
55434     },
55435
55436     /**
55437      * Returns the column for a specified id.
55438      * @param {String} id The column id
55439      * @return {Object} the column
55440      */
55441     getColumnById : function(id){
55442         return this.lookup[id];
55443     },
55444
55445     
55446     /**
55447      * Returns the column for a specified dataIndex.
55448      * @param {String} dataIndex The column dataIndex
55449      * @return {Object|Boolean} the column or false if not found
55450      */
55451     getColumnByDataIndex: function(dataIndex){
55452         var index = this.findColumnIndex(dataIndex);
55453         return index > -1 ? this.config[index] : false;
55454     },
55455     
55456     /**
55457      * Returns the index for a specified column id.
55458      * @param {String} id The column id
55459      * @return {Number} the index, or -1 if not found
55460      */
55461     getIndexById : function(id){
55462         for(var i = 0, len = this.config.length; i < len; i++){
55463             if(this.config[i].id == id){
55464                 return i;
55465             }
55466         }
55467         return -1;
55468     },
55469     
55470     /**
55471      * Returns the index for a specified column dataIndex.
55472      * @param {String} dataIndex The column dataIndex
55473      * @return {Number} the index, or -1 if not found
55474      */
55475     
55476     findColumnIndex : function(dataIndex){
55477         for(var i = 0, len = this.config.length; i < len; i++){
55478             if(this.config[i].dataIndex == dataIndex){
55479                 return i;
55480             }
55481         }
55482         return -1;
55483     },
55484     
55485     
55486     moveColumn : function(oldIndex, newIndex){
55487         var c = this.config[oldIndex];
55488         this.config.splice(oldIndex, 1);
55489         this.config.splice(newIndex, 0, c);
55490         this.dataMap = null;
55491         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55492     },
55493
55494     isLocked : function(colIndex){
55495         return this.config[colIndex].locked === true;
55496     },
55497
55498     setLocked : function(colIndex, value, suppressEvent){
55499         if(this.isLocked(colIndex) == value){
55500             return;
55501         }
55502         this.config[colIndex].locked = value;
55503         if(!suppressEvent){
55504             this.fireEvent("columnlockchange", this, colIndex, value);
55505         }
55506     },
55507
55508     getTotalLockedWidth : function(){
55509         var totalWidth = 0;
55510         for(var i = 0; i < this.config.length; i++){
55511             if(this.isLocked(i) && !this.isHidden(i)){
55512                 this.totalWidth += this.getColumnWidth(i);
55513             }
55514         }
55515         return totalWidth;
55516     },
55517
55518     getLockedCount : function(){
55519         for(var i = 0, len = this.config.length; i < len; i++){
55520             if(!this.isLocked(i)){
55521                 return i;
55522             }
55523         }
55524     },
55525
55526     /**
55527      * Returns the number of columns.
55528      * @return {Number}
55529      */
55530     getColumnCount : function(visibleOnly){
55531         if(visibleOnly === true){
55532             var c = 0;
55533             for(var i = 0, len = this.config.length; i < len; i++){
55534                 if(!this.isHidden(i)){
55535                     c++;
55536                 }
55537             }
55538             return c;
55539         }
55540         return this.config.length;
55541     },
55542
55543     /**
55544      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55545      * @param {Function} fn
55546      * @param {Object} scope (optional)
55547      * @return {Array} result
55548      */
55549     getColumnsBy : function(fn, scope){
55550         var r = [];
55551         for(var i = 0, len = this.config.length; i < len; i++){
55552             var c = this.config[i];
55553             if(fn.call(scope||this, c, i) === true){
55554                 r[r.length] = c;
55555             }
55556         }
55557         return r;
55558     },
55559
55560     /**
55561      * Returns true if the specified column is sortable.
55562      * @param {Number} col The column index
55563      * @return {Boolean}
55564      */
55565     isSortable : function(col){
55566         if(typeof this.config[col].sortable == "undefined"){
55567             return this.defaultSortable;
55568         }
55569         return this.config[col].sortable;
55570     },
55571
55572     /**
55573      * Returns the rendering (formatting) function defined for the column.
55574      * @param {Number} col The column index.
55575      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55576      */
55577     getRenderer : function(col){
55578         if(!this.config[col].renderer){
55579             return Roo.grid.ColumnModel.defaultRenderer;
55580         }
55581         return this.config[col].renderer;
55582     },
55583
55584     /**
55585      * Sets the rendering (formatting) function for a column.
55586      * @param {Number} col The column index
55587      * @param {Function} fn The function to use to process the cell's raw data
55588      * to return HTML markup for the grid view. The render function is called with
55589      * the following parameters:<ul>
55590      * <li>Data value.</li>
55591      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55592      * <li>css A CSS style string to apply to the table cell.</li>
55593      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55594      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55595      * <li>Row index</li>
55596      * <li>Column index</li>
55597      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55598      */
55599     setRenderer : function(col, fn){
55600         this.config[col].renderer = fn;
55601     },
55602
55603     /**
55604      * Returns the width for the specified column.
55605      * @param {Number} col The column index
55606      * @return {Number}
55607      */
55608     getColumnWidth : function(col){
55609         return this.config[col].width * 1 || this.defaultWidth;
55610     },
55611
55612     /**
55613      * Sets the width for a column.
55614      * @param {Number} col The column index
55615      * @param {Number} width The new width
55616      */
55617     setColumnWidth : function(col, width, suppressEvent){
55618         this.config[col].width = width;
55619         this.totalWidth = null;
55620         if(!suppressEvent){
55621              this.fireEvent("widthchange", this, col, width);
55622         }
55623     },
55624
55625     /**
55626      * Returns the total width of all columns.
55627      * @param {Boolean} includeHidden True to include hidden column widths
55628      * @return {Number}
55629      */
55630     getTotalWidth : function(includeHidden){
55631         if(!this.totalWidth){
55632             this.totalWidth = 0;
55633             for(var i = 0, len = this.config.length; i < len; i++){
55634                 if(includeHidden || !this.isHidden(i)){
55635                     this.totalWidth += this.getColumnWidth(i);
55636                 }
55637             }
55638         }
55639         return this.totalWidth;
55640     },
55641
55642     /**
55643      * Returns the header for the specified column.
55644      * @param {Number} col The column index
55645      * @return {String}
55646      */
55647     getColumnHeader : function(col){
55648         return this.config[col].header;
55649     },
55650
55651     /**
55652      * Sets the header for a column.
55653      * @param {Number} col The column index
55654      * @param {String} header The new header
55655      */
55656     setColumnHeader : function(col, header){
55657         this.config[col].header = header;
55658         this.fireEvent("headerchange", this, col, header);
55659     },
55660
55661     /**
55662      * Returns the tooltip for the specified column.
55663      * @param {Number} col The column index
55664      * @return {String}
55665      */
55666     getColumnTooltip : function(col){
55667             return this.config[col].tooltip;
55668     },
55669     /**
55670      * Sets the tooltip for a column.
55671      * @param {Number} col The column index
55672      * @param {String} tooltip The new tooltip
55673      */
55674     setColumnTooltip : function(col, tooltip){
55675             this.config[col].tooltip = tooltip;
55676     },
55677
55678     /**
55679      * Returns the dataIndex for the specified column.
55680      * @param {Number} col The column index
55681      * @return {Number}
55682      */
55683     getDataIndex : function(col){
55684         return this.config[col].dataIndex;
55685     },
55686
55687     /**
55688      * Sets the dataIndex for a column.
55689      * @param {Number} col The column index
55690      * @param {Number} dataIndex The new dataIndex
55691      */
55692     setDataIndex : function(col, dataIndex){
55693         this.config[col].dataIndex = dataIndex;
55694     },
55695
55696     
55697     
55698     /**
55699      * Returns true if the cell is editable.
55700      * @param {Number} colIndex The column index
55701      * @param {Number} rowIndex The row index
55702      * @return {Boolean}
55703      */
55704     isCellEditable : function(colIndex, rowIndex){
55705         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55706     },
55707
55708     /**
55709      * Returns the editor defined for the cell/column.
55710      * return false or null to disable editing.
55711      * @param {Number} colIndex The column index
55712      * @param {Number} rowIndex The row index
55713      * @return {Object}
55714      */
55715     getCellEditor : function(colIndex, rowIndex){
55716         return this.config[colIndex].editor;
55717     },
55718
55719     /**
55720      * Sets if a column is editable.
55721      * @param {Number} col The column index
55722      * @param {Boolean} editable True if the column is editable
55723      */
55724     setEditable : function(col, editable){
55725         this.config[col].editable = editable;
55726     },
55727
55728
55729     /**
55730      * Returns true if the column is hidden.
55731      * @param {Number} colIndex The column index
55732      * @return {Boolean}
55733      */
55734     isHidden : function(colIndex){
55735         return this.config[colIndex].hidden;
55736     },
55737
55738
55739     /**
55740      * Returns true if the column width cannot be changed
55741      */
55742     isFixed : function(colIndex){
55743         return this.config[colIndex].fixed;
55744     },
55745
55746     /**
55747      * Returns true if the column can be resized
55748      * @return {Boolean}
55749      */
55750     isResizable : function(colIndex){
55751         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55752     },
55753     /**
55754      * Sets if a column is hidden.
55755      * @param {Number} colIndex The column index
55756      * @param {Boolean} hidden True if the column is hidden
55757      */
55758     setHidden : function(colIndex, hidden){
55759         this.config[colIndex].hidden = hidden;
55760         this.totalWidth = null;
55761         this.fireEvent("hiddenchange", this, colIndex, hidden);
55762     },
55763
55764     /**
55765      * Sets the editor for a column.
55766      * @param {Number} col The column index
55767      * @param {Object} editor The editor object
55768      */
55769     setEditor : function(col, editor){
55770         this.config[col].editor = editor;
55771     }
55772 });
55773
55774 Roo.grid.ColumnModel.defaultRenderer = function(value){
55775         if(typeof value == "string" && value.length < 1){
55776             return "&#160;";
55777         }
55778         return value;
55779 };
55780
55781 // Alias for backwards compatibility
55782 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55783 /*
55784  * Based on:
55785  * Ext JS Library 1.1.1
55786  * Copyright(c) 2006-2007, Ext JS, LLC.
55787  *
55788  * Originally Released Under LGPL - original licence link has changed is not relivant.
55789  *
55790  * Fork - LGPL
55791  * <script type="text/javascript">
55792  */
55793
55794 /**
55795  * @class Roo.grid.AbstractSelectionModel
55796  * @extends Roo.util.Observable
55797  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55798  * implemented by descendant classes.  This class should not be directly instantiated.
55799  * @constructor
55800  */
55801 Roo.grid.AbstractSelectionModel = function(){
55802     this.locked = false;
55803     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55804 };
55805
55806 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55807     /** @ignore Called by the grid automatically. Do not call directly. */
55808     init : function(grid){
55809         this.grid = grid;
55810         this.initEvents();
55811     },
55812
55813     /**
55814      * Locks the selections.
55815      */
55816     lock : function(){
55817         this.locked = true;
55818     },
55819
55820     /**
55821      * Unlocks the selections.
55822      */
55823     unlock : function(){
55824         this.locked = false;
55825     },
55826
55827     /**
55828      * Returns true if the selections are locked.
55829      * @return {Boolean}
55830      */
55831     isLocked : function(){
55832         return this.locked;
55833     }
55834 });/*
55835  * Based on:
55836  * Ext JS Library 1.1.1
55837  * Copyright(c) 2006-2007, Ext JS, LLC.
55838  *
55839  * Originally Released Under LGPL - original licence link has changed is not relivant.
55840  *
55841  * Fork - LGPL
55842  * <script type="text/javascript">
55843  */
55844 /**
55845  * @extends Roo.grid.AbstractSelectionModel
55846  * @class Roo.grid.RowSelectionModel
55847  * The default SelectionModel used by {@link Roo.grid.Grid}.
55848  * It supports multiple selections and keyboard selection/navigation. 
55849  * @constructor
55850  * @param {Object} config
55851  */
55852 Roo.grid.RowSelectionModel = function(config){
55853     Roo.apply(this, config);
55854     this.selections = new Roo.util.MixedCollection(false, function(o){
55855         return o.id;
55856     });
55857
55858     this.last = false;
55859     this.lastActive = false;
55860
55861     this.addEvents({
55862         /**
55863              * @event selectionchange
55864              * Fires when the selection changes
55865              * @param {SelectionModel} this
55866              */
55867             "selectionchange" : true,
55868         /**
55869              * @event afterselectionchange
55870              * Fires after the selection changes (eg. by key press or clicking)
55871              * @param {SelectionModel} this
55872              */
55873             "afterselectionchange" : true,
55874         /**
55875              * @event beforerowselect
55876              * Fires when a row is selected being selected, return false to cancel.
55877              * @param {SelectionModel} this
55878              * @param {Number} rowIndex The selected index
55879              * @param {Boolean} keepExisting False if other selections will be cleared
55880              */
55881             "beforerowselect" : true,
55882         /**
55883              * @event rowselect
55884              * Fires when a row is selected.
55885              * @param {SelectionModel} this
55886              * @param {Number} rowIndex The selected index
55887              * @param {Roo.data.Record} r The record
55888              */
55889             "rowselect" : true,
55890         /**
55891              * @event rowdeselect
55892              * Fires when a row is deselected.
55893              * @param {SelectionModel} this
55894              * @param {Number} rowIndex The selected index
55895              */
55896         "rowdeselect" : true
55897     });
55898     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55899     this.locked = false;
55900 };
55901
55902 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55903     /**
55904      * @cfg {Boolean} singleSelect
55905      * True to allow selection of only one row at a time (defaults to false)
55906      */
55907     singleSelect : false,
55908
55909     // private
55910     initEvents : function(){
55911
55912         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55913             this.grid.on("mousedown", this.handleMouseDown, this);
55914         }else{ // allow click to work like normal
55915             this.grid.on("rowclick", this.handleDragableRowClick, this);
55916         }
55917
55918         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55919             "up" : function(e){
55920                 if(!e.shiftKey){
55921                     this.selectPrevious(e.shiftKey);
55922                 }else if(this.last !== false && this.lastActive !== false){
55923                     var last = this.last;
55924                     this.selectRange(this.last,  this.lastActive-1);
55925                     this.grid.getView().focusRow(this.lastActive);
55926                     if(last !== false){
55927                         this.last = last;
55928                     }
55929                 }else{
55930                     this.selectFirstRow();
55931                 }
55932                 this.fireEvent("afterselectionchange", this);
55933             },
55934             "down" : function(e){
55935                 if(!e.shiftKey){
55936                     this.selectNext(e.shiftKey);
55937                 }else if(this.last !== false && this.lastActive !== false){
55938                     var last = this.last;
55939                     this.selectRange(this.last,  this.lastActive+1);
55940                     this.grid.getView().focusRow(this.lastActive);
55941                     if(last !== false){
55942                         this.last = last;
55943                     }
55944                 }else{
55945                     this.selectFirstRow();
55946                 }
55947                 this.fireEvent("afterselectionchange", this);
55948             },
55949             scope: this
55950         });
55951
55952         var view = this.grid.view;
55953         view.on("refresh", this.onRefresh, this);
55954         view.on("rowupdated", this.onRowUpdated, this);
55955         view.on("rowremoved", this.onRemove, this);
55956     },
55957
55958     // private
55959     onRefresh : function(){
55960         var ds = this.grid.dataSource, i, v = this.grid.view;
55961         var s = this.selections;
55962         s.each(function(r){
55963             if((i = ds.indexOfId(r.id)) != -1){
55964                 v.onRowSelect(i);
55965                 s.add(ds.getAt(i)); // updating the selection relate data
55966             }else{
55967                 s.remove(r);
55968             }
55969         });
55970     },
55971
55972     // private
55973     onRemove : function(v, index, r){
55974         this.selections.remove(r);
55975     },
55976
55977     // private
55978     onRowUpdated : function(v, index, r){
55979         if(this.isSelected(r)){
55980             v.onRowSelect(index);
55981         }
55982     },
55983
55984     /**
55985      * Select records.
55986      * @param {Array} records The records to select
55987      * @param {Boolean} keepExisting (optional) True to keep existing selections
55988      */
55989     selectRecords : function(records, keepExisting){
55990         if(!keepExisting){
55991             this.clearSelections();
55992         }
55993         var ds = this.grid.dataSource;
55994         for(var i = 0, len = records.length; i < len; i++){
55995             this.selectRow(ds.indexOf(records[i]), true);
55996         }
55997     },
55998
55999     /**
56000      * Gets the number of selected rows.
56001      * @return {Number}
56002      */
56003     getCount : function(){
56004         return this.selections.length;
56005     },
56006
56007     /**
56008      * Selects the first row in the grid.
56009      */
56010     selectFirstRow : function(){
56011         this.selectRow(0);
56012     },
56013
56014     /**
56015      * Select the last row.
56016      * @param {Boolean} keepExisting (optional) True to keep existing selections
56017      */
56018     selectLastRow : function(keepExisting){
56019         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
56020     },
56021
56022     /**
56023      * Selects the row immediately following the last selected row.
56024      * @param {Boolean} keepExisting (optional) True to keep existing selections
56025      */
56026     selectNext : function(keepExisting){
56027         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
56028             this.selectRow(this.last+1, keepExisting);
56029             this.grid.getView().focusRow(this.last);
56030         }
56031     },
56032
56033     /**
56034      * Selects the row that precedes the last selected row.
56035      * @param {Boolean} keepExisting (optional) True to keep existing selections
56036      */
56037     selectPrevious : function(keepExisting){
56038         if(this.last){
56039             this.selectRow(this.last-1, keepExisting);
56040             this.grid.getView().focusRow(this.last);
56041         }
56042     },
56043
56044     /**
56045      * Returns the selected records
56046      * @return {Array} Array of selected records
56047      */
56048     getSelections : function(){
56049         return [].concat(this.selections.items);
56050     },
56051
56052     /**
56053      * Returns the first selected record.
56054      * @return {Record}
56055      */
56056     getSelected : function(){
56057         return this.selections.itemAt(0);
56058     },
56059
56060
56061     /**
56062      * Clears all selections.
56063      */
56064     clearSelections : function(fast){
56065         if(this.locked) return;
56066         if(fast !== true){
56067             var ds = this.grid.dataSource;
56068             var s = this.selections;
56069             s.each(function(r){
56070                 this.deselectRow(ds.indexOfId(r.id));
56071             }, this);
56072             s.clear();
56073         }else{
56074             this.selections.clear();
56075         }
56076         this.last = false;
56077     },
56078
56079
56080     /**
56081      * Selects all rows.
56082      */
56083     selectAll : function(){
56084         if(this.locked) return;
56085         this.selections.clear();
56086         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
56087             this.selectRow(i, true);
56088         }
56089     },
56090
56091     /**
56092      * Returns True if there is a selection.
56093      * @return {Boolean}
56094      */
56095     hasSelection : function(){
56096         return this.selections.length > 0;
56097     },
56098
56099     /**
56100      * Returns True if the specified row is selected.
56101      * @param {Number/Record} record The record or index of the record to check
56102      * @return {Boolean}
56103      */
56104     isSelected : function(index){
56105         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
56106         return (r && this.selections.key(r.id) ? true : false);
56107     },
56108
56109     /**
56110      * Returns True if the specified record id is selected.
56111      * @param {String} id The id of record to check
56112      * @return {Boolean}
56113      */
56114     isIdSelected : function(id){
56115         return (this.selections.key(id) ? true : false);
56116     },
56117
56118     // private
56119     handleMouseDown : function(e, t){
56120         var view = this.grid.getView(), rowIndex;
56121         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
56122             return;
56123         };
56124         if(e.shiftKey && this.last !== false){
56125             var last = this.last;
56126             this.selectRange(last, rowIndex, e.ctrlKey);
56127             this.last = last; // reset the last
56128             view.focusRow(rowIndex);
56129         }else{
56130             var isSelected = this.isSelected(rowIndex);
56131             if(e.button !== 0 && isSelected){
56132                 view.focusRow(rowIndex);
56133             }else if(e.ctrlKey && isSelected){
56134                 this.deselectRow(rowIndex);
56135             }else if(!isSelected){
56136                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
56137                 view.focusRow(rowIndex);
56138             }
56139         }
56140         this.fireEvent("afterselectionchange", this);
56141     },
56142     // private
56143     handleDragableRowClick :  function(grid, rowIndex, e) 
56144     {
56145         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
56146             this.selectRow(rowIndex, false);
56147             grid.view.focusRow(rowIndex);
56148              this.fireEvent("afterselectionchange", this);
56149         }
56150     },
56151     
56152     /**
56153      * Selects multiple rows.
56154      * @param {Array} rows Array of the indexes of the row to select
56155      * @param {Boolean} keepExisting (optional) True to keep existing selections
56156      */
56157     selectRows : function(rows, keepExisting){
56158         if(!keepExisting){
56159             this.clearSelections();
56160         }
56161         for(var i = 0, len = rows.length; i < len; i++){
56162             this.selectRow(rows[i], true);
56163         }
56164     },
56165
56166     /**
56167      * Selects a range of rows. All rows in between startRow and endRow are also selected.
56168      * @param {Number} startRow The index of the first row in the range
56169      * @param {Number} endRow The index of the last row in the range
56170      * @param {Boolean} keepExisting (optional) True to retain existing selections
56171      */
56172     selectRange : function(startRow, endRow, keepExisting){
56173         if(this.locked) return;
56174         if(!keepExisting){
56175             this.clearSelections();
56176         }
56177         if(startRow <= endRow){
56178             for(var i = startRow; i <= endRow; i++){
56179                 this.selectRow(i, true);
56180             }
56181         }else{
56182             for(var i = startRow; i >= endRow; i--){
56183                 this.selectRow(i, true);
56184             }
56185         }
56186     },
56187
56188     /**
56189      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
56190      * @param {Number} startRow The index of the first row in the range
56191      * @param {Number} endRow The index of the last row in the range
56192      */
56193     deselectRange : function(startRow, endRow, preventViewNotify){
56194         if(this.locked) return;
56195         for(var i = startRow; i <= endRow; i++){
56196             this.deselectRow(i, preventViewNotify);
56197         }
56198     },
56199
56200     /**
56201      * Selects a row.
56202      * @param {Number} row The index of the row to select
56203      * @param {Boolean} keepExisting (optional) True to keep existing selections
56204      */
56205     selectRow : function(index, keepExisting, preventViewNotify){
56206         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
56207         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
56208             if(!keepExisting || this.singleSelect){
56209                 this.clearSelections();
56210             }
56211             var r = this.grid.dataSource.getAt(index);
56212             this.selections.add(r);
56213             this.last = this.lastActive = index;
56214             if(!preventViewNotify){
56215                 this.grid.getView().onRowSelect(index);
56216             }
56217             this.fireEvent("rowselect", this, index, r);
56218             this.fireEvent("selectionchange", this);
56219         }
56220     },
56221
56222     /**
56223      * Deselects a row.
56224      * @param {Number} row The index of the row to deselect
56225      */
56226     deselectRow : function(index, preventViewNotify){
56227         if(this.locked) return;
56228         if(this.last == index){
56229             this.last = false;
56230         }
56231         if(this.lastActive == index){
56232             this.lastActive = false;
56233         }
56234         var r = this.grid.dataSource.getAt(index);
56235         this.selections.remove(r);
56236         if(!preventViewNotify){
56237             this.grid.getView().onRowDeselect(index);
56238         }
56239         this.fireEvent("rowdeselect", this, index);
56240         this.fireEvent("selectionchange", this);
56241     },
56242
56243     // private
56244     restoreLast : function(){
56245         if(this._last){
56246             this.last = this._last;
56247         }
56248     },
56249
56250     // private
56251     acceptsNav : function(row, col, cm){
56252         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56253     },
56254
56255     // private
56256     onEditorKey : function(field, e){
56257         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
56258         if(k == e.TAB){
56259             e.stopEvent();
56260             ed.completeEdit();
56261             if(e.shiftKey){
56262                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56263             }else{
56264                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56265             }
56266         }else if(k == e.ENTER && !e.ctrlKey){
56267             e.stopEvent();
56268             ed.completeEdit();
56269             if(e.shiftKey){
56270                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
56271             }else{
56272                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
56273             }
56274         }else if(k == e.ESC){
56275             ed.cancelEdit();
56276         }
56277         if(newCell){
56278             g.startEditing(newCell[0], newCell[1]);
56279         }
56280     }
56281 });/*
56282  * Based on:
56283  * Ext JS Library 1.1.1
56284  * Copyright(c) 2006-2007, Ext JS, LLC.
56285  *
56286  * Originally Released Under LGPL - original licence link has changed is not relivant.
56287  *
56288  * Fork - LGPL
56289  * <script type="text/javascript">
56290  */
56291 /**
56292  * @class Roo.grid.CellSelectionModel
56293  * @extends Roo.grid.AbstractSelectionModel
56294  * This class provides the basic implementation for cell selection in a grid.
56295  * @constructor
56296  * @param {Object} config The object containing the configuration of this model.
56297  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
56298  */
56299 Roo.grid.CellSelectionModel = function(config){
56300     Roo.apply(this, config);
56301
56302     this.selection = null;
56303
56304     this.addEvents({
56305         /**
56306              * @event beforerowselect
56307              * Fires before a cell is selected.
56308              * @param {SelectionModel} this
56309              * @param {Number} rowIndex The selected row index
56310              * @param {Number} colIndex The selected cell index
56311              */
56312             "beforecellselect" : true,
56313         /**
56314              * @event cellselect
56315              * Fires when a cell is selected.
56316              * @param {SelectionModel} this
56317              * @param {Number} rowIndex The selected row index
56318              * @param {Number} colIndex The selected cell index
56319              */
56320             "cellselect" : true,
56321         /**
56322              * @event selectionchange
56323              * Fires when the active selection changes.
56324              * @param {SelectionModel} this
56325              * @param {Object} selection null for no selection or an object (o) with two properties
56326                 <ul>
56327                 <li>o.record: the record object for the row the selection is in</li>
56328                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
56329                 </ul>
56330              */
56331             "selectionchange" : true,
56332         /**
56333              * @event tabend
56334              * Fires when the tab (or enter) was pressed on the last editable cell
56335              * You can use this to trigger add new row.
56336              * @param {SelectionModel} this
56337              */
56338             "tabend" : true,
56339          /**
56340              * @event beforeeditnext
56341              * Fires before the next editable sell is made active
56342              * You can use this to skip to another cell or fire the tabend
56343              *    if you set cell to false
56344              * @param {Object} eventdata object : { cell : [ row, col ] } 
56345              */
56346             "beforeeditnext" : true
56347     });
56348     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
56349 };
56350
56351 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
56352     
56353     enter_is_tab: false,
56354
56355     /** @ignore */
56356     initEvents : function(){
56357         this.grid.on("mousedown", this.handleMouseDown, this);
56358         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
56359         var view = this.grid.view;
56360         view.on("refresh", this.onViewChange, this);
56361         view.on("rowupdated", this.onRowUpdated, this);
56362         view.on("beforerowremoved", this.clearSelections, this);
56363         view.on("beforerowsinserted", this.clearSelections, this);
56364         if(this.grid.isEditor){
56365             this.grid.on("beforeedit", this.beforeEdit,  this);
56366         }
56367     },
56368
56369         //private
56370     beforeEdit : function(e){
56371         this.select(e.row, e.column, false, true, e.record);
56372     },
56373
56374         //private
56375     onRowUpdated : function(v, index, r){
56376         if(this.selection && this.selection.record == r){
56377             v.onCellSelect(index, this.selection.cell[1]);
56378         }
56379     },
56380
56381         //private
56382     onViewChange : function(){
56383         this.clearSelections(true);
56384     },
56385
56386         /**
56387          * Returns the currently selected cell,.
56388          * @return {Array} The selected cell (row, column) or null if none selected.
56389          */
56390     getSelectedCell : function(){
56391         return this.selection ? this.selection.cell : null;
56392     },
56393
56394     /**
56395      * Clears all selections.
56396      * @param {Boolean} true to prevent the gridview from being notified about the change.
56397      */
56398     clearSelections : function(preventNotify){
56399         var s = this.selection;
56400         if(s){
56401             if(preventNotify !== true){
56402                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
56403             }
56404             this.selection = null;
56405             this.fireEvent("selectionchange", this, null);
56406         }
56407     },
56408
56409     /**
56410      * Returns true if there is a selection.
56411      * @return {Boolean}
56412      */
56413     hasSelection : function(){
56414         return this.selection ? true : false;
56415     },
56416
56417     /** @ignore */
56418     handleMouseDown : function(e, t){
56419         var v = this.grid.getView();
56420         if(this.isLocked()){
56421             return;
56422         };
56423         var row = v.findRowIndex(t);
56424         var cell = v.findCellIndex(t);
56425         if(row !== false && cell !== false){
56426             this.select(row, cell);
56427         }
56428     },
56429
56430     /**
56431      * Selects a cell.
56432      * @param {Number} rowIndex
56433      * @param {Number} collIndex
56434      */
56435     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56436         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56437             this.clearSelections();
56438             r = r || this.grid.dataSource.getAt(rowIndex);
56439             this.selection = {
56440                 record : r,
56441                 cell : [rowIndex, colIndex]
56442             };
56443             if(!preventViewNotify){
56444                 var v = this.grid.getView();
56445                 v.onCellSelect(rowIndex, colIndex);
56446                 if(preventFocus !== true){
56447                     v.focusCell(rowIndex, colIndex);
56448                 }
56449             }
56450             this.fireEvent("cellselect", this, rowIndex, colIndex);
56451             this.fireEvent("selectionchange", this, this.selection);
56452         }
56453     },
56454
56455         //private
56456     isSelectable : function(rowIndex, colIndex, cm){
56457         return !cm.isHidden(colIndex);
56458     },
56459
56460     /** @ignore */
56461     handleKeyDown : function(e){
56462         //Roo.log('Cell Sel Model handleKeyDown');
56463         if(!e.isNavKeyPress()){
56464             return;
56465         }
56466         var g = this.grid, s = this.selection;
56467         if(!s){
56468             e.stopEvent();
56469             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56470             if(cell){
56471                 this.select(cell[0], cell[1]);
56472             }
56473             return;
56474         }
56475         var sm = this;
56476         var walk = function(row, col, step){
56477             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56478         };
56479         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56480         var newCell;
56481
56482       
56483
56484         switch(k){
56485             case e.TAB:
56486                 // handled by onEditorKey
56487                 if (g.isEditor && g.editing) {
56488                     return;
56489                 }
56490                 if(e.shiftKey) {
56491                     newCell = walk(r, c-1, -1);
56492                 } else {
56493                     newCell = walk(r, c+1, 1);
56494                 }
56495                 break;
56496             
56497             case e.DOWN:
56498                newCell = walk(r+1, c, 1);
56499                 break;
56500             
56501             case e.UP:
56502                 newCell = walk(r-1, c, -1);
56503                 break;
56504             
56505             case e.RIGHT:
56506                 newCell = walk(r, c+1, 1);
56507                 break;
56508             
56509             case e.LEFT:
56510                 newCell = walk(r, c-1, -1);
56511                 break;
56512             
56513             case e.ENTER:
56514                 
56515                 if(g.isEditor && !g.editing){
56516                    g.startEditing(r, c);
56517                    e.stopEvent();
56518                    return;
56519                 }
56520                 
56521                 
56522              break;
56523         };
56524         if(newCell){
56525             this.select(newCell[0], newCell[1]);
56526             e.stopEvent();
56527             
56528         }
56529     },
56530
56531     acceptsNav : function(row, col, cm){
56532         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56533     },
56534     /**
56535      * Selects a cell.
56536      * @param {Number} field (not used) - as it's normally used as a listener
56537      * @param {Number} e - event - fake it by using
56538      *
56539      * var e = Roo.EventObjectImpl.prototype;
56540      * e.keyCode = e.TAB
56541      *
56542      * 
56543      */
56544     onEditorKey : function(field, e){
56545         
56546         var k = e.getKey(),
56547             newCell,
56548             g = this.grid,
56549             ed = g.activeEditor,
56550             forward = false;
56551         ///Roo.log('onEditorKey' + k);
56552         
56553         
56554         if (this.enter_is_tab && k == e.ENTER) {
56555             k = e.TAB;
56556         }
56557         
56558         if(k == e.TAB){
56559             if(e.shiftKey){
56560                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56561             }else{
56562                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56563                 forward = true;
56564             }
56565             
56566             e.stopEvent();
56567             
56568         } else if(k == e.ENTER &&  !e.ctrlKey){
56569             ed.completeEdit();
56570             e.stopEvent();
56571             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56572         
56573                 } else if(k == e.ESC){
56574             ed.cancelEdit();
56575         }
56576                 
56577         if (newCell) {
56578             var ecall = { cell : newCell, forward : forward };
56579             this.fireEvent('beforeeditnext', ecall );
56580             newCell = ecall.cell;
56581                         forward = ecall.forward;
56582         }
56583                 
56584         if(newCell){
56585             //Roo.log('next cell after edit');
56586             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56587         } else if (forward) {
56588             // tabbed past last
56589             this.fireEvent.defer(100, this, ['tabend',this]);
56590         }
56591     }
56592 });/*
56593  * Based on:
56594  * Ext JS Library 1.1.1
56595  * Copyright(c) 2006-2007, Ext JS, LLC.
56596  *
56597  * Originally Released Under LGPL - original licence link has changed is not relivant.
56598  *
56599  * Fork - LGPL
56600  * <script type="text/javascript">
56601  */
56602  
56603 /**
56604  * @class Roo.grid.EditorGrid
56605  * @extends Roo.grid.Grid
56606  * Class for creating and editable grid.
56607  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56608  * The container MUST have some type of size defined for the grid to fill. The container will be 
56609  * automatically set to position relative if it isn't already.
56610  * @param {Object} dataSource The data model to bind to
56611  * @param {Object} colModel The column model with info about this grid's columns
56612  */
56613 Roo.grid.EditorGrid = function(container, config){
56614     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56615     this.getGridEl().addClass("xedit-grid");
56616
56617     if(!this.selModel){
56618         this.selModel = new Roo.grid.CellSelectionModel();
56619     }
56620
56621     this.activeEditor = null;
56622
56623         this.addEvents({
56624             /**
56625              * @event beforeedit
56626              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56627              * <ul style="padding:5px;padding-left:16px;">
56628              * <li>grid - This grid</li>
56629              * <li>record - The record being edited</li>
56630              * <li>field - The field name being edited</li>
56631              * <li>value - The value for the field being edited.</li>
56632              * <li>row - The grid row index</li>
56633              * <li>column - The grid column index</li>
56634              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56635              * </ul>
56636              * @param {Object} e An edit event (see above for description)
56637              */
56638             "beforeedit" : true,
56639             /**
56640              * @event afteredit
56641              * Fires after a cell is edited. <br />
56642              * <ul style="padding:5px;padding-left:16px;">
56643              * <li>grid - This grid</li>
56644              * <li>record - The record being edited</li>
56645              * <li>field - The field name being edited</li>
56646              * <li>value - The value being set</li>
56647              * <li>originalValue - The original value for the field, before the edit.</li>
56648              * <li>row - The grid row index</li>
56649              * <li>column - The grid column index</li>
56650              * </ul>
56651              * @param {Object} e An edit event (see above for description)
56652              */
56653             "afteredit" : true,
56654             /**
56655              * @event validateedit
56656              * Fires after a cell is edited, but before the value is set in the record. 
56657          * You can use this to modify the value being set in the field, Return false
56658              * to cancel the change. The edit event object has the following properties <br />
56659              * <ul style="padding:5px;padding-left:16px;">
56660          * <li>editor - This editor</li>
56661              * <li>grid - This grid</li>
56662              * <li>record - The record being edited</li>
56663              * <li>field - The field name being edited</li>
56664              * <li>value - The value being set</li>
56665              * <li>originalValue - The original value for the field, before the edit.</li>
56666              * <li>row - The grid row index</li>
56667              * <li>column - The grid column index</li>
56668              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56669              * </ul>
56670              * @param {Object} e An edit event (see above for description)
56671              */
56672             "validateedit" : true
56673         });
56674     this.on("bodyscroll", this.stopEditing,  this);
56675     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56676 };
56677
56678 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56679     /**
56680      * @cfg {Number} clicksToEdit
56681      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56682      */
56683     clicksToEdit: 2,
56684
56685     // private
56686     isEditor : true,
56687     // private
56688     trackMouseOver: false, // causes very odd FF errors
56689
56690     onCellDblClick : function(g, row, col){
56691         this.startEditing(row, col);
56692     },
56693
56694     onEditComplete : function(ed, value, startValue){
56695         this.editing = false;
56696         this.activeEditor = null;
56697         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56698         var r = ed.record;
56699         var field = this.colModel.getDataIndex(ed.col);
56700         var e = {
56701             grid: this,
56702             record: r,
56703             field: field,
56704             originalValue: startValue,
56705             value: value,
56706             row: ed.row,
56707             column: ed.col,
56708             cancel:false,
56709             editor: ed
56710         };
56711         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56712         cell.show();
56713           
56714         if(String(value) !== String(startValue)){
56715             
56716             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56717                 r.set(field, e.value);
56718                 // if we are dealing with a combo box..
56719                 // then we also set the 'name' colum to be the displayField
56720                 if (ed.field.displayField && ed.field.name) {
56721                     r.set(ed.field.name, ed.field.el.dom.value);
56722                 }
56723                 
56724                 delete e.cancel; //?? why!!!
56725                 this.fireEvent("afteredit", e);
56726             }
56727         } else {
56728             this.fireEvent("afteredit", e); // always fire it!
56729         }
56730         this.view.focusCell(ed.row, ed.col);
56731     },
56732
56733     /**
56734      * Starts editing the specified for the specified row/column
56735      * @param {Number} rowIndex
56736      * @param {Number} colIndex
56737      */
56738     startEditing : function(row, col){
56739         this.stopEditing();
56740         if(this.colModel.isCellEditable(col, row)){
56741             this.view.ensureVisible(row, col, true);
56742           
56743             var r = this.dataSource.getAt(row);
56744             var field = this.colModel.getDataIndex(col);
56745             var cell = Roo.get(this.view.getCell(row,col));
56746             var e = {
56747                 grid: this,
56748                 record: r,
56749                 field: field,
56750                 value: r.data[field],
56751                 row: row,
56752                 column: col,
56753                 cancel:false 
56754             };
56755             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56756                 this.editing = true;
56757                 var ed = this.colModel.getCellEditor(col, row);
56758                 
56759                 if (!ed) {
56760                     return;
56761                 }
56762                 if(!ed.rendered){
56763                     ed.render(ed.parentEl || document.body);
56764                 }
56765                 ed.field.reset();
56766                
56767                 cell.hide();
56768                 
56769                 (function(){ // complex but required for focus issues in safari, ie and opera
56770                     ed.row = row;
56771                     ed.col = col;
56772                     ed.record = r;
56773                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56774                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56775                     this.activeEditor = ed;
56776                     var v = r.data[field];
56777                     ed.startEdit(this.view.getCell(row, col), v);
56778                     // combo's with 'displayField and name set
56779                     if (ed.field.displayField && ed.field.name) {
56780                         ed.field.el.dom.value = r.data[ed.field.name];
56781                     }
56782                     
56783                     
56784                 }).defer(50, this);
56785             }
56786         }
56787     },
56788         
56789     /**
56790      * Stops any active editing
56791      */
56792     stopEditing : function(){
56793         if(this.activeEditor){
56794             this.activeEditor.completeEdit();
56795         }
56796         this.activeEditor = null;
56797     },
56798         
56799          /**
56800      * Called to get grid's drag proxy text, by default returns this.ddText.
56801      * @return {String}
56802      */
56803     getDragDropText : function(){
56804         var count = this.selModel.getSelectedCell() ? 1 : 0;
56805         return String.format(this.ddText, count, count == 1 ? '' : 's');
56806     }
56807         
56808 });/*
56809  * Based on:
56810  * Ext JS Library 1.1.1
56811  * Copyright(c) 2006-2007, Ext JS, LLC.
56812  *
56813  * Originally Released Under LGPL - original licence link has changed is not relivant.
56814  *
56815  * Fork - LGPL
56816  * <script type="text/javascript">
56817  */
56818
56819 // private - not really -- you end up using it !
56820 // This is a support class used internally by the Grid components
56821
56822 /**
56823  * @class Roo.grid.GridEditor
56824  * @extends Roo.Editor
56825  * Class for creating and editable grid elements.
56826  * @param {Object} config any settings (must include field)
56827  */
56828 Roo.grid.GridEditor = function(field, config){
56829     if (!config && field.field) {
56830         config = field;
56831         field = Roo.factory(config.field, Roo.form);
56832     }
56833     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56834     field.monitorTab = false;
56835 };
56836
56837 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56838     
56839     /**
56840      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56841      */
56842     
56843     alignment: "tl-tl",
56844     autoSize: "width",
56845     hideEl : false,
56846     cls: "x-small-editor x-grid-editor",
56847     shim:false,
56848     shadow:"frame"
56849 });/*
56850  * Based on:
56851  * Ext JS Library 1.1.1
56852  * Copyright(c) 2006-2007, Ext JS, LLC.
56853  *
56854  * Originally Released Under LGPL - original licence link has changed is not relivant.
56855  *
56856  * Fork - LGPL
56857  * <script type="text/javascript">
56858  */
56859   
56860
56861   
56862 Roo.grid.PropertyRecord = Roo.data.Record.create([
56863     {name:'name',type:'string'},  'value'
56864 ]);
56865
56866
56867 Roo.grid.PropertyStore = function(grid, source){
56868     this.grid = grid;
56869     this.store = new Roo.data.Store({
56870         recordType : Roo.grid.PropertyRecord
56871     });
56872     this.store.on('update', this.onUpdate,  this);
56873     if(source){
56874         this.setSource(source);
56875     }
56876     Roo.grid.PropertyStore.superclass.constructor.call(this);
56877 };
56878
56879
56880
56881 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56882     setSource : function(o){
56883         this.source = o;
56884         this.store.removeAll();
56885         var data = [];
56886         for(var k in o){
56887             if(this.isEditableValue(o[k])){
56888                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56889             }
56890         }
56891         this.store.loadRecords({records: data}, {}, true);
56892     },
56893
56894     onUpdate : function(ds, record, type){
56895         if(type == Roo.data.Record.EDIT){
56896             var v = record.data['value'];
56897             var oldValue = record.modified['value'];
56898             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56899                 this.source[record.id] = v;
56900                 record.commit();
56901                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56902             }else{
56903                 record.reject();
56904             }
56905         }
56906     },
56907
56908     getProperty : function(row){
56909        return this.store.getAt(row);
56910     },
56911
56912     isEditableValue: function(val){
56913         if(val && val instanceof Date){
56914             return true;
56915         }else if(typeof val == 'object' || typeof val == 'function'){
56916             return false;
56917         }
56918         return true;
56919     },
56920
56921     setValue : function(prop, value){
56922         this.source[prop] = value;
56923         this.store.getById(prop).set('value', value);
56924     },
56925
56926     getSource : function(){
56927         return this.source;
56928     }
56929 });
56930
56931 Roo.grid.PropertyColumnModel = function(grid, store){
56932     this.grid = grid;
56933     var g = Roo.grid;
56934     g.PropertyColumnModel.superclass.constructor.call(this, [
56935         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56936         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56937     ]);
56938     this.store = store;
56939     this.bselect = Roo.DomHelper.append(document.body, {
56940         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56941             {tag: 'option', value: 'true', html: 'true'},
56942             {tag: 'option', value: 'false', html: 'false'}
56943         ]
56944     });
56945     Roo.id(this.bselect);
56946     var f = Roo.form;
56947     this.editors = {
56948         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56949         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56950         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56951         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56952         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56953     };
56954     this.renderCellDelegate = this.renderCell.createDelegate(this);
56955     this.renderPropDelegate = this.renderProp.createDelegate(this);
56956 };
56957
56958 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56959     
56960     
56961     nameText : 'Name',
56962     valueText : 'Value',
56963     
56964     dateFormat : 'm/j/Y',
56965     
56966     
56967     renderDate : function(dateVal){
56968         return dateVal.dateFormat(this.dateFormat);
56969     },
56970
56971     renderBool : function(bVal){
56972         return bVal ? 'true' : 'false';
56973     },
56974
56975     isCellEditable : function(colIndex, rowIndex){
56976         return colIndex == 1;
56977     },
56978
56979     getRenderer : function(col){
56980         return col == 1 ?
56981             this.renderCellDelegate : this.renderPropDelegate;
56982     },
56983
56984     renderProp : function(v){
56985         return this.getPropertyName(v);
56986     },
56987
56988     renderCell : function(val){
56989         var rv = val;
56990         if(val instanceof Date){
56991             rv = this.renderDate(val);
56992         }else if(typeof val == 'boolean'){
56993             rv = this.renderBool(val);
56994         }
56995         return Roo.util.Format.htmlEncode(rv);
56996     },
56997
56998     getPropertyName : function(name){
56999         var pn = this.grid.propertyNames;
57000         return pn && pn[name] ? pn[name] : name;
57001     },
57002
57003     getCellEditor : function(colIndex, rowIndex){
57004         var p = this.store.getProperty(rowIndex);
57005         var n = p.data['name'], val = p.data['value'];
57006         
57007         if(typeof(this.grid.customEditors[n]) == 'string'){
57008             return this.editors[this.grid.customEditors[n]];
57009         }
57010         if(typeof(this.grid.customEditors[n]) != 'undefined'){
57011             return this.grid.customEditors[n];
57012         }
57013         if(val instanceof Date){
57014             return this.editors['date'];
57015         }else if(typeof val == 'number'){
57016             return this.editors['number'];
57017         }else if(typeof val == 'boolean'){
57018             return this.editors['boolean'];
57019         }else{
57020             return this.editors['string'];
57021         }
57022     }
57023 });
57024
57025 /**
57026  * @class Roo.grid.PropertyGrid
57027  * @extends Roo.grid.EditorGrid
57028  * This class represents the  interface of a component based property grid control.
57029  * <br><br>Usage:<pre><code>
57030  var grid = new Roo.grid.PropertyGrid("my-container-id", {
57031       
57032  });
57033  // set any options
57034  grid.render();
57035  * </code></pre>
57036   
57037  * @constructor
57038  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57039  * The container MUST have some type of size defined for the grid to fill. The container will be
57040  * automatically set to position relative if it isn't already.
57041  * @param {Object} config A config object that sets properties on this grid.
57042  */
57043 Roo.grid.PropertyGrid = function(container, config){
57044     config = config || {};
57045     var store = new Roo.grid.PropertyStore(this);
57046     this.store = store;
57047     var cm = new Roo.grid.PropertyColumnModel(this, store);
57048     store.store.sort('name', 'ASC');
57049     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
57050         ds: store.store,
57051         cm: cm,
57052         enableColLock:false,
57053         enableColumnMove:false,
57054         stripeRows:false,
57055         trackMouseOver: false,
57056         clicksToEdit:1
57057     }, config));
57058     this.getGridEl().addClass('x-props-grid');
57059     this.lastEditRow = null;
57060     this.on('columnresize', this.onColumnResize, this);
57061     this.addEvents({
57062          /**
57063              * @event beforepropertychange
57064              * Fires before a property changes (return false to stop?)
57065              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57066              * @param {String} id Record Id
57067              * @param {String} newval New Value
57068          * @param {String} oldval Old Value
57069              */
57070         "beforepropertychange": true,
57071         /**
57072              * @event propertychange
57073              * Fires after a property changes
57074              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57075              * @param {String} id Record Id
57076              * @param {String} newval New Value
57077          * @param {String} oldval Old Value
57078              */
57079         "propertychange": true
57080     });
57081     this.customEditors = this.customEditors || {};
57082 };
57083 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
57084     
57085      /**
57086      * @cfg {Object} customEditors map of colnames=> custom editors.
57087      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
57088      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
57089      * false disables editing of the field.
57090          */
57091     
57092       /**
57093      * @cfg {Object} propertyNames map of property Names to their displayed value
57094          */
57095     
57096     render : function(){
57097         Roo.grid.PropertyGrid.superclass.render.call(this);
57098         this.autoSize.defer(100, this);
57099     },
57100
57101     autoSize : function(){
57102         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
57103         if(this.view){
57104             this.view.fitColumns();
57105         }
57106     },
57107
57108     onColumnResize : function(){
57109         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
57110         this.autoSize();
57111     },
57112     /**
57113      * Sets the data for the Grid
57114      * accepts a Key => Value object of all the elements avaiable.
57115      * @param {Object} data  to appear in grid.
57116      */
57117     setSource : function(source){
57118         this.store.setSource(source);
57119         //this.autoSize();
57120     },
57121     /**
57122      * Gets all the data from the grid.
57123      * @return {Object} data  data stored in grid
57124      */
57125     getSource : function(){
57126         return this.store.getSource();
57127     }
57128 });/*
57129   
57130  * Licence LGPL
57131  
57132  */
57133  
57134 /**
57135  * @class Roo.grid.Calendar
57136  * @extends Roo.util.Grid
57137  * This class extends the Grid to provide a calendar widget
57138  * <br><br>Usage:<pre><code>
57139  var grid = new Roo.grid.Calendar("my-container-id", {
57140      ds: myDataStore,
57141      cm: myColModel,
57142      selModel: mySelectionModel,
57143      autoSizeColumns: true,
57144      monitorWindowResize: false,
57145      trackMouseOver: true
57146      eventstore : real data store..
57147  });
57148  // set any options
57149  grid.render();
57150   
57151   * @constructor
57152  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57153  * The container MUST have some type of size defined for the grid to fill. The container will be
57154  * automatically set to position relative if it isn't already.
57155  * @param {Object} config A config object that sets properties on this grid.
57156  */
57157 Roo.grid.Calendar = function(container, config){
57158         // initialize the container
57159         this.container = Roo.get(container);
57160         this.container.update("");
57161         this.container.setStyle("overflow", "hidden");
57162     this.container.addClass('x-grid-container');
57163
57164     this.id = this.container.id;
57165
57166     Roo.apply(this, config);
57167     // check and correct shorthanded configs
57168     
57169     var rows = [];
57170     var d =1;
57171     for (var r = 0;r < 6;r++) {
57172         
57173         rows[r]=[];
57174         for (var c =0;c < 7;c++) {
57175             rows[r][c]= '';
57176         }
57177     }
57178     if (this.eventStore) {
57179         this.eventStore= Roo.factory(this.eventStore, Roo.data);
57180         this.eventStore.on('load',this.onLoad, this);
57181         this.eventStore.on('beforeload',this.clearEvents, this);
57182          
57183     }
57184     
57185     this.dataSource = new Roo.data.Store({
57186             proxy: new Roo.data.MemoryProxy(rows),
57187             reader: new Roo.data.ArrayReader({}, [
57188                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
57189     });
57190
57191     this.dataSource.load();
57192     this.ds = this.dataSource;
57193     this.ds.xmodule = this.xmodule || false;
57194     
57195     
57196     var cellRender = function(v,x,r)
57197     {
57198         return String.format(
57199             '<div class="fc-day  fc-widget-content"><div>' +
57200                 '<div class="fc-event-container"></div>' +
57201                 '<div class="fc-day-number">{0}</div>'+
57202                 
57203                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
57204             '</div></div>', v);
57205     
57206     }
57207     
57208     
57209     this.colModel = new Roo.grid.ColumnModel( [
57210         {
57211             xtype: 'ColumnModel',
57212             xns: Roo.grid,
57213             dataIndex : 'weekday0',
57214             header : 'Sunday',
57215             renderer : cellRender
57216         },
57217         {
57218             xtype: 'ColumnModel',
57219             xns: Roo.grid,
57220             dataIndex : 'weekday1',
57221             header : 'Monday',
57222             renderer : cellRender
57223         },
57224         {
57225             xtype: 'ColumnModel',
57226             xns: Roo.grid,
57227             dataIndex : 'weekday2',
57228             header : 'Tuesday',
57229             renderer : cellRender
57230         },
57231         {
57232             xtype: 'ColumnModel',
57233             xns: Roo.grid,
57234             dataIndex : 'weekday3',
57235             header : 'Wednesday',
57236             renderer : cellRender
57237         },
57238         {
57239             xtype: 'ColumnModel',
57240             xns: Roo.grid,
57241             dataIndex : 'weekday4',
57242             header : 'Thursday',
57243             renderer : cellRender
57244         },
57245         {
57246             xtype: 'ColumnModel',
57247             xns: Roo.grid,
57248             dataIndex : 'weekday5',
57249             header : 'Friday',
57250             renderer : cellRender
57251         },
57252         {
57253             xtype: 'ColumnModel',
57254             xns: Roo.grid,
57255             dataIndex : 'weekday6',
57256             header : 'Saturday',
57257             renderer : cellRender
57258         }
57259     ]);
57260     this.cm = this.colModel;
57261     this.cm.xmodule = this.xmodule || false;
57262  
57263         
57264           
57265     //this.selModel = new Roo.grid.CellSelectionModel();
57266     //this.sm = this.selModel;
57267     //this.selModel.init(this);
57268     
57269     
57270     if(this.width){
57271         this.container.setWidth(this.width);
57272     }
57273
57274     if(this.height){
57275         this.container.setHeight(this.height);
57276     }
57277     /** @private */
57278         this.addEvents({
57279         // raw events
57280         /**
57281          * @event click
57282          * The raw click event for the entire grid.
57283          * @param {Roo.EventObject} e
57284          */
57285         "click" : true,
57286         /**
57287          * @event dblclick
57288          * The raw dblclick event for the entire grid.
57289          * @param {Roo.EventObject} e
57290          */
57291         "dblclick" : true,
57292         /**
57293          * @event contextmenu
57294          * The raw contextmenu event for the entire grid.
57295          * @param {Roo.EventObject} e
57296          */
57297         "contextmenu" : true,
57298         /**
57299          * @event mousedown
57300          * The raw mousedown event for the entire grid.
57301          * @param {Roo.EventObject} e
57302          */
57303         "mousedown" : true,
57304         /**
57305          * @event mouseup
57306          * The raw mouseup event for the entire grid.
57307          * @param {Roo.EventObject} e
57308          */
57309         "mouseup" : true,
57310         /**
57311          * @event mouseover
57312          * The raw mouseover event for the entire grid.
57313          * @param {Roo.EventObject} e
57314          */
57315         "mouseover" : true,
57316         /**
57317          * @event mouseout
57318          * The raw mouseout event for the entire grid.
57319          * @param {Roo.EventObject} e
57320          */
57321         "mouseout" : true,
57322         /**
57323          * @event keypress
57324          * The raw keypress event for the entire grid.
57325          * @param {Roo.EventObject} e
57326          */
57327         "keypress" : true,
57328         /**
57329          * @event keydown
57330          * The raw keydown event for the entire grid.
57331          * @param {Roo.EventObject} e
57332          */
57333         "keydown" : true,
57334
57335         // custom events
57336
57337         /**
57338          * @event cellclick
57339          * Fires when a cell is clicked
57340          * @param {Grid} this
57341          * @param {Number} rowIndex
57342          * @param {Number} columnIndex
57343          * @param {Roo.EventObject} e
57344          */
57345         "cellclick" : true,
57346         /**
57347          * @event celldblclick
57348          * Fires when a cell is double clicked
57349          * @param {Grid} this
57350          * @param {Number} rowIndex
57351          * @param {Number} columnIndex
57352          * @param {Roo.EventObject} e
57353          */
57354         "celldblclick" : true,
57355         /**
57356          * @event rowclick
57357          * Fires when a row is clicked
57358          * @param {Grid} this
57359          * @param {Number} rowIndex
57360          * @param {Roo.EventObject} e
57361          */
57362         "rowclick" : true,
57363         /**
57364          * @event rowdblclick
57365          * Fires when a row is double clicked
57366          * @param {Grid} this
57367          * @param {Number} rowIndex
57368          * @param {Roo.EventObject} e
57369          */
57370         "rowdblclick" : true,
57371         /**
57372          * @event headerclick
57373          * Fires when a header is clicked
57374          * @param {Grid} this
57375          * @param {Number} columnIndex
57376          * @param {Roo.EventObject} e
57377          */
57378         "headerclick" : true,
57379         /**
57380          * @event headerdblclick
57381          * Fires when a header cell is double clicked
57382          * @param {Grid} this
57383          * @param {Number} columnIndex
57384          * @param {Roo.EventObject} e
57385          */
57386         "headerdblclick" : true,
57387         /**
57388          * @event rowcontextmenu
57389          * Fires when a row is right clicked
57390          * @param {Grid} this
57391          * @param {Number} rowIndex
57392          * @param {Roo.EventObject} e
57393          */
57394         "rowcontextmenu" : true,
57395         /**
57396          * @event cellcontextmenu
57397          * Fires when a cell is right clicked
57398          * @param {Grid} this
57399          * @param {Number} rowIndex
57400          * @param {Number} cellIndex
57401          * @param {Roo.EventObject} e
57402          */
57403          "cellcontextmenu" : true,
57404         /**
57405          * @event headercontextmenu
57406          * Fires when a header is right clicked
57407          * @param {Grid} this
57408          * @param {Number} columnIndex
57409          * @param {Roo.EventObject} e
57410          */
57411         "headercontextmenu" : true,
57412         /**
57413          * @event bodyscroll
57414          * Fires when the body element is scrolled
57415          * @param {Number} scrollLeft
57416          * @param {Number} scrollTop
57417          */
57418         "bodyscroll" : true,
57419         /**
57420          * @event columnresize
57421          * Fires when the user resizes a column
57422          * @param {Number} columnIndex
57423          * @param {Number} newSize
57424          */
57425         "columnresize" : true,
57426         /**
57427          * @event columnmove
57428          * Fires when the user moves a column
57429          * @param {Number} oldIndex
57430          * @param {Number} newIndex
57431          */
57432         "columnmove" : true,
57433         /**
57434          * @event startdrag
57435          * Fires when row(s) start being dragged
57436          * @param {Grid} this
57437          * @param {Roo.GridDD} dd The drag drop object
57438          * @param {event} e The raw browser event
57439          */
57440         "startdrag" : true,
57441         /**
57442          * @event enddrag
57443          * Fires when a drag operation is complete
57444          * @param {Grid} this
57445          * @param {Roo.GridDD} dd The drag drop object
57446          * @param {event} e The raw browser event
57447          */
57448         "enddrag" : true,
57449         /**
57450          * @event dragdrop
57451          * Fires when dragged row(s) are dropped on a valid DD target
57452          * @param {Grid} this
57453          * @param {Roo.GridDD} dd The drag drop object
57454          * @param {String} targetId The target drag drop object
57455          * @param {event} e The raw browser event
57456          */
57457         "dragdrop" : true,
57458         /**
57459          * @event dragover
57460          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57461          * @param {Grid} this
57462          * @param {Roo.GridDD} dd The drag drop object
57463          * @param {String} targetId The target drag drop object
57464          * @param {event} e The raw browser event
57465          */
57466         "dragover" : true,
57467         /**
57468          * @event dragenter
57469          *  Fires when the dragged row(s) first cross another DD target while being dragged
57470          * @param {Grid} this
57471          * @param {Roo.GridDD} dd The drag drop object
57472          * @param {String} targetId The target drag drop object
57473          * @param {event} e The raw browser event
57474          */
57475         "dragenter" : true,
57476         /**
57477          * @event dragout
57478          * Fires when the dragged row(s) leave another DD target while being dragged
57479          * @param {Grid} this
57480          * @param {Roo.GridDD} dd The drag drop object
57481          * @param {String} targetId The target drag drop object
57482          * @param {event} e The raw browser event
57483          */
57484         "dragout" : true,
57485         /**
57486          * @event rowclass
57487          * Fires when a row is rendered, so you can change add a style to it.
57488          * @param {GridView} gridview   The grid view
57489          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57490          */
57491         'rowclass' : true,
57492
57493         /**
57494          * @event render
57495          * Fires when the grid is rendered
57496          * @param {Grid} grid
57497          */
57498         'render' : true,
57499             /**
57500              * @event select
57501              * Fires when a date is selected
57502              * @param {DatePicker} this
57503              * @param {Date} date The selected date
57504              */
57505         'select': true,
57506         /**
57507              * @event monthchange
57508              * Fires when the displayed month changes 
57509              * @param {DatePicker} this
57510              * @param {Date} date The selected month
57511              */
57512         'monthchange': true,
57513         /**
57514              * @event evententer
57515              * Fires when mouse over an event
57516              * @param {Calendar} this
57517              * @param {event} Event
57518              */
57519         'evententer': true,
57520         /**
57521              * @event eventleave
57522              * Fires when the mouse leaves an
57523              * @param {Calendar} this
57524              * @param {event}
57525              */
57526         'eventleave': true,
57527         /**
57528              * @event eventclick
57529              * Fires when the mouse click an
57530              * @param {Calendar} this
57531              * @param {event}
57532              */
57533         'eventclick': true,
57534         /**
57535              * @event eventrender
57536              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57537              * @param {Calendar} this
57538              * @param {data} data to be modified
57539              */
57540         'eventrender': true
57541         
57542     });
57543
57544     Roo.grid.Grid.superclass.constructor.call(this);
57545     this.on('render', function() {
57546         this.view.el.addClass('x-grid-cal'); 
57547         
57548         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57549
57550     },this);
57551     
57552     if (!Roo.grid.Calendar.style) {
57553         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57554             
57555             
57556             '.x-grid-cal .x-grid-col' :  {
57557                 height: 'auto !important',
57558                 'vertical-align': 'top'
57559             },
57560             '.x-grid-cal  .fc-event-hori' : {
57561                 height: '14px'
57562             }
57563              
57564             
57565         }, Roo.id());
57566     }
57567
57568     
57569     
57570 };
57571 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57572     /**
57573      * @cfg {Store} eventStore The store that loads events.
57574      */
57575     eventStore : 25,
57576
57577      
57578     activeDate : false,
57579     startDay : 0,
57580     autoWidth : true,
57581     monitorWindowResize : false,
57582
57583     
57584     resizeColumns : function() {
57585         var col = (this.view.el.getWidth() / 7) - 3;
57586         // loop through cols, and setWidth
57587         for(var i =0 ; i < 7 ; i++){
57588             this.cm.setColumnWidth(i, col);
57589         }
57590     },
57591      setDate :function(date) {
57592         
57593         Roo.log('setDate?');
57594         
57595         this.resizeColumns();
57596         var vd = this.activeDate;
57597         this.activeDate = date;
57598 //        if(vd && this.el){
57599 //            var t = date.getTime();
57600 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57601 //                Roo.log('using add remove');
57602 //                
57603 //                this.fireEvent('monthchange', this, date);
57604 //                
57605 //                this.cells.removeClass("fc-state-highlight");
57606 //                this.cells.each(function(c){
57607 //                   if(c.dateValue == t){
57608 //                       c.addClass("fc-state-highlight");
57609 //                       setTimeout(function(){
57610 //                            try{c.dom.firstChild.focus();}catch(e){}
57611 //                       }, 50);
57612 //                       return false;
57613 //                   }
57614 //                   return true;
57615 //                });
57616 //                return;
57617 //            }
57618 //        }
57619         
57620         var days = date.getDaysInMonth();
57621         
57622         var firstOfMonth = date.getFirstDateOfMonth();
57623         var startingPos = firstOfMonth.getDay()-this.startDay;
57624         
57625         if(startingPos < this.startDay){
57626             startingPos += 7;
57627         }
57628         
57629         var pm = date.add(Date.MONTH, -1);
57630         var prevStart = pm.getDaysInMonth()-startingPos;
57631 //        
57632         
57633         
57634         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57635         
57636         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57637         //this.cells.addClassOnOver('fc-state-hover');
57638         
57639         var cells = this.cells.elements;
57640         var textEls = this.textNodes;
57641         
57642         //Roo.each(cells, function(cell){
57643         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57644         //});
57645         
57646         days += startingPos;
57647
57648         // convert everything to numbers so it's fast
57649         var day = 86400000;
57650         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57651         //Roo.log(d);
57652         //Roo.log(pm);
57653         //Roo.log(prevStart);
57654         
57655         var today = new Date().clearTime().getTime();
57656         var sel = date.clearTime().getTime();
57657         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57658         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57659         var ddMatch = this.disabledDatesRE;
57660         var ddText = this.disabledDatesText;
57661         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57662         var ddaysText = this.disabledDaysText;
57663         var format = this.format;
57664         
57665         var setCellClass = function(cal, cell){
57666             
57667             //Roo.log('set Cell Class');
57668             cell.title = "";
57669             var t = d.getTime();
57670             
57671             //Roo.log(d);
57672             
57673             
57674             cell.dateValue = t;
57675             if(t == today){
57676                 cell.className += " fc-today";
57677                 cell.className += " fc-state-highlight";
57678                 cell.title = cal.todayText;
57679             }
57680             if(t == sel){
57681                 // disable highlight in other month..
57682                 cell.className += " fc-state-highlight";
57683                 
57684             }
57685             // disabling
57686             if(t < min) {
57687                 //cell.className = " fc-state-disabled";
57688                 cell.title = cal.minText;
57689                 return;
57690             }
57691             if(t > max) {
57692                 //cell.className = " fc-state-disabled";
57693                 cell.title = cal.maxText;
57694                 return;
57695             }
57696             if(ddays){
57697                 if(ddays.indexOf(d.getDay()) != -1){
57698                     // cell.title = ddaysText;
57699                    // cell.className = " fc-state-disabled";
57700                 }
57701             }
57702             if(ddMatch && format){
57703                 var fvalue = d.dateFormat(format);
57704                 if(ddMatch.test(fvalue)){
57705                     cell.title = ddText.replace("%0", fvalue);
57706                    cell.className = " fc-state-disabled";
57707                 }
57708             }
57709             
57710             if (!cell.initialClassName) {
57711                 cell.initialClassName = cell.dom.className;
57712             }
57713             
57714             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57715         };
57716
57717         var i = 0;
57718         
57719         for(; i < startingPos; i++) {
57720             cells[i].dayName =  (++prevStart);
57721             Roo.log(textEls[i]);
57722             d.setDate(d.getDate()+1);
57723             
57724             //cells[i].className = "fc-past fc-other-month";
57725             setCellClass(this, cells[i]);
57726         }
57727         
57728         var intDay = 0;
57729         
57730         for(; i < days; i++){
57731             intDay = i - startingPos + 1;
57732             cells[i].dayName =  (intDay);
57733             d.setDate(d.getDate()+1);
57734             
57735             cells[i].className = ''; // "x-date-active";
57736             setCellClass(this, cells[i]);
57737         }
57738         var extraDays = 0;
57739         
57740         for(; i < 42; i++) {
57741             //textEls[i].innerHTML = (++extraDays);
57742             
57743             d.setDate(d.getDate()+1);
57744             cells[i].dayName = (++extraDays);
57745             cells[i].className = "fc-future fc-other-month";
57746             setCellClass(this, cells[i]);
57747         }
57748         
57749         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57750         
57751         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57752         
57753         // this will cause all the cells to mis
57754         var rows= [];
57755         var i =0;
57756         for (var r = 0;r < 6;r++) {
57757             for (var c =0;c < 7;c++) {
57758                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57759             }    
57760         }
57761         
57762         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57763         for(i=0;i<cells.length;i++) {
57764             
57765             this.cells.elements[i].dayName = cells[i].dayName ;
57766             this.cells.elements[i].className = cells[i].className;
57767             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57768             this.cells.elements[i].title = cells[i].title ;
57769             this.cells.elements[i].dateValue = cells[i].dateValue ;
57770         }
57771         
57772         
57773         
57774         
57775         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57776         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57777         
57778         ////if(totalRows != 6){
57779             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57780            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57781        // }
57782         
57783         this.fireEvent('monthchange', this, date);
57784         
57785         
57786     },
57787  /**
57788      * Returns the grid's SelectionModel.
57789      * @return {SelectionModel}
57790      */
57791     getSelectionModel : function(){
57792         if(!this.selModel){
57793             this.selModel = new Roo.grid.CellSelectionModel();
57794         }
57795         return this.selModel;
57796     },
57797
57798     load: function() {
57799         this.eventStore.load()
57800         
57801         
57802         
57803     },
57804     
57805     findCell : function(dt) {
57806         dt = dt.clearTime().getTime();
57807         var ret = false;
57808         this.cells.each(function(c){
57809             //Roo.log("check " +c.dateValue + '?=' + dt);
57810             if(c.dateValue == dt){
57811                 ret = c;
57812                 return false;
57813             }
57814             return true;
57815         });
57816         
57817         return ret;
57818     },
57819     
57820     findCells : function(rec) {
57821         var s = rec.data.start_dt.clone().clearTime().getTime();
57822        // Roo.log(s);
57823         var e= rec.data.end_dt.clone().clearTime().getTime();
57824        // Roo.log(e);
57825         var ret = [];
57826         this.cells.each(function(c){
57827              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57828             
57829             if(c.dateValue > e){
57830                 return ;
57831             }
57832             if(c.dateValue < s){
57833                 return ;
57834             }
57835             ret.push(c);
57836         });
57837         
57838         return ret;    
57839     },
57840     
57841     findBestRow: function(cells)
57842     {
57843         var ret = 0;
57844         
57845         for (var i =0 ; i < cells.length;i++) {
57846             ret  = Math.max(cells[i].rows || 0,ret);
57847         }
57848         return ret;
57849         
57850     },
57851     
57852     
57853     addItem : function(rec)
57854     {
57855         // look for vertical location slot in
57856         var cells = this.findCells(rec);
57857         
57858         rec.row = this.findBestRow(cells);
57859         
57860         // work out the location.
57861         
57862         var crow = false;
57863         var rows = [];
57864         for(var i =0; i < cells.length; i++) {
57865             if (!crow) {
57866                 crow = {
57867                     start : cells[i],
57868                     end :  cells[i]
57869                 };
57870                 continue;
57871             }
57872             if (crow.start.getY() == cells[i].getY()) {
57873                 // on same row.
57874                 crow.end = cells[i];
57875                 continue;
57876             }
57877             // different row.
57878             rows.push(crow);
57879             crow = {
57880                 start: cells[i],
57881                 end : cells[i]
57882             };
57883             
57884         }
57885         
57886         rows.push(crow);
57887         rec.els = [];
57888         rec.rows = rows;
57889         rec.cells = cells;
57890         for (var i = 0; i < cells.length;i++) {
57891             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57892             
57893         }
57894         
57895         
57896     },
57897     
57898     clearEvents: function() {
57899         
57900         if (!this.eventStore.getCount()) {
57901             return;
57902         }
57903         // reset number of rows in cells.
57904         Roo.each(this.cells.elements, function(c){
57905             c.rows = 0;
57906         });
57907         
57908         this.eventStore.each(function(e) {
57909             this.clearEvent(e);
57910         },this);
57911         
57912     },
57913     
57914     clearEvent : function(ev)
57915     {
57916         if (ev.els) {
57917             Roo.each(ev.els, function(el) {
57918                 el.un('mouseenter' ,this.onEventEnter, this);
57919                 el.un('mouseleave' ,this.onEventLeave, this);
57920                 el.remove();
57921             },this);
57922             ev.els = [];
57923         }
57924     },
57925     
57926     
57927     renderEvent : function(ev,ctr) {
57928         if (!ctr) {
57929              ctr = this.view.el.select('.fc-event-container',true).first();
57930         }
57931         
57932          
57933         this.clearEvent(ev);
57934             //code
57935        
57936         
57937         
57938         ev.els = [];
57939         var cells = ev.cells;
57940         var rows = ev.rows;
57941         this.fireEvent('eventrender', this, ev);
57942         
57943         for(var i =0; i < rows.length; i++) {
57944             
57945             cls = '';
57946             if (i == 0) {
57947                 cls += ' fc-event-start';
57948             }
57949             if ((i+1) == rows.length) {
57950                 cls += ' fc-event-end';
57951             }
57952             
57953             //Roo.log(ev.data);
57954             // how many rows should it span..
57955             var cg = this.eventTmpl.append(ctr,Roo.apply({
57956                 fccls : cls
57957                 
57958             }, ev.data) , true);
57959             
57960             
57961             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57962             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57963             cg.on('click', this.onEventClick, this, ev);
57964             
57965             ev.els.push(cg);
57966             
57967             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57968             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57969             //Roo.log(cg);
57970              
57971             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57972             cg.setWidth(ebox.right - sbox.x -2);
57973         }
57974     },
57975     
57976     renderEvents: function()
57977     {   
57978         // first make sure there is enough space..
57979         
57980         if (!this.eventTmpl) {
57981             this.eventTmpl = new Roo.Template(
57982                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57983                     '<div class="fc-event-inner">' +
57984                         '<span class="fc-event-time">{time}</span>' +
57985                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57986                     '</div>' +
57987                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57988                 '</div>'
57989             );
57990                 
57991         }
57992                
57993         
57994         
57995         this.cells.each(function(c) {
57996             //Roo.log(c.select('.fc-day-content div',true).first());
57997             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57998         });
57999         
58000         var ctr = this.view.el.select('.fc-event-container',true).first();
58001         
58002         var cls;
58003         this.eventStore.each(function(ev){
58004             
58005             this.renderEvent(ev);
58006              
58007              
58008         }, this);
58009         this.view.layout();
58010         
58011     },
58012     
58013     onEventEnter: function (e, el,event,d) {
58014         this.fireEvent('evententer', this, el, event);
58015     },
58016     
58017     onEventLeave: function (e, el,event,d) {
58018         this.fireEvent('eventleave', this, el, event);
58019     },
58020     
58021     onEventClick: function (e, el,event,d) {
58022         this.fireEvent('eventclick', this, el, event);
58023     },
58024     
58025     onMonthChange: function () {
58026         this.store.load();
58027     },
58028     
58029     onLoad: function () {
58030         
58031         //Roo.log('calendar onload');
58032 //         
58033         if(this.eventStore.getCount() > 0){
58034             
58035            
58036             
58037             this.eventStore.each(function(d){
58038                 
58039                 
58040                 // FIXME..
58041                 var add =   d.data;
58042                 if (typeof(add.end_dt) == 'undefined')  {
58043                     Roo.log("Missing End time in calendar data: ");
58044                     Roo.log(d);
58045                     return;
58046                 }
58047                 if (typeof(add.start_dt) == 'undefined')  {
58048                     Roo.log("Missing Start time in calendar data: ");
58049                     Roo.log(d);
58050                     return;
58051                 }
58052                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
58053                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
58054                 add.id = add.id || d.id;
58055                 add.title = add.title || '??';
58056                 
58057                 this.addItem(d);
58058                 
58059              
58060             },this);
58061         }
58062         
58063         this.renderEvents();
58064     }
58065     
58066
58067 });
58068 /*
58069  grid : {
58070                 xtype: 'Grid',
58071                 xns: Roo.grid,
58072                 listeners : {
58073                     render : function ()
58074                     {
58075                         _this.grid = this;
58076                         
58077                         if (!this.view.el.hasClass('course-timesheet')) {
58078                             this.view.el.addClass('course-timesheet');
58079                         }
58080                         if (this.tsStyle) {
58081                             this.ds.load({});
58082                             return; 
58083                         }
58084                         Roo.log('width');
58085                         Roo.log(_this.grid.view.el.getWidth());
58086                         
58087                         
58088                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
58089                             '.course-timesheet .x-grid-row' : {
58090                                 height: '80px'
58091                             },
58092                             '.x-grid-row td' : {
58093                                 'vertical-align' : 0
58094                             },
58095                             '.course-edit-link' : {
58096                                 'color' : 'blue',
58097                                 'text-overflow' : 'ellipsis',
58098                                 'overflow' : 'hidden',
58099                                 'white-space' : 'nowrap',
58100                                 'cursor' : 'pointer'
58101                             },
58102                             '.sub-link' : {
58103                                 'color' : 'green'
58104                             },
58105                             '.de-act-sup-link' : {
58106                                 'color' : 'purple',
58107                                 'text-decoration' : 'line-through'
58108                             },
58109                             '.de-act-link' : {
58110                                 'color' : 'red',
58111                                 'text-decoration' : 'line-through'
58112                             },
58113                             '.course-timesheet .course-highlight' : {
58114                                 'border-top-style': 'dashed !important',
58115                                 'border-bottom-bottom': 'dashed !important'
58116                             },
58117                             '.course-timesheet .course-item' : {
58118                                 'font-family'   : 'tahoma, arial, helvetica',
58119                                 'font-size'     : '11px',
58120                                 'overflow'      : 'hidden',
58121                                 'padding-left'  : '10px',
58122                                 'padding-right' : '10px',
58123                                 'padding-top' : '10px' 
58124                             }
58125                             
58126                         }, Roo.id());
58127                                 this.ds.load({});
58128                     }
58129                 },
58130                 autoWidth : true,
58131                 monitorWindowResize : false,
58132                 cellrenderer : function(v,x,r)
58133                 {
58134                     return v;
58135                 },
58136                 sm : {
58137                     xtype: 'CellSelectionModel',
58138                     xns: Roo.grid
58139                 },
58140                 dataSource : {
58141                     xtype: 'Store',
58142                     xns: Roo.data,
58143                     listeners : {
58144                         beforeload : function (_self, options)
58145                         {
58146                             options.params = options.params || {};
58147                             options.params._month = _this.monthField.getValue();
58148                             options.params.limit = 9999;
58149                             options.params['sort'] = 'when_dt';    
58150                             options.params['dir'] = 'ASC';    
58151                             this.proxy.loadResponse = this.loadResponse;
58152                             Roo.log("load?");
58153                             //this.addColumns();
58154                         },
58155                         load : function (_self, records, options)
58156                         {
58157                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
58158                                 // if you click on the translation.. you can edit it...
58159                                 var el = Roo.get(this);
58160                                 var id = el.dom.getAttribute('data-id');
58161                                 var d = el.dom.getAttribute('data-date');
58162                                 var t = el.dom.getAttribute('data-time');
58163                                 //var id = this.child('span').dom.textContent;
58164                                 
58165                                 //Roo.log(this);
58166                                 Pman.Dialog.CourseCalendar.show({
58167                                     id : id,
58168                                     when_d : d,
58169                                     when_t : t,
58170                                     productitem_active : id ? 1 : 0
58171                                 }, function() {
58172                                     _this.grid.ds.load({});
58173                                 });
58174                            
58175                            });
58176                            
58177                            _this.panel.fireEvent('resize', [ '', '' ]);
58178                         }
58179                     },
58180                     loadResponse : function(o, success, response){
58181                             // this is overridden on before load..
58182                             
58183                             Roo.log("our code?");       
58184                             //Roo.log(success);
58185                             //Roo.log(response)
58186                             delete this.activeRequest;
58187                             if(!success){
58188                                 this.fireEvent("loadexception", this, o, response);
58189                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58190                                 return;
58191                             }
58192                             var result;
58193                             try {
58194                                 result = o.reader.read(response);
58195                             }catch(e){
58196                                 Roo.log("load exception?");
58197                                 this.fireEvent("loadexception", this, o, response, e);
58198                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58199                                 return;
58200                             }
58201                             Roo.log("ready...");        
58202                             // loop through result.records;
58203                             // and set this.tdate[date] = [] << array of records..
58204                             _this.tdata  = {};
58205                             Roo.each(result.records, function(r){
58206                                 //Roo.log(r.data);
58207                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
58208                                     _this.tdata[r.data.when_dt.format('j')] = [];
58209                                 }
58210                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
58211                             });
58212                             
58213                             //Roo.log(_this.tdata);
58214                             
58215                             result.records = [];
58216                             result.totalRecords = 6;
58217                     
58218                             // let's generate some duumy records for the rows.
58219                             //var st = _this.dateField.getValue();
58220                             
58221                             // work out monday..
58222                             //st = st.add(Date.DAY, -1 * st.format('w'));
58223                             
58224                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58225                             
58226                             var firstOfMonth = date.getFirstDayOfMonth();
58227                             var days = date.getDaysInMonth();
58228                             var d = 1;
58229                             var firstAdded = false;
58230                             for (var i = 0; i < result.totalRecords ; i++) {
58231                                 //var d= st.add(Date.DAY, i);
58232                                 var row = {};
58233                                 var added = 0;
58234                                 for(var w = 0 ; w < 7 ; w++){
58235                                     if(!firstAdded && firstOfMonth != w){
58236                                         continue;
58237                                     }
58238                                     if(d > days){
58239                                         continue;
58240                                     }
58241                                     firstAdded = true;
58242                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
58243                                     row['weekday'+w] = String.format(
58244                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
58245                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
58246                                                     d,
58247                                                     date.format('Y-m-')+dd
58248                                                 );
58249                                     added++;
58250                                     if(typeof(_this.tdata[d]) != 'undefined'){
58251                                         Roo.each(_this.tdata[d], function(r){
58252                                             var is_sub = '';
58253                                             var deactive = '';
58254                                             var id = r.id;
58255                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
58256                                             if(r.parent_id*1>0){
58257                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
58258                                                 id = r.parent_id;
58259                                             }
58260                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
58261                                                 deactive = 'de-act-link';
58262                                             }
58263                                             
58264                                             row['weekday'+w] += String.format(
58265                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
58266                                                     id, //0
58267                                                     r.product_id_name, //1
58268                                                     r.when_dt.format('h:ia'), //2
58269                                                     is_sub, //3
58270                                                     deactive, //4
58271                                                     desc // 5
58272                                             );
58273                                         });
58274                                     }
58275                                     d++;
58276                                 }
58277                                 
58278                                 // only do this if something added..
58279                                 if(added > 0){ 
58280                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
58281                                 }
58282                                 
58283                                 
58284                                 // push it twice. (second one with an hour..
58285                                 
58286                             }
58287                             //Roo.log(result);
58288                             this.fireEvent("load", this, o, o.request.arg);
58289                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
58290                         },
58291                     sortInfo : {field: 'when_dt', direction : 'ASC' },
58292                     proxy : {
58293                         xtype: 'HttpProxy',
58294                         xns: Roo.data,
58295                         method : 'GET',
58296                         url : baseURL + '/Roo/Shop_course.php'
58297                     },
58298                     reader : {
58299                         xtype: 'JsonReader',
58300                         xns: Roo.data,
58301                         id : 'id',
58302                         fields : [
58303                             {
58304                                 'name': 'id',
58305                                 'type': 'int'
58306                             },
58307                             {
58308                                 'name': 'when_dt',
58309                                 'type': 'string'
58310                             },
58311                             {
58312                                 'name': 'end_dt',
58313                                 'type': 'string'
58314                             },
58315                             {
58316                                 'name': 'parent_id',
58317                                 'type': 'int'
58318                             },
58319                             {
58320                                 'name': 'product_id',
58321                                 'type': 'int'
58322                             },
58323                             {
58324                                 'name': 'productitem_id',
58325                                 'type': 'int'
58326                             },
58327                             {
58328                                 'name': 'guid',
58329                                 'type': 'int'
58330                             }
58331                         ]
58332                     }
58333                 },
58334                 toolbar : {
58335                     xtype: 'Toolbar',
58336                     xns: Roo,
58337                     items : [
58338                         {
58339                             xtype: 'Button',
58340                             xns: Roo.Toolbar,
58341                             listeners : {
58342                                 click : function (_self, e)
58343                                 {
58344                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58345                                     sd.setMonth(sd.getMonth()-1);
58346                                     _this.monthField.setValue(sd.format('Y-m-d'));
58347                                     _this.grid.ds.load({});
58348                                 }
58349                             },
58350                             text : "Back"
58351                         },
58352                         {
58353                             xtype: 'Separator',
58354                             xns: Roo.Toolbar
58355                         },
58356                         {
58357                             xtype: 'MonthField',
58358                             xns: Roo.form,
58359                             listeners : {
58360                                 render : function (_self)
58361                                 {
58362                                     _this.monthField = _self;
58363                                    // _this.monthField.set  today
58364                                 },
58365                                 select : function (combo, date)
58366                                 {
58367                                     _this.grid.ds.load({});
58368                                 }
58369                             },
58370                             value : (function() { return new Date(); })()
58371                         },
58372                         {
58373                             xtype: 'Separator',
58374                             xns: Roo.Toolbar
58375                         },
58376                         {
58377                             xtype: 'TextItem',
58378                             xns: Roo.Toolbar,
58379                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
58380                         },
58381                         {
58382                             xtype: 'Fill',
58383                             xns: Roo.Toolbar
58384                         },
58385                         {
58386                             xtype: 'Button',
58387                             xns: Roo.Toolbar,
58388                             listeners : {
58389                                 click : function (_self, e)
58390                                 {
58391                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58392                                     sd.setMonth(sd.getMonth()+1);
58393                                     _this.monthField.setValue(sd.format('Y-m-d'));
58394                                     _this.grid.ds.load({});
58395                                 }
58396                             },
58397                             text : "Next"
58398                         }
58399                     ]
58400                 },
58401                  
58402             }
58403         };
58404         
58405         *//*
58406  * Based on:
58407  * Ext JS Library 1.1.1
58408  * Copyright(c) 2006-2007, Ext JS, LLC.
58409  *
58410  * Originally Released Under LGPL - original licence link has changed is not relivant.
58411  *
58412  * Fork - LGPL
58413  * <script type="text/javascript">
58414  */
58415  
58416 /**
58417  * @class Roo.LoadMask
58418  * A simple utility class for generically masking elements while loading data.  If the element being masked has
58419  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
58420  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
58421  * element's UpdateManager load indicator and will be destroyed after the initial load.
58422  * @constructor
58423  * Create a new LoadMask
58424  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58425  * @param {Object} config The config object
58426  */
58427 Roo.LoadMask = function(el, config){
58428     this.el = Roo.get(el);
58429     Roo.apply(this, config);
58430     if(this.store){
58431         this.store.on('beforeload', this.onBeforeLoad, this);
58432         this.store.on('load', this.onLoad, this);
58433         this.store.on('loadexception', this.onLoadException, this);
58434         this.removeMask = false;
58435     }else{
58436         var um = this.el.getUpdateManager();
58437         um.showLoadIndicator = false; // disable the default indicator
58438         um.on('beforeupdate', this.onBeforeLoad, this);
58439         um.on('update', this.onLoad, this);
58440         um.on('failure', this.onLoad, this);
58441         this.removeMask = true;
58442     }
58443 };
58444
58445 Roo.LoadMask.prototype = {
58446     /**
58447      * @cfg {Boolean} removeMask
58448      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58449      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58450      */
58451     /**
58452      * @cfg {String} msg
58453      * The text to display in a centered loading message box (defaults to 'Loading...')
58454      */
58455     msg : 'Loading...',
58456     /**
58457      * @cfg {String} msgCls
58458      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58459      */
58460     msgCls : 'x-mask-loading',
58461
58462     /**
58463      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58464      * @type Boolean
58465      */
58466     disabled: false,
58467
58468     /**
58469      * Disables the mask to prevent it from being displayed
58470      */
58471     disable : function(){
58472        this.disabled = true;
58473     },
58474
58475     /**
58476      * Enables the mask so that it can be displayed
58477      */
58478     enable : function(){
58479         this.disabled = false;
58480     },
58481     
58482     onLoadException : function()
58483     {
58484         Roo.log(arguments);
58485         
58486         if (typeof(arguments[3]) != 'undefined') {
58487             Roo.MessageBox.alert("Error loading",arguments[3]);
58488         } 
58489         /*
58490         try {
58491             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58492                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58493             }   
58494         } catch(e) {
58495             
58496         }
58497         */
58498     
58499         
58500         
58501         this.el.unmask(this.removeMask);
58502     },
58503     // private
58504     onLoad : function()
58505     {
58506         this.el.unmask(this.removeMask);
58507     },
58508
58509     // private
58510     onBeforeLoad : function(){
58511         if(!this.disabled){
58512             this.el.mask(this.msg, this.msgCls);
58513         }
58514     },
58515
58516     // private
58517     destroy : function(){
58518         if(this.store){
58519             this.store.un('beforeload', this.onBeforeLoad, this);
58520             this.store.un('load', this.onLoad, this);
58521             this.store.un('loadexception', this.onLoadException, this);
58522         }else{
58523             var um = this.el.getUpdateManager();
58524             um.un('beforeupdate', this.onBeforeLoad, this);
58525             um.un('update', this.onLoad, this);
58526             um.un('failure', this.onLoad, this);
58527         }
58528     }
58529 };/*
58530  * Based on:
58531  * Ext JS Library 1.1.1
58532  * Copyright(c) 2006-2007, Ext JS, LLC.
58533  *
58534  * Originally Released Under LGPL - original licence link has changed is not relivant.
58535  *
58536  * Fork - LGPL
58537  * <script type="text/javascript">
58538  */
58539
58540
58541 /**
58542  * @class Roo.XTemplate
58543  * @extends Roo.Template
58544  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58545 <pre><code>
58546 var t = new Roo.XTemplate(
58547         '&lt;select name="{name}"&gt;',
58548                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58549         '&lt;/select&gt;'
58550 );
58551  
58552 // then append, applying the master template values
58553  </code></pre>
58554  *
58555  * Supported features:
58556  *
58557  *  Tags:
58558
58559 <pre><code>
58560       {a_variable} - output encoded.
58561       {a_variable.format:("Y-m-d")} - call a method on the variable
58562       {a_variable:raw} - unencoded output
58563       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58564       {a_variable:this.method_on_template(...)} - call a method on the template object.
58565  
58566 </code></pre>
58567  *  The tpl tag:
58568 <pre><code>
58569         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58570         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58571         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58572         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58573   
58574         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58575         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58576 </code></pre>
58577  *      
58578  */
58579 Roo.XTemplate = function()
58580 {
58581     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58582     if (this.html) {
58583         this.compile();
58584     }
58585 };
58586
58587
58588 Roo.extend(Roo.XTemplate, Roo.Template, {
58589
58590     /**
58591      * The various sub templates
58592      */
58593     tpls : false,
58594     /**
58595      *
58596      * basic tag replacing syntax
58597      * WORD:WORD()
58598      *
58599      * // you can fake an object call by doing this
58600      *  x.t:(test,tesT) 
58601      * 
58602      */
58603     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58604
58605     /**
58606      * compile the template
58607      *
58608      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58609      *
58610      */
58611     compile: function()
58612     {
58613         var s = this.html;
58614      
58615         s = ['<tpl>', s, '</tpl>'].join('');
58616     
58617         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58618             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58619             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58620             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58621             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58622             m,
58623             id     = 0,
58624             tpls   = [];
58625     
58626         while(true == !!(m = s.match(re))){
58627             var forMatch   = m[0].match(nameRe),
58628                 ifMatch   = m[0].match(ifRe),
58629                 execMatch   = m[0].match(execRe),
58630                 namedMatch   = m[0].match(namedRe),
58631                 
58632                 exp  = null, 
58633                 fn   = null,
58634                 exec = null,
58635                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58636                 
58637             if (ifMatch) {
58638                 // if - puts fn into test..
58639                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58640                 if(exp){
58641                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58642                 }
58643             }
58644             
58645             if (execMatch) {
58646                 // exec - calls a function... returns empty if true is  returned.
58647                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58648                 if(exp){
58649                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58650                 }
58651             }
58652             
58653             
58654             if (name) {
58655                 // for = 
58656                 switch(name){
58657                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58658                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58659                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58660                 }
58661             }
58662             var uid = namedMatch ? namedMatch[1] : id;
58663             
58664             
58665             tpls.push({
58666                 id:     namedMatch ? namedMatch[1] : id,
58667                 target: name,
58668                 exec:   exec,
58669                 test:   fn,
58670                 body:   m[1] || ''
58671             });
58672             if (namedMatch) {
58673                 s = s.replace(m[0], '');
58674             } else { 
58675                 s = s.replace(m[0], '{xtpl'+ id + '}');
58676             }
58677             ++id;
58678         }
58679         this.tpls = [];
58680         for(var i = tpls.length-1; i >= 0; --i){
58681             this.compileTpl(tpls[i]);
58682             this.tpls[tpls[i].id] = tpls[i];
58683         }
58684         this.master = tpls[tpls.length-1];
58685         return this;
58686     },
58687     /**
58688      * same as applyTemplate, except it's done to one of the subTemplates
58689      * when using named templates, you can do:
58690      *
58691      * var str = pl.applySubTemplate('your-name', values);
58692      *
58693      * 
58694      * @param {Number} id of the template
58695      * @param {Object} values to apply to template
58696      * @param {Object} parent (normaly the instance of this object)
58697      */
58698     applySubTemplate : function(id, values, parent)
58699     {
58700         
58701         
58702         var t = this.tpls[id];
58703         
58704         
58705         try { 
58706             if(t.test && !t.test.call(this, values, parent)){
58707                 return '';
58708             }
58709         } catch(e) {
58710             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58711             Roo.log(e.toString());
58712             Roo.log(t.test);
58713             return ''
58714         }
58715         try { 
58716             
58717             if(t.exec && t.exec.call(this, values, parent)){
58718                 return '';
58719             }
58720         } catch(e) {
58721             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58722             Roo.log(e.toString());
58723             Roo.log(t.exec);
58724             return ''
58725         }
58726         try {
58727             var vs = t.target ? t.target.call(this, values, parent) : values;
58728             parent = t.target ? values : parent;
58729             if(t.target && vs instanceof Array){
58730                 var buf = [];
58731                 for(var i = 0, len = vs.length; i < len; i++){
58732                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58733                 }
58734                 return buf.join('');
58735             }
58736             return t.compiled.call(this, vs, parent);
58737         } catch (e) {
58738             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58739             Roo.log(e.toString());
58740             Roo.log(t.compiled);
58741             return '';
58742         }
58743     },
58744
58745     compileTpl : function(tpl)
58746     {
58747         var fm = Roo.util.Format;
58748         var useF = this.disableFormats !== true;
58749         var sep = Roo.isGecko ? "+" : ",";
58750         var undef = function(str) {
58751             Roo.log("Property not found :"  + str);
58752             return '';
58753         };
58754         
58755         var fn = function(m, name, format, args)
58756         {
58757             //Roo.log(arguments);
58758             args = args ? args.replace(/\\'/g,"'") : args;
58759             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58760             if (typeof(format) == 'undefined') {
58761                 format= 'htmlEncode';
58762             }
58763             if (format == 'raw' ) {
58764                 format = false;
58765             }
58766             
58767             if(name.substr(0, 4) == 'xtpl'){
58768                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58769             }
58770             
58771             // build an array of options to determine if value is undefined..
58772             
58773             // basically get 'xxxx.yyyy' then do
58774             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58775             //    (function () { Roo.log("Property not found"); return ''; })() :
58776             //    ......
58777             
58778             var udef_ar = [];
58779             var lookfor = '';
58780             Roo.each(name.split('.'), function(st) {
58781                 lookfor += (lookfor.length ? '.': '') + st;
58782                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58783             });
58784             
58785             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58786             
58787             
58788             if(format && useF){
58789                 
58790                 args = args ? ',' + args : "";
58791                  
58792                 if(format.substr(0, 5) != "this."){
58793                     format = "fm." + format + '(';
58794                 }else{
58795                     format = 'this.call("'+ format.substr(5) + '", ';
58796                     args = ", values";
58797                 }
58798                 
58799                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58800             }
58801              
58802             if (args.length) {
58803                 // called with xxyx.yuu:(test,test)
58804                 // change to ()
58805                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58806             }
58807             // raw.. - :raw modifier..
58808             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58809             
58810         };
58811         var body;
58812         // branched to use + in gecko and [].join() in others
58813         if(Roo.isGecko){
58814             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58815                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58816                     "';};};";
58817         }else{
58818             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58819             body.push(tpl.body.replace(/(\r\n|\n)/g,
58820                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58821             body.push("'].join('');};};");
58822             body = body.join('');
58823         }
58824         
58825         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58826        
58827         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58828         eval(body);
58829         
58830         return this;
58831     },
58832
58833     applyTemplate : function(values){
58834         return this.master.compiled.call(this, values, {});
58835         //var s = this.subs;
58836     },
58837
58838     apply : function(){
58839         return this.applyTemplate.apply(this, arguments);
58840     }
58841
58842  });
58843
58844 Roo.XTemplate.from = function(el){
58845     el = Roo.getDom(el);
58846     return new Roo.XTemplate(el.value || el.innerHTML);
58847 };